commit 313c337a23b4ada012109aef21e6cb2eb73e8332
parent 1ca3897e4a6b5f0c06b0308583e65ee9cbb25d0e
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Sun, 20 Sep 2026 15:16:16 -0300
c-lib: update libwebp to 1.6.0
Diffstat:
301 files changed, 27403 insertions(+), 13272 deletions(-)
diff --git a/c-lib/.cmake-format.py b/c-lib/.cmake-format.py
@@ -0,0 +1,240 @@
+# ----------------------------------
+# Options affecting listfile parsing
+# ----------------------------------
+with section("parse"):
+
+ # Specify structure for custom cmake functions
+ additional_commands = { 'foo': { 'flags': ['BAR', 'BAZ'],
+ 'kwargs': {'DEPENDS': '*', 'HEADERS': '*', 'SOURCES': '*'}}}
+
+ # Override configurations per-command where available
+ override_spec = {}
+
+ # Specify variable tags.
+ vartags = []
+
+ # Specify property tags.
+ proptags = []
+
+# -----------------------------
+# Options affecting formatting.
+# -----------------------------
+with section("format"):
+
+ # Disable formatting entirely, making cmake-format a no-op
+ disable = False
+
+ # How wide to allow formatted cmake files
+ line_width = 80
+
+ # How many spaces to tab for indent
+ tab_size = 2
+
+ # If true, lines are indented using tab characters (utf-8 0x09) instead of
+ # <tab_size> space characters (utf-8 0x20). In cases where the layout would
+ # require a fractional tab character, the behavior of the fractional
+ # indentation is governed by <fractional_tab_policy>
+ use_tabchars = False
+
+ # If <use_tabchars> is True, then the value of this variable indicates how
+ # fractional indentions are handled during whitespace replacement. If set to
+ # 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set
+ # to `round-up` fractional indentation is replaced with a single tab character
+ # (utf-8 0x09) effectively shifting the column to the next tabstop
+ fractional_tab_policy = 'use-space'
+
+ # If an argument group contains more than this many sub-groups (parg or kwarg
+ # groups) then force it to a vertical layout.
+ max_subgroups_hwrap = 3
+
+ # If a positional argument group contains more than this many arguments, then
+ # force it to a vertical layout.
+ max_pargs_hwrap = 6
+
+ # If a cmdline positional group consumes more than this many lines without
+ # nesting, then invalidate the layout (and nest)
+ max_rows_cmdline = 2
+
+ # If true, separate flow control names from their parentheses with a space
+ separate_ctrl_name_with_space = False
+
+ # If true, separate function names from parentheses with a space
+ separate_fn_name_with_space = False
+
+ # If a statement is wrapped to more than one line, than dangle the closing
+ # parenthesis on its own line.
+ dangle_parens = False
+
+ # If the trailing parenthesis must be 'dangled' on its on line, then align it
+ # to this reference: `prefix`: the start of the statement, `prefix-indent`:
+ # the start of the statement, plus one indentation level, `child`: align to
+ # the column of the arguments
+ dangle_align = 'prefix'
+
+ # If the statement spelling length (including space and parenthesis) is
+ # smaller than this amount, then force reject nested layouts.
+ min_prefix_chars = 4
+
+ # If the statement spelling length (including space and parenthesis) is larger
+ # than the tab width by more than this amount, then force reject un-nested
+ # layouts.
+ max_prefix_chars = 10
+
+ # If a candidate layout is wrapped horizontally but it exceeds this many
+ # lines, then reject the layout.
+ max_lines_hwrap = 2
+
+ # What style line endings to use in the output.
+ line_ending = 'unix'
+
+ # Format command names consistently as 'lower' or 'upper' case
+ command_case = 'canonical'
+
+ # Format keywords consistently as 'lower' or 'upper' case
+ keyword_case = 'unchanged'
+
+ # A list of command names which should always be wrapped
+ always_wrap = []
+
+ # If true, the argument lists which are known to be sortable will be sorted
+ # lexicographicall
+ enable_sort = True
+
+ # If true, the parsers may infer whether or not an argument list is sortable
+ # (without annotation).
+ autosort = False
+
+ # By default, if cmake-format cannot successfully fit everything into the
+ # desired linewidth it will apply the last, most agressive attempt that it
+ # made. If this flag is True, however, cmake-format will print error, exit
+ # with non-zero status code, and write-out nothing
+ require_valid_layout = False
+
+ # A dictionary mapping layout nodes to a list of wrap decisions. See the
+ # documentation for more information.
+ layout_passes = {}
+
+# ------------------------------------------------
+# Options affecting comment reflow and formatting.
+# ------------------------------------------------
+with section("markup"):
+
+ # What character to use for bulleted lists
+ bullet_char = '*'
+
+ # What character to use as punctuation after numerals in an enumerated list
+ enum_char = '.'
+
+ # If comment markup is enabled, don't reflow the first comment block in each
+ # listfile. Use this to preserve formatting of your copyright/license
+ # statements.
+ first_comment_is_literal = True
+
+ # If comment markup is enabled, don't reflow any comment block which matches
+ # this (regex) pattern. Default is `None` (disabled).
+ literal_comment_pattern = None
+
+ # Regular expression to match preformat fences in comments default=
+ # ``r'^\s*([`~]{3}[`~]*)(.*)$'``
+ fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$'
+
+ # Regular expression to match rulers in comments default=
+ # ``r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'``
+ ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$'
+
+ # If a comment line matches starts with this pattern then it is explicitly a
+ # trailing comment for the preceeding argument. Default is '#<'
+ explicit_trailing_pattern = '#<'
+
+ # If a comment line starts with at least this many consecutive hash
+ # characters, then don't lstrip() them off. This allows for lazy hash rulers
+ # where the first hash char is not separated by space
+ hashruler_min_length = 10
+
+ # If true, then insert a space between the first hash char and remaining hash
+ # chars in a hash ruler, and normalize its length to fill the column
+ canonicalize_hashrulers = True
+
+ # enable comment markup parsing and reflow
+ enable_markup = True
+
+# ----------------------------
+# Options affecting the linter
+# ----------------------------
+with section("lint"):
+
+ # a list of lint codes to disable
+ disabled_codes = []
+
+ # regular expression pattern describing valid function names
+ function_pattern = '[0-9a-z_]+'
+
+ # regular expression pattern describing valid macro names
+ macro_pattern = '[0-9A-Z_]+'
+
+ # regular expression pattern describing valid names for variables with global
+ # (cache) scope
+ global_var_pattern = '[A-Z][0-9A-Z_]+'
+
+ # regular expression pattern describing valid names for variables with global
+ # scope (but internal semantic)
+ internal_var_pattern = '_[A-Z][0-9A-Z_]+'
+
+ # regular expression pattern describing valid names for variables with local
+ # scope
+ local_var_pattern = '[a-z][a-z0-9_]+'
+
+ # regular expression pattern describing valid names for privatedirectory
+ # variables
+ private_var_pattern = '_[0-9a-z_]+'
+
+ # regular expression pattern describing valid names for public directory
+ # variables
+ public_var_pattern = '[A-Z][0-9A-Z_]+'
+
+ # regular expression pattern describing valid names for function/macro
+ # arguments and loop variables.
+ argument_var_pattern = '[a-z][a-z0-9_]+'
+
+ # regular expression pattern describing valid names for keywords used in
+ # functions or macros
+ keyword_pattern = '[A-Z][0-9A-Z_]+'
+
+ # In the heuristic for C0201, how many conditionals to match within a loop in
+ # before considering the loop a parser.
+ max_conditionals_custom_parser = 2
+
+ # Require at least this many newlines between statements
+ min_statement_spacing = 1
+
+ # Require no more than this many newlines between statements
+ max_statement_spacing = 2
+ max_returns = 6
+ max_branches = 12
+ max_arguments = 5
+ max_localvars = 15
+ max_statements = 50
+
+# -------------------------------
+# Options affecting file encoding
+# -------------------------------
+with section("encode"):
+
+ # If true, emit the unicode byte-order mark (BOM) at the start of the file
+ emit_byteorder_mark = False
+
+ # Specify the encoding of the input file. Defaults to utf-8
+ input_encoding = 'utf-8'
+
+ # Specify the encoding of the output file. Defaults to utf-8. Note that cmake
+ # only claims to support utf-8 so be careful when using anything else
+ output_encoding = 'utf-8'
+
+# -------------------------------------
+# Miscellaneous configurations options.
+# -------------------------------------
+with section("misc"):
+
+ # A dictionary containing any per-command configuration overrides. Currently
+ # only `command_case` is supported.
+ per_command = {}
diff --git a/c-lib/.gitignore b/c-lib/.gitignore
@@ -3,7 +3,9 @@
*.pc
.DS_Store
.deps
+.idea
.libs
+.vscode
/aclocal.m4
/ar-lib
/autom4te.cache
@@ -50,5 +52,6 @@ tests/fuzzer/animdecoder_fuzzer
tests/fuzzer/animencoder_fuzzer
tests/fuzzer/demux_api_fuzzer
tests/fuzzer/enc_dec_fuzzer
+tests/fuzzer/huffman_fuzzer
tests/fuzzer/mux_demux_api_fuzzer
tests/fuzzer/simple_api_fuzzer
diff --git a/c-lib/.mailmap b/c-lib/.mailmap
@@ -8,8 +8,13 @@ Vikas Arora <vikasa@google.com>
<vikasa@google.com> <vikaas.arora@gmail.com>
<slobodan.prijic@imgtec.com> <Slobodan.Prijic@imgtec.com>
<vrabaud@google.com> <vincent.rabaud@gmail.com>
+Vincent Rabaud <vrabaud@google.com>
Tamar Levy <tamar.levy@intel.com>
<qrczak@google.com> <qrczak>
Hui Su <huisu@google.com>
James Zern <jzern@google.com>
Roberto Alanis <alanisbaez@google.com>
+Brian Ledger <brianpl@google.com>
+Maryla Ustarroz-Calonge <maryla@google.com>
+Yannis Guyon <yguyon@google.com>
+Henner Zeller <hzeller@google.com> <h.zeller@acm.org>
diff --git a/c-lib/.style.yapf b/c-lib/.style.yapf
@@ -1,2 +1,2 @@
[style]
-based_on_style = chromium
-\ No newline at end of file
+based_on_style = yapf
+\ No newline at end of file
diff --git a/c-lib/AUTHORS b/c-lib/AUTHORS
@@ -1,19 +1,32 @@
Contributors:
- Aidan O'Loan (aidanol at gmail dot com)
- Alan Browning (browning at google dot com)
+- Alexandru Ardelean (ardeleanalex at gmail dot com)
+- Anuraag Agrawal (anuraaga at gmail dot com)
+- Arthur Eubanks (aeubanks at google dot com)
+- Brian Ledger (brianpl at google dot com)
- Charles Munger (clm at google dot com)
- Cheng Yi (cyi at google dot com)
- Christian Duvivier (cduvivier at google dot com)
- Christopher Degawa (ccom at randomderp dot com)
- Clement Courbet (courbet at google dot com)
+- devtools-clrobot at google dot com (devtools-clrobot@google dot com)
- Djordje Pesut (djordje dot pesut at imgtec dot com)
+- Frank (1433351828 at qq dot com)
+- Frank Barchard (fbarchard at google dot com)
+- Henner Zeller (hzeller at google dot com)
- Hui Su (huisu at google dot com)
+- H. Vetinari (h dot vetinari at gmx dot com)
- Ilya Kurdyukov (jpegqs at gmail dot com)
- Ingvar Stepanyan (rreverser at google dot com)
+- Istvan Stefan (Istvan dot Stefan at arm dot com)
- James Zern (jzern at google dot com)
- Jan Engelhardt (jengelh at medozas dot de)
- Jehan (jehan at girinstud dot io)
+- Jeremy Maitin-Shepard (jbms at google dot com)
- Johann Koenig (johann dot koenig at duck dot com)
+- Jonathan Grant (jgrantinfotech at gmail dot com)
+- Jonliu1993 (13720414433 at 163 dot com)
- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
- Jyrki Alakuijala (jyrki at google dot com)
- Konstantin Ivlev (tomskside at gmail dot com)
@@ -22,12 +35,17 @@ Contributors:
- Mans Rullgard (mans at mansr dot com)
- Marcin Kowalczyk (qrczak at google dot com)
- Martin Olsson (mnemo at minimum dot se)
+- Maryla Ustarroz-Calonge (maryla at google dot com)
+- Masahiro Hanada (hanada at atmark-techno dot com)
- Mikołaj Zalewski (mikolajz at google dot com)
- Mislav Bradac (mislavm at google dot com)
+- natewood (natewood at fb dot com)
- Nico Weber (thakis at chromium dot org)
- Noel Chromium (noel at chromium dot org)
+- Nozomi Isozaki (nontan at pixiv dot co dot jp)
- Oliver Wolff (oliver dot wolff at qt dot io)
- Owen Rodley (orodley at google dot com)
+- Ozkan Sezer (sezeroz at gmail dot com)
- Parag Salasakar (img dot mips1 at gmail dot com)
- Pascal Massimino (pascal dot massimino at gmail dot com)
- Paweł Hajdan, Jr (phajdan dot jr at chromium dot org)
@@ -41,12 +59,14 @@ Contributors:
- Somnath Banerjee (somnath dot banerjee at gmail dot com)
- Sriraman Tallam (tmsriram at google dot com)
- Tamar Levy (tamar dot levy at intel dot com)
+- Thiago Perrotta (tperrotta at google dot com)
- Timothy Gu (timothygu99 at gmail dot com)
- Urvang Joshi (urvang at google dot com)
- Vikas Arora (vikasa at google dot com)
- Vincent Rabaud (vrabaud at google dot com)
- Vlad Tsyrklevich (vtsyrklevich at chromium dot org)
- Wan-Teh Chang (wtc at google dot com)
+- wrv (wrv at utexas dot edu)
- Yang Zhang (yang dot zhang at arm dot com)
- Yannis Guyon (yguyon at google dot com)
- Zhi An Ng (zhin at chromium dot org)
diff --git a/c-lib/Android.mk b/c-lib/Android.mk
@@ -1,3 +1,5 @@
+# Ignore this file during non-NDK builds.
+ifdef NDK_ROOT
LOCAL_PATH := $(call my-dir)
WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD
@@ -33,6 +35,15 @@ else
NEON := c
endif
+sharpyuv_srcs := \
+ sharpyuv/sharpyuv.c \
+ sharpyuv/sharpyuv_cpu.c \
+ sharpyuv/sharpyuv_csp.c \
+ sharpyuv/sharpyuv_dsp.c \
+ sharpyuv/sharpyuv_gamma.c \
+ sharpyuv/sharpyuv_neon.$(NEON) \
+ sharpyuv/sharpyuv_sse2.c \
+
dec_srcs := \
src/dec/alpha_dec.c \
src/dec/buffer_dec.c \
@@ -153,6 +164,7 @@ utils_dec_srcs := \
src/utils/color_cache_utils.c \
src/utils/filters_utils.c \
src/utils/huffman_utils.c \
+ src/utils/palette.c \
src/utils/quant_levels_dec_utils.c \
src/utils/random_utils.c \
src/utils/rescaler_utils.c \
@@ -204,12 +216,13 @@ endif # ENABLE_SHARED=1
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
+ $(sharpyuv_srcs) \
$(dsp_enc_srcs) \
$(enc_srcs) \
$(utils_enc_srcs) \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
-LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src $(LOCAL_PATH)
# prefer arm over thumb mode for performance gains
LOCAL_ARM_MODE := arm
@@ -279,3 +292,4 @@ include $(WEBP_SRC_PATH)/examples/Android.mk
ifeq ($(USE_CPUFEATURES),yes)
$(call import-module,android/cpufeatures)
endif
+endif # NDK_ROOT
diff --git a/c-lib/CMakeLists.txt b/c-lib/CMakeLists.txt
@@ -6,15 +6,23 @@
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
-cmake_minimum_required(VERSION 3.7)
-
-if(POLICY CMP0072)
- cmake_policy(SET CMP0072 NEW)
+if(APPLE)
+ cmake_minimum_required(VERSION 3.17)
+else()
+ cmake_minimum_required(VERSION 3.16)
endif()
project(WebP C)
# Options for coder / decoder executables.
+if(BUILD_SHARED_LIBS)
+ set(WEBP_LINK_STATIC_DEFAULT OFF)
+else()
+ set(WEBP_LINK_STATIC_DEFAULT ON)
+endif()
+option(WEBP_LINK_STATIC
+ "Link using static libraries. If OFF, use dynamic libraries."
+ ${WEBP_LINK_STATIC_DEFAULT})
if(NOT EMSCRIPTEN)
# Disable SIMD on Emscripten by default, as it's a new unstable Wasm feature.
# Users can still explicitly opt-in to make a SIMD-enabled build.
@@ -33,12 +41,27 @@ option(WEBP_BUILD_LIBWEBPMUX "Build the libwebpmux library." ON)
option(WEBP_BUILD_WEBPMUX "Build the webpmux command line tool." ON)
option(WEBP_BUILD_EXTRAS "Build extras." ON)
option(WEBP_BUILD_WEBP_JS "Emscripten build of webp.js." OFF)
+option(WEBP_BUILD_FUZZTEST "Build the fuzztest tests." OFF)
option(WEBP_USE_THREAD "Enable threading support" ON)
option(WEBP_NEAR_LOSSLESS "Enable near-lossless encoding" ON)
option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces."
OFF)
set(WEBP_BITTRACE "0" CACHE STRING "Bit trace mode (0=none, 1=bit, 2=bytes)")
set_property(CACHE WEBP_BITTRACE PROPERTY STRINGS 0 1 2)
+option(WEBP_ENABLE_WUNUSED_RESULT "Add [[nodiscard]] to some functions. \
+ CMake must be at least 3.21 to force C23" OFF)
+
+if(WEBP_LINK_STATIC)
+ if(WIN32)
+ set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
+ else()
+ set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
+ endif()
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+ # vwebp does not compile on Ubuntu with static libraries so disabling it for
+ # now.
+ set(WEBP_BUILD_VWEBP OFF)
+endif()
# Option needed for handling Unicode file names on Windows.
if(WIN32)
@@ -58,23 +81,51 @@ if(WEBP_BUILD_WEBP_JS)
set(WEBP_USE_THREAD OFF)
if(WEBP_ENABLE_SIMD)
- message("wasm2js does not support SIMD, disabling webp.js generation.")
+ message(NOTICE
+ "wasm2js does not support SIMD, disabling webp.js generation.")
+ endif()
+
+ if(NOT EMSCRIPTEN_VERSION)
+ message(
+ WARNING
+ "EMSCRIPTEN_VERSION not detected!\n"
+ "WEBP_BUILD_WEBP_JS is only supported with emcmake/emmake.\n"
+ "The build may fail if those tools are not used. See webp_js/README.md."
+ )
endif()
endif()
+set(SHARPYUV_DEP_LIBRARIES)
+set(SHARPYUV_DEP_INCLUDE_DIRS)
set(WEBP_DEP_LIBRARIES)
set(WEBP_DEP_INCLUDE_DIRS)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release"
CACHE STRING "Build type: Release, Debug, MinSizeRel or RelWithDebInfo"
- FORCE)
+ FORCE)
endif()
# Include dependencies.
+if(WEBP_BUILD_ANIM_UTILS
+ OR WEBP_BUILD_CWEBP
+ OR WEBP_BUILD_DWEBP
+ OR WEBP_BUILD_EXTRAS
+ OR WEBP_BUILD_GIF2WEBP
+ OR WEBP_BUILD_IMG2WEBP)
+ set(WEBP_FIND_IMG_LIBS TRUE)
+else()
+ set(WEBP_FIND_IMG_LIBS FALSE)
+endif()
include(cmake/deps.cmake)
include(GNUInstallDirs)
+if(BUILD_SHARED_LIBS AND NOT DEFINED CMAKE_INSTALL_RPATH)
+ # Set the rpath to match autoconf/libtool behavior. Note this must be set
+ # before target creation.
+ set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+endif()
+
# ##############################################################################
# Options.
if(WEBP_ENABLE_SWAP_16BIT_CSP)
@@ -90,24 +141,63 @@ if(WEBP_UNICODE)
add_definitions(-DUNICODE -D_UNICODE)
endif()
+if(WIN32 AND BUILD_SHARED_LIBS)
+ add_definitions(-DWEBP_DLL)
+endif()
+
+# pkg-config variables used by *.pc.in.
set(prefix ${CMAKE_INSTALL_PREFIX})
-set(exec_prefix "\$\{prefix\}")
-set(libdir "\$\{prefix\}/lib")
-set(includedir "\$\{prefix\}/include")
+set(exec_prefix "\${prefix}")
+if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
+ set(libdir "${CMAKE_INSTALL_LIBDIR}")
+else()
+ set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+endif()
+if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
+ set(includedir "${CMAKE_INSTALL_INCLUDEDIR}")
+else()
+ set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+endif()
set(PTHREAD_LIBS ${CMAKE_THREAD_LIBS_INIT})
set(INSTALLED_LIBRARIES)
+if(MSVC)
+ # match the naming convention used by nmake
+ set(webp_libname_prefix "lib")
+ set(CMAKE_SHARED_LIBRARY_PREFIX "${webp_libname_prefix}")
+ set(CMAKE_IMPORT_LIBRARY_PREFIX "${webp_libname_prefix}")
+ set(CMAKE_STATIC_LIBRARY_PREFIX "${webp_libname_prefix}")
+endif()
+
+if(NOT WIN32)
+ set(CMAKE_C_VISIBILITY_PRESET hidden)
+endif()
+
+if(WEBP_ENABLE_WUNUSED_RESULT)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21.0)
+ set(CMAKE_C_STANDARD 23)
+ else()
+ unset(CMAKE_C_STANDARD)
+ add_compile_options($<$<COMPILE_LANGUAGE:C>:-std=gnu2x>)
+ endif()
+ add_compile_options(-Wunused-result)
+ add_definitions(-DWEBP_ENABLE_NODISCARD=1)
+endif()
+
# ##############################################################################
# Android only.
if(ANDROID)
include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
- add_library(cpufeatures STATIC
+ add_library(cpufeatures-webp STATIC
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
- list(APPEND INSTALLED_LIBRARIES cpufeatures)
- target_link_libraries(cpufeatures dl)
- set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} cpufeatures)
- set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS}
- ${ANDROID_NDK}/sources/android/cpufeatures)
+ list(APPEND INSTALLED_LIBRARIES cpufeatures-webp)
+ target_link_libraries(cpufeatures-webp dl)
+ set(SHARPYUV_DEP_LIBRARIES ${SHARPYUV_DEP_LIBRARIES} cpufeatures-webp)
+ set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} cpufeatures-webp)
+ set(cpufeatures_include_dir ${ANDROID_NDK}/sources/android/cpufeatures)
+ set(SHARPYUV_DEP_INCLUDE_DIRS ${SHARPYUV_DEP_INCLUDE_DIRS}
+ ${cpufeatures_include_dir})
+ set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS} ${cpufeatures_include_dir})
add_definitions(-DHAVE_CPU_FEATURES_H=1)
set(HAVE_CPU_FEATURES_H 1)
else()
@@ -116,7 +206,7 @@ endif()
function(configure_pkg_config FILE)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${FILE}.in"
- "${CMAKE_CURRENT_BINARY_DIR}/${FILE}")
+ "${CMAKE_CURRENT_BINARY_DIR}/${FILE}" @ONLY)
if(HAVE_MATH_LIBRARY)
# MSVC doesn't have libm
@@ -125,10 +215,8 @@ function(configure_pkg_config FILE)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${FILE} ${data})
endif()
- install(
- FILES "${CMAKE_CURRENT_BINARY_DIR}/${FILE}"
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
- )
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${FILE}"
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endfunction()
# ##############################################################################
@@ -138,23 +226,15 @@ endfunction()
# E.g.: libimagedec_la_SOURCES = image_dec.c image_dec.h
function(parse_Makefile_am FOLDER VAR SRC_REGEX)
file(READ ${FOLDER}/Makefile.am MAKEFILE_AM)
- string(REGEX MATCHALL
- "${SRC_REGEX}_SOURCES[ ]*\\+?=[ ]+[0-9a-z\\._ ]*"
- FILES_PER_LINE
- ${MAKEFILE_AM})
+ string(REGEX MATCHALL "${SRC_REGEX}_SOURCES[ ]*\\+?=[ ]+[0-9a-z\\._ ]*"
+ FILES_PER_LINE ${MAKEFILE_AM})
set(SRCS ${${VAR}})
foreach(FILES ${FILES_PER_LINE})
string(FIND ${FILES} "=" OFFSET)
math(EXPR OFFSET "${OFFSET} + 2")
- string(SUBSTRING ${FILES}
- ${OFFSET}
- -1
- FILES)
+ string(SUBSTRING ${FILES} ${OFFSET} -1 FILES)
if(FILES)
- string(REGEX MATCHALL
- "[0-9a-z\\._]+"
- FILES
- ${FILES})
+ string(REGEX MATCHALL "[0-9a-z\\._]+" FILES ${FILES})
foreach(FILE ${FILES})
list(APPEND SRCS ${FOLDER}/${FILE})
endforeach()
@@ -183,9 +263,52 @@ endforeach()
# Generate the config.h file.
configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in
- ${CMAKE_CURRENT_BINARY_DIR}/src/webp/config.h)
+ ${CMAKE_CURRENT_BINARY_DIR}/src/webp/config.h @ONLY)
add_definitions(-DHAVE_CONFIG_H)
+# Set the version numbers.
+macro(set_version FILE TARGET_NAME NAME_IN_MAKEFILE)
+ file(READ ${CMAKE_CURRENT_SOURCE_DIR}/${FILE} SOURCE_FILE)
+ string(REGEX MATCH
+ "${NAME_IN_MAKEFILE}_la_LDFLAGS[^\n]* -version-info [0-9:]+" TMP
+ ${SOURCE_FILE})
+ string(REGEX MATCH "[0-9:]+" TMP ${TMP})
+ string(REGEX REPLACE ":" " " LT_VERSION ${TMP})
+
+ # See the libtool docs for more information:
+ # https://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
+ #
+ # c=<current>, a=<age>, r=<revision>
+ #
+ # libtool generates a .so file as .so.[c-a].a.r, while -version-info c:r:a is
+ # passed to libtool.
+ #
+ # We set FULL = [c-a].a.r and MAJOR = [c-a].
+ separate_arguments(LT_VERSION)
+ list(GET LT_VERSION 0 LT_CURRENT)
+ list(GET LT_VERSION 1 LT_REVISION)
+ list(GET LT_VERSION 2 LT_AGE)
+ math(EXPR LT_CURRENT_MINUS_AGE "${LT_CURRENT} - ${LT_AGE}")
+
+ set_target_properties(
+ ${TARGET_NAME}
+ PROPERTIES VERSION ${LT_CURRENT_MINUS_AGE}.${LT_AGE}.${LT_REVISION}
+ SOVERSION ${LT_CURRENT_MINUS_AGE})
+ if(APPLE)
+ # For compatibility, set MACHO_COMPATIBILITY_VERSION and
+ # MACHO_CURRENT_VERSION to match libtool. These properties were introduced
+ # in 3.17:
+ # https://cmake.org/cmake/help/latest/prop_tgt/MACHO_COMPATIBILITY_VERSION.html
+ math(EXPR LIBWEBP_MACHO_COMPATIBILITY_VERSION "${LT_CURRENT} + 1")
+ set_target_properties(
+ ${TARGET_NAME}
+ PROPERTIES MACHO_COMPATIBILITY_VERSION
+ ${LIBWEBP_MACHO_COMPATIBILITY_VERSION}
+ MACHO_CURRENT_VERSION
+ ${LIBWEBP_MACHO_COMPATIBILITY_VERSION}.${LT_REVISION})
+ endif()
+endmacro()
+
# ##############################################################################
# Build the webpdecoder library.
@@ -194,13 +317,12 @@ add_definitions(-DHAVE_CONFIG_H)
#
# See also:
# https://cmake.org/cmake/help/v3.18/command/add_library.html#object-libraries
-# "Some native build systems (such as Xcode) may not like targets that have
-# only object files, so consider adding at least one real source file to any
-# target that references $<TARGET_OBJECTS:objlib>."
+# "Some native build systems (such as Xcode) may not like targets that have only
+# object files, so consider adding at least one real source file to any target
+# that references $<TARGET_OBJECTS:objlib>."
function(libwebp_add_stub_file TARGET)
set(stub_source_dir "${CMAKE_BINARY_DIR}")
- set(stub_source_file
- "${stub_source_dir}/libwebp_${TARGET}_stub.c")
+ set(stub_source_file "${stub_source_dir}/libwebp_${TARGET}_stub.c")
set(stub_source_code
"// Generated file. DO NOT EDIT!\n"
"// C source file created for target ${TARGET}.\n"
@@ -211,32 +333,49 @@ function(libwebp_add_stub_file TARGET)
target_sources(${TARGET} PRIVATE ${stub_source_file})
endfunction()
+parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv "WEBP_SHARPYUV_SRCS" "")
+add_library(sharpyuv ${WEBP_SHARPYUV_SRCS})
+target_link_libraries(sharpyuv ${SHARPYUV_DEP_LIBRARIES})
+set_version(sharpyuv/Makefile.am sharpyuv sharpyuv)
+target_include_directories(
+ sharpyuv PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/src)
+set_target_properties(
+ sharpyuv
+ PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv/sharpyuv.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv/sharpyuv_csp.h")
+configure_pkg_config("sharpyuv/libsharpyuv.pc")
+install(
+ TARGETS sharpyuv
+ EXPORT ${PROJECT_NAME}Targets
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webp/sharpyuv
+ INCLUDES
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+ ${CMAKE_INSTALL_INCLUDEDIR}/webp
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
if(MSVC)
# avoid security warnings for e.g., fopen() used in the examples.
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
else()
- add_definitions(-Wall)
+ add_compile_options(-Wall)
endif()
include_directories(${WEBP_DEP_INCLUDE_DIRS})
add_library(webpdecode OBJECT ${WEBP_DEC_SRCS})
-target_include_directories(webpdecode
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(webpdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR})
add_library(webpdspdecode OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS})
-target_include_directories(webpdspdecode
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR})
-add_library(webputilsdecode
- OBJECT
- ${WEBP_UTILS_COMMON_SRCS}
- ${WEBP_UTILS_DEC_SRCS})
-target_include_directories(webputilsdecode
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR})
-add_library(webpdecoder
- $<TARGET_OBJECTS:webpdecode>
- $<TARGET_OBJECTS:webpdspdecode>
- $<TARGET_OBJECTS:webputilsdecode>)
+target_include_directories(webpdspdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(webputilsdecode OBJECT ${WEBP_UTILS_COMMON_SRCS}
+ ${WEBP_UTILS_DEC_SRCS})
+target_include_directories(webputilsdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(
+ webpdecoder $<TARGET_OBJECTS:webpdecode> $<TARGET_OBJECTS:webpdspdecode>
+ $<TARGET_OBJECTS:webputilsdecode>)
if(XCODE)
libwebp_add_stub_file(webpdecoder)
endif()
@@ -244,8 +383,9 @@ target_link_libraries(webpdecoder ${WEBP_DEP_LIBRARIES})
target_include_directories(
webpdecoder
PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
- INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
- $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+ INTERFACE
+ "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR}>"
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
set_target_properties(
webpdecoder
PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
@@ -255,38 +395,26 @@ configure_pkg_config("src/libwebpdecoder.pc")
# Build the webp library.
add_library(webpencode OBJECT ${WEBP_ENC_SRCS})
-target_include_directories(webpencode
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR})
-add_library(webpdsp
- OBJECT
- ${WEBP_DSP_COMMON_SRCS}
- ${WEBP_DSP_DEC_SRCS}
- ${WEBP_DSP_ENC_SRCS})
-target_include_directories(webpdsp
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR})
-add_library(webputils
- OBJECT
- ${WEBP_UTILS_COMMON_SRCS}
- ${WEBP_UTILS_DEC_SRCS}
- ${WEBP_UTILS_ENC_SRCS})
-target_include_directories(webputils
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR})
-add_library(webp
- $<TARGET_OBJECTS:webpdecode>
- $<TARGET_OBJECTS:webpdsp>
- $<TARGET_OBJECTS:webpencode>
- $<TARGET_OBJECTS:webputils>)
+target_include_directories(
+ webpencode PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/src)
+add_library(webpdsp OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS}
+ ${WEBP_DSP_ENC_SRCS})
+target_include_directories(webpdsp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(webputils OBJECT ${WEBP_UTILS_COMMON_SRCS} ${WEBP_UTILS_DEC_SRCS}
+ ${WEBP_UTILS_ENC_SRCS})
+target_include_directories(webputils PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(webp $<TARGET_OBJECTS:webpdecode> $<TARGET_OBJECTS:webpdsp>
+ $<TARGET_OBJECTS:webpencode> $<TARGET_OBJECTS:webputils>)
+target_link_libraries(webp sharpyuv)
if(XCODE)
libwebp_add_stub_file(webp)
endif()
target_link_libraries(webp ${WEBP_DEP_LIBRARIES})
target_include_directories(
- webp
- PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_BINARY_DIR}
+ webp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include>)
set_target_properties(
@@ -297,82 +425,33 @@ ${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h")
# Make sure the OBJECT libraries are built with position independent code (it is
# not ON by default).
-set_target_properties(webpdecode
- webpdspdecode
- webputilsdecode
- webpencode
- webpdsp
- webputils
- PROPERTIES POSITION_INDEPENDENT_CODE ON)
+set_target_properties(webpdecode webpdspdecode webputilsdecode webpencode
+ webpdsp webputils PROPERTIES POSITION_INDEPENDENT_CODE ON)
configure_pkg_config("src/libwebp.pc")
# Build the webp demux library.
add_library(webpdemux ${WEBP_DEMUX_SRCS})
target_link_libraries(webpdemux webp)
-target_include_directories(webpdemux
- PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_BINARY_DIR}
- PUBLIC $<INSTALL_INTERFACE:include>)
+target_include_directories(
+ webpdemux PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+ PUBLIC $<INSTALL_INTERFACE:include>)
set_target_properties(
webpdemux
- PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
+ PROPERTIES
+ PUBLIC_HEADER
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
${CMAKE_CURRENT_SOURCE_DIR}/src/webp/demux.h;\
${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h;\
${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h")
configure_pkg_config("src/demux/libwebpdemux.pc")
-# Set the version numbers.
-macro(set_version FILE TARGET_NAME NAME_IN_MAKEFILE)
- file(READ ${CMAKE_CURRENT_SOURCE_DIR}/src/${FILE} SOURCE_FILE)
- string(REGEX MATCH
- "${NAME_IN_MAKEFILE}_la_LDFLAGS[^\n]* -version-info [0-9:]+"
- TMP
- ${SOURCE_FILE})
- string(REGEX MATCH
- "[0-9:]+"
- TMP
- ${TMP})
- string(REGEX
- REPLACE ":"
- " "
- LT_VERSION
- ${TMP})
-
- # See the libtool docs for more information:
- # https://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
- #
- # c=<current>, a=<age>, r=<revision>
- #
- # libtool generates a .so file as .so.[c-a].a.r, while -version-info c:r:a is
- # passed to libtool.
- #
- # We set FULL = [c-a].a.r and MAJOR = [c-a].
- separate_arguments(LT_VERSION)
- list(GET LT_VERSION 0 LT_CURRENT)
- list(GET LT_VERSION 1 LT_REVISION)
- list(GET LT_VERSION 2 LT_AGE)
- math(EXPR LT_CURRENT_MINUS_AGE "${LT_CURRENT} - ${LT_AGE}")
-
- set_target_properties(
- ${TARGET_NAME}
- PROPERTIES VERSION
- ${LT_CURRENT_MINUS_AGE}.${LT_AGE}.${LT_REVISION}
- SOVERSION
- ${LT_CURRENT_MINUS_AGE})
-endmacro()
-set_version(Makefile.am webp webp)
-set_version(Makefile.am webpdecoder webpdecoder)
-set_version(demux/Makefile.am webpdemux webpdemux)
+set_version(src/Makefile.am webp webp)
+set_version(src/Makefile.am webpdecoder webpdecoder)
+set_version(src/demux/Makefile.am webpdemux webpdemux)
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_FILE)
-string(REGEX MATCH
- "AC_INIT\\([^\n]*\\[[0-9\\.]+\\]"
- TMP
- ${CONFIGURE_FILE})
-string(REGEX MATCH
- "[0-9\\.]+"
- PROJECT_VERSION
- ${TMP})
+string(REGEX MATCH "AC_INIT\\([^\n]*\\[[0-9\\.]+\\]" TMP ${CONFIGURE_FILE})
+string(REGEX MATCH "[0-9\\.]+" PROJECT_VERSION ${TMP})
# Define the libraries to install.
list(APPEND INSTALLED_LIBRARIES webpdecoder webp webpdemux)
@@ -385,10 +464,8 @@ math(EXPR WEBP_SIMD_FILES_TO_INCLUDE_RANGE
foreach(I_FILE RANGE ${WEBP_SIMD_FILES_TO_INCLUDE_RANGE})
list(GET WEBP_SIMD_FILES_TO_INCLUDE ${I_FILE} FILE)
list(GET WEBP_SIMD_FLAGS_TO_INCLUDE ${I_FILE} SIMD_COMPILE_FLAG)
- set_source_files_properties(${FILE}
- PROPERTIES
- COMPILE_FLAGS
- ${SIMD_COMPILE_FLAG})
+ set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS
+ ${SIMD_COMPILE_FLAG})
endforeach()
if(NOT WEBP_BUILD_LIBWEBPMUX)
@@ -409,32 +486,32 @@ endif()
if(WEBP_BUILD_ANIM_UTILS
OR WEBP_BUILD_CWEBP
OR WEBP_BUILD_DWEBP
+ OR WEBP_BUILD_EXTRAS
+ OR WEBP_BUILD_FUZZTEST
OR WEBP_BUILD_GIF2WEBP
OR WEBP_BUILD_IMG2WEBP
- OR WEBP_BUILD_VWEBP)
+ OR WEBP_BUILD_VWEBP
+ OR WEBP_BUILD_WEBPMUX
+ OR WEBP_BUILD_WEBPINFO)
# Example utility library.
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "EXAMPLEUTIL_SRCS"
"example_util_[^ ]*")
list(APPEND EXAMPLEUTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h)
add_library(exampleutil STATIC ${EXAMPLEUTIL_SRCS})
- target_link_libraries(exampleutil imageioutil)
target_include_directories(
- exampleutil
- PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
+ exampleutil PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEIOUTILS_SRCS"
"imageio_util_[^ ]*")
add_library(imageioutil STATIC ${IMAGEIOUTILS_SRCS})
target_link_libraries(imageioutil webp)
+ target_link_libraries(exampleutil imageioutil)
# Image-decoding utility library.
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEDEC_SRCS"
"imagedec_[^ ]*")
add_library(imagedec STATIC ${IMAGEDEC_SRCS})
- target_link_libraries(imagedec
- imageioutil
- webpdemux
- webp
+ target_link_libraries(imagedec imageioutil webpdemux webp
${WEBP_DEP_IMG_LIBRARIES})
# Image-encoding utility library.
@@ -443,12 +520,12 @@ if(WEBP_BUILD_ANIM_UTILS
add_library(imageenc STATIC ${IMAGEENC_SRCS})
target_link_libraries(imageenc imageioutil webp)
- set_property(TARGET exampleutil
- imageioutil
- imagedec
- imageenc
- PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src
- ${CMAKE_CURRENT_BINARY_DIR}/src)
+ set_property(
+ TARGET exampleutil imageioutil imagedec imageenc
+ PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${CMAKE_CURRENT_BINARY_DIR}/src)
+ target_include_directories(imagedec PRIVATE ${WEBP_DEP_IMG_INCLUDE_DIRS})
+ target_include_directories(imageenc PRIVATE ${WEBP_DEP_IMG_INCLUDE_DIRS})
endif()
if(WEBP_BUILD_DWEBP)
@@ -465,7 +542,8 @@ if(WEBP_BUILD_CWEBP)
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "CWEBP_SRCS" "cwebp")
add_executable(cwebp ${CWEBP_SRCS})
target_link_libraries(cwebp exampleutil imagedec webp)
- target_include_directories(cwebp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+ target_include_directories(cwebp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+ ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS cwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
@@ -473,13 +551,12 @@ if(WEBP_BUILD_LIBWEBPMUX)
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "WEBP_MUX_SRCS" "")
add_library(libwebpmux ${WEBP_MUX_SRCS})
target_link_libraries(libwebpmux webp)
- target_include_directories(libwebpmux
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR})
- set_version(mux/Makefile.am libwebpmux webpmux)
- set_target_properties(libwebpmux
- PROPERTIES PUBLIC_HEADER
- "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux.h;\
+ target_include_directories(libwebpmux PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR})
+ set_version(src/mux/Makefile.am libwebpmux webpmux)
+ set_target_properties(
+ libwebpmux
+ PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux.h;\
${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h;\
${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h;")
set_target_properties(libwebpmux PROPERTIES OUTPUT_NAME webpmux)
@@ -493,13 +570,10 @@ if(WEBP_BUILD_GIF2WEBP)
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "GIF2WEBP_SRCS"
"gif2webp")
add_executable(gif2webp ${GIF2WEBP_SRCS})
- target_link_libraries(gif2webp
- exampleutil
- imageioutil
- webp
- libwebpmux
+ target_link_libraries(gif2webp exampleutil imageioutil webp libwebpmux
${WEBP_DEP_GIF_LIBRARIES})
- target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+ target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+ ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS gif2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
@@ -509,13 +583,10 @@ if(WEBP_BUILD_IMG2WEBP)
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "IMG2WEBP_SRCS"
"img2webp")
add_executable(img2webp ${IMG2WEBP_SRCS})
- target_link_libraries(img2webp
- exampleutil
- imagedec
- imageioutil
- webp
+ target_link_libraries(img2webp exampleutil imagedec imageioutil webp
libwebpmux)
- target_include_directories(img2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+ target_include_directories(img2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+ ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS img2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
@@ -526,17 +597,17 @@ if(WEBP_BUILD_VWEBP)
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "VWEBP_SRCS" "vwebp")
add_executable(vwebp ${VWEBP_SRCS})
- target_link_libraries(vwebp
- ${OPENGL_LIBRARIES}
- exampleutil
- ${GLUT_glut_LIBRARY}
- imageioutil
- webp
- webpdemux)
- target_include_directories(vwebp
- PRIVATE ${GLUT_INCLUDE_DIR}
- ${CMAKE_CURRENT_BINARY_DIR}/src
- ${OPENGL_INCLUDE_DIR})
+ target_link_libraries(
+ vwebp
+ ${OPENGL_LIBRARIES}
+ exampleutil
+ GLUT::GLUT
+ imageioutil
+ webp
+ webpdemux)
+ target_include_directories(
+ vwebp PRIVATE ${GLUT_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src
+ ${OPENGL_INCLUDE_DIR})
install(TARGETS vwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
check_c_compiler_flag("-Wno-deprecated-declarations" HAS_NO_DEPRECATED)
@@ -554,7 +625,8 @@ if(WEBP_BUILD_WEBPINFO)
"webpinfo")
add_executable(webpinfo ${WEBPINFO_SRCS})
target_link_libraries(webpinfo exampleutil imageioutil)
- target_include_directories(webpinfo PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+ target_include_directories(webpinfo PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+ ${CMAKE_CURRENT_SOURCE_DIR}/src)
install(TARGETS webpinfo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
@@ -575,61 +647,101 @@ if(WEBP_BUILD_EXTRAS)
parse_makefile_am(${EXTRAS_MAKEFILE} "WEBP_QUALITY_SRCS" "webp_quality")
parse_makefile_am(${EXTRAS_MAKEFILE} "VWEBP_SDL_SRCS" "vwebp_sdl")
+ # libextras
+ add_library(extras STATIC ${WEBP_EXTRAS_SRCS})
+ target_include_directories(
+ extras PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/src)
+
# get_disto
add_executable(get_disto ${GET_DISTO_SRCS})
target_link_libraries(get_disto imagedec)
- target_include_directories(get_disto
- PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_BINARY_DIR}/src)
+ target_include_directories(get_disto PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}/src)
# webp_quality
- add_executable(webp_quality ${WEBP_QUALITY_SRCS} ${WEBP_EXTRAS_SRCS})
- target_link_libraries(webp_quality exampleutil imagedec)
- target_include_directories(webp_quality
- PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_BINARY_DIR})
+ add_executable(webp_quality ${WEBP_QUALITY_SRCS})
+ target_link_libraries(webp_quality exampleutil imagedec extras)
+ target_include_directories(webp_quality PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR})
# vwebp_sdl
- find_package(SDL)
- if(SDL_FOUND)
+ find_package(SDL2 QUIET)
+ if(WEBP_BUILD_VWEBP AND SDL2_FOUND)
add_executable(vwebp_sdl ${VWEBP_SDL_SRCS})
- target_link_libraries(vwebp_sdl ${SDL_LIBRARY} imageioutil webp)
- target_include_directories(vwebp_sdl
- PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_BINARY_DIR}
- ${CMAKE_CURRENT_BINARY_DIR}/src
- ${SDL_INCLUDE_DIR})
+ target_link_libraries(vwebp_sdl ${SDL2_LIBRARIES} imageioutil webp)
+ target_include_directories(
+ vwebp_sdl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}/src ${SDL2_INCLUDE_DIRS})
set(WEBP_HAVE_SDL 1)
target_compile_definitions(vwebp_sdl PUBLIC WEBP_HAVE_SDL)
+
+ set(CMAKE_REQUIRED_INCLUDES "${SDL2_INCLUDE_DIRS}")
+ check_c_source_compiles(
+ "
+ #define SDL_MAIN_HANDLED
+ #include \"SDL.h\"
+ int main(void) {
+ return 0;
+ }
+ "
+ HAVE_JUST_SDL_H)
+ set(CMAKE_REQUIRED_INCLUDES)
+ if(HAVE_JUST_SDL_H)
+ target_compile_definitions(vwebp_sdl PRIVATE WEBP_HAVE_JUST_SDL_H)
+ endif()
endif()
endif()
if(WEBP_BUILD_WEBP_JS)
+ # The default stack size changed from 5MB to 64KB in 3.1.27. See
+ # https://crbug.com/webp/614.
+ if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "3.1.27")
+ # TOTAL_STACK size was renamed to STACK_SIZE in 3.1.27. The old name was
+ # kept for compatibility, but prefer the new one in case it is removed in
+ # the future.
+ set(emscripten_stack_size "-sSTACK_SIZE=5MB")
+ else()
+ set(emscripten_stack_size "-sTOTAL_STACK=5MB")
+ endif()
+ find_package(SDL2 REQUIRED)
# wasm2js does not support SIMD.
if(NOT WEBP_ENABLE_SIMD)
# JavaScript version
add_executable(webp_js ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
- target_link_libraries(webp_js webpdecoder SDL)
+ target_link_libraries(webp_js webpdecoder SDL2)
target_include_directories(webp_js PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
set(WEBP_HAVE_SDL 1)
set_target_properties(
webp_js
- PROPERTIES LINK_FLAGS "-s WASM=0 \
- -s EXPORTED_FUNCTIONS='[\"_WebpToSDL\"]' -s INVOKE_RUN=0 \
- -s EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
+ PROPERTIES
+ # Emscripten puts -sUSE_SDL2=1 in this variable, though it's needed at
+ # compile time to ensure the headers are downloaded.
+ COMPILE_OPTIONS "${SDL2_LIBRARIES}"
+ LINK_FLAGS
+ "-sWASM=0 ${emscripten_stack_size} \
+ -sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
+ -sEXPORTED_RUNTIME_METHODS=cwrap ${SDL2_LIBRARIES} \
+ -sALLOW_MEMORY_GROWTH")
set_target_properties(webp_js PROPERTIES OUTPUT_NAME webp)
target_compile_definitions(webp_js PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
endif()
# WASM version
add_executable(webp_wasm ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
- target_link_libraries(webp_wasm webpdecoder SDL)
+ target_link_libraries(webp_wasm webpdecoder SDL2)
target_include_directories(webp_wasm PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
set_target_properties(
webp_wasm
- PROPERTIES LINK_FLAGS "-s WASM=1 \
- -s EXPORTED_FUNCTIONS='[\"_WebpToSDL\"]' -s INVOKE_RUN=0 \
- -s EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
+ PROPERTIES
+ # Emscripten puts -sUSE_SDL2=1 in this variable, though it's needed at
+ # compile time to ensure the headers are downloaded.
+ COMPILE_OPTIONS "${SDL2_LIBRARIES}"
+ LINK_FLAGS
+ "-sWASM=1 ${emscripten_stack_size} \
+ -sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
+ -sEXPORTED_RUNTIME_METHODS=cwrap ${SDL2_LIBRARIES} \
+ -sALLOW_MEMORY_GROWTH")
target_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
target_compile_definitions(webpdspdecode PUBLIC EMSCRIPTEN)
@@ -641,14 +753,15 @@ if(WEBP_BUILD_ANIM_UTILS)
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "ANIM_DIFF_SRCS"
"anim_diff")
add_executable(anim_diff ${ANIM_DIFF_SRCS})
- target_link_libraries(anim_diff
- exampleutil
- imagedec
- imageenc
- imageioutil
- webp
- webpdemux
- ${WEBP_DEP_GIF_LIBRARIES})
+ target_link_libraries(
+ anim_diff
+ exampleutil
+ imagedec
+ imageenc
+ imageioutil
+ webp
+ webpdemux
+ ${WEBP_DEP_GIF_LIBRARIES})
target_include_directories(anim_diff PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
# anim_dump
@@ -656,45 +769,63 @@ if(WEBP_BUILD_ANIM_UTILS)
parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "ANIM_DUMP_SRCS"
"anim_dump")
add_executable(anim_dump ${ANIM_DUMP_SRCS})
- target_link_libraries(anim_dump
- exampleutil
- imagedec
- imageenc
- imageioutil
- webp
- webpdemux
- ${WEBP_DEP_GIF_LIBRARIES})
+ target_link_libraries(
+ anim_dump
+ exampleutil
+ imagedec
+ imageenc
+ imageioutil
+ webp
+ webpdemux
+ ${WEBP_DEP_GIF_LIBRARIES})
target_include_directories(anim_dump PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
endif()
+if(WEBP_BUILD_FUZZTEST)
+ add_subdirectory(tests/fuzzer)
+endif()
+
# Install the different headers and libraries.
-install(TARGETS ${INSTALLED_LIBRARIES}
- EXPORT ${PROJECT_NAME}Targets
- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webp
- INCLUDES
- DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(
+ TARGETS ${INSTALLED_LIBRARIES}
+ EXPORT ${PROJECT_NAME}Targets
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webp
+ INCLUDES
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/)
-install(EXPORT ${PROJECT_NAME}Targets
- NAMESPACE ${PROJECT_NAME}::
+install(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}::
DESTINATION ${ConfigPackageLocation})
# Create the CMake version file.
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
- VERSION ${PACKAGE_VERSION}
- COMPATIBILITY AnyNewerVersion)
+ VERSION ${PACKAGE_VERSION} COMPATIBILITY AnyNewerVersion)
# Create the Config file.
include(CMakePackageConfigHelpers)
+# Fix libwebpmux reference. The target name libwebpmux is used for compatibility
+# purposes, but the library mentioned in WebPConfig.cmake should be the
+# unprefixed version. Note string(...) can be replaced with list(TRANSFORM ...)
+# if cmake_minimum_required is >= 3.12.
+string(REGEX REPLACE "libwebpmux" "webpmux" INSTALLED_LIBRARIES
+ "${INSTALLED_LIBRARIES}")
+
+if(MSVC)
+ # For compatibility with nmake, MSVC builds use a custom prefix (lib) that
+ # needs to be included in the library name.
+ string(REGEX REPLACE "[A-Za-z0-9_]+" "${CMAKE_STATIC_LIBRARY_PREFIX}\\0"
+ INSTALLED_LIBRARIES "${INSTALLED_LIBRARIES}")
+endif()
+
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/WebPConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake
- INSTALL_DESTINATION
- ${ConfigPackageLocation})
+ INSTALL_DESTINATION ${ConfigPackageLocation}
+ PATH_VARS CMAKE_INSTALL_INCLUDEDIR)
# Install the generated CMake files.
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
@@ -726,7 +857,6 @@ foreach(I_MAN RANGE ${MAN_PAGES_RANGE})
if(WEBP_BUILD_${EXEC_BUILD})
list(GET MAN_PAGES ${I_MAN} MAN_PAGE)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/man/${MAN_PAGE}
- DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
- COMPONENT doc)
+ DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc)
endif()
endforeach()
diff --git a/c-lib/CONTRIBUTING.md b/c-lib/CONTRIBUTING.md
@@ -19,10 +19,59 @@ again.
All submissions, including submissions by project members, require review. We
use a [Gerrit](https://www.gerritcodereview.com) instance hosted at
-https://chromium-review.googlesource.com for this purpose. See the
+https://chromium-review.googlesource.com for this purpose.
+
+## Sending patches
+
+The basic git workflow for modifying libwebp code and sending for review is:
+
+1. Get the latest version of the repository locally:
+
+ ```sh
+ git clone https://chromium.googlesource.com/webm/libwebp && cd libwebp
+ ```
+
+2. Copy the commit-msg script into ./git/hooks (this will add an ID to all of
+ your commits):
+
+ ```sh
+ curl -Lo .git/hooks/commit-msg https://chromium-review.googlesource.com/tools/hooks/commit-msg && chmod u+x .git/hooks/commit-msg
+ ```
+
+3. Modify the local copy of libwebp. Make sure the code
+ [builds successfully](https://chromium.googlesource.com/webm/libwebp/+/HEAD/doc/building.md#cmake).
+
+4. Choose a short and representative commit message:
+
+ ```sh
+ git commit -a -m "Set commit message here"
+ ```
+
+5. Send the patch for review:
+
+ ```sh
+ git push https://chromium-review.googlesource.com/webm/libwebp HEAD:refs/for/main
+ ```
+
+ Go to https://chromium-review.googlesource.com to view your patch and
+ request a review from the maintainers.
+
+See the
[WebM Project page](https://www.webmproject.org/code/contribute/submitting-patches/)
for additional details.
+## Code Style
+
+The C code style is based on the
+[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) and
+`clang-format --style=Google`, though this project doesn't use the tool to
+enforce the formatting.
+
+CMake files are formatted with
+[cmake-format](https://cmake-format.readthedocs.io/en/latest/). `cmake-format
+-i` can be used to format individual files, it will use the settings from
+`.cmake-format.py`.
+
## Community Guidelines
This project follows
diff --git a/c-lib/ChangeLog b/c-lib/ChangeLog
@@ -1,12 +1,706 @@
+370aa581 webp_js/README.md: add some more code formatting (``)
+f83c6b32 CMakeLists: add warning for incorrect emscripten config
+6a3e656b update ChangeLog (tag: v1.6.0-rc1)
+bf0bf1e7 api.md: add WebPValidateDecoderConfig to pseudocode
+e8ae210d update NEWS
+ce53efd7 bump version to 1.6.0
+1c333170 update AUTHORS
+85e098e5 webpmux: fix heap overflow w/-get/-set
+418340d8 Merge "Make histogram allocation and access more readable and type-safe." into main
+23ce76fa Merge "VP8BitReaderSetBuffer: move NULL check to call site" into main
+bbf3cbb1 VP8BitReaderSetBuffer: move NULL check to call site
+f6b87e03 Fix const style guide
+8852f89a Have lossless return the same results with/without -mt
+e015dcc0 Make histogram allocation and access more readable and type-safe.
+753ed11e enc_neon.c: fix aarch64 compilation w/gcc < 8.5.0
+0cd0b7a7 enc_fuzzer.cc: remove duplicate <cstdlib> include
+2209ffba swig,cosmetics: normalize includes
+15e2e1ee analysis_enc.c: remove unused include
+98c27801 IWYU: Include all headers for symbols used in files.
+eb3ff781 Only use valid histograms in VP8LHistogramSet
+57e324e2 Refactor VP8LHistogram histogram_enc.cc
+7191a602 Merge "Generalize trivial histograms" into main
+19696e0a Merge "alpha_processing_sse2: quiet signed conv warning" into main
+89b01ecc Merge "cwebp: add `-resize_mode`" into main
+52a430a7 Generalize trivial histograms
+e53e2130 Cache all costs in the histograms
+f8b360c4 alpha_processing_sse2: quiet signed conv warning
+eb4f8137 cwebp: add `-resize_mode`
+ad52d5fc dec/dsp/enc/utils,cosmetics: rm struct member '_' suffix
+ed7cd6a7 utils.c,cosmetics: rm struct member '_' suffix
+3a23b0f0 random_utils.[hc],cosmetics: rm struct member '_' suffix
+a99d0e6f quant_levels_dec_utils.c,cosmetics: rm struct member '_' suffix
+1ed4654d huffman_encode_utils.[hc],cosmetics: rm struct member '_' suffix
+f0689e48 config_enc.c,cosmetics: rm struct member '_' suffix
+24262266 mux,cosmetics: rm struct member '_' suffix
+3f54b1aa demux,cosmetics: rm struct member '_' suffix
+295804e4 examples,cosmetics: rm struct member '_' suffix
+5225592f Refactor VP8LHistogram to hide initializations from the user.
+00338240 Remove some computations in histogram clustering
+44f91b0d Speed DispatchAlpha_SSE2 up
+ee8e8c62 Fix member naming for VP8LHistogram
+a1ad3f1e Merge "Remove now unused ExtraCostCombined" into main
+321561b4 Remove now unused ExtraCostCombined
+e0ae21d2 WebPMemoryWriterClear: use WebPMemoryWriterInit
+a4183d94 Remove the computation of ExtraCost when comparing histograms
+f2b3f527 Get AVX2 into WebP lossless
+7c70ff7a Clean dsp/lossless includes
+9dd5ae81 Use the full register in PredictorSub13_SSE2
+613be8fc Makefile.vc: add /MP to CFLAGS
+1d86819f Merge changes I1437390a,I10a20de5,I1ac777d1 into main
+743a5f09 enc_neon: enable vld1q_u8_x4 for clang & msvc
+565da148 pngdec.c: add support for 'eXIf' tag
+319860e9 pngdec.c: support ImageMagick app1 exif text data
+815fc1e1 pngdec.c: add missing #ifdef for png_get_iCCP
+980b708e enc_neon: fix build w/aarch64 gcc < 9.4.0
+73b728cb cmake: bump minimum version to 3.16
+6a22b670 Add a function to validate a WebPDecoderConfig
+7ed2b10e Use consistently signed stride types.
+654bfb04 Avoid nullptr arithmetic in VP8BitReaderSetBuffer
+f8f24107 Fix potential "divide by zero" in examples found by coverity
+2af6c034 Merge tag 'v1.5.0'
+a4d7a715 update ChangeLog (tag: v1.5.0, origin/1.5.0)
+c3d85ce4 update NEWS
+ad14e811 tests/fuzzer/*: add missing <string_view> include
+74cd026e fuzz_utils.cc: fix build error w/WEBP_REDUCE_SIZE
+a027aa93 mux_demux_api_fuzzer.cc: fix -Wshadow warning
+25e17c68 update ChangeLog (tag: v1.5.0-rc1)
+aa2684fc update NEWS
+36923846 bump version to 1.5.0
+ceea8ff6 update AUTHORS
+e4f7a9f0 img2webp: add a warning for unused options
+1b4c967f Merge "Properly check the data size against the end of the RIFF chunk" into main
+9e5ecfaf Properly check the data size against the end of the RIFF chunk
+da0d9c7d examples: exit w/failure w/no args
+fcff86c7 {gif,img}2webp: sync -m help w/cwebp
+b76c4a84 man/img2webp.1: sync -m text w/cwebp.1 & gif2webp.1
+30633519 muxread: fix reading of buffers > riff size
+4c85d860 yuv.h: update RGB<->YUV coefficients in comment
+0ab789e0 Merge changes I6dfedfd5,I2376e2dc into main
+03236450 {ios,xcframework}build.sh: fix compilation w/Xcode 16
+61e2cfda rework AddVectorEq_SSE2
+7bda3deb rework AddVector_SSE2
+2ddaaf0a Fix variable names in SharpYuvComputeConversionMatrix
+a3ba6f19 Makefile.vc: fix gif2webp link error
+f999d94f gif2webp: add -sharp_yuv/-near_lossless
+dfdcb7f9 Merge "lossless.h: fix function declaration mismatches" into main (tag: webp-rfc9649)
+78ed6839 fix overread in Intra4Preds_NEON
+d516a68e lossless.h: fix function declaration mismatches
+87406904 Merge "Improve documentation of SharpYuvConversionMatrix." into main
+fdb229ea Merge changes I07a7e36a,Ib29980f7,I2316122d,I2356e314,I32b53dd3, ... into main
+0c3cd9cc Improve documentation of SharpYuvConversionMatrix.
+169dfbf9 disable Intra4Preds_NEON
+2dd5eb98 dsp/yuv*: use WEBP_RESTRICT qualifier
+23bbafbe dsp/upsampling*: use WEBP_RESTRICT qualifier
+35915b38 dsp/rescaler*: use WEBP_RESTRICT qualifier
+a32b436b dsp/lossless*: use WEBP_RESTRICT qualifier
+04d4b4f3 dsp/filters*: use WEBP_RESTRICT qualifier
+b1cb37e6 dsp/enc*: use WEBP_RESTRICT qualifier
+201894ef dsp/dec*: use WEBP_RESTRICT qualifier
+02eac8a7 dsp/cost*: use WEBP_RESTRICT qualifier
+84b118c9 Merge "webp-container-spec: normalize notes & unknown chunk link" into main
+052cf42f webp-container-spec: normalize notes & unknown chunk link
+220ee529 Search for best predictor transform bits
+78619478 Try to reduce the sampling for the entropy image
+14f09ab7 webp-container-spec: reorder chunk size - N text
+a78c5356 Remove a useless malloc for entropy image
+bc491763 Merge "Refactor predictor finding" into main
+34f92238 man/{cwebp,img2webp}.1: rm 'if needed' from -sharp_yuv
+367ca938 Refactor predictor finding
+a582b53b webp-lossless-bitstream-spec: clarify some text
+0fd25d84 Merge "anim_encode.c: fix function ref in comment" into main
+f8882913 anim_encode.c: fix function ref in comment
+40e4ca60 specs_generation.md: update kramdown command line
+57883c78 img2webp: add -exact/-noexact per-frame options
+1c8eba97 img2webp,cosmetics: add missing '.' spacers to help
+2e81017c Convert predictor_enc.c to fixed point
+94de6c7f Merge "Fix fuzztest link errors w/-DBUILD_SHARED_LIBS=1" into main
+51d9832a Fix fuzztest link errors w/-DBUILD_SHARED_LIBS=1
+7bcb36b8 Merge "Fix static overflow warning." into main
+8e0cc14c Fix static overflow warning.
+cea68462 README.md: add security report note
+615e5874 Merge "make VP8LPredictor[01]_C() static" into main
+233e86b9 Merge changes Ie43dc5ef,I94cd8bab into main
+1a29fd2f make VP8LPredictor[01]_C() static
+dd9d3770 Do*Filter_*: remove row & num_rows parameters
+ab451a49 Do*Filter_C: remove dead 'inverse' code paths
+f9a480f7 {TrueMotion,TM16}_NEON: remove zero extension
+04834aca Merge changes I25c30a9e,I0a192fc6,I4cf89575 into main
+39a602af webp-lossless-bitstream-spec: normalize predictor transform ref
+f28c837d Merge "webp-container-spec: align anim pseudocode w/prose" into main
+74be8e22 Fix implicit conversion issues
+0c01db7c Merge "Increase the transform bits if possible." into main
+f2d6dc1e Increase the transform bits if possible.
+caa19e5b update link to issue tracker
+c9dd9bd4 webp-container-spec: align anim pseudocode w/prose
+8a7c8dc6 WASM: Enable VP8L_USE_FAST_LOAD
+f0c53cd9 WASM: don't use USE_GENERIC_TREE
+eef903d0 WASM: Enable 64-bit BITS caching
+6296cc8d iterator_enc: make VP8IteratorReset() static
+fbd93896 histogram_enc: make VP8LGetHistogramSize static
+cc7ff545 cost_enc: make VP8CalculateLevelCosts[] static
+4e2828ba vp8l_dec: make VP8LClear() static
+d742b24a Intra16Preds_NEON: fix truemotion saturation
+c7bb4cb5 Intra4Preds_NEON: fix truemotion saturation
+952a989b Merge "Remove TODO now that log is using fixed point." into main
+dde11574 Remove TODO now that log is using fixed point.
+a1ca153d Fix hidden myerr in my_error_exit
+3bd94202 Merge changes Iff6e47ed,I24c67cd5,Id781e761 into main
+d27d246e Merge "Convert VP8LFastSLog2 to fixed point" into main
+4838611f Disable msg_code use in fuzzing mode
+314a142a Use QuantizeBlock_NEON for VP8EncQuantizeBlockWHT on Arm
+3bfb05e3 Add AArch64 Neon implementation of Intra16Preds
+baa93808 Add AArch64 Neon implementation of Intra4Preds
+41a5e582 Fix errors when compiling code as C++
+fb444b69 Convert VP8LFastSLog2 to fixed point
+c1c89f51 Fix WEBP_NODISCARD comment and C++ version
+66408c2c Switch the histogram_enc.h API to fixed point
+ac1e410d Remove leftover tiff dep
+b78d3957 Disable TIFF on fuzztest.
+cff21a7d Do not build statically on oss-fuzz.
+6853a8e5 Merge "Move more internal fuzzers to public." into main
+9bc09db4 Merge "Convert VP8LFastLog2 to fixed point" into main
+0a9f1c19 Convert VP8LFastLog2 to fixed point
+db0cb9c2 Move more internal fuzzers to public.
+ff2b5b15 Merge "advanced_api_fuzzer.cc: use crop dims in OOM check" into main
+c4af79d0 Put 0 at the end of a palette and do not store it.
+0ec80aef Delete last references to delta palettization
+96d79f84 advanced_api_fuzzer.cc: use crop dims in OOM check
+c35c7e02 Fix huffman fuzzer to not leak.
+f2fe8dec Bump fuzztest dependency.
+9ce982fd Fix fuzz tests to work on oss-fuzz
+3ba8af1a Do not escape quotes anymore in build.sh
+ea0e121b Allow centipede to be used as a fuzzing engine.
+27731afd make VP8I4ModeOffsets & VP8MakeIntra4Preds static
+ddd6245e oss-fuzz/build.sh: use heredoc for script creation
+50074930 oss-fuzz/build.sh,cosmetics: fix indent
+20e92f7d Limit the possible fuzz engines.
+4f200de5 Switch public fuzz tests to fuzztest.
+64186bb3 Add huffman_fuzzer to .gitignore
+0905f61c Move build script from oss-fuzz repo to here.
+e8678758 Fix link to Javascript documentation
+5e5b8f0c Fix SSE2 Transform_AC3 function name
+45129ee0 Revert "Check all the rows."
+ee26766a Check all the rows.
+7ec51c59 Increase the transform bits if possible.
+3cd16fd3 Revert "Increase the transform bits if possible."
+971a03d8 Increase the transform bits if possible.
+1bf198a2 Allow transform_bits to be different during encoding.
+1e462ca8 Define MAX_TRANSFORM_BITS according to the specification.
+64d1ec23 Use (MIN/NUM)_(TRANSFORM/HUFFMAN)_BITS where appropriate
+a90160e1 Refactor histograms in predictors.
+a7aa7525 Fix some function declarations
+68ff4e1e Merge "jpegdec: add a hint for EOF/READ errors" into main
+79e7968a jpegdec: add a hint for EOF/READ errors
+d33455cd man/*: s/BUGS/REPORTING BUGS/
+a67ff735 normalize example exit status
+edc28909 upsampling_{neon,sse41}: fix int sanitizer warning
+3cada4ce ImgIoUtilReadFile: check ftell() return
+dc950585 Merge tag 'v1.4.0'
+845d5476 update ChangeLog (tag: v1.4.0, origin/1.4.0)
+8a6a55bb update NEWS
+cf7c5a5d provide a way to opt-out/override WEBP_NODISCARD
+cc34288a update ChangeLog (tag: v1.4.0-rc1)
+f13c0886 NEWS: fix date
+74555950 Merge "vwebp: fix window title when options are given" into 1.4.0
+d781646c vwebp: fix window title when options are given
+c2e394de update NEWS
+f6d15cb7 bump version to 1.4.0
+57c388b8 update AUTHORS
+b3d1b2cb Merge changes I26f4aa22,I83386b6c,I320ed1a2 into main
+07216886 webp-container-spec: fix VP8 chunk ref ('VP8'->'VP8 ')
+f88666eb webp_js/*.html: fix canvas mapping
+e2c8f233 cmake,wasm: simplify SDL2 related flags
+d537cd37 cmake: fix vwebp_sdl compile w/libsdl-org release
+6c484cbf CMakeLists.txt: add missing WEBP_BUILD_EXTRAS check
+7b0bc235 man/cwebp.1: add more detail to -partition_limit
+3c0011bb WebPMuxGetChunk: add an assert
+955a3d14 Merge "muxread,MuxGet: add an assert" into main
+00abc000 muxread,MuxGet: add an assert
+40e85a0b Have the window title reflect the filename.
+1bf46358 man/cwebp.1: clarify -pass > 1 behavior w/o -size/-psnr
+eba03acb webp-container-spec: replace 'above' with 'earlier'
+a16d30cb webp-container-spec: clarify chunk order requirements
+8a7e9112 Merge "CMakeLists.txt: apply cmake-format" into main
+7fac6c1b Merge "Copy C code to not have multiplication overflow" into main
+e2922e43 Merge "Check for the presence of the ANDROID_ABI variable" into main
+501d9274 Copy C code to not have multiplication overflow
+fba7d62e CMakeLists.txt: apply cmake-format
+661c1b66 Merge "windows exports: use dllexport attribute, instead of visibility." into main
+8487860a windows exports: use dllexport attribute, instead of visibility.
+8ea678b9 webp/mux.h: data lifetime note w/copy_data=0
+79e05c7f Check for the presence of the ANDROID_ABI variable
+45f995a3 Expose functions for managing non-image chunks on WebPAnimEncoder
+1fb9f3dc gifdec: fix ErrorGIFNotAvailable() declaration
+4723db65 cosmetics: s/SANITY_CHECK/DCHECK/
+f4b9bc9e clear -Wextra-semi-stmt warnings
+713982b8 Limit animdecoder_fuzzer to 320MB
+cbe825e4 cmake: fix sharpyuv simd files' build
+f99305e9 Makefile.vc: add ARM64 support
+5efd6300 mv SharpYuvEstimate420Risk to extras/
+e78e924f Makefile.vc: add sharpyuv_risk_table.obj
+d7a0506d Add YUV420 riskiness metric.
+89c5b917 Merge "BuildHuffmanTable check sorted[] array bounds before writing" into main
+34c80749 Remove alpha encoding pessimization.
+13d9c30b Add a WEBP_NODISCARD
+24d7f9cb Switch code to SDL2.
+0b56dedc BuildHuffmanTable check sorted[] array bounds before writing
+a429c0de sharpyuv: convert some for() to do/while
+f0cd7861 DoSharpArgbToYuv: remove constant from loop
+339231cc SharpYuvConvertWithOptions,cosmetics: fix formatting
+307071f1 Remove medium/large code model-specific inline asm
+deadc339 Fix transfer functions where toGamma and toLinear are swapped.
+e7b78d43 Merge "Fix bug in FromLinearLog100." into main
+15a1309e Merge "webp-lossless-bitstream-spec: delete extra blank line" into main
+54ca9752 Fix bug in FromLinearLog100.
+d2cb2d8c Dereference after NULL check.
+e9d50107 webp-lossless-bitstream-spec: delete extra blank line
+78657971 Merge changes Ief442c90,Ie6e9c9a5 into main
+e30a5884 webp-lossless-bitstream-spec: update variable names
+09ca1368 Merge "webp-container-spec: change assert to MUST be TRUE" into main
+38cb4fc0 iosbuild,xcframeworkbuild: add SharpYuv framework
+40afa926 webp-lossless-bitstream-spec: simplify abstract
+9db21143 webp-container-spec: change assert to MUST be TRUE
+cdbf88ae Fix typo in API docs for incremental decoding
+05c46984 Reformat vcpkg build instructions.
+8534f539 Merge "Never send VP8_STATUS_SUSPENDED back in non-incremental." into main
+35e197bd Never send VP8_STATUS_SUSPENDED back in non-incremental.
+61441425 Add vcpkg installation instructions
+dce8397f Fix next is invalid pointer when WebPSafeMalloc fails
+57c58105 Cmake: wrong public macro WEBP_INCLUDE_DIRS
+c1ffd9ac Merge "vp8l_enc: fix non-C90 code" into main
+a3965948 Merge changes If628bb93,Ic79f6309,I45f0db23 into main
+f80e9b7e vp8l_enc: fix non-C90 code
+accd141d Update lossless spec for two simple codes.
+ac17ffff Fix non-C90 code.
+433c7dca Fix static analyzer warnings.
+5fac76cf Merge tag 'v1.3.2'
+ca332209 update ChangeLog (tag: v1.3.2)
+1ace578c update NEWS
+63234c42 bump version to 1.3.2
+a35ea50d Add a fuzzer for ReadHuffmanCodes
+95ea5226 Fix invalid incremental decoding check.
+2af26267 Fix OOB write in BuildHuffmanTable.
+902bc919 Fix OOB write in BuildHuffmanTable.
+7ba44f80 Homogenize "__asm__ volatile" vs "asm volatile"
+68e27135 webp-container-spec: reorder example chunk layout
+943b932a Merge changes I6a4d0a04,Ibc37b91e into main
+1cc94f95 decode.h: wrap idec example in /* */
+63acdd1e decode.h: fix decode example
+aac5c5d0 ReadHuffmanCode: rm redundant num code lengths check
+a2de25f6 webp-lossless-bitstream-spec: normalize list item case
+68820f0e webp-lossless-bitstream-spec: normalize pixel ref
+cdb31aa8 webp-lossless-bitstream-spec: add missing periods
+0535a8cf webp-lossless-bitstream-spec: fix grammar
+b6c4ce26 normalize numbered list item format
+dd7364c3 Merge "palette.c: fix msvc warnings" into main
+c63c5df6 palette.c: fix msvc warnings
+0a2cad51 webp-container-spec: move terms from intro section
+dd88d2ff webp-lossless-bitstream-spec: color_cache -> color cache
+6e750547 Merge changes I644d7d39,Icf05491e,Ic02e6652,I63b11258 into main
+67a7cc2b webp-lossless-bitstream-spec: fix code blocks
+1432ebba Refactor palette sorting computation.
+cd436142 webp-lossless-bitstream-spec: block -> chunk
+3cb66f64 webp-lossless-bitstream-spec: add some missing commas
+56471a53 webp-lossless-bitstream-spec: normalize item text in 5.1
+af7fbfd2 vp8l_dec,ReadTransform: improve error status reporting
+7d8e0896 vp8l_dec: add VP8LSetError()
+a71ce1cf animencoder_fuzzer: fix error check w/Nallocfuzz
+e94b36d6 webp-lossless-bitstream-spec: relocate details from 5.1
+84628e56 webp-lossless-bitstream-spec: clarify image width changes
+ee722997 alpha_dec: add missing VP8SetError()
+0081693d enc_dec_fuzzer: use WebPDecode()
+0fcb311c enc_dec_fuzzer: fix WebPEncode/pic.error_code check
+982c177c webp-lossless-bitstream-spec: fix struct member refs
+56cf5625 webp-lossless-bitstream-spec: use RFC 7405 for ABNF
+6c6b3fd3 webp-lossless-bitstream-spec,cosmetics: delete blank lines
+29b9eb15 Merge changes Id56ca4fd,I662bd1d7 into main
+47c0af8d ReadHuffmanCodes: rm max_alphabet_size calc
+b92deba3 animencoder_fuzzer: no WebPAnimEncoderAssemble check w/nallocfuzz
+6be9bf8b animencoder_fuzzer: fix leak on alloc failure
+5c965e55 vp8l_dec,cosmetics: add some /*param=*/ comments
+e4fc2f78 webp-lossless-bitstream-spec: add validity note for max_symbol
+71916726 webp-lossless-bitstream-spec: fix max_symbol definition
+eac3bd5c Have the palette code be in its own file.
+e2c85878 Add an initializer for the SharpYuvOptions struct.
+4222b006 Merge tag 'v1.3.1'
+25d94f47 Implement more transfer functions in libsharpyuv
+2153a679 Merge changes Id0300937,I5dba5ccf,I57bb68e0,I2dba7b4e,I172aca36, ... into main
+4298e976 webp-lossless-bitstream-spec: add PredictorTransformOutput
+cd7e02be webp-lossless-bitstream-spec: fix RIFF-header ABNF
+6c3845f9 webp-lossless-bitstream-spec: split LZ77 Backward Ref section
+7f1b6799 webp-lossless-bitstream-spec: split Meta Prefix Codes section
+7b634d8f webp-lossless-bitstream-spec: note transform order
+6d6d4915 webp-lossless-bitstream-spec: update transformations text
+fd7bb21c update ChangeLog (tag: v1.3.1-rc2, tag: v1.3.1)
+e1adea50 update NEWS
+6b1c722a lossless_common.h,cosmetics: fix a typo
+08d60d60 webp-lossless-bitstream-spec: split code length section
+7a12afcc webp-lossless-bitstream-spec: rm unused anchor
+43393320 enc/*: normalize WebPEncodingSetError() calls
+287fdefe enc/*: add missing WebPEncodingSetError() calls
+c3bd7cff EncodeAlphaInternal: add missing error check
+14a9dbfb webp-lossless-bitstream-spec: refine single node text
+64819c7c Implement ExtractGreen_SSE2
+d49cfbb3 vp8l_enc,WriteImage: add missing error check
+2e5a9ec3 muxread,MuxImageParse: add missing error checks
+ebb6f949 cmake,emscripten: explicitly set stack size
+59a2b1f9 WebPDecodeYUV: check u/v/stride/uv_stride ptrs
+8e965ccb Call png_get_channels() to see if image has alpha
+fe80fbbd webp-container-spec: add some missing commas
+e8ed3176 Merge "treat FILTER_NONE as a regular Unfilter[] call" into main
+03a7a048 webp-lossless-bitstream-spec: rm redundant statement
+c437c7aa webp-lossless-bitstream-spec: mv up prefix code group def
+e4f17a31 webp-lossless-bitstream-spec: fix section reference
+e2ecd5e9 webp-lossless-bitstream-spec: clarify ABNF syntax
+8b55425a webp-lossless-bitstream-spec: refine pixel copy text
+29c9f2d4 webp-lossless-bitstream-spec: minor wording updates
+6b02f660 treat FILTER_NONE as a regular Unfilter[] call
+7f75c91c webp-container-spec: fix location of informative msg
+f6499943 webp-container-spec: consistently quote FourCCs
+49918af3 webp-container-spec: minor wording updates
+7f0a3419 update ChangeLog (tag: v1.3.1-rc1)
+bab7efbe update NEWS
+7138bf8f bump version to 1.3.1
+435b4ded update AUTHORS
+47351229 update .mailmap
+46bc4fc9 Merge "Switch ExtraCost to ints and implement it in SSE." into main
+828b4ce0 Switch ExtraCost to ints and implement it in SSE.
+ff6c7f4e CONTRIBUTING.md: add C style / cmake-format notes
+dd530437 add .cmake-format.py
+adbe2cb1 cmake,cosmetics: apply cmake-format
+15b36508 doc/webp-container-spec: rm future codec comment
+c369c4bf doc/webp-lossless-bitstream-spec: improve link text
+1de35f47 doc/webp-container-spec: don't use 'currently'
+bb06a16e doc/webp-container-spec: prefer present tense
+9f38b71e doc/webp-lossless-bitstream-spec: prefer present tense
+7acb6b82 doc/webp-container-spec: avoid i.e. & e.g.
+4967e7cd doc/webp-lossless-bitstream-spec: avoid i.e. & e.g.
+e3366659 Merge "Do not find_package image libraries if not needed." into main
+428588ef clarify single leaf node trees and use of canonical prefix coding
+709ec152 Do not find_package image libraries if not needed.
+8dd80ef8 fuzz_utils.h: lower kFuzzPxLimit w/ASan
+8f187b9f Clean message calls in CMake
+cba30078 WebPConfig.cmake.in: use calculated include path
+6cf9a76a Merge "webp-lossless-bitstream-spec: remove use of 'dynamics'" into main
+740943b2 Merge "Specialize and optimize ITransform_SSE2 using do_two" into main
+2d547e24 Compare kFuzzPxLimit to max_num_operations
+ac42dde1 Specialize and optimize ITransform_SSE2 using do_two
+17e0ef1d webp-lossless-bitstream-spec: remove use of 'dynamics'
+ed274371 neon.h,cosmetics: clear a couple lint warnings
+3fb82947 cpu.h,cosmetics: segment defines
+0c496a4f cpu.h: add WEBP_AARCH64
+8151f388 move VP8GetCPUInfo declaration to cpu.c
+916548c2 Make kFuzzPxLimit sanitizer dependent
+4070b271 advanced_api_fuzzer: reduce scaling limit
+761f49c3 Merge "webp-lossless-bitstream-spec: add missing bits to ABNF" into main
+84d04c48 webp-lossless-bitstream-spec: add missing bits to ABNF
+0696e1a7 advanced_api_fuzzer: reduce scaling limit
+93d88aa2 Merge "deps.cmake: remove unneeded header checks" into main
+118e0035 deps.cmake: remove unneeded header checks
+4c3d7018 webp-lossless-bitstream-spec: condense normal-prefix-code
+a6a09b32 webp-lossless-bitstream-spec: fix 2 code typos
+50ac4f7c Merge "cpu.h: enable NEON w/_M_ARM64EC" into main
+4b7d7b4f Add contribution instructions
+0afbd97b cpu.h: enable NEON w/_M_ARM64EC
+349f4353 Merge changes Ibd89e56b,Ic57e7f84,I89096614 into main
+8f7513b7 upsampling_neon.c: fix WEBP_SWAP_16BIT_CSP check
+cbf624b5 advanced_api_fuzzer: reduce scaling limit
+89edfdd1 Skip slow scaling in libwebp advanced_api_fuzzer
+859f19f7 Reduce libwebp advanced_api_fuzzer threshold
+a4f04835 Merge changes Ic389aaa2,I329ccd79 into main
+1275fac8 Makefile.vc: fix img2webp link w/dynamic cfg
+2fe27bb9 img2webp: normalize help output
+24bed3d9 cwebp: reflow -near_lossless help text
+0825faa4 img2webp: add -sharp_yuv/-near_lossless
+d64e6d7d Merge "PaletteSortModifiedZeng: fix leak on error" into main
+0e12a22d Merge "EncodeAlphaInternal: clear result->bw on error" into main
+0edbb6ea PaletteSortModifiedZeng: fix leak on error
+41ffe04e Merge "Update yapf style from "chromium" to "yapf"" into main
+2d9d9265 Update yapf style from "chromium" to "yapf"
+a486d800 EncodeAlphaInternal: clear result->bw on error
+1347a32d Skip big scaled advanced_api_fuzzer
+52b6f067 Fix scaling limit in advanced_api_fuzzer.c
+73618428 Limit scaling in libwebp advanced_api_fuzzer.c
+b54d21a0 Merge "CMakeLists.txt: allow CMAKE_INSTALL_RPATH to be set empty" into main
+31c28db5 libwebp{,demux,mux}.pc.in: Requires -> Requires.private
+d9a505ff CMakeLists.txt: allow CMAKE_INSTALL_RPATH to be set empty
+bdf33d03 Merge tag 'v1.3.0'
+b5577769 update ChangeLog (tag: v1.3.0-rc1, tag: v1.3.0)
+0ba77244 update NEWS
+e763eb1e bump version to 1.3.0
+2a8686fc update AUTHORS
+106a57c1 Merge "*/Android.mk: add a check for NDK_ROOT" into main
+c5e841c4 Merge "extras: WebpToSDL -> WebPToSDL" into main
+dbc30715 Merge "xcframeworkbuild.sh: bump MACOSX_CATALYST_MIN_VERSION" into main
+6fc1a9f9 */Android.mk: add a check for NDK_ROOT
+d3e151fc doc/api.md,webp_js/README.md: Webp -> WebP
+ed92a626 extras: WebpToSDL -> WebPToSDL
+6eb0189b xcframeworkbuild.sh: bump MACOSX_CATALYST_MIN_VERSION
+1d58575b CMake: align .pc variables with autoconf
+e5fe2cfc webp-lossless-bitstream-spec,cosmetics: reflow paragraphs
+0ceeeab9 webp-lossless-bitstream-spec: add amendment note
+607611cd Merge "webp-container-spec: normalize section title case" into main
+f853685e lossless: SUBTRACT_GREEN -> SUBTRACT_GREEN_TRANSFORM
+786497e4 webp-lossless-bitstream-spec: fix inv color txfm description
+c6ac672d webp-lossless-bitstream-spec: fix num_code_lengths check
+b5700efb webp-lossless-bitstream-spec,cosmetics: grammar/capitalization
+d8ed8c11 webp-container-spec: normalize section title case
+52ec0b8f Merge changes Ie975dbb5,Ifc8c93af,I6ca7c5d6,I2e8d66f5,I152477b8 into main
+5097ef62 webp-container-spec,cosmetics: grammar/capitalization
+e3ba2b1f webp-lossless-bitstream-spec,cosmetics: reflow abstract
+1e8e3ded webp-lossless-bitstream-spec: reword abstract re alpha
+017cb6fa webp-container-spec,cosmetics: normalize range syntax
+f6a4684b webp-lossless-bitstream-spec,cosmetics: normalize range syntax
+54ebd5a3 webp-lossless-bitstream-spec: limit dist map lut to 69 cols
+44741f9c webp-lossless-bitstream-spec: fix dist mapping example
+fad0ece7 pnmdec.c: use snprintf instead of sprintf
+3f73e8f7 sharpyuv: add SharpYuvGetVersion()
+ce2f2d66 SharpYuvConvert: fix a race on SharpYuvGetCPUInfo
+a458e308 sharpyuv_dsp.h: restore sharpyuv_cpu.h include
+9ba800a7 Merge changes Id72fbf3b,Ic59d23a2 into main
+979c0ebb sharpyuv: add SharpYuvGetCPUInfo
+8bab09a4 Merge "*.pc.in: rename lib_prefix to webp_libname_prefix" into main
+769387c5 cpu.c,cosmetics: fix a typo
+a02978c2 sharpyuv/Makefile.am+cmake: add missing -lm
+28aedcb9 *.pc.in: rename lib_prefix to webp_libname_prefix
+c42e6d5a configure.ac: export an empty lib_prefix variable
+dfc843aa Merge "*.pc.in: add lib prefix to lib names w/MSVC" into main
+2498209b *.pc.in: add lib prefix to lib names w/MSVC
+ac252b61 Merge "analysis_enc.c: fix a dead store warning" into main
+56944762 analysis_enc.c: fix a dead store warning
+d34f9b99 Merge "webp-lossless-bitstream-spec: convert BNF to ABNF" into main
+dc05b4db Merge changes I96bc063c,I45880467,If9e18e5a,I6ee938e4,I0a410b28, ... into main
+83270c7f webp-container-spec: add prose for rendering process
+73b19b64 webp-container-spec: note reserved fields MUST be ignored
+57101d3f webp-lossless-bitstream-spec: improve 'small' color table stmt
+dfd32e45 webp-container-spec: remove redundant sentence
+8a6185dd doc/webp-*: fix some punctuation, grammar
+72776530 webp-lossless-bitstream-spec: convert BNF to ABNF
+d992bb08 cmake: rename cpufeatures target to cpufeatures-webp
+3ed2b275 webp-container-spec: clarify background color note
+951c292d webp-container-spec: come too late -> out of order
+902dd787 webp-container-spec: prefer hex literals
+a8f6b5ee webp-container-spec: change SHOULD to MUST w/ANIM chunk
+1dc59435 webp-container-spec: add unknown fields MUST be ignored
+280a810f webp-container-spec: make padding byte=0 a MUST
+41f0bf68 webp-container-spec: update note on trailing data
+6bdd36db webp-container-spec: clarify Chunk Size is in bytes
+87e36c48 Merge "webp_js/README.md,cosmetics: reflow some lines" into main
+5b01f321 Merge "Update Windows makefile to build libsharpyuv library." into main
+19b1a71c webp_js/README.md,cosmetics: reflow some lines
+780db756 Update Windows makefile to build libsharpyuv library.
+e407d4b3 CMakeLists.txt: replace GLUT_glut_LIBRARY w/GLUT::GLUT
+abf73d62 Merge "WebPConfig.cmake.in: add find_dependency(Threads)" into main
+25807fb4 Merge "cmake: restore compatibility with cmake < 3.12" into main
+5dbc4bfa WebPConfig.cmake.in: add find_dependency(Threads)
+b2a175dd Merge "Update wasm instructions." into main
+cb90f76b Update wasm instructions.
+02d15258 cmake: restore compatibility with cmake < 3.12
+5ba046e2 CMake: add_definitions -> add_compile_options
+e68765af dsp,neon: use vaddv in a few more places
+e8f83de2 Set libsharpyuv include dir to 'webp' subdirectory.
+15a91ab1 cmake,cosmetics: apply cmake-format
+0dd49d1a CMakeLists.txt: set @ONLY in configure_file() calls
+62b1bfe8 Merge changes I2877e7bb,I777cad70,I15af7d1a,I686e6740,If10538a9, ... into main
+95c8fe5f Merge changes Iecea3603,I9dc228ab into main
+e7c805cf picture_csp_enc.c: remove SafeInitSharpYuv
+6af8845a sharpyuv: prefer webp/types.h
+639619ce cmake: fix dll exports
+782ed48c sharpyuv,SharpYuvInit: add mutex protection when available
+cad0d5ad sharyuv_{neon,sse2}.c: merge WEBP_USE_* sections
+ef70ee06 add a few missing <stddef.h> includes for NULL
+f0f9eda4 sharpyuv.h: remove <inttypes.h>
+9b902cba Merge "picture_csp_enc.c,CheckNonOpaque: rm unneeded local" into main
+9c1d457c cmake/cpu.cmake: remove unused variable
+9ac25bcb CMakeLists.txt,win32: match naming convention used by nmake
+76c353ba picture_csp_enc.c,CheckNonOpaque: rm unneeded local
+5000de54 Merge "cwebp: fix WebPPictureHasTransparency call" into main
+e1729309 Merge "WebPPictureHasTransparency: add missing pointer check" into main
+00ff988a vp8l_enc,AddSingleSubGreen: clear int sanitizer warnings
+e2fecc22 dsp/lossless_enc.c: clear int sanitizer warnings
+129cf9e9 dsp/lossless.c: clear int sanitizer warnings
+ad7d1753 dsp/lossless_enc.c: clear int sanitizer warnings
+5037220e VP8LSubtractGreenFromBlueAndRed_C: clear int sanitizer warnings
+2ee786c7 upsampling_sse2.c: clear int sanitizer warnings
+4cc157d4 ParseOptionalChunks: clear int sanitizer warning
+892cf033 BuildHuffmanTable: clear int sanitizer warning
+3a9a4d45 VP8GetSigned: clear int sanitizer warnings
+704a3d0a dsp/lossless.c: quiet int sanitizer warnings
+1a6c109c WebPPictureHasTransparency: add missing pointer check
+c626e7d5 cwebp: fix WebPPictureHasTransparency call
+866e349c Merge tag 'v1.2.4'
+c170df38 Merge "Create libsharpyuv.a in makefile.unix." into main
+9d7ff74a Create libsharpyuv.a in makefile.unix.
+0d1f1254 update ChangeLog (tag: v1.2.4)
+fcbc2d78 Merge "doc/*.txt: restrict code to 69 columns" into main
+4ad0e189 Merge "webp-container-spec.txt: normalize fourcc spelling" into main
+980d2488 update NEWS
+9fde8127 bump version to 1.2.4
+7a0a9935 doc/*.txt: restrict code to 69 columns
+c040a615 webp-container-spec.txt: normalize fourcc spelling
+aff1c546 dsp,x86: normalize types w/_mm_cvtsi128_si32 calls
+ab540ae0 dsp,x86: normalize types w/_mm_cvtsi32_si128 calls
+8980362e dsp,x86: normalize types w/_mm_set* calls (2)
+e626925c lossless: fix crunch mode w/WEBP_REDUCE_SIZE
+83539239 dsp,x86: normalize types w/_mm_set* calls
+8a4576ce webp-container-spec.txt: replace & with &
+db870881 Merge "webp-container-spec.txt: make reserved 0 values a MUST" into main
+01d7d378 webp-lossless-bitstream-spec: number all sections
+337cf69f webp-lossless-bitstream-spec: mv Nomenclature after Intro
+79be856e Merge changes I7111d1f7,I872cd62c into main
+5b87983a webp-container-spec.txt: make reserved 0 values a MUST
+bd939123 Merge changes I7a25b1a6,I51b2c2a0,I87d0cbcf,I6ec60af6,I0a3fe9dc into main
+04764b56 libwebp.pc: add libsharpyuv to requires
+7deee810 libsharpyuv: add pkg-config file
+1a64a7e6 webp-container-spec.txt: clarify some SHOULDs
+bec2c88a webp-container-spec.txt: move ChunkHeader to terminology
+c9359332 webp-container-spec.txt: clarify 'VP8 '/'XMP ' fourccs
+70fe3063 webp-container-spec.txt: rightsize table entries
+ddbf3f3f webp-container-spec.txt: update 'key words' text
+c151e95b utils.h,WEBP_ALIGN: make bitmask unsigned
+748e92bb add WebPInt32ToMem
+3fe15b67 Merge "Build libsharpyuv as a full installable library." into main
+4f402f34 add WebPMemToInt32
+a3b68c19 Build libsharpyuv as a full installable library.
+b4994eaa CMake: set rpath for shared objects
+94cd7117 Merge "CMake: fix dylib versioning" into main
+e91451b6 Fix the lossless specs a bit more.
+231bdfb7 CMake: fix dylib versioning
+bfad7ab5 CMakeLists.txt: correct libwebpmux name in WebPConfig.cmake
+c2e3fd30 Revert "cmake: fix webpmux lib name for cmake linking"
+7366f7f3 Merge "lossless: fix crunch mode w/WEBP_REDUCE_SIZE" into main
+84163d9d lossless: fix crunch mode w/WEBP_REDUCE_SIZE
+d01c1eb3 webp-lossless-bitstream-spec,cosmetics: normalize capitalization
+8813ca8e Merge tag 'v1.2.3'
+3c4a0fbf update ChangeLog (tag: v1.2.3)
+56a480e8 dsp/cpu.h: add missing extern "C"
+62b45bdd update ChangeLog (tag: v1.2.3-rc1)
+8764ec7a Merge changes Idb037953,Id582e395 into 1.2.3
+bcb872c3 vwebp: fix file name display in windows unicode build
+67c44ac5 webpmux: fix -frame option in windows unicode build
+8278825a makefile.unix: add sharpyuv objects to clean target
+14a49e01 update NEWS
+34b1dc33 bump version to 1.2.3
+0b397fda update AUTHORS
+c16488ac update .mailmap
+5a2d929c Merge "unicode.h: set console mode before using wprintf" into main
+169f867f unicode.h: set console mode before using wprintf
+a94b855c Merge "libsharpyuv: add version defines" into main
+f83bdb52 libsharpyuv: add version defines
+bef0d797 unicode_gif.h: fix -Wdeclaration-after-statement
+404c1622 Rename Huffman coding to prefix coding in the bitstream spec
+8895f8a3 Merge "run_static_analysis.sh: fix scan-build archive path" into main
+92a673d2 Merge "Add -fvisibility=hidden flag in CMakeLists." into main
+67c1d722 Merge "add WEBP_MSAN" into main
+1124ff66 Add -fvisibility=hidden flag in CMakeLists.
+e15b3560 add WEBP_MSAN
+ec9e782a sharpyuv: remove minimum image size from sharpyuv library
+7bd07f3b run_static_analysis.sh: fix scan-build archive path
+5ecee06f Merge "sharpyuv: increase precision of gamma<->linear conversion" into main
+f81dd7d6 Merge changes I3d17d529,I53026880,I1bd61639,I6bd4b25d,Icfec8fba into main
+2d607ee6 sharpyuv: increase precision of gamma<->linear conversion
+266cbbc5 sharpyuv: add 32bit version of SharpYuvFilterRow.
+9fc12274 CMake: add src to webpinfo includes
+7d18f40a CMake: add WEBP_BUILD_WEBPINFO to list of checks for exampleutil
+11309aa5 CMake: add WEBP_BUILD_WEBPMUX to list of checks for exampleutil
+4bc762f7 CMake: link imageioutil to exampleutil after defined
+0d1b9bc4 WEBP_DEP_LIBRARIES: use Threads::Threads
+20ef48f0 Merge "sharpyuv: add support for 10/12/16 bit rgb and 10/12 bit yuv." into main
+93c54371 sharpyuv: add support for 10/12/16 bit rgb and 10/12 bit yuv.
+53cf2b49 normalize WebPValidatePicture declaration w/definition
+d3006f4b sharpyuv: slightly improve precision
+ea967098 Merge changes Ia01bd397,Ibf3771af into main
+11bc8410 Merge changes I2d317c4b,I9e77f6db into main
+30453ea4 Add an internal WebPValidatePicture.
+6c43219a Some renamings for consistency.
+4f59fa73 update .mailmap
+e74f8a62 webp-lossless-bitstream-spec,cosmetics: normalize range syntax
+5a709ec0 webp-lossless-bitstream-spec,cosmetics: fix code typo
+a2093acc webp-lossless-bitstream-spec: add amendment note
+86c66930 webp-lossless-bitstream-spec: fix BNF
+232f22da webp-lossless-bitstream-spec: fix 'simple code' snippet
+44dd765d webp-lossless-bitstream-spec: fix ColorTransform impl
+7a7e33e9 webp-lossless-bitstream-spec: fix TR-pixel right border note
+86f94ee0 Update lossless spec with Huffman codes.
+a3927cc8 sharpyuv.c,cosmetics: fix indent
+6c45cef7 Make sure the stride has a minimum value in the importer.
+0c8b0e67 sharpyuv: cleanup/cosmetic changes
+dc3841e0 {histogram,predictor}_enc: quiet int -> float warnings
+a19a25bb Replace doubles by floats in lossless misc cost estimations.
+42888f6c Add an option to enable static builds.
+7efcf3cc Merge "Fix typo in color constants: Marix -> Matrix" into main
+8f4b5c62 Fix typo in color constants: Marix -> Matrix
+90084d84 Merge "demux,IsValidExtendedFormat: remove unused variable" into main
+ed643f61 Merge changes I452d2485,Ic6d75475 into main
+8fa053d1 Rename SharpYUV to SharpYuv for consistency.
+99a87562 SharpYuvComputeConversionMatrix: quiet int->float warnings
+deb426be Makefile.vc: add sharpyuv_csp.obj to SHARPYUV_OBJS
+779597d4 demux,IsValidExtendedFormat: remove unused variable
+40e8aa57 Merge "libsharpyuv: add colorspace utilities" into main
+01a05de1 libsharpyuv: add colorspace utilities
+2de4b05a Merge changes Id9890a60,I376d81e6,I1c958838 into main
+b8bca81f Merge "configure.ac: use LT_INIT if available" into main
+e8e77b9c Merge changes I479bc487,I39864691,I5d486c2c,I186d13be into main
+7e7d5d50 Merge ".gitignore: add Android Studio & VS code dirs" into main
+10c50848 normalize label indent
+89f774e6 mux{edit,internal}: fix leaks on error
+2d3293ad ExUtilInitCommandLineArguments: fix leak on error
+ec34fd70 anim_util: fix leaks on error
+e4717287 gif2webp: fix segfault on OOM
+e3cfafaf GetBackwardReferences: fail on alloc error
+a828a59b BackwardReferencesHashChainDistanceOnly: fix segfault on OOM
+fe153fae VP8LEncodeStream: fix segfault on OOM
+919acc0e .gitignore: add Android Studio & VS code dirs
+efa0731b configure.ac: use LT_INIT if available
+0957fd69 tiffdec: add grayscale support
+e685feef Merge "Make libsharpyuv self-contained by removing dependency on cpu.c" into main
+841960b6 Make libsharpyuv self-contained by removing dependency on cpu.c
+617cf036 image_dec: add WebPGetEnabledInputFileFormats()
+7a68afaa Let SharpArgbToYuv caller pass in an RGB>YUV conversion matrix.
+34bb332c man/cwebp.1: add note about crop/resize order
+f0e9351c webp-lossless-bitstream-spec,cosmetics: fix some typos
+5ccbd6ed vp8l_dec.c,cosmetics: fix a few typos
+c3d0c2d7 fix ios build scripts after sharpyuv dep added
+d0d2292e Merge "Make libwebp depend on libsharpyuv." into main
+03d12190 alpha_processing_neon.c: fix 0x01... typo
+d55d447c Make libwebp depend on libsharpyuv.
+e4cbcdd2 Fix lossless encoding for MIPS.
+924e7ca6 alpha_processing_neon.c: fix Dispatch/ExtractAlpha_NEON
+0fa0ea54 Makefile.vc: use /MANIFEST:EMBED
+29cc95ce Basic version of libsharpyuv in libwebp, in C.
+a30f2190 examples/webpmux.c: fix a couple of typos
+66b3ce23 Fix bad overflow check in ReadTIFF()
+54e61a38 Markdownify libwebp docs and reorganize them.
+b4533deb CMakeLists.txt,cosmetics: break long line
+b9d2f9cd quant_enc.c: use WEBP_RESTRICT qualifier
+ec178f2c Add progress hook granularity in lossless
+26139c73 Rename MAX_COST to MAX_BIT_COST in histogram_enc.c
+13b82816 cmake: fix webpmux lib name for cmake linking
+88b6a396 webp-container-spec.txt,cosmetics: normalize formatting
+6f496540 Merge tag 'v1.2.2'
+4074acf8 dsp.h: bump msvc arm64 version requirement to 16.6
+b0a86089 update ChangeLog (tag: v1.2.2)
6db8248c libwebp: Fix VP8EncTokenLoop() progress
827a307f BMP enc: fix the transparency case
+db25f1b4 libwebp: Fix VP8EncTokenLoop() progress
286e7fce libwebp: do not destroy jpeg codec twice on error
+6e8a4126 libwebp: do not destroy jpeg codec twice on error
+faf21968 Merge "BMP enc: fix the transparency case" into main
+480cd51d BMP enc: fix the transparency case
9195ea05 update ChangeLog (tag: v1.2.2-rc2)
4acae017 update NEWS
883f0633 man/img2webp.1: update date
567e1f44 Reword img2webp synopsis command line
+1b0c15db man/img2webp.1: update date
+17bade38 Merge "Reword img2webp synopsis command line" into main
+a80954a1 Reword img2webp synopsis command line
f084244d anim_decode: fix alpha blending with big-endian
b217b4ff webpinfo: fix fourcc comparison w/big-endian
+ec497b75 Merge "anim_decode: fix alpha blending with big-endian" into main
+e4886716 anim_decode: fix alpha blending with big-endian
+e3cb052c webpinfo: fix fourcc comparison w/big-endian
+a510fedb patch-check: detect duplicated files
f035d2e4 update ChangeLog (tag: v1.2.2-rc1)
7031946a update NEWS
973390b6 bump version to 1.2.2
@@ -45,7 +739,7 @@ e23cd548 dsp.h: enable NEON w/VS2019+ ARM64 targets
42592af8 webp,cmake: Remove unnecessary include dirs
e298e05f Add patch-check steps in PRESUBMIT.py
29148919 Merge tag 'v1.2.1'
-9ce5843d update ChangeLog (tag: v1.2.1, origin/1.2.1)
+9ce5843d update ChangeLog (tag: v1.2.1)
d9191588 fuzzer/*: normalize src/ includes
c5bc3624 fuzzer/*: normalize src/ includes
53b6f762 fix indent
@@ -224,7 +918,7 @@ a99078c1 remove call to MBAnalyzeBestIntra4Mode for method >= 5
6a0ff358 Enc: add a qmin / qmax range for quality factor
0fa56f30 Merge tag 'v1.1.0'
6cf504d0 PNM decoding: handle max_value != 255
-d7844e97 update ChangeLog (tag: v1.1.0-rc2, tag: v1.1.0, origin/1.1.0)
+d7844e97 update ChangeLog (tag: v1.1.0-rc2, tag: v1.1.0)
7f006436 Makefile.vc: fix webp_quality.exe link
cf047e83 Makefile.vc: fix webp_quality.exe link
c074c653 update NEWS
@@ -869,7 +1563,7 @@ b016cb91 NEON: faster fancy upsampling
f04eb376 Merge tag 'v0.5.2'
341d711c NEON: 5% faster conversion to RGB565 and RGBA4444
abb54827 remove Clang warnings with unused arch arguments.
-ece9684f update ChangeLog (tag: v0.5.2-rc2, tag: v0.5.2, origin/0.5.2)
+ece9684f update ChangeLog (tag: v0.5.2-rc2, tag: v0.5.2)
aa7744ca anim_util: quiet implicit conv warnings in 32-bit
d9120271 jpegdec: correct ContextFill signature
24eb3940 Remove some errors when compiling the code as C++.
@@ -1156,7 +1850,7 @@ bbb6ecd9 Merge "Add MSA optimized distortion functions"
c0991a14 io,EmitRescaledAlphaYUV: factor out a common expr
48bf5ed1 build.gradle: remove tab
bfef6c9f Merge tag 'v0.5.1'
-3d97bb75 update ChangeLog (tag: v0.5.1, origin/0.5.1)
+3d97bb75 update ChangeLog (tag: v0.5.1)
deb54d91 Clarify the expected 'config' lifespan in WebPIDecode()
435308e0 Add MSA optimized encoder transform functions
dce64bfa Add MSA optimized alpha filter functions
diff --git a/c-lib/Makefile.am b/c-lib/Makefile.am
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = src imageio man
+SUBDIRS = sharpyuv src imageio man
EXTRA_DIST = COPYING autogen.sh
if BUILD_EXTRAS
diff --git a/c-lib/Makefile.vc b/c-lib/Makefile.vc
@@ -5,12 +5,15 @@ LIBWEBPDECODER_BASENAME = libwebpdecoder
LIBWEBP_BASENAME = libwebp
LIBWEBPMUX_BASENAME = libwebpmux
LIBWEBPDEMUX_BASENAME = libwebpdemux
+LIBSHARPYUV_BASENAME = libsharpyuv
!IFNDEF ARCH
!IF ! [ cl 2>&1 | find "x86" > NUL ]
ARCH = x86
!ELSE IF ! [ cl 2>&1 | find "x64" > NUL ]
ARCH = x64
+!ELSE IF ! [ cl 2>&1 | find "ARM64" > NUL ]
+ARCH = ARM64
!ELSE IF ! [ cl 2>&1 | find "ARM" > NUL ]
ARCH = ARM
!ELSE
@@ -29,14 +32,13 @@ PLATFORM_LDFLAGS = /SAFESEH
NOLOGO = /nologo
CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG
CCDEBUG = cl.exe $(NOLOGO) /Od /Zi /D_DEBUG /RTC1
-CFLAGS = /I. /Isrc $(NOLOGO) /W3 /EHsc /c
+CFLAGS = /I. /Isrc $(NOLOGO) /MP /W3 /EHsc /c
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
-LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE
+LDFLAGS = /LARGEADDRESSAWARE /MANIFEST:EMBED /NXCOMPAT /DYNAMICBASE
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
LNKDLL = link.exe /DLL $(NOLOGO)
LNKEXE = link.exe $(NOLOGO)
LNKLIB = lib.exe $(NOLOGO)
-MT = mt.exe $(NOLOGO)
RCNODBG = rc.exe $(NOLOGO) /l"0x0409" # 0x409 = U.S. English
RCDEBUG = $(RCNODBG) /D_DEBUG
@@ -82,6 +84,7 @@ OUTPUT_DIRS = $(DIRBIN) $(DIRINC) $(DIRLIB) \
$(DIROBJ)\extras \
$(DIROBJ)\imageio \
$(DIROBJ)\mux \
+ $(DIROBJ)\sharpyuv \
$(DIROBJ)\utils \
# Target configuration
@@ -96,6 +99,7 @@ LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
+LIBSHARPYUV_BASENAME = $(LIBSHARPYUV_BASENAME)_debug
!ELSE IF "$(CFG)" == "release-dynamic"
CC = $(CCNODBG)
RC = $(RCNODBG)
@@ -109,6 +113,7 @@ LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
+LIBSHARPYUV_BASENAME = $(LIBSHARPYUV_BASENAME)_debug
!ENDIF
!IF "$(STATICLIBBUILD)" == "TRUE"
@@ -118,13 +123,14 @@ LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME).lib
LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME).lib
LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib
LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib
+LIBSHARPYUV = $(DIRLIB)\$(LIBSHARPYUV_BASENAME).lib
!ELSE IF "$(DLLBUILD)" == "TRUE"
-DLLINC = webp_dll.h
-CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL
+CC = $(CC) /I$(DIROBJ) $(RTLIB) /DWEBP_DLL
LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME)_dll.lib
LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib
LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME)_dll.lib
LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME)_dll.lib
+LIBSHARPYUV = $(DIRLIB)\$(LIBSHARPYUV_BASENAME)_dll.lib
LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME)_dll.pdb
CFGSET = TRUE
!ENDIF
@@ -174,6 +180,15 @@ CFLAGS = $(CFLAGS) /D_UNICODE /DUNICODE
# A config was provided, so the library can be built.
#
+SHARPYUV_OBJS = \
+ $(DIROBJ)\sharpyuv\sharpyuv.obj \
+ $(DIROBJ)\sharpyuv\sharpyuv_cpu.obj \
+ $(DIROBJ)\sharpyuv\sharpyuv_csp.obj \
+ $(DIROBJ)\sharpyuv\sharpyuv_dsp.obj \
+ $(DIROBJ)\sharpyuv\sharpyuv_gamma.obj \
+ $(DIROBJ)\sharpyuv\sharpyuv_neon.obj \
+ $(DIROBJ)\sharpyuv\sharpyuv_sse2.obj \
+
DEC_OBJS = \
$(DIROBJ)\dec\alpha_dec.obj \
$(DIROBJ)\dec\buffer_dec.obj \
@@ -216,6 +231,7 @@ DSP_DEC_OBJS = \
$(DIROBJ)\dsp\lossless_neon.obj \
$(DIROBJ)\dsp\lossless_sse2.obj \
$(DIROBJ)\dsp\lossless_sse41.obj \
+ $(DIROBJ)\dsp\lossless_avx2.obj \
$(DIROBJ)\dsp\rescaler.obj \
$(DIROBJ)\dsp\rescaler_mips32.obj \
$(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
@@ -255,6 +271,7 @@ DSP_ENC_OBJS = \
$(DIROBJ)\dsp\lossless_enc_neon.obj \
$(DIROBJ)\dsp\lossless_enc_sse2.obj \
$(DIROBJ)\dsp\lossless_enc_sse41.obj \
+ $(DIROBJ)\dsp\lossless_enc_avx2.obj \
$(DIROBJ)\dsp\ssim.obj \
$(DIROBJ)\dsp\ssim_sse2.obj \
@@ -308,6 +325,7 @@ ENC_OBJS = \
EXTRAS_OBJS = \
$(DIROBJ)\extras\extras.obj \
$(DIROBJ)\extras\quality_estimate.obj \
+ $(DIROBJ)\extras\sharpyuv_risk_table.obj \
IMAGEIO_UTIL_OBJS = \
$(DIROBJ)\imageio\imageio_util.obj \
@@ -323,6 +341,7 @@ UTILS_DEC_OBJS = \
$(DIROBJ)\utils\color_cache_utils.obj \
$(DIROBJ)\utils\filters_utils.obj \
$(DIROBJ)\utils\huffman_utils.obj \
+ $(DIROBJ)\utils\palette.obj \
$(DIROBJ)\utils\quant_levels_dec_utils.obj \
$(DIROBJ)\utils\rescaler_utils.obj \
$(DIROBJ)\utils\random_utils.obj \
@@ -335,12 +354,13 @@ UTILS_ENC_OBJS = \
$(DIROBJ)\utils\quant_levels_utils.obj \
LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
-LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
- $(UTILS_ENC_OBJS) $(DLL_OBJS)
+LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) \
+ $(DSP_ENC_OBJS) $(UTILS_ENC_OBJS) $(DLL_OBJS)
LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS)
+LIBSHARPYUV_OBJS = $(SHARPYUV_OBJS)
-OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP)
+OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP) $(LIBSHARPYUV)
!IF "$(ARCH)" == "ARM"
ex: $(OUT_LIBS)
all: ex
@@ -368,14 +388,14 @@ $(DIRBIN)\anim_dump.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
$(DIRBIN)\anim_dump.exe: $(IMAGEIO_ENC_OBJS)
$(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(IMAGEIO_DEC_OBJS)
$(DIRBIN)\cwebp.exe: $(IMAGEIO_UTIL_OBJS)
-$(DIRBIN)\cwebp.exe: $(LIBWEBPDEMUX)
+$(DIRBIN)\cwebp.exe: $(LIBWEBPDEMUX) $(LIBSHARPYUV)
$(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(IMAGEIO_DEC_OBJS)
$(DIRBIN)\dwebp.exe: $(IMAGEIO_ENC_OBJS)
$(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS)
$(DIRBIN)\dwebp.exe: $(LIBWEBPDEMUX)
$(DIRBIN)\gif2webp.exe: $(DIROBJ)\examples\gif2webp.obj $(EX_GIF_DEC_OBJS)
$(DIRBIN)\gif2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBPMUX)
-$(DIRBIN)\gif2webp.exe: $(LIBWEBP)
+$(DIRBIN)\gif2webp.exe: $(LIBWEBP) $(LIBSHARPYUV)
$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj $(EX_UTIL_OBJS)
$(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\vwebp_sdl.obj
@@ -386,7 +406,7 @@ $(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
$(DIRBIN)\img2webp.exe: $(DIROBJ)\examples\img2webp.obj $(LIBWEBPMUX)
$(DIRBIN)\img2webp.exe: $(IMAGEIO_DEC_OBJS)
$(DIRBIN)\img2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
-$(DIRBIN)\img2webp.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\img2webp.exe: $(LIBWEBPDEMUX) $(LIBWEBP) $(LIBSHARPYUV)
$(DIRBIN)\get_disto.exe: $(DIROBJ)\extras\get_disto.obj
$(DIRBIN)\get_disto.exe: $(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS)
$(DIRBIN)\get_disto.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
@@ -409,17 +429,16 @@ $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS): $(OUTPUT_DIRS)
$(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS) $(EXTRAS_OBJS): $(OUTPUT_DIRS)
!ENDIF # ARCH == ARM
+$(LIBSHARPYUV): $(LIBSHARPYUV_OBJS)
$(LIBWEBPDECODER): $(LIBWEBPDECODER_OBJS)
-$(LIBWEBP): $(LIBWEBP_OBJS)
+$(LIBWEBP): $(LIBWEBP_OBJS) $(LIBSHARPYUV)
$(LIBWEBPMUX): $(LIBWEBPMUX_OBJS)
$(LIBWEBPDEMUX): $(LIBWEBPDEMUX_OBJS)
-$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): $(OUTPUT_DIRS)
+$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS) $(LIBSHARPYUV_OBJS): \
+ $(OUTPUT_DIRS)
!IF "$(DLLBUILD)" == "TRUE"
-$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \
- $(DIROBJ)\$(DLLINC)
-
{$(DIROBJ)}.c{$(DIROBJ)}.obj:
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@ $<
@@ -429,20 +448,20 @@ $(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \
$(RC) /fo$@ $<
{src\mux}.rc{$(DIROBJ)\mux}.res:
$(RC) /fo$@ $<
+{sharpyuv}.rc{$(DIROBJ)\sharpyuv}.res:
+ $(RC) /fo$@ $<
-$(LIBWEBP): $(DIROBJ)\$(LIBWEBP_BASENAME:_debug=).res
+$(LIBSHARPYUV): $(DIROBJ)\sharpyuv\$(LIBSHARPYUV_BASENAME:_debug=).res
+$(LIBWEBP): $(LIBSHARPYUV) $(DIROBJ)\$(LIBWEBP_BASENAME:_debug=).res
$(LIBWEBPDECODER): $(DIROBJ)\$(LIBWEBPDECODER_BASENAME:_debug=).res
$(LIBWEBPMUX): $(LIBWEBP) $(DIROBJ)\mux\$(LIBWEBPMUX_BASENAME:_debug=).res
$(LIBWEBPDEMUX): $(LIBWEBP) $(DIROBJ)\demux\$(LIBWEBPDEMUX_BASENAME:_debug=).res
-$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
+$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX) $(LIBSHARPYUV):
$(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $**
-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
-
-clean::
- @-erase /s $(DIROBJ)\$(DLLINC) 2> NUL
!ELSE
-$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
+$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX) $(LIBSHARPYUV):
$(LNKLIB) /out:$@ $**
-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
!ENDIF
@@ -450,13 +469,6 @@ $(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
$(OUTPUT_DIRS):
@if not exist "$(@)" mkdir "$(@)"
-# generate a helper include to define WEBP_EXTERN suitable for the DLL build
-$(DIROBJ)\$(DLLINC):
- @echo #ifndef WEBP_DLL_H_ > $@
- @echo #define WEBP_DLL_H_ >> $@
- @echo #define WEBP_EXTERN __declspec(dllexport) >> $@
- @echo #endif /* WEBP_DLL_H_ */ >> $@
-
.SUFFIXES: .c .obj .res .exe
# File-specific flag builds. Note batch rules take precedence over wildcards,
# so for now name each file individually.
@@ -482,6 +494,8 @@ $(DIROBJ)\examples\gifdec.obj: examples\gifdec.c
$(CC) $(CFLAGS) /Fd$(DIROBJ)\extras\ /Fo$(DIROBJ)\extras\ $<
{imageio}.c{$(DIROBJ)\imageio}.obj::
$(CC) $(CFLAGS) /Fd$(DIROBJ)\imageio\ /Fo$(DIROBJ)\imageio\ $<
+{sharpyuv}.c{$(DIROBJ)\sharpyuv}.obj::
+ $(CC) $(CFLAGS) /Fd$(DIROBJ)\sharpyuv\ /Fo$(DIROBJ)\sharpyuv\ $<
{src\dec}.c{$(DIROBJ)\dec}.obj::
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $<
{src\demux}.c{$(DIROBJ)\demux}.obj::
@@ -502,13 +516,9 @@ LNKLIBS = $(LNKLIBS) Shell32.lib
{$(DIROBJ)\examples}.obj{$(DIRBIN)}.exe:
$(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS)
- $(MT) -manifest $@.manifest -outputresource:$@;1
- del $@.manifest
{$(DIROBJ)\extras}.obj{$(DIRBIN)}.exe:
$(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS)
- $(MT) -manifest $@.manifest -outputresource:$@;1
- del $@.manifest
clean::
@-erase /s $(DIROBJ)\*.dll 2> NUL
diff --git a/c-lib/NEWS b/c-lib/NEWS
@@ -1,3 +1,94 @@
+- 6/30/2025 version 1.6.0
+ This is a binary compatible release.
+ API changes:
+ - libwebp: WebPValidateDecoderConfig
+ * additional x86 (AVX2, SSE2), general optimizations and compression
+ improvements for lossless
+ * `-mt` returns same results as single-threaded lossless (regressed in
+ 1.5.0, #426506716)
+ * miscellaneous warning, bug & build fixes (#393104377, #397130631,
+ #398288323, #398066379, #427503509)
+ Tool updates:
+ * cwebp can restrict the use of `-resize` with `-resize_mode` (#405437935)
+
+- 12/19/2024 version 1.5.0
+ This is a binary compatible release.
+ API changes:
+ - `cross_color_transform_bits` added to WebPAuxStats
+ * minor lossless encoder speed and compression improvements
+ * lossless encoding does not use floats anymore
+ * additional Arm optimizations for lossy & lossless + general code generation
+ improvements
+ * improvements to WASM performance (#643)
+ * improvements and corrections in webp-container-spec.txt and
+ webp-lossless-bitstream-spec.txt (#646, #355607636)
+ * further security related hardening and increased fuzzing coverage w/fuzztest
+ (oss-fuzz: #382816119, #70112, #70102, #69873, #69825, #69508, #69208)
+ * miscellaneous warning, bug & build fixes (#499, #562, #381372617,
+ #381109771, #42340561, #375011696, #372109644, chromium: #334120888)
+ Tool updates:
+ * gif2webp: add -sharp_yuv & -near_lossless
+ * img2webp: add -exact & -noexact
+ * exit codes normalized; running an example program with no
+ arguments will output its help and exit with an error (#42340557,
+ #381372617)
+
+- 4/12/2024: version 1.4.0
+ This is a binary compatible release.
+ * API changes:
+ - libwebpmux: WebPAnimEncoderSetChunk, WebPAnimEncoderGetChunk,
+ WebPAnimEncoderDeleteChunk
+ - libsharpyuv: SharpYuvOptionsInit, SharpYuvConvertWithOptions
+ - extras: SharpYuvEstimate420Risk
+ * further security related hardening in libwebp & examples
+ * some minor optimizations in the lossless encoder
+ * added WEBP_NODISCARD to report unused result warnings; enable with
+ -DWEBP_ENABLE_NODISCARD=1
+ * improvements and corrections in webp-container-spec.txt and
+ webp-lossless-bitstream-spec.txt (#611)
+ * miscellaneous warning, bug & build fixes (#615, #619, #632, #635)
+
+- 9/13/2023: version 1.3.2
+ This is a binary compatible release.
+ * security fix for lossless decoder (chromium: #1479274, CVE-2023-4863)
+
+- 6/23/2023: version 1.3.1
+ This is a binary compatible release.
+ * security fixes for lossless encoder (#603, chromium: #1420107, #1455619,
+ CVE-2023-1999)
+ * improve error reporting through WebPPicture error codes
+ * fix upsampling for RGB565 and RGBA4444 in NEON builds
+ * img2webp: add -sharp_yuv & -near_lossless
+ * Windows builds:
+ - fix compatibility with clang-cl (#607)
+ - improve Arm64 performance with cl.exe
+ - add Arm64EC support
+ * fix webp_js with emcc >= 3.1.27 (stack size change, #614)
+ * CMake fixes (#592, #610, #612)
+ * further updates to the container and lossless bitstream docs (#581, #611)
+
+- 12/16/2022: version 1.3.0
+ This is a binary compatible release.
+ * add libsharpyuv, which exposes -sharp_yuv/config.use_sharp_yuv
+ functionality to other libraries; libwebp now depends on this library
+ * major updates to the container and lossless bitstream docs (#448, #546,
+ #551)
+ * miscellaneous warning, bug & build fixes (#576, #583, #584)
+
+- 8/4/2022: version 1.2.4
+ This is a binary compatible release.
+ * restore CMake libwebpmux target name for compatibility with 1.2.2 (#575)
+ * fix lossless crunch mode encoding with WEBP_REDUCE_SIZE
+ (chromium: #1345547, #1345595, #1345772, #1345804)
+
+- 6/30/2022: version 1.2.3
+ This is a binary compatible release.
+ * security fix for lossless encoder (#565, chromium:1313709)
+ * improved progress granularity in WebPReportProgress() when using lossless
+ * improved precision in Sharp YUV (-sharp_yuv) conversion
+ * many corrections to webp-lossless-bitstream-spec.txt (#551)
+ * crash/leak fixes on error/OOM and other bug fixes (#558, #563, #569, #573)
+
- 1/11/2022: version 1.2.2
This is a binary compatible release.
* webpmux: add "-set bgcolor A,R,G,B"
diff --git a/c-lib/README b/c-lib/README
@@ -1,795 +0,0 @@
- __ __ ____ ____ ____
- / \\/ \/ _ \/ _ )/ _ \
- \ / __/ _ \ __/
- \__\__/\____/\_____/__/ ____ ___
- / _/ / \ \ / _ \/ _/
- / \_/ / / \ \ __/ \__
- \____/____/\_____/_____/____/v1.2.2
-
-Description:
-============
-
-WebP codec: library to encode and decode images in WebP format. This package
-contains the library that can be used in other programs to add WebP support,
-as well as the command line tools 'cwebp' and 'dwebp'.
-
-See https://developers.google.com/speed/webp
-
-The latest source tree is available at
-https://chromium.googlesource.com/webm/libwebp
-
-It is released under the same license as the WebM project.
-See https://www.webmproject.org/license/software/ or the
-"COPYING" file for details. An additional intellectual
-property rights grant can be found in the file PATENTS.
-
-Building:
-=========
-
-Windows build:
---------------
-
-By running:
-
- nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output
-
-the directory output\release-static\(x64|x86)\bin will contain the tools
-cwebp.exe and dwebp.exe. The directory output\release-static\(x64|x86)\lib will
-contain the libwebp static library.
-The target architecture (x86/x64) is detected by Makefile.vc from the Visual
-Studio compiler (cl.exe) available in the system path.
-
-Unix build using makefile.unix:
--------------------------------
-
-On platforms with GNU tools installed (gcc and make), running
-
- make -f makefile.unix
-
-will build the binaries examples/cwebp and examples/dwebp, along
-with the static library src/libwebp.a. No system-wide installation
-is supplied, as this is a simple alternative to the full installation
-system based on the autoconf tools (see below).
-Please refer to makefile.unix for additional details and customizations.
-
-Using autoconf tools:
----------------------
-Prerequisites:
-A compiler (e.g., gcc), make, autoconf, automake, libtool.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install gcc make autoconf automake libtool
-
-When building from git sources, you will need to run autogen.sh to generate the
-configure script.
-
-./configure
-make
-make install
-
-should be all you need to have the following files
-
-/usr/local/include/webp/decode.h
-/usr/local/include/webp/encode.h
-/usr/local/include/webp/types.h
-/usr/local/lib/libwebp.*
-/usr/local/bin/cwebp
-/usr/local/bin/dwebp
-
-installed.
-
-Note: A decode-only library, libwebpdecoder, is available using the
-'--enable-libwebpdecoder' flag. The encode library is built separately and can
-be installed independently using a minor modification in the corresponding
-Makefile.am configure files (see comments there). See './configure --help' for
-more options.
-
-Building for MIPS Linux:
-------------------------
-MIPS Linux toolchain stable available releases can be found at:
-https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/
-
-# Add toolchain to PATH
-export PATH=$PATH:/path/to/toolchain/bin
-
-# 32-bit build for mips32r5 (p5600)
-HOST=mips-mti-linux-gnu
-MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \
- -msched-weight -mload-store-pairs -fPIE"
-MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie"
-
-# 64-bit build for mips64r6 (i6400)
-HOST=mips-img-linux-gnu
-MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \
- -msched-weight -mload-store-pairs -fPIE"
-MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie"
-
-./configure --host=${HOST} --build=`config.guess` \
- CC="${HOST}-gcc -EL" \
- CFLAGS="$MIPS_CFLAGS" \
- LDFLAGS="$MIPS_LDFLAGS"
-make
-make install
-
-CMake:
-------
-With CMake, you can compile libwebp, cwebp, dwebp, gif2webp, img2webp, webpinfo
-and the JS bindings.
-
-Prerequisites:
-A compiler (e.g., gcc with autotools) and CMake.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install build-essential cmake
-
-When building from git sources, you will need to run cmake to generate the
-makefiles.
-
-mkdir build && cd build && cmake ../
-make
-make install
-
-If you also want any of the executables, you will need to enable them through
-CMake, e.g.:
-
-cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
-
-or through your favorite interface (like ccmake or cmake-qt-gui).
-
-Use option -DWEBP_UNICODE=ON for Unicode support on Windows (with chcp 65001).
-
-Finally, once installed, you can also use WebP in your CMake project by doing:
-
-find_package(WebP)
-
-which will define the CMake variables WebP_INCLUDE_DIRS and WebP_LIBRARIES.
-
-Gradle:
--------
-The support for Gradle is minimal: it only helps you compile libwebp, cwebp and
-dwebp and webpmux_example.
-
-Prerequisites:
-A compiler (e.g., gcc with autotools) and gradle.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install build-essential gradle
-
-When building from git sources, you will need to run the Gradle wrapper with the
-appropriate target, e.g. :
-
-./gradlew buildAllExecutables
-
-SWIG bindings:
---------------
-
-To generate language bindings from swig/libwebp.swig at least swig-1.3
-(http://www.swig.org) is required.
-
-Currently the following functions are mapped:
-Decode:
- WebPGetDecoderVersion
- WebPGetInfo
- WebPDecodeRGBA
- WebPDecodeARGB
- WebPDecodeBGRA
- WebPDecodeBGR
- WebPDecodeRGB
-
-Encode:
- WebPGetEncoderVersion
- WebPEncodeRGBA
- WebPEncodeBGRA
- WebPEncodeRGB
- WebPEncodeBGR
- WebPEncodeLosslessRGBA
- WebPEncodeLosslessBGRA
- WebPEncodeLosslessRGB
- WebPEncodeLosslessBGR
-
-See swig/README for more detailed build instructions.
-
-Java bindings:
-
-To build the swig-generated JNI wrapper code at least JDK-1.5 (or equivalent)
-is necessary for enum support. The output is intended to be a shared object /
-DLL that can be loaded via System.loadLibrary("webp_jni").
-
-Python bindings:
-
-To build the swig-generated Python extension code at least Python 2.6 is
-required. Python < 2.6 may build with some minor changes to libwebp.swig or the
-generated code, but is untested.
-
-Encoding tool:
-==============
-
-The examples/ directory contains tools for encoding (cwebp) and
-decoding (dwebp) images.
-
-The easiest use should look like:
- cwebp input.png -q 80 -o output.webp
-which will convert the input file to a WebP file using a quality factor of 80
-on a 0->100 scale (0 being the lowest quality, 100 being the best. Default
-value is 75).
-You might want to try the -lossless flag too, which will compress the source
-(in RGBA format) without any loss. The -q quality parameter will in this case
-control the amount of processing time spent trying to make the output file as
-small as possible.
-
-A longer list of options is available using the -longhelp command line flag:
-
-> cwebp -longhelp
-Usage:
- cwebp [-preset <...>] [options] in_file [-o out_file]
-
-If input size (-s) for an image is not specified, it is
-assumed to be a PNG, JPEG, TIFF or WebP file.
-Note: Animated PNG and WebP files are not supported.
-
-Options:
- -h / -help ............. short help
- -H / -longhelp ......... long help
- -q <float> ............. quality factor (0:small..100:big), default=75
- -alpha_q <int> ......... transparency-compression quality (0..100),
- default=100
- -preset <string> ....... preset setting, one of:
- default, photo, picture,
- drawing, icon, text
- -preset must come first, as it overwrites other parameters
- -z <int> ............... activates lossless preset with given
- level in [0:fast, ..., 9:slowest]
-
- -m <int> ............... compression method (0=fast, 6=slowest), default=4
- -segments <int> ........ number of segments to use (1..4), default=4
- -size <int> ............ target size (in bytes)
- -psnr <float> .......... target PSNR (in dB. typically: 42)
-
- -s <int> <int> ......... input size (width x height) for YUV
- -sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
- -f <int> ............... filter strength (0=off..100), default=60
- -sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
- -strong ................ use strong filter instead of simple (default)
- -nostrong .............. use simple filter instead of strong
- -sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
- -partition_limit <int> . limit quality to fit the 512k limit on
- the first partition (0=no degradation ... 100=full)
- -pass <int> ............ analysis pass number (1..10)
- -qrange <min> <max> .... specifies the permissible quality range
- (default: 0 100)
- -crop <x> <y> <w> <h> .. crop picture with the given rectangle
- -resize <w> <h> ........ resize picture (after any cropping)
- -mt .................... use multi-threading if available
- -low_memory ............ reduce memory usage (slower encoding)
- -map <int> ............. print map of extra info
- -print_psnr ............ prints averaged PSNR distortion
- -print_ssim ............ prints averaged SSIM distortion
- -print_lsim ............ prints local-similarity distortion
- -d <file.pgm> .......... dump the compressed output (PGM file)
- -alpha_method <int> .... transparency-compression method (0..1), default=1
- -alpha_filter <string> . predictive filtering for alpha plane,
- one of: none, fast (default) or best
- -exact ................. preserve RGB values in transparent area, default=off
- -blend_alpha <hex> ..... blend colors against background color
- expressed as RGB values written in
- hexadecimal, e.g. 0xc0e0d0 for red=0xc0
- green=0xe0 and blue=0xd0
- -noalpha ............... discard any transparency information
- -lossless .............. encode image losslessly, default=off
- -near_lossless <int> ... use near-lossless image
- preprocessing (0..100=off), default=100
- -hint <string> ......... specify image characteristics hint,
- one of: photo, picture or graph
-
- -metadata <string> ..... comma separated list of metadata to
- copy from the input to the output if present.
- Valid values: all, none (default), exif, icc, xmp
-
- -short ................. condense printed message
- -quiet ................. don't print anything
- -version ............... print version number and exit
- -noasm ................. disable all assembly optimizations
- -v ..................... verbose, e.g. print encoding/decoding times
- -progress .............. report encoding progress
-
-Experimental Options:
- -jpeg_like ............. roughly match expected JPEG size
- -af .................... auto-adjust filter strength
- -pre <int> ............. pre-processing filter
-
-
-The main options you might want to try in order to further tune the
-visual quality are:
- -preset
- -sns
- -f
- -m
-
-Namely:
- * 'preset' will set up a default encoding configuration targeting a
- particular type of input. It should appear first in the list of options,
- so that subsequent options can take effect on top of this preset.
- Default value is 'default'.
- * 'sns' will progressively turn on (when going from 0 to 100) some additional
- visual optimizations (like: segmentation map re-enforcement). This option
- will balance the bit allocation differently. It tries to take bits from the
- "easy" parts of the picture and use them in the "difficult" ones instead.
- Usually, raising the sns value (at fixed -q value) leads to larger files,
- but with better quality.
- Typical value is around '75'.
- * 'f' option directly links to the filtering strength used by the codec's
- in-loop processing. The higher the value, the smoother the
- highly-compressed area will look. This is particularly useful when aiming
- at very small files. Typical values are around 20-30. Note that using the
- option -strong/-nostrong will change the type of filtering. Use "-f 0" to
- turn filtering off.
- * 'm' controls the trade-off between encoding speed and quality. Default is 4.
- You can try -m 5 or -m 6 to explore more (time-consuming) encoding
- possibilities. A lower value will result in faster encoding at the expense
- of quality.
-
-Decoding tool:
-==============
-
-There is a decoding sample in examples/dwebp.c which will take
-a .webp file and decode it to a PNG image file (amongst other formats).
-This is simply to demonstrate the use of the API. You can verify the
-file test.webp decodes to exactly the same as test_ref.ppm by using:
-
- cd examples
- ./dwebp test.webp -ppm -o test.ppm
- diff test.ppm test_ref.ppm
-
-The full list of options is available using -h:
-
-> dwebp -h
-Usage: dwebp in_file [options] [-o out_file]
-
-Decodes the WebP image file to PNG format [Default].
-Note: Animated WebP files are not supported.
-
-Use following options to convert into alternate image formats:
- -pam ......... save the raw RGBA samples as a color PAM
- -ppm ......... save the raw RGB samples as a color PPM
- -bmp ......... save as uncompressed BMP format
- -tiff ........ save as uncompressed TIFF format
- -pgm ......... save the raw YUV samples as a grayscale PGM
- file with IMC4 layout
- -yuv ......... save the raw YUV samples in flat layout
-
- Other options are:
- -version ..... print version number and exit
- -nofancy ..... don't use the fancy YUV420 upscaler
- -nofilter .... disable in-loop filtering
- -nodither .... disable dithering
- -dither <d> .. dithering strength (in 0..100)
- -alpha_dither use alpha-plane dithering if needed
- -mt .......... use multi-threading
- -crop <x> <y> <w> <h> ... crop output with the given rectangle
- -resize <w> <h> ......... scale the output (*after* any cropping)
- -flip ........ flip the output vertically
- -alpha ....... only save the alpha plane
- -incremental . use incremental decoding (useful for tests)
- -h ........... this help message
- -v ........... verbose (e.g. print encoding/decoding times)
- -quiet ....... quiet mode, don't print anything
- -noasm ....... disable all assembly optimizations
-
-WebP file analysis tool:
-========================
-
-'webpinfo' can be used to print out the chunk level structure and bitstream
-header information of WebP files. It can also check if the files are of valid
-WebP format.
-
-Usage: webpinfo [options] in_files
-Note: there could be multiple input files;
- options must come before input files.
-Options:
- -version ........... Print version number and exit.
- -quiet ............. Do not show chunk parsing information.
- -diag .............. Show parsing error diagnosis.
- -summary ........... Show chunk stats summary.
- -bitstream_info .... Parse bitstream header.
-
-Visualization tool:
-===================
-
-There's a little self-serve visualization tool called 'vwebp' under the
-examples/ directory. It uses OpenGL to open a simple drawing window and show
-a decoded WebP file. It's not yet integrated in the automake build system, but
-you can try to manually compile it using the recommendations below.
-
-Usage: vwebp in_file [options]
-
-Decodes the WebP image file and visualize it using OpenGL
-Options are:
- -version ..... print version number and exit
- -noicc ....... don't use the icc profile if present
- -nofancy ..... don't use the fancy YUV420 upscaler
- -nofilter .... disable in-loop filtering
- -dither <int> dithering strength (0..100), default=50
- -noalphadither disable alpha plane dithering
- -usebgcolor .. display background color
- -mt .......... use multi-threading
- -info ........ print info
- -h ........... this help message
-
-Keyboard shortcuts:
- 'c' ................ toggle use of color profile
- 'b' ................ toggle background color display
- 'i' ................ overlay file information
- 'd' ................ disable blending & disposal (debug)
- 'q' / 'Q' / ESC .... quit
-
-Building:
----------
-
-Prerequisites:
-1) OpenGL & OpenGL Utility Toolkit (GLUT)
- Linux:
- $ sudo apt-get install freeglut3-dev mesa-common-dev
- Mac + Xcode:
- - These libraries should be available in the OpenGL / GLUT frameworks.
- Windows:
- http://freeglut.sourceforge.net/index.php#download
-
-2) (Optional) qcms (Quick Color Management System)
- i. Download qcms from Mozilla / Chromium:
- https://hg.mozilla.org/mozilla-central/file/0e7639e3bdfb/gfx/qcms
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/qcms/;drc=d4a2f8e1ed461d8fc05ed88d1ae2dc94c9773825
- ii. Build and archive the source files as libqcms.a / qcms.lib
- iii. Update makefile.unix / Makefile.vc
- a) Define WEBP_HAVE_QCMS
- b) Update include / library paths to reference the qcms directory.
-
-Build using makefile.unix / Makefile.vc:
-$ make -f makefile.unix examples/vwebp
-> nmake /f Makefile.vc CFG=release-static \
- ../obj/x64/release-static/bin/vwebp.exe
-
-Animation creation tool:
-========================
-The utility 'img2webp' can turn a sequence of input images (PNG, JPEG, ...)
-into an animated WebP file. It offers fine control over duration, encoding
-modes, etc.
-
-Usage:
-
- img2webp [file_options] [[frame_options] frame_file]...
-
-File-level options (only used at the start of compression):
- -min_size ............ minimize size
- -loop <int> .......... loop count (default: 0, = infinite loop)
- -kmax <int> .......... maximum number of frame between key-frames
- (0=only keyframes)
- -kmin <int> .......... minimum number of frame between key-frames
- (0=disable key-frames altogether)
- -mixed ............... use mixed lossy/lossless automatic mode
- -v ................... verbose mode
- -h ................... this help
- -version ............. print version number and exit
-
-Per-frame options (only used for subsequent images input):
- -d <int> ............. frame duration in ms (default: 100)
- -lossless ........... use lossless mode (default)
- -lossy ... ........... use lossy mode
- -q <float> ........... quality
- -m <int> ............. method to use
-
-example: img2webp -loop 2 in0.png -lossy in1.jpg
- -d 80 in2.tiff -o out.webp
-
-Note: if a single file name is passed as the argument, the arguments will be
-tokenized from this file. The file name must not start with the character '-'.
-
-Animated GIF conversion:
-========================
-Animated GIF files can be converted to WebP files with animation using the
-gif2webp utility available under examples/. The files can then be viewed using
-vwebp.
-
-Usage:
- gif2webp [options] gif_file -o webp_file
-Options:
- -h / -help ............. this help
- -lossy ................. encode image using lossy compression
- -mixed ................. for each frame in the image, pick lossy
- or lossless compression heuristically
- -q <float> ............. quality factor (0:small..100:big)
- -m <int> ............... compression method (0=fast, 6=slowest)
- -min_size .............. minimize output size (default:off)
- lossless compression by default; can be
- combined with -q, -m, -lossy or -mixed
- options
- -kmin <int> ............ min distance between key frames
- -kmax <int> ............ max distance between key frames
- -f <int> ............... filter strength (0=off..100)
- -metadata <string> ..... comma separated list of metadata to
- copy from the input to the output if present
- Valid values: all, none, icc, xmp (default)
- -loop_compatibility .... use compatibility mode for Chrome
- version prior to M62 (inclusive)
- -mt .................... use multi-threading if available
-
- -version ............... print version number and exit
- -v ..................... verbose
- -quiet ................. don't print anything
-
-Building:
----------
-With the libgif development files installed, gif2webp can be built using
-makefile.unix:
-$ make -f makefile.unix examples/gif2webp
-
-or using autoconf:
-$ ./configure --enable-everything
-$ make
-
-Comparison of animated images:
-==============================
-Test utility anim_diff under examples/ can be used to compare two animated
-images (each can be GIF or WebP).
-
-Usage: anim_diff <image1> <image2> [options]
-
-Options:
- -dump_frames <folder> dump decoded frames in PAM format
- -min_psnr <float> ... minimum per-frame PSNR
- -raw_comparison ..... if this flag is not used, RGB is
- premultiplied before comparison
- -max_diff <int> ..... maximum allowed difference per channel
- between corresponding pixels in subsequent
- frames
- -h .................. this help
- -version ............ print version number and exit
-
-Building:
----------
-With the libgif development files and a C++ compiler installed, anim_diff can
-be built using makefile.unix:
-$ make -f makefile.unix examples/anim_diff
-
-or using autoconf:
-$ ./configure --enable-everything
-$ make
-
-Encoding API:
-=============
-
-The main encoding functions are available in the header src/webp/encode.h
-The ready-to-use ones are:
-size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride,
- float quality_factor, uint8_t** output);
-size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride,
- float quality_factor, uint8_t** output);
-size_t WebPEncodeRGBA(const uint8_t* rgba, int width, int height, int stride,
- float quality_factor, uint8_t** output);
-size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride,
- float quality_factor, uint8_t** output);
-
-They will convert raw RGB samples to a WebP data. The only control supplied
-is the quality factor.
-
-There are some variants for using the lossless format:
-
-size_t WebPEncodeLosslessRGB(const uint8_t* rgb, int width, int height,
- int stride, uint8_t** output);
-size_t WebPEncodeLosslessBGR(const uint8_t* bgr, int width, int height,
- int stride, uint8_t** output);
-size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, int width, int height,
- int stride, uint8_t** output);
-size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
- int stride, uint8_t** output);
-
-Of course in this case, no quality factor is needed since the compression
-occurs without loss of the input values, at the expense of larger output sizes.
-
-Advanced encoding API:
-----------------------
-
-A more advanced API is based on the WebPConfig and WebPPicture structures.
-
-WebPConfig contains the encoding settings and is not tied to a particular
-picture.
-WebPPicture contains input data, on which some WebPConfig will be used for
-compression.
-The encoding flow looks like:
-
--------------------------------------- BEGIN PSEUDO EXAMPLE
-
-#include <webp/encode.h>
-
- // Setup a config, starting form a preset and tuning some additional
- // parameters
- WebPConfig config;
- if (!WebPConfigPreset(&config, WEBP_PRESET_PHOTO, quality_factor)) {
- return 0; // version error
- }
- // ... additional tuning
- config.sns_strength = 90;
- config.filter_sharpness = 6;
- config_error = WebPValidateConfig(&config); // not mandatory, but useful
-
- // Setup the input data
- WebPPicture pic;
- if (!WebPPictureInit(&pic)) {
- return 0; // version error
- }
- pic.width = width;
- pic.height = height;
- // allocated picture of dimension width x height
- if (!WebPPictureAlloc(&pic)) {
- return 0; // memory error
- }
- // at this point, 'pic' has been initialized as a container,
- // and can receive the Y/U/V samples.
- // Alternatively, one could use ready-made import functions like
- // WebPPictureImportRGB(), which will take care of memory allocation.
- // In any case, past this point, one will have to call
- // WebPPictureFree(&pic) to reclaim memory.
-
- // Set up a byte-output write method. WebPMemoryWriter, for instance.
- WebPMemoryWriter wrt;
- WebPMemoryWriterInit(&wrt); // initialize 'wrt'
-
- pic.writer = MyFileWriter;
- pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work;
-
- // Compress!
- int ok = WebPEncode(&config, &pic); // ok = 0 => error occurred!
- WebPPictureFree(&pic); // must be called independently of the 'ok' result.
-
- // output data should have been handled by the writer at that point.
- // -> compressed data is the memory buffer described by wrt.mem / wrt.size
-
- // deallocate the memory used by compressed data
- WebPMemoryWriterClear(&wrt);
-
--------------------------------------- END PSEUDO EXAMPLE
-
-Decoding API:
-=============
-
-This is mainly just one function to call:
-
-#include "webp/decode.h"
-uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
- int* width, int* height);
-
-Please have a look at the file src/webp/decode.h for the details.
-There are variants for decoding in BGR/RGBA/ARGB/BGRA order, along with
-decoding to raw Y'CbCr samples. One can also decode the image directly into a
-pre-allocated buffer.
-
-To detect a WebP file and gather the picture's dimensions, the function:
- int WebPGetInfo(const uint8_t* data, size_t data_size,
- int* width, int* height);
-is supplied. No decoding is involved when using it.
-
-Incremental decoding API:
-=========================
-
-In the case when data is being progressively transmitted, pictures can still
-be incrementally decoded using a slightly more complicated API. Decoder state
-is stored into an instance of the WebPIDecoder object. This object can be
-created with the purpose of decoding either RGB or Y'CbCr samples.
-For instance:
-
- WebPDecBuffer buffer;
- WebPInitDecBuffer(&buffer);
- buffer.colorspace = MODE_BGR;
- ...
- WebPIDecoder* idec = WebPINewDecoder(&buffer);
-
-As data is made progressively available, this incremental-decoder object
-can be used to decode the picture further. There are two (mutually exclusive)
-ways to pass freshly arrived data:
-
-either by appending the fresh bytes:
-
- WebPIAppend(idec, fresh_data, size_of_fresh_data);
-
-or by just mentioning the new size of the transmitted data:
-
- WebPIUpdate(idec, buffer, size_of_transmitted_buffer);
-
-Note that 'buffer' can be modified between each call to WebPIUpdate, in
-particular when the buffer is resized to accommodate larger data.
-
-These functions will return the decoding status: either VP8_STATUS_SUSPENDED if
-decoding is not finished yet or VP8_STATUS_OK when decoding is done. Any other
-status is an error condition.
-
-The 'idec' object must always be released (even upon an error condition) by
-calling: WebPDelete(idec).
-
-To retrieve partially decoded picture samples, one must use the corresponding
-method: WebPIDecGetRGB or WebPIDecGetYUVA.
-It will return the last displayable pixel row.
-
-Lastly, note that decoding can also be performed into a pre-allocated pixel
-buffer. This buffer must be passed when creating a WebPIDecoder, calling
-WebPINewRGB() or WebPINewYUVA().
-
-Please have a look at the src/webp/decode.h header for further details.
-
-Advanced Decoding API:
-======================
-
-WebP decoding supports an advanced API which provides on-the-fly cropping and
-rescaling, something of great usefulness on memory-constrained environments like
-mobile phones. Basically, the memory usage will scale with the output's size,
-not the input's, when one only needs a quick preview or a zoomed in portion of
-an otherwise too-large picture. Some CPU can be saved too, incidentally.
-
--------------------------------------- BEGIN PSEUDO EXAMPLE
- // A) Init a configuration object
- WebPDecoderConfig config;
- CHECK(WebPInitDecoderConfig(&config));
-
- // B) optional: retrieve the bitstream's features.
- CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
-
- // C) Adjust 'config' options, if needed
- config.options.no_fancy_upsampling = 1;
- config.options.use_scaling = 1;
- config.options.scaled_width = scaledWidth();
- config.options.scaled_height = scaledHeight();
- // etc.
-
- // D) Specify 'config' output options for specifying output colorspace.
- // Optionally the external image decode buffer can also be specified.
- config.output.colorspace = MODE_BGRA;
- // Optionally, the config.output can be pointed to an external buffer as
- // well for decoding the image. This externally supplied memory buffer
- // should be big enough to store the decoded picture.
- config.output.u.RGBA.rgba = (uint8_t*) memory_buffer;
- config.output.u.RGBA.stride = scanline_stride;
- config.output.u.RGBA.size = total_size_of_the_memory_buffer;
- config.output.is_external_memory = 1;
-
- // E) Decode the WebP image. There are two variants w.r.t decoding image.
- // The first one (E.1) decodes the full image and the second one (E.2) is
- // used to incrementally decode the image using small input buffers.
- // Any one of these steps can be used to decode the WebP image.
-
- // E.1) Decode full image.
- CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
-
- // E.2) Decode image incrementally.
- WebPIDecoder* const idec = WebPIDecode(NULL, NULL, &config);
- CHECK(idec != NULL);
- while (bytes_remaining > 0) {
- VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
- if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
- bytes_remaining -= bytes_read;
- } else {
- break;
- }
- }
- WebPIDelete(idec);
-
- // F) Decoded image is now in config.output (and config.output.u.RGBA).
- // It can be saved, displayed or otherwise processed.
-
- // G) Reclaim memory allocated in config's object. It's safe to call
- // this function even if the memory is external and wasn't allocated
- // by WebPDecode().
- WebPFreeDecBuffer(&config.output);
-
--------------------------------------- END PSEUDO EXAMPLE
-
-Bugs:
-=====
-
-Please report all bugs to the issue tracker:
- https://bugs.chromium.org/p/webp
-Patches welcome! See this page to get started:
- https://www.webmproject.org/code/contribute/submitting-patches/
-
-Discuss:
-========
-
-Email: webp-discuss@webmproject.org
-Web: https://groups.google.com/a/webmproject.org/group/webp-discuss
diff --git a/c-lib/README.md b/c-lib/README.md
@@ -0,0 +1,54 @@
+# WebP Codec
+
+```
+ __ __ ____ ____ ____
+ / \\/ \/ _ \/ _ )/ _ \
+ \ / __/ _ \ __/
+ \__\__/\____/\_____/__/ ____ ___
+ / _/ / \ \ / _ \/ _/
+ / \_/ / / \ \ __/ \__
+ \____/____/\_____/_____/____/v1.6.0
+```
+
+WebP codec is a library to encode and decode images in WebP format. This package
+contains the library that can be used in other programs to add WebP support, as
+well as the command line tools 'cwebp' and 'dwebp' to compress and decompress
+images respectively.
+
+See https://developers.google.com/speed/webp for details on the image format.
+
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+
+It is released under the same license as the WebM project. See
+https://www.webmproject.org/license/software/ or the "COPYING" file for details.
+An additional intellectual property rights grant can be found in the file
+PATENTS.
+
+## Building
+
+See the [building documentation](doc/building.md).
+
+## Encoding and Decoding Tools
+
+The examples/ directory contains tools to encode and decode images and
+animations, view information about WebP images, and more. See the
+[tools documentation](doc/tools.md).
+
+## APIs
+
+See the [APIs documentation](doc/api.md), and API usage examples in the
+`examples/` directory.
+
+## Bugs
+
+Please report all bugs to the [issue tracker](https://issues.webmproject.org).
+For security reports, select 'Security report' from the Template dropdown.
+
+Patches welcome! See [how to contribute](CONTRIBUTING.md).
+
+## Discuss
+
+Email: webp-discuss@webmproject.org
+
+Web: https://groups.google.com/a/webmproject.org/group/webp-discuss
diff --git a/c-lib/README.mux b/c-lib/README.mux
@@ -1,258 +0,0 @@
- __ __ ____ ____ ____ __ __ _ __ __
- / \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
- \ / __/ _ \ __/ / / (_/ /__
- \__\__/\_____/_____/__/ \__//_/\_____/__/___/v1.2.2
-
-
-Description:
-============
-
-WebPMux: set of two libraries 'Mux' and 'Demux' for creation, extraction and
-manipulation of an extended format WebP file, which can have features like
-color profile, metadata and animation. Reference command-line tools 'webpmux'
-and 'vwebp' as well as the WebP container specification
-'doc/webp-container-spec.txt' are also provided in this package.
-
-WebP Mux tool:
-==============
-
-The examples/ directory contains a tool (webpmux) for manipulating WebP
-files. The webpmux tool can be used to create an extended format WebP file and
-also to extract or strip relevant data from such a file.
-
-A list of options is available using the -help command line flag:
-
-> webpmux -help
-Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
- webpmux -set SET_OPTIONS INPUT -o OUTPUT
- webpmux -duration DURATION_OPTIONS [-duration ...]
- INPUT -o OUTPUT
- webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
- webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
- [-bgcolor BACKGROUND_COLOR] -o OUTPUT
- webpmux -info INPUT
- webpmux [-h|-help]
- webpmux -version
- webpmux argument_file_name
-
-GET_OPTIONS:
- Extract relevant data:
- icc get ICC profile
- exif get EXIF metadata
- xmp get XMP metadata
- frame n get nth frame
-
-SET_OPTIONS:
- Set color profile/metadata/parameters:
- loop LOOP_COUNT set the loop count
- bgcolor BACKGROUND_COLOR set the animation background color
- icc file.icc set ICC profile
- exif file.exif set EXIF metadata
- xmp file.xmp set XMP metadata
- where: 'file.icc' contains the ICC profile to be set,
- 'file.exif' contains the EXIF metadata to be set
- 'file.xmp' contains the XMP metadata to be set
-
-DURATION_OPTIONS:
- Set duration of selected frames:
- duration set duration for each frames
- duration,frame set duration of a particular frame
- duration,start,end set duration of frames in the
- interval [start,end])
- where: 'duration' is the duration in milliseconds
- 'start' is the start frame index
- 'end' is the inclusive end frame index
- The special 'end' value '0' means: last frame.
-
-STRIP_OPTIONS:
- Strip color profile/metadata:
- icc strip ICC profile
- exif strip EXIF metadata
- xmp strip XMP metadata
-
-FRAME_OPTIONS(i):
- Create animation:
- file_i +di+[xi+yi[+mi[bi]]]
- where: 'file_i' is the i'th animation frame (WebP format),
- 'di' is the pause duration before next frame,
- 'xi','yi' specify the image offset for this frame,
- 'mi' is the dispose method for this frame (0 or 1),
- 'bi' is the blending method for this frame (+b or -b)
-
-LOOP_COUNT:
- Number of times to repeat the animation.
- Valid range is 0 to 65535 [Default: 0 (infinite)].
-
-BACKGROUND_COLOR:
- Background color of the canvas.
- A,R,G,B
- where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying
- the Alpha, Red, Green and Blue component values respectively
- [Default: 255,255,255,255]
-
-INPUT & OUTPUT are in WebP format.
-
-Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
-valid.
-
-Note: if a single file name is passed as the argument, the arguments will be
-tokenized from this file. The file name must not start with the character '-'.
-
-Visualization tool:
-===================
-
-The examples/ directory also contains a tool (vwebp) for viewing WebP files.
-It decodes the image and visualizes it using OpenGL. See the libwebp README
-for details on building and running this program.
-
-Mux API:
-========
-The Mux API contains methods for adding data to and reading data from WebP
-files. This API currently supports XMP/EXIF metadata, ICC profile and animation.
-Other features may be added in subsequent releases.
-
-Example#1 (pseudo code): Creating a WebPMux object with image data, color
-profile and XMP metadata.
-
- int copy_data = 0;
- WebPMux* mux = WebPMuxNew();
- // ... (Prepare image data).
- WebPMuxSetImage(mux, &image, copy_data);
- // ... (Prepare ICC profile data).
- WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
- // ... (Prepare XMP metadata).
- WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
- // Get data from mux in WebP RIFF format.
- WebPMuxAssemble(mux, &output_data);
- WebPMuxDelete(mux);
- // ... (Consume output_data; e.g. write output_data.bytes to file).
- WebPDataClear(&output_data);
-
-
-Example#2 (pseudo code): Get image and color profile data from a WebP file.
-
- int copy_data = 0;
- // ... (Read data from file).
- WebPMux* mux = WebPMuxCreate(&data, copy_data);
- WebPMuxGetFrame(mux, 1, &image);
- // ... (Consume image; e.g. call WebPDecode() to decode the data).
- WebPMuxGetChunk(mux, "ICCP", &icc_profile);
- // ... (Consume icc_profile).
- WebPMuxDelete(mux);
- free(data);
-
-
-For a detailed Mux API reference, please refer to the header file
-(src/webp/mux.h).
-
-Demux API:
-==========
-The Demux API enables extraction of images and extended format data from
-WebP files. This API currently supports reading of XMP/EXIF metadata, ICC
-profile and animated images. Other features may be added in subsequent
-releases.
-
-Code example: Demuxing WebP data to extract all the frames, ICC profile
-and EXIF/XMP metadata.
-
- WebPDemuxer* demux = WebPDemux(&webp_data);
- uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
- uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
- // ... (Get information about the features present in the WebP file).
- uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
-
- // ... (Iterate over all frames).
- WebPIterator iter;
- if (WebPDemuxGetFrame(demux, 1, &iter)) {
- do {
- // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(),
- // ... and get other frame properties like width, height, offsets etc.
- // ... see 'struct WebPIterator' below for more info).
- } while (WebPDemuxNextFrame(&iter));
- WebPDemuxReleaseIterator(&iter);
- }
-
- // ... (Extract metadata).
- WebPChunkIterator chunk_iter;
- if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter);
- // ... (Consume the ICC profile in 'chunk_iter.chunk').
- WebPDemuxReleaseChunkIterator(&chunk_iter);
- if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter);
- // ... (Consume the EXIF metadata in 'chunk_iter.chunk').
- WebPDemuxReleaseChunkIterator(&chunk_iter);
- if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter);
- // ... (Consume the XMP metadata in 'chunk_iter.chunk').
- WebPDemuxReleaseChunkIterator(&chunk_iter);
- WebPDemuxDelete(demux);
-
-
-For a detailed Demux API reference, please refer to the header file
-(src/webp/demux.h).
-
-AnimEncoder API:
-================
-The AnimEncoder API can be used to create animated WebP images.
-
-Code example:
-
- WebPAnimEncoderOptions enc_options;
- WebPAnimEncoderOptionsInit(&enc_options);
- // ... (Tune 'enc_options' as needed).
- WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
- while(<there are more frames>) {
- WebPConfig config;
- WebPConfigInit(&config);
- // ... (Tune 'config' as needed).
- WebPAnimEncoderAdd(enc, frame, duration, &config);
- }
- WebPAnimEncoderAssemble(enc, webp_data);
- WebPAnimEncoderDelete(enc);
- // ... (Write the 'webp_data' to a file, or re-mux it further).
-
-
-For a detailed AnimEncoder API reference, please refer to the header file
-(src/webp/mux.h).
-
-AnimDecoder API:
-================
-This AnimDecoder API allows decoding (possibly) animated WebP images.
-
-Code Example:
-
- WebPAnimDecoderOptions dec_options;
- WebPAnimDecoderOptionsInit(&dec_options);
- // Tune 'dec_options' as needed.
- WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options);
- WebPAnimInfo anim_info;
- WebPAnimDecoderGetInfo(dec, &anim_info);
- for (uint32_t i = 0; i < anim_info.loop_count; ++i) {
- while (WebPAnimDecoderHasMoreFrames(dec)) {
- uint8_t* buf;
- int timestamp;
- WebPAnimDecoderGetNext(dec, &buf, ×tamp);
- // ... (Render 'buf' based on 'timestamp').
- // ... (Do NOT free 'buf', as it is owned by 'dec').
- }
- WebPAnimDecoderReset(dec);
- }
- const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec);
- // ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data).
- WebPAnimDecoderDelete(dec);
-
-For a detailed AnimDecoder API reference, please refer to the header file
-(src/webp/demux.h).
-
-
-Bugs:
-=====
-
-Please report all bugs to the issue tracker:
- https://bugs.chromium.org/p/webp
-Patches welcome! See this page to get started:
- https://www.webmproject.org/code/contribute/submitting-patches/
-
-Discuss:
-========
-
-Email: webp-discuss@webmproject.org
-Web: https://groups.google.com/a/webmproject.org/group/webp-discuss
diff --git a/c-lib/README.webp_js b/c-lib/README.webp_js
@@ -1,75 +0,0 @@
- __ __ ____ ____ ____ __ ____
- / \\/ \ _ \ _ \ _ \ (__)/ __\
- \ / __/ _ \ __/ _) \_ \
- \__\__/_____/____/_/ /____/____/
-
-Description:
-============
-
-This file describes the compilation of libwebp into a JavaScript decoder
-using Emscripten and CMake.
-
- - install the Emscripten SDK following the procedure described at:
- https://emscripten.org/docs/getting_started/downloads.html#installation-instructions-using-the-emsdk-recommended
- After installation, you should have some global variable positioned to the
- location of the SDK. In particular, $EMSDK should point to the
- top-level directory containing Emscripten tools.
-
- - configure the project 'WEBP_JS' with CMake using:
-
- cd webp_js && \
- emcmake cmake -DWEBP_BUILD_WEBP_JS=ON \
- ../
-
- - compile webp.js using 'emmake make'.
-
- - that's it! Upon completion, you should have the webp.js and
- webp.wasm files generated.
-
-The callable JavaScript function is WebPToSDL(), which decodes a raw WebP
-bitstream into a canvas. See webp_js/index.html for a simple usage sample
-(see below for instructions).
-
-Demo HTML page:
-===============
-
- The HTML page webp_js/index.html requires an HTTP server to serve the WebP
- image example. It's easy to just use Python for that.
-
-cd webp_js && python -m SimpleHTTPServer 8080
-
-and then navigate to http://localhost:8080 in your favorite browser.
-
-
-Web-Assembly (WASM) version:
-============================
-
- CMakeLists.txt is configured to build the WASM version when using
- the option WEBP_BUILD_WEBP_JS=ON. The compilation step will assemble
- the files 'webp_wasm.js', 'webp_wasm.wasm' in the webp_js/ directory.
- See webp_js/index_wasm.html for a simple demo page using the WASM version
- of the library.
-
- You will need a fairly recent version of Emscripten (at least 2.0.18,
- latest-upstream is recommended) and of your WASM-enabled browser to run this
- version.
-
-Caveat:
-=======
-
- - First decoding using the library is usually slower, due to just-in-time
- compilation.
-
- - Some versions of llvm produce the following compile error when SSE2 is
- enabled.
-
-"Unsupported: %516 = bitcast <8 x i16> %481 to i128
- LLVM ERROR: BitCast Instruction not yet supported for integer types larger than 64 bits"
-
- The corresponding Emscripten bug is at:
- https://github.com/kripken/emscripten/issues/3788
-
- Therefore, SSE2 optimization is currently disabled in CMakeLists.txt.
-
- - If WEBP_ENABLE_SIMD is set to 1 the JavaScript version (webp.js) will be
- disabled as wasm2js does not support SIMD.
diff --git a/c-lib/build.gradle b/c-lib/build.gradle
@@ -105,6 +105,14 @@ model {
sources {
c {
source {
+ srcDir "sharpyuv"
+ include "sharpyuv.c"
+ include "sharpyuv_cpu.c"
+ include "sharpyuv_csp.c"
+ include "sharpyuv_dsp.c"
+ include "sharpyuv_gamma.c"
+ include "sharpyuv_neon.c"
+ include "sharpyuv_sse2.c"
srcDir "src/dec"
include "alpha_dec.c"
include "buffer_dec.c"
@@ -165,6 +173,7 @@ model {
include "color_cache_utils.c"
include "filters_utils.c"
include "huffman_utils.c"
+ include "palette.c"
include "quant_levels_dec_utils.c"
include "random_utils.c"
include "rescaler_utils.c"
diff --git a/c-lib/cmake/WebPConfig.cmake.in b/c-lib/cmake/WebPConfig.cmake.in
@@ -3,9 +3,17 @@ set(WEBP_VERSION ${WebP_VERSION})
@PACKAGE_INIT@
-include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+if(@WEBP_USE_THREAD@)
+ include(CMakeFindDependencyMacro)
+ find_dependency(Threads REQUIRED)
+endif()
-set(WebP_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
-set(WEBP_INCLUDE_DIRS ${WebP_INCLUDE_DIRS})
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(WebP_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set(WebP_INCLUDE_DIRS ${WebP_INCLUDE_DIR})
+set(WEBP_INCLUDE_DIRS ${WebP_INCLUDE_DIR})
set(WebP_LIBRARIES "@INSTALLED_LIBRARIES@")
set(WEBP_LIBRARIES "${WebP_LIBRARIES}")
+
+check_required_components(WebP)
diff --git a/c-lib/cmake/config.h.in b/c-lib/cmake/config.h.in
@@ -16,48 +16,18 @@
/* Define to 1 if you have the <cpu-features.h> header file. */
#cmakedefine HAVE_CPU_FEATURES_H 1
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#cmakedefine HAVE_DLFCN_H 1
-
/* Define to 1 if you have the <GLUT/glut.h> header file. */
#cmakedefine HAVE_GLUT_GLUT_H 1
/* Define to 1 if you have the <GL/glut.h> header file. */
#cmakedefine HAVE_GL_GLUT_H 1
-/* Define to 1 if you have the <inttypes.h> header file. */
-#cmakedefine HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#cmakedefine HAVE_MEMORY_H 1
-
/* Define to 1 if you have the <OpenGL/glut.h> header file. */
#cmakedefine HAVE_OPENGL_GLUT_H 1
-/* Have PTHREAD_PRIO_INHERIT. */
-#cmakedefine HAVE_PTHREAD_PRIO_INHERIT @HAVE_PTHREAD_PRIO_INHERIT@
-
/* Define to 1 if you have the <shlwapi.h> header file. */
#cmakedefine HAVE_SHLWAPI_H 1
-/* Define to 1 if you have the <stdint.h> header file. */
-#cmakedefine HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#cmakedefine HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#cmakedefine HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#cmakedefine HAVE_STRING_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#cmakedefine HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#cmakedefine HAVE_SYS_TYPES_H 1
-
/* Define to 1 if you have the <unistd.h> header file. */
#cmakedefine HAVE_UNISTD_H 1
@@ -93,9 +63,6 @@
/* Define to the version of this package. */
#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@"
-/* Define to 1 if you have the ANSI C header files. */
-#cmakedefine STDC_HEADERS 1
-
/* Version number of package */
#cmakedefine VERSION "@VERSION@"
@@ -127,6 +94,9 @@
/* Set to 1 if SSE4.1 is supported */
#cmakedefine WEBP_HAVE_SSE41 1
+/* Set to 1 if AVX2 is supported */
+#cmakedefine WEBP_HAVE_AVX2 1
+
/* Set to 1 if TIFF library is installed */
#cmakedefine WEBP_HAVE_TIFF 1
diff --git a/c-lib/cmake/cpu.cmake b/c-lib/cmake/cpu.cmake
@@ -18,7 +18,8 @@ function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD)
unset(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} CACHE)
cmake_push_check_state()
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR})
- check_c_source_compiles("
+ check_c_source_compiles(
+ "
#include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\"
int main(void) {
#if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
@@ -26,7 +27,8 @@ function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD)
#endif
return 0;
}
- " WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
+ "
+ WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
cmake_pop_check_state()
if(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
set(WEBP_HAVE_${WEBP_SIMD_FLAG} 1 PARENT_SCOPE)
@@ -36,9 +38,9 @@ function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD)
endfunction()
# those are included in the names of WEBP_USE_* in c++ code.
-set(WEBP_SIMD_FLAGS "SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA")
+set(WEBP_SIMD_FLAGS "AVX2;SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA")
set(WEBP_SIMD_FILE_EXTENSIONS
- "_sse41.c;_sse2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
+ "_avx2.c;_sse41.c;_sse2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")
# With at least Visual Studio 12 (2013)+ /arch is not necessary to build SSE2
# or SSE4 code unless a lesser /arch is forced. MSVC does not have a SSE4
@@ -48,21 +50,18 @@ if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if(MSVC_VERSION GREATER_EQUAL 1800 AND NOT CMAKE_C_FLAGS MATCHES "/arch:")
set(SIMD_ENABLE_FLAGS)
else()
- set(SIMD_ENABLE_FLAGS "/arch:AVX;/arch:SSE2;;;;")
+ set(SIMD_ENABLE_FLAGS "/arch:AVX2;/arch:AVX;/arch:SSE2;;;;")
endif()
set(SIMD_DISABLE_FLAGS)
else()
- set(SIMD_ENABLE_FLAGS
- "-msse4.1;-msse2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
- set(SIMD_DISABLE_FLAGS
- "-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa")
+ set(SIMD_ENABLE_FLAGS "-mavx2;-msse4.1;-msse2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
+ set(SIMD_DISABLE_FLAGS "-mno-avx2;-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa")
endif()
-set(WEBP_SIMD_FILES_TO_NOT_INCLUDE)
set(WEBP_SIMD_FILES_TO_INCLUDE)
set(WEBP_SIMD_FLAGS_TO_INCLUDE)
-if(${ANDROID})
+if(ANDROID AND ANDROID_ABI)
if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
# This is because Android studio uses the configuration "-march=armv7-a
# -mfloat-abi=softfp -mfpu=vfpv3-d16" that does not trigger neon
@@ -75,8 +74,8 @@ list(LENGTH WEBP_SIMD_FLAGS WEBP_SIMD_FLAGS_LENGTH)
math(EXPR WEBP_SIMD_FLAGS_RANGE "${WEBP_SIMD_FLAGS_LENGTH} - 1")
foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
- # With Emscripten 2.0.9 -msimd128 -mfpu=neon will enable NEON, but the
- # source will fail to compile.
+ # With Emscripten 2.0.9 -msimd128 -mfpu=neon will enable NEON, but the source
+ # will fail to compile.
if(EMSCRIPTEN AND ${I_SIMD} GREATER_EQUAL 2)
break()
endif()
@@ -107,8 +106,9 @@ foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
endif()
# Check which files we should include or not.
list(GET WEBP_SIMD_FILE_EXTENSIONS ${I_SIMD} WEBP_SIMD_FILE_EXTENSION)
- file(GLOB SIMD_FILES "${CMAKE_CURRENT_LIST_DIR}/../"
- "src/dsp/*${WEBP_SIMD_FILE_EXTENSION}")
+ file(GLOB SIMD_FILES
+ "${CMAKE_CURRENT_LIST_DIR}/../sharpyuv/*${WEBP_SIMD_FILE_EXTENSION}"
+ "${CMAKE_CURRENT_LIST_DIR}/../src/dsp/*${WEBP_SIMD_FILE_EXTENSION}")
if(WEBP_HAVE_${WEBP_SIMD_FLAG})
# Memorize the file and flags.
foreach(FILE ${SIMD_FILES})
@@ -142,11 +142,9 @@ foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
set(COMMON_PATTERNS)
endif()
set(CMAKE_REQUIRED_DEFINITIONS ${SIMD_COMPILE_FLAG})
- check_c_source_compiles("int main(void) {return 0;}"
- FLAG_${SIMD_COMPILE_FLAG}
- FAIL_REGEX
- "warning: argument unused during compilation:"
- ${COMMON_PATTERNS})
+ check_c_source_compiles(
+ "int main(void) {return 0;}" FLAG_${SIMD_COMPILE_FLAG} FAIL_REGEX
+ "warning: argument unused during compilation:" ${COMMON_PATTERNS})
if(NOT FLAG_${SIMD_COMPILE_FLAG})
unset(HAS_COMPILE_FLAG)
unset(HAS_COMPILE_FLAG CACHE)
diff --git a/c-lib/cmake/deps.cmake b/c-lib/cmake/deps.cmake
@@ -10,24 +10,30 @@
# Check for compiler options.
include(CheckCSourceCompiles)
-check_c_source_compiles("
+check_c_source_compiles(
+ "
int main(void) {
(void)__builtin_bswap16(0);
return 0;
}
- " HAVE_BUILTIN_BSWAP16)
-check_c_source_compiles("
+ "
+ HAVE_BUILTIN_BSWAP16)
+check_c_source_compiles(
+ "
int main(void) {
(void)__builtin_bswap32(0);
return 0;
}
- " HAVE_BUILTIN_BSWAP32)
-check_c_source_compiles("
+ "
+ HAVE_BUILTIN_BSWAP32)
+check_c_source_compiles(
+ "
int main(void) {
(void)__builtin_bswap64(0);
return 0;
}
- " HAVE_BUILTIN_BSWAP64)
+ "
+ HAVE_BUILTIN_BSWAP64)
# Check for libraries.
if(WEBP_USE_THREAD)
@@ -37,15 +43,7 @@ if(WEBP_USE_THREAD)
if(CMAKE_USE_PTHREADS_INIT AND NOT CMAKE_SYSTEM_NAME STREQUAL "QNX")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
endif()
- check_c_source_compiles("
- #include <pthread.h>
- int main (void) {
- int attr = PTHREAD_PRIO_INHERIT;
- return attr;
- }
- " FLAG_HAVE_PTHREAD_PRIO_INHERIT)
- set(HAVE_PTHREAD_PRIO_INHERIT ${FLAG_HAVE_PTHREAD_PRIO_INHERIT})
- list(APPEND WEBP_DEP_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
+ list(APPEND WEBP_DEP_LIBRARIES Threads::Threads)
endif()
set(WEBP_USE_THREAD ${Threads_FOUND})
endif()
@@ -59,89 +57,88 @@ set(WEBP_HAVE_GL ${OPENGL_FOUND})
# Check if we need to link to the C math library. We do not look for it as it is
# not found when cross-compiling, while it is here.
-check_c_source_compiles("
+check_c_source_compiles(
+ "
#include <math.h>
int main(int argc, char** argv) {
return (int)pow(argc, 2.5);
}
- " HAVE_MATH_LIBRARY)
+ "
+ HAVE_MATH_LIBRARY)
if(NOT HAVE_MATH_LIBRARY)
message(STATUS "Adding -lm flag.")
+ list(APPEND SHARPYUV_DEP_LIBRARIES m)
list(APPEND WEBP_DEP_LIBRARIES m)
endif()
# Find the standard image libraries.
set(WEBP_DEP_IMG_LIBRARIES)
set(WEBP_DEP_IMG_INCLUDE_DIRS)
-foreach(I_LIB PNG JPEG TIFF)
- find_package(${I_LIB})
- set(WEBP_HAVE_${I_LIB} ${${I_LIB}_FOUND})
- if(${I_LIB}_FOUND)
- list(APPEND WEBP_DEP_IMG_LIBRARIES ${${I_LIB}_LIBRARIES})
- list(APPEND WEBP_DEP_IMG_INCLUDE_DIRS ${${I_LIB}_INCLUDE_DIR}
- ${${I_LIB}_INCLUDE_DIRS})
+if(WEBP_FIND_IMG_LIBS)
+ foreach(I_LIB PNG JPEG TIFF)
+ # Disable tiff when compiling in static mode as it is failing on Ubuntu.
+ if(WEBP_LINK_STATIC AND ${I_LIB} STREQUAL "TIFF")
+ message(STATUS "TIFF is disabled when statically linking.")
+ continue()
+ endif()
+ find_package(${I_LIB})
+ set(WEBP_HAVE_${I_LIB} ${${I_LIB}_FOUND})
+ if(${I_LIB}_FOUND)
+ list(APPEND WEBP_DEP_IMG_LIBRARIES ${${I_LIB}_LIBRARIES})
+ list(APPEND WEBP_DEP_IMG_INCLUDE_DIRS ${${I_LIB}_INCLUDE_DIR}
+ ${${I_LIB}_INCLUDE_DIRS})
+ endif()
+ endforeach()
+ if(WEBP_DEP_IMG_INCLUDE_DIRS)
+ list(REMOVE_DUPLICATES WEBP_DEP_IMG_INCLUDE_DIRS)
endif()
-endforeach()
-if(WEBP_DEP_IMG_INCLUDE_DIRS)
- list(REMOVE_DUPLICATES WEBP_DEP_IMG_INCLUDE_DIRS)
-endif()
-# GIF detection, gifdec isn't part of the imageio lib.
-include(CMakePushCheckState)
-set(WEBP_DEP_GIF_LIBRARIES)
-set(WEBP_DEP_GIF_INCLUDE_DIRS)
-find_package(GIF)
-set(WEBP_HAVE_GIF ${GIF_FOUND})
-if(GIF_FOUND)
- # GIF find_package only locates the header and library, it doesn't fail
- # compile tests when detecting the version, but falls back to 3 (as of at
- # least cmake 3.7.2). Make sure the library links to avoid incorrect detection
- # when cross compiling.
- cmake_push_check_state()
- set(CMAKE_REQUIRED_LIBRARIES ${GIF_LIBRARIES})
- set(CMAKE_REQUIRED_INCLUDES ${GIF_INCLUDE_DIR})
- check_c_source_compiles("
+ # GIF detection, gifdec isn't part of the imageio lib.
+ include(CMakePushCheckState)
+ set(WEBP_DEP_GIF_LIBRARIES)
+ set(WEBP_DEP_GIF_INCLUDE_DIRS)
+ find_package(GIF)
+ set(WEBP_HAVE_GIF ${GIF_FOUND})
+ if(GIF_FOUND)
+ # GIF find_package only locates the header and library, it doesn't fail
+ # compile tests when detecting the version, but falls back to 3 (as of at
+ # least cmake 3.7.2). Make sure the library links to avoid incorrect
+ # detection when cross compiling.
+ cmake_push_check_state()
+ set(CMAKE_REQUIRED_LIBRARIES ${GIF_LIBRARIES})
+ set(CMAKE_REQUIRED_INCLUDES ${GIF_INCLUDE_DIR})
+ check_c_source_compiles(
+ "
#include <gif_lib.h>
int main(void) {
(void)DGifOpenFileHandle;
return 0;
}
- " GIF_COMPILES)
- cmake_pop_check_state()
- if(GIF_COMPILES)
- list(APPEND WEBP_DEP_GIF_LIBRARIES ${GIF_LIBRARIES})
- list(APPEND WEBP_DEP_GIF_INCLUDE_DIRS ${GIF_INCLUDE_DIR})
- else()
- unset(GIF_FOUND)
+ "
+ GIF_COMPILES)
+ cmake_pop_check_state()
+ if(GIF_COMPILES)
+ list(APPEND WEBP_DEP_GIF_LIBRARIES ${GIF_LIBRARIES})
+ list(APPEND WEBP_DEP_GIF_INCLUDE_DIRS ${GIF_INCLUDE_DIR})
+ else()
+ unset(GIF_FOUND)
+ endif()
endif()
endif()
# Check for specific headers.
include(CheckIncludeFiles)
-check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
-check_include_files(dlfcn.h HAVE_DLFCN_H)
check_include_files(GLUT/glut.h HAVE_GLUT_GLUT_H)
check_include_files(GL/glut.h HAVE_GL_GLUT_H)
-check_include_files(inttypes.h HAVE_INTTYPES_H)
-check_include_files(memory.h HAVE_MEMORY_H)
check_include_files(OpenGL/glut.h HAVE_OPENGL_GLUT_H)
check_include_files(shlwapi.h HAVE_SHLWAPI_H)
-check_include_files(stdint.h HAVE_STDINT_H)
-check_include_files(stdlib.h HAVE_STDLIB_H)
-check_include_files(strings.h HAVE_STRINGS_H)
-check_include_files(string.h HAVE_STRING_H)
-check_include_files(sys/stat.h HAVE_SYS_STAT_H)
-check_include_files(sys/types.h HAVE_SYS_TYPES_H)
check_include_files(unistd.h HAVE_UNISTD_H)
check_include_files(wincodec.h HAVE_WINCODEC_H)
check_include_files(windows.h HAVE_WINDOWS_H)
# Windows specifics
if(HAVE_WINCODEC_H)
- list(APPEND WEBP_DEP_LIBRARIES
- shlwapi
- ole32
- windowscodecs)
+ list(APPEND WEBP_DEP_LIBRARIES shlwapi ole32 windowscodecs)
endif()
# Check for SIMD extensions.
@@ -153,17 +150,12 @@ set(PACKAGE_NAME ${PROJECT_NAME})
# Read from configure.ac.
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_AC)
-string(REGEX MATCHALL
- "\\[([0-9a-z\\.:/]*)\\]"
- CONFIGURE_AC_PACKAGE_INFO
+string(REGEX MATCHALL "\\[([0-9a-z\\.:/]*)\\]" CONFIGURE_AC_PACKAGE_INFO
${CONFIGURE_AC})
function(strip_bracket VAR)
string(LENGTH ${${VAR}} TMP_LEN)
math(EXPR TMP_LEN ${TMP_LEN}-2)
- string(SUBSTRING ${${VAR}}
- 1
- ${TMP_LEN}
- TMP_SUB)
+ string(SUBSTRING ${${VAR}} 1 ${TMP_LEN} TMP_SUB)
set(${VAR} ${TMP_SUB} PARENT_SCOPE)
endfunction()
diff --git a/c-lib/configure.ac b/c-lib/configure.ac
@@ -1,5 +1,5 @@
-AC_INIT([libwebp], [1.2.2],
- [https://bugs.chromium.org/p/webp],,
+AC_INIT([libwebp], [1.6.0],
+ [https://issues.webmproject.org],,
[https://developers.google.com/speed/webp])
AC_CANONICAL_HOST
AC_PREREQ([2.60])
@@ -9,7 +9,8 @@ dnl === automake >= 1.12 requires this for 'unusual archivers' support.
dnl === it must occur before LT_INIT (AC_PROG_LIBTOOL).
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
-AC_PROG_LIBTOOL
+dnl === AC_PROG_LIBTOOL is deprecated.
+m4_ifdef([LT_INIT], [LT_INIT], [AC_PROG_LIBTOOL])
AC_PROG_SED
AM_PROG_CC_C_O
@@ -27,7 +28,8 @@ AC_ARG_ENABLE([everything],
AS_HELP_STRING([--enable-everything],
[Enable all optional targets. These can still be
disabled with --disable-target]),
- [SET_IF_UNSET([enable_libwebpdecoder], [$enableval])
+ [SET_IF_UNSET([enable_libsharpyuv], [$enableval])
+ SET_IF_UNSET([enable_libwebpdecoder], [$enableval])
SET_IF_UNSET([enable_libwebpdemux], [$enableval])
SET_IF_UNSET([enable_libwebpextras], [$enableval])
SET_IF_UNSET([enable_libwebpmux], [$enableval])])
@@ -104,6 +106,7 @@ TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wall])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wconstant-conversion])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wdeclaration-after-statement])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra-semi-stmt])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wfloat-conversion])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-nonliteral])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-security])
@@ -113,6 +116,7 @@ TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wparentheses-equality])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshorten-64-to-32])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wstrict-prototypes])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wundef])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code-aggressive])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code])
@@ -157,6 +161,25 @@ AS_IF([test "$GCC" = "yes" ], [
AC_SUBST([AM_CFLAGS])
dnl === Check for machine specific flags
+AC_ARG_ENABLE([avx2],
+ AS_HELP_STRING([--disable-avx2],
+ [Disable detection of AVX2 support
+ @<:@default=auto@:>@]))
+
+AS_IF([test "x$enable_avx2" != "xno" -a "x$enable_sse4_1" != "xno"
+ -a "x$enable_sse2" != "xno"], [
+ AVX2_FLAGS="$INTRINSICS_CFLAGS $AVX2_FLAGS"
+ TEST_AND_ADD_CFLAGS([AVX2_FLAGS], [-mavx2])
+ AS_IF([test -n "$AVX2_FLAGS"], [
+ SAVED_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS $AVX2_FLAGS"
+ AC_CHECK_HEADER([immintrin.h],
+ [AC_DEFINE(WEBP_HAVE_AVX2, [1],
+ [Set to 1 if AVX2 is supported])],
+ [AVX2_FLAGS=""])
+ CFLAGS=$SAVED_CFLAGS])
+ AC_SUBST([AVX2_FLAGS])])
+
AC_ARG_ENABLE([sse4.1],
AS_HELP_STRING([--disable-sse4.1],
[Disable detection of SSE4.1 support
@@ -462,7 +485,7 @@ AC_ARG_ENABLE([sdl],
@<:@default=auto@:>@]))
AS_IF([test "x$enable_sdl" != "xno"], [
CLEAR_LIBVARS([SDL])
- AC_PATH_PROGS([LIBSDL_CONFIG], [sdl-config])
+ AC_PATH_PROGS([LIBSDL_CONFIG], [sdl2-config])
if test -n "$LIBSDL_CONFIG"; then
SDL_INCLUDES=`$LIBSDL_CONFIG --cflags`
SDL_LIBS="`$LIBSDL_CONFIG --libs`"
@@ -472,13 +495,12 @@ AS_IF([test "x$enable_sdl" != "xno"], [
sdl_header="no"
LIBCHECK_PROLOGUE([SDL])
- AC_CHECK_HEADER([SDL/SDL.h], [sdl_header="SDL/SDL.h"],
- [AC_CHECK_HEADER([SDL.h], [sdl_header="SDL.h"],
- [AC_MSG_WARN(SDL library not available - no sdl.h)])])
+ AC_CHECK_HEADER([SDL2/SDL.h], [sdl_header="SDL2/SDL.h"],
+ [AC_MSG_WARN(SDL2 library not available - no SDL.h)])
if test x"$sdl_header" != "xno"; then
AC_LANG_PUSH(C)
SDL_SAVED_LIBS="$LIBS"
- for lib in "" "-lSDL" "-lSDLmain -lSDL"; do
+ for lib in "" "-lSDL2" "-lSDL2main -lSDL2"; do
LIBS="$SDL_SAVED_LIBS $lib"
# Perform a full link to ensure SDL_main is resolved if needed.
AC_LINK_IFELSE(
@@ -747,17 +769,21 @@ fi
dnl =========================
+dnl Add an empty webp_libname_prefix variable for use in *.pc.in.
+AC_SUBST([webp_libname_prefix])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([src/webp/config.h])
AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \
examples/Makefile extras/Makefile imageio/Makefile \
+ sharpyuv/Makefile sharpyuv/libsharpyuv.pc \
src/dec/Makefile src/enc/Makefile src/dsp/Makefile \
src/demux/Makefile src/mux/Makefile \
src/utils/Makefile \
src/libwebp.pc src/libwebpdecoder.pc \
src/demux/libwebpdemux.pc src/mux/libwebpmux.pc])
-
+dnl fix exports from MinGW builds
+AC_CONFIG_COMMANDS_POST([$SED -i 's/-DDLL_EXPORT/-DWEBP_DLL/' config.status])
AC_OUTPUT
AC_MSG_NOTICE([
diff --git a/c-lib/doc/README b/c-lib/doc/README
@@ -1,29 +0,0 @@
-
-Generate libwebp Container Spec Docs from Text Source
-=====================================================
-
-HTML generation requires kramdown [1], easily installed as a
-rubygem [2]. Rubygems installation should satisfy dependencies
-automatically.
-
-[1]: https://kramdown.gettalong.org/
-[2]: https://rubygems.org/
-
-HTML generation can then be done from the project root:
-
-$ kramdown doc/webp-container-spec.txt --template doc/template.html > \
- doc/output/webp-container-spec.html
-
-kramdown can optionally syntax highlight code blocks, using CodeRay [3],
-a dependency of kramdown that rubygems will install automatically. The
-following will apply inline CSS styling; an external stylesheet is not
-needed.
-
-$ kramdown doc/webp-lossless-bitstream-spec.txt --template \
- doc/template.html --coderay-css style --coderay-line-numbers ' ' \
- --coderay-default-lang c > \
- doc/output/webp-lossless-bitstream-spec.html
-
-Optimally, use kramdown 0.13.7 or newer if syntax highlighting desired.
-
-[3]: https://github.com/rubychan/coderay
diff --git a/c-lib/doc/api.md b/c-lib/doc/api.md
@@ -0,0 +1,386 @@
+# WebP APIs
+
+## Encoding API
+
+The main encoding functions are available in the header src/webp/encode.h
+
+The ready-to-use ones are:
+
+```c
+size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+size_t WebPEncodeRGBA(const uint8_t* rgba, int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+```
+
+They will convert raw RGB samples to a WebP data. The only control supplied is
+the quality factor.
+
+There are some variants for using the lossless format:
+
+```c
+size_t WebPEncodeLosslessRGB(const uint8_t* rgb, int width, int height,
+ int stride, uint8_t** output);
+size_t WebPEncodeLosslessBGR(const uint8_t* bgr, int width, int height,
+ int stride, uint8_t** output);
+size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, int width, int height,
+ int stride, uint8_t** output);
+size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
+ int stride, uint8_t** output);
+```
+
+Of course in this case, no quality factor is needed since the compression occurs
+without loss of the input values, at the expense of larger output sizes.
+
+### Advanced encoding API
+
+A more advanced API is based on the WebPConfig and WebPPicture structures.
+
+WebPConfig contains the encoding settings and is not tied to a particular
+picture. WebPPicture contains input data, on which some WebPConfig will be used
+for compression. The encoding flow looks like:
+
+```c
+#include <webp/encode.h>
+
+// Setup a config, starting form a preset and tuning some additional
+// parameters
+WebPConfig config;
+if (!WebPConfigPreset(&config, WEBP_PRESET_PHOTO, quality_factor)) {
+ return 0; // version error
+}
+// ... additional tuning
+config.sns_strength = 90;
+config.filter_sharpness = 6;
+config_error = WebPValidateConfig(&config); // not mandatory, but useful
+
+// Setup the input data
+WebPPicture pic;
+if (!WebPPictureInit(&pic)) {
+ return 0; // version error
+}
+pic.width = width;
+pic.height = height;
+// allocated picture of dimension width x height
+if (!WebPPictureAlloc(&pic)) {
+ return 0; // memory error
+}
+// at this point, 'pic' has been initialized as a container,
+// and can receive the Y/U/V samples.
+// Alternatively, one could use ready-made import functions like
+// WebPPictureImportRGB(), which will take care of memory allocation.
+// In any case, past this point, one will have to call
+// WebPPictureFree(&pic) to reclaim memory.
+
+// Set up a byte-output write method. WebPMemoryWriter, for instance.
+WebPMemoryWriter wrt;
+WebPMemoryWriterInit(&wrt); // initialize 'wrt'
+
+pic.writer = MyFileWriter;
+pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work;
+
+// Compress!
+int ok = WebPEncode(&config, &pic); // ok = 0 => error occurred!
+WebPPictureFree(&pic); // must be called independently of the 'ok' result.
+
+// output data should have been handled by the writer at that point.
+// -> compressed data is the memory buffer described by wrt.mem / wrt.size
+
+// deallocate the memory used by compressed data
+WebPMemoryWriterClear(&wrt);
+```
+
+## Decoding API
+
+This is mainly just one function to call:
+
+```c
+#include "webp/decode.h"
+uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+```
+
+Please have a look at the file src/webp/decode.h for the details. There are
+variants for decoding in BGR/RGBA/ARGB/BGRA order, along with decoding to raw
+Y'CbCr samples. One can also decode the image directly into a pre-allocated
+buffer.
+
+To detect a WebP file and gather the picture's dimensions, the function:
+
+```c
+int WebPGetInfo(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+```
+
+is supplied. No decoding is involved when using it.
+
+### Incremental decoding API
+
+In the case when data is being progressively transmitted, pictures can still be
+incrementally decoded using a slightly more complicated API. Decoder state is
+stored into an instance of the WebPIDecoder object. This object can be created
+with the purpose of decoding either RGB or Y'CbCr samples. For instance:
+
+```c
+WebPDecBuffer buffer;
+WebPInitDecBuffer(&buffer);
+buffer.colorspace = MODE_BGR;
+...
+WebPIDecoder* idec = WebPINewDecoder(&buffer);
+```
+
+As data is made progressively available, this incremental-decoder object can be
+used to decode the picture further. There are two (mutually exclusive) ways to
+pass freshly arrived data:
+
+either by appending the fresh bytes:
+
+```c
+WebPIAppend(idec, fresh_data, size_of_fresh_data);
+```
+
+or by just mentioning the new size of the transmitted data:
+
+```c
+WebPIUpdate(idec, buffer, size_of_transmitted_buffer);
+```
+
+Note that 'buffer' can be modified between each call to WebPIUpdate, in
+particular when the buffer is resized to accommodate larger data.
+
+These functions will return the decoding status: either VP8_STATUS_SUSPENDED if
+decoding is not finished yet or VP8_STATUS_OK when decoding is done. Any other
+status is an error condition.
+
+The 'idec' object must always be released (even upon an error condition) by
+calling: WebPIDelete(idec).
+
+To retrieve partially decoded picture samples, one must use the corresponding
+method: WebPIDecGetRGB or WebPIDecGetYUVA. It will return the last displayable
+pixel row.
+
+Lastly, note that decoding can also be performed into a pre-allocated pixel
+buffer. This buffer must be passed when creating a WebPIDecoder, calling
+WebPINewRGB() or WebPINewYUVA().
+
+Please have a look at the src/webp/decode.h header for further details.
+
+### Advanced Decoding API
+
+WebP decoding supports an advanced API which provides on-the-fly cropping and
+rescaling, something of great usefulness on memory-constrained environments like
+mobile phones. Basically, the memory usage will scale with the output's size,
+not the input's, when one only needs a quick preview or a zoomed in portion of
+an otherwise too-large picture. Some CPU can be saved too, incidentally.
+
+```c
+// A) Init a configuration object
+WebPDecoderConfig config;
+CHECK(WebPInitDecoderConfig(&config));
+
+// B) optional: retrieve the bitstream's features.
+CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
+
+// C) Adjust 'config' options, if needed
+config.options.no_fancy_upsampling = 1;
+config.options.use_scaling = 1;
+config.options.scaled_width = scaledWidth();
+config.options.scaled_height = scaledHeight();
+// etc.
+
+// D) Specify 'config' output options for specifying output colorspace.
+// Optionally the external image decode buffer can also be specified.
+config.output.colorspace = MODE_BGRA;
+// Optionally, the config.output can be pointed to an external buffer as
+// well for decoding the image. This externally supplied memory buffer
+// should be big enough to store the decoded picture.
+config.output.u.RGBA.rgba = (uint8_t*) memory_buffer;
+config.output.u.RGBA.stride = scanline_stride;
+config.output.u.RGBA.size = total_size_of_the_memory_buffer;
+config.output.is_external_memory = 1;
+config_error = WebPValidateDecoderConfig(&config); // not mandatory, but useful
+
+// E) Decode the WebP image. There are two variants w.r.t decoding image.
+// The first one (E.1) decodes the full image and the second one (E.2) is
+// used to incrementally decode the image using small input buffers.
+// Any one of these steps can be used to decode the WebP image.
+
+// E.1) Decode full image.
+CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
+
+// E.2) Decode image incrementally.
+WebPIDecoder* const idec = WebPIDecode(NULL, NULL, &config);
+CHECK(idec != NULL);
+while (bytes_remaining > 0) {
+ VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
+ if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
+ bytes_remaining -= bytes_read;
+ } else {
+ break;
+ }
+}
+WebPIDelete(idec);
+
+// F) Decoded image is now in config.output (and config.output.u.RGBA).
+// It can be saved, displayed or otherwise processed.
+
+// G) Reclaim memory allocated in config's object. It's safe to call
+// this function even if the memory is external and wasn't allocated
+// by WebPDecode().
+WebPFreeDecBuffer(&config.output);
+```
+
+## WebP Mux
+
+WebPMux is a set of two libraries 'Mux' and 'Demux' for creation, extraction and
+manipulation of an extended format WebP file, which can have features like color
+profile, metadata and animation. Reference command-line tools `webpmux` and
+`vwebp` as well as the WebP container specification
+'doc/webp-container-spec.txt' are also provided in this package, see the
+[tools documentation](tools.md).
+
+### Mux API
+
+The Mux API contains methods for adding data to and reading data from WebP
+files. This API currently supports XMP/EXIF metadata, ICC profile and animation.
+Other features may be added in subsequent releases.
+
+Example#1 (pseudo code): Creating a WebPMux object with image data, color
+profile and XMP metadata.
+
+```c
+int copy_data = 0;
+WebPMux* mux = WebPMuxNew();
+// ... (Prepare image data).
+WebPMuxSetImage(mux, &image, copy_data);
+// ... (Prepare ICC profile data).
+WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
+// ... (Prepare XMP metadata).
+WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
+// Get data from mux in WebP RIFF format.
+WebPMuxAssemble(mux, &output_data);
+WebPMuxDelete(mux);
+// ... (Consume output_data; e.g. write output_data.bytes to file).
+WebPDataClear(&output_data);
+```
+
+Example#2 (pseudo code): Get image and color profile data from a WebP file.
+
+```c
+int copy_data = 0;
+// ... (Read data from file).
+WebPMux* mux = WebPMuxCreate(&data, copy_data);
+WebPMuxGetFrame(mux, 1, &image);
+// ... (Consume image; e.g. call WebPDecode() to decode the data).
+WebPMuxGetChunk(mux, "ICCP", &icc_profile);
+// ... (Consume icc_profile).
+WebPMuxDelete(mux);
+free(data);
+```
+
+For a detailed Mux API reference, please refer to the header file
+(src/webp/mux.h).
+
+### Demux API
+
+The Demux API enables extraction of images and extended format data from WebP
+files. This API currently supports reading of XMP/EXIF metadata, ICC profile and
+animated images. Other features may be added in subsequent releases.
+
+Code example: Demuxing WebP data to extract all the frames, ICC profile and
+EXIF/XMP metadata.
+
+```c
+WebPDemuxer* demux = WebPDemux(&webp_data);
+uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
+uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
+// ... (Get information about the features present in the WebP file).
+uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
+
+// ... (Iterate over all frames).
+WebPIterator iter;
+if (WebPDemuxGetFrame(demux, 1, &iter)) {
+ do {
+ // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(),
+ // ... and get other frame properties like width, height, offsets etc.
+ // ... see 'struct WebPIterator' below for more info).
+ } while (WebPDemuxNextFrame(&iter));
+ WebPDemuxReleaseIterator(&iter);
+}
+
+// ... (Extract metadata).
+WebPChunkIterator chunk_iter;
+if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter);
+// ... (Consume the ICC profile in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter);
+// ... (Consume the EXIF metadata in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter);
+// ... (Consume the XMP metadata in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+WebPDemuxDelete(demux);
+```
+
+For a detailed Demux API reference, please refer to the header file
+(src/webp/demux.h).
+
+## AnimEncoder API
+
+The AnimEncoder API can be used to create animated WebP images.
+
+Code example:
+
+```c
+WebPAnimEncoderOptions enc_options;
+WebPAnimEncoderOptionsInit(&enc_options);
+// ... (Tune 'enc_options' as needed).
+WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
+while(<there are more frames>) {
+ WebPConfig config;
+ WebPConfigInit(&config);
+ // ... (Tune 'config' as needed).
+ WebPAnimEncoderAdd(enc, frame, duration, &config);
+}
+WebPAnimEncoderAssemble(enc, webp_data);
+WebPAnimEncoderDelete(enc);
+// ... (Write the 'webp_data' to a file, or re-mux it further).
+```
+
+For a detailed AnimEncoder API reference, please refer to the header file
+(src/webp/mux.h).
+
+## AnimDecoder API
+
+This AnimDecoder API allows decoding (possibly) animated WebP images.
+
+Code Example:
+
+```c
+WebPAnimDecoderOptions dec_options;
+WebPAnimDecoderOptionsInit(&dec_options);
+// Tune 'dec_options' as needed.
+WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options);
+WebPAnimInfo anim_info;
+WebPAnimDecoderGetInfo(dec, &anim_info);
+for (uint32_t i = 0; i < anim_info.loop_count; ++i) {
+ while (WebPAnimDecoderHasMoreFrames(dec)) {
+ uint8_t* buf;
+ int timestamp;
+ WebPAnimDecoderGetNext(dec, &buf, ×tamp);
+ // ... (Render 'buf' based on 'timestamp').
+ // ... (Do NOT free 'buf', as it is owned by 'dec').
+ }
+ WebPAnimDecoderReset(dec);
+}
+const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec);
+// ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data).
+WebPAnimDecoderDelete(dec);
+```
+
+For a detailed AnimDecoder API reference, please refer to the header file
+(src/webp/demux.h).
diff --git a/c-lib/doc/building.md b/c-lib/doc/building.md
@@ -0,0 +1,231 @@
+# Building
+
+## Windows build
+
+By running:
+
+```batch
+nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output
+```
+
+the directory `output\release-static\(x64|x86)\bin` will contain the tools
+cwebp.exe and dwebp.exe. The directory `output\release-static\(x64|x86)\lib`
+will contain the libwebp static library. The target architecture (x86/x64) is
+detected by Makefile.vc from the Visual Studio compiler (cl.exe) available in
+the system path.
+
+## Unix build using makefile.unix
+
+On platforms with GNU tools installed (gcc and make), running
+
+```shell
+make -f makefile.unix
+```
+
+will build the binaries examples/cwebp and examples/dwebp, along with the static
+library src/libwebp.a. No system-wide installation is supplied, as this is a
+simple alternative to the full installation system based on the autoconf tools
+(see below). Please refer to makefile.unix for additional details and
+customizations.
+
+## Using autoconf tools
+
+Prerequisites: a compiler (e.g., gcc), make, autoconf, automake, libtool.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install gcc make autoconf automake libtool
+```
+
+When building from git sources, you will need to run autogen.sh to generate the
+configure script.
+
+```shell
+./configure
+make
+make install
+```
+
+should be all you need to have the following files
+
+```
+/usr/local/include/webp/decode.h
+/usr/local/include/webp/encode.h
+/usr/local/include/webp/types.h
+/usr/local/lib/libwebp.*
+/usr/local/bin/cwebp
+/usr/local/bin/dwebp
+```
+
+installed.
+
+Note: A decode-only library, libwebpdecoder, is available using the
+`--enable-libwebpdecoder` flag. The encode library is built separately and can
+be installed independently using a minor modification in the corresponding
+Makefile.am configure files (see comments there). See `./configure --help` for
+more options.
+
+## Building for MIPS Linux
+
+MIPS Linux toolchain stable available releases can be found at:
+https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/
+
+```shell
+# Add toolchain to PATH
+export PATH=$PATH:/path/to/toolchain/bin
+
+# 32-bit build for mips32r5 (p5600)
+HOST=mips-mti-linux-gnu
+MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \
+ -msched-weight -mload-store-pairs -fPIE"
+MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie"
+
+# 64-bit build for mips64r6 (i6400)
+HOST=mips-img-linux-gnu
+MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \
+ -msched-weight -mload-store-pairs -fPIE"
+MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie"
+
+./configure --host=${HOST} --build=`config.guess` \
+ CC="${HOST}-gcc -EL" \
+ CFLAGS="$MIPS_CFLAGS" \
+ LDFLAGS="$MIPS_LDFLAGS"
+make
+make install
+```
+
+## Building libwebp - Using vcpkg
+
+You can download and install libwebp using the
+[vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
+
+```shell
+git clone https://github.com/Microsoft/vcpkg.git
+cd vcpkg
+./bootstrap-vcpkg.sh
+./vcpkg integrate install
+./vcpkg install libwebp
+```
+
+The libwebp port in vcpkg is kept up to date by Microsoft team members and
+community contributors. If the version is out of date, please
+[create an issue or pull request](https://github.com/Microsoft/vcpkg) on the
+vcpkg repository.
+
+## CMake
+
+With CMake, you can compile libwebp, cwebp, dwebp, gif2webp, img2webp, webpinfo
+and the JS bindings.
+
+Prerequisites: a compiler (e.g., gcc with autotools) and CMake.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install build-essential cmake
+```
+
+When building from git sources, you will need to run cmake to generate the
+makefiles.
+
+```shell
+mkdir build && cd build && cmake ../
+make
+make install
+```
+
+If you also want any of the executables, you will need to enable them through
+CMake, e.g.:
+
+```shell
+cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
+```
+
+or through your favorite interface (like ccmake or cmake-qt-gui).
+
+Use option `-DWEBP_UNICODE=ON` for Unicode support on Windows (with chcp 65001).
+
+Finally, once installed, you can also use WebP in your CMake project by doing:
+
+```cmake
+find_package(WebP)
+```
+
+which will define the CMake variables WebP_INCLUDE_DIRS and WebP_LIBRARIES.
+
+## Gradle
+
+The support for Gradle is minimal: it only helps you compile libwebp, cwebp and
+dwebp and webpmux_example.
+
+Prerequisites: a compiler (e.g., gcc with autotools) and gradle.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install build-essential gradle
+```
+
+When building from git sources, you will need to run the Gradle wrapper with the
+appropriate target, e.g. :
+
+```shell
+./gradlew buildAllExecutables
+```
+
+## SWIG bindings
+
+To generate language bindings from swig/libwebp.swig at least swig-1.3
+(http://www.swig.org) is required.
+
+Currently the following functions are mapped:
+
+Decode:
+
+```
+WebPGetDecoderVersion
+WebPGetInfo
+WebPDecodeRGBA
+WebPDecodeARGB
+WebPDecodeBGRA
+WebPDecodeBGR
+WebPDecodeRGB
+```
+
+Encode:
+
+```
+WebPGetEncoderVersion
+WebPEncodeRGBA
+WebPEncodeBGRA
+WebPEncodeRGB
+WebPEncodeBGR
+WebPEncodeLosslessRGBA
+WebPEncodeLosslessBGRA
+WebPEncodeLosslessRGB
+WebPEncodeLosslessBGR
+```
+
+See also the [swig documentation](../swig/README.md) for more detailed build
+instructions and usage examples.
+
+### Java bindings
+
+To build the swig-generated JNI wrapper code at least JDK-1.5 (or equivalent) is
+necessary for enum support. The output is intended to be a shared object / DLL
+that can be loaded via `System.loadLibrary("webp_jni")`.
+
+### Python bindings
+
+To build the swig-generated Python extension code at least Python 2.6 is
+required. Python < 2.6 may build with some minor changes to libwebp.swig or the
+generated code, but is untested.
+
+## Javascript decoder
+
+Libwebp can be compiled into a JavaScript decoder using Emscripten and CMake.
+See the [corresponding documentation](../webp_js/README.md)
diff --git a/c-lib/doc/specs_generation.md b/c-lib/doc/specs_generation.md
@@ -0,0 +1,27 @@
+# Generate libwebp Container Spec Docs from Text Source
+
+HTML generation requires [kramdown](https://kramdown.gettalong.org/), easily
+installed as a [rubygem](https://rubygems.org/). Rubygems installation should
+satisfy dependencies automatically.
+
+HTML generation can then be done from the project root:
+
+```shell
+$ kramdown doc/webp-container-spec.txt --template doc/template.html > \
+ doc/output/webp-container-spec.html
+```
+
+kramdown can optionally syntax highlight code blocks, using
+[CodeRay](https://github.com/rubychan/coderay), a dependency of kramdown that
+rubygems will install automatically. The following will apply inline CSS
+styling; an external stylesheet is not needed.
+
+```shell
+$ kramdown doc/webp-lossless-bitstream-spec.txt \
+ --template doc/template.html \
+ -x syntax-coderay --syntax-highlighter coderay \
+ --syntax-highlighter-opts "{default_lang: c, line_numbers: , css: style}" \
+ > doc/output/webp-lossless-bitstream-spec.html
+```
+
+Optimally, use kramdown 0.13.7 or newer if syntax highlighting desired.
diff --git a/c-lib/doc/tools.md b/c-lib/doc/tools.md
@@ -0,0 +1,524 @@
+# WebP tools
+
+## Encoding tool
+
+The examples/ directory contains tools for encoding (cwebp) and decoding (dwebp)
+images.
+
+The easiest use should look like:
+
+```shell
+cwebp input.png -q 80 -o output.webp
+```
+
+which will convert the input file to a WebP file using a quality factor of 80 on
+a 0->100 scale (0 being the lowest quality, 100 being the best. Default value is
+75).
+
+You might want to try the `-lossless` flag too, which will compress the source
+(in RGBA format) without any loss. The `-q` quality parameter will in this case
+control the amount of processing time spent trying to make the output file as
+small as possible.
+
+A longer list of options is available using the `-longhelp` command line flag:
+
+```shell
+> cwebp -longhelp
+Usage:
+ cwebp [-preset <...>] [options] in_file [-o out_file]
+```
+
+If input size (-s) for an image is not specified, it is assumed to be a PNG,
+JPEG, TIFF or WebP file. Note: Animated PNG and WebP files are not supported.
+
+Options:
+
+```
+-h / -help ............. short help
+-H / -longhelp ......... long help
+-q <float> ............. quality factor (0:small..100:big), default=75
+-alpha_q <int> ......... transparency-compression quality (0..100),
+ default=100
+-preset <string> ....... preset setting, one of:
+ default, photo, picture,
+ drawing, icon, text
+ -preset must come first, as it overwrites other parameters
+-z <int> ............... activates lossless preset with given
+ level in [0:fast, ..., 9:slowest]
+
+-m <int> ............... compression method (0=fast, 6=slowest), default=4
+-segments <int> ........ number of segments to use (1..4), default=4
+-size <int> ............ target size (in bytes)
+-psnr <float> .......... target PSNR (in dB. typically: 42)
+
+-s <int> <int> ......... input size (width x height) for YUV
+-sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
+-f <int> ............... filter strength (0=off..100), default=60
+-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
+-strong ................ use strong filter instead of simple (default)
+-nostrong .............. use simple filter instead of strong
+-sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
+-partition_limit <int> . limit quality to fit the 512k limit on
+ the first partition (0=no degradation ... 100=full)
+-pass <int> ............ analysis pass number (1..10)
+-qrange <min> <max> .... specifies the permissible quality range
+ (default: 0 100)
+-crop <x> <y> <w> <h> .. crop picture with the given rectangle
+-resize <w> <h> ........ resize picture (*after* any cropping)
+-resize_mode <string> .. one of: up_only, down_only, always (default)
+-mt .................... use multi-threading if available
+-low_memory ............ reduce memory usage (slower encoding)
+-map <int> ............. print map of extra info
+-print_psnr ............ prints averaged PSNR distortion
+-print_ssim ............ prints averaged SSIM distortion
+-print_lsim ............ prints local-similarity distortion
+-d <file.pgm> .......... dump the compressed output (PGM file)
+-alpha_method <int> .... transparency-compression method (0..1), default=1
+-alpha_filter <string> . predictive filtering for alpha plane,
+ one of: none, fast (default) or best
+-exact ................. preserve RGB values in transparent area, default=off
+-blend_alpha <hex> ..... blend colors against background color
+ expressed as RGB values written in
+ hexadecimal, e.g. 0xc0e0d0 for red=0xc0
+ green=0xe0 and blue=0xd0
+-noalpha ............... discard any transparency information
+-lossless .............. encode image losslessly, default=off
+-near_lossless <int> ... use near-lossless image preprocessing
+ (0..100=off), default=100
+-hint <string> ......... specify image characteristics hint,
+ one of: photo, picture or graph
+
+-metadata <string> ..... comma separated list of metadata to
+ copy from the input to the output if present.
+ Valid values: all, none (default), exif, icc, xmp
+
+-short ................. condense printed message
+-quiet ................. don't print anything
+-version ............... print version number and exit
+-noasm ................. disable all assembly optimizations
+-v ..................... verbose, e.g. print encoding/decoding times
+-progress .............. report encoding progress
+```
+
+Experimental Options:
+
+```
+-jpeg_like ............. roughly match expected JPEG size
+-af .................... auto-adjust filter strength
+-pre <int> ............. pre-processing filter
+```
+
+The main options you might want to try in order to further tune the visual
+quality are:
+
+-preset -sns -f -m
+
+Namely:
+
+* `preset` will set up a default encoding configuration targeting a particular
+ type of input. It should appear first in the list of options, so that
+ subsequent options can take effect on top of this preset. Default value is
+ 'default'.
+* `sns` will progressively turn on (when going from 0 to 100) some additional
+ visual optimizations (like: segmentation map re-enforcement). This option
+ will balance the bit allocation differently. It tries to take bits from the
+ "easy" parts of the picture and use them in the "difficult" ones instead.
+ Usually, raising the sns value (at fixed -q value) leads to larger files,
+ but with better quality. Typical value is around '75'.
+* `f` option directly links to the filtering strength used by the codec's
+ in-loop processing. The higher the value, the smoother the highly-compressed
+ area will look. This is particularly useful when aiming at very small files.
+ Typical values are around 20-30. Note that using the option
+ -strong/-nostrong will change the type of filtering. Use "-f 0" to turn
+ filtering off.
+* `m` controls the trade-off between encoding speed and quality. Default is 4.
+ You can try -m 5 or -m 6 to explore more (time-consuming) encoding
+ possibilities. A lower value will result in faster encoding at the expense
+ of quality.
+
+## Decoding tool
+
+There is a decoding sample in examples/dwebp.c which will take a .webp file and
+decode it to a PNG image file (amongst other formats). This is simply to
+demonstrate the use of the API. You can verify the file test.webp decodes to
+exactly the same as test_ref.ppm by using:
+
+```shell
+cd examples
+./dwebp test.webp -ppm -o test.ppm
+diff test.ppm test_ref.ppm
+```
+
+The full list of options is available using -h:
+
+```shell
+> dwebp -h
+Usage: dwebp in_file [options] [-o out_file]
+```
+
+Decodes the WebP image file to PNG format [Default]. Note: Animated WebP files
+are not supported.
+
+Use following options to convert into alternate image formats:
+
+```
+-pam ......... save the raw RGBA samples as a color PAM
+-ppm ......... save the raw RGB samples as a color PPM
+-bmp ......... save as uncompressed BMP format
+-tiff ........ save as uncompressed TIFF format
+-pgm ......... save the raw YUV samples as a grayscale PGM
+ file with IMC4 layout
+-yuv ......... save the raw YUV samples in flat layout
+```
+
+Other options are:
+
+```
+-version ..... print version number and exit
+-nofancy ..... don't use the fancy YUV420 upscaler
+-nofilter .... disable in-loop filtering
+-nodither .... disable dithering
+-dither <d> .. dithering strength (in 0..100)
+-alpha_dither use alpha-plane dithering if needed
+-mt .......... use multi-threading
+-crop <x> <y> <w> <h> ... crop output with the given rectangle
+-resize <w> <h> ......... resize output (*after* any cropping)
+-flip ........ flip the output vertically
+-alpha ....... only save the alpha plane
+-incremental . use incremental decoding (useful for tests)
+-h ........... this help message
+-v ........... verbose (e.g. print encoding/decoding times)
+-quiet ....... quiet mode, don't print anything
+-noasm ....... disable all assembly optimizations
+```
+
+## WebP file analysis tool
+
+`webpinfo` can be used to print out the chunk level structure and bitstream
+header information of WebP files. It can also check if the files are of valid
+WebP format.
+
+Usage:
+
+```shell
+webpinfo [options] in_files
+```
+
+Note: there could be multiple input files; options must come before input files.
+
+Options:
+
+```
+-version ........... Print version number and exit.
+-quiet ............. Do not show chunk parsing information.
+-diag .............. Show parsing error diagnosis.
+-summary ........... Show chunk stats summary.
+-bitstream_info .... Parse bitstream header.
+```
+
+## Visualization tool
+
+There's a little self-serve visualization tool called 'vwebp' under the
+examples/ directory. It uses OpenGL to open a simple drawing window and show a
+decoded WebP file. It's not yet integrated in the automake build system, but you
+can try to manually compile it using the recommendations below.
+
+Usage:
+
+```shell
+vwebp in_file [options]
+```
+
+Decodes the WebP image file and visualize it using OpenGL
+
+Options are:
+
+```
+-version ..... print version number and exit
+-noicc ....... don't use the icc profile if present
+-nofancy ..... don't use the fancy YUV420 upscaler
+-nofilter .... disable in-loop filtering
+-dither <int> dithering strength (0..100), default=50
+-noalphadither disable alpha plane dithering
+-usebgcolor .. display background color
+-mt .......... use multi-threading
+-info ........ print info
+-h ........... this help message
+```
+
+Keyboard shortcuts:
+
+```
+'c' ................ toggle use of color profile
+'b' ................ toggle background color display
+'i' ................ overlay file information
+'d' ................ disable blending & disposal (debug)
+'q' / 'Q' / ESC .... quit
+```
+
+### Building
+
+Prerequisites:
+
+1. OpenGL & OpenGL Utility Toolkit (GLUT)
+
+ Linux: `sudo apt-get install freeglut3-dev mesa-common-dev`
+
+ Mac + Xcode: These libraries should be available in the OpenGL / GLUT
+ frameworks.
+
+ Windows: http://freeglut.sourceforge.net/index.php#download
+
+2. (Optional) qcms (Quick Color Management System)
+
+ 1. Download qcms from Mozilla / Chromium:
+ https://hg.mozilla.org/mozilla-central/file/0e7639e3bdfb/gfx/qcms
+ https://source.chromium.org/chromium/chromium/src/+/main:third_party/qcms/;drc=d4a2f8e1ed461d8fc05ed88d1ae2dc94c9773825
+ 2. Build and archive the source files as libqcms.a / qcms.lib
+ 3. Update makefile.unix / Makefile.vc
+ 1. Define WEBP_HAVE_QCMS
+ 2. Update include / library paths to reference the qcms directory.
+
+Build using makefile.unix / Makefile.vc:
+
+```shell
+$ make -f makefile.unix examples/vwebp
+> nmake /f Makefile.vc CFG=release-static \
+ ../obj/x64/release-static/bin/vwebp.exe
+```
+
+## Animation creation tool
+
+The utility `img2webp` can turn a sequence of input images (PNG, JPEG, ...) into
+an animated WebP file. It offers fine control over duration, encoding modes,
+etc.
+
+Usage:
+
+```shell
+img2webp [file_options] [[frame_options] frame_file]... [-o webp_file]
+```
+
+File-level options (only used at the start of compression):
+
+```
+-min_size ............ minimize size
+-kmax <int> .......... maximum number of frame between key-frames
+ (0=only keyframes)
+-kmin <int> .......... minimum number of frame between key-frames
+ (0=disable key-frames altogether)
+-mixed ............... use mixed lossy/lossless automatic mode
+-near_lossless <int> . use near-lossless image preprocessing
+ (0..100=off), default=100
+-sharp_yuv ........... use sharper (and slower) RGB->YUV conversion
+ (lossy only)
+-loop <int> .......... loop count (default: 0, = infinite loop)
+-v ................... verbose mode
+-h ................... this help
+-version ............. print version number and exit
+```
+
+Per-frame options (only used for subsequent images input):
+
+```
+-d <int> ............. frame duration in ms (default: 100)
+-lossless ............ use lossless mode (default)
+-lossy ............... use lossy mode
+-q <float> ........... quality
+-m <int> ............. compression method (0=fast, 6=slowest), default=4
+-exact, -noexact ..... preserve or alter RGB values in transparent area
+ (default: -noexact, may cause artifacts
+ with lossy animations)
+```
+
+example: `img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp`
+
+Note: if a single file name is passed as the argument, the arguments will be
+tokenized from this file. The file name must not start with the character '-'.
+
+## Animated GIF conversion
+
+Animated GIF files can be converted to WebP files with animation using the
+gif2webp utility available under examples/. The files can then be viewed using
+vwebp.
+
+Usage:
+
+```shell
+gif2webp [options] gif_file -o webp_file
+```
+
+Options:
+
+```
+-h / -help ............. this help
+-lossy ................. encode image using lossy compression
+-mixed ................. for each frame in the image, pick lossy
+ or lossless compression heuristically
+-near_lossless <int> ... use near-lossless image preprocessing
+ (0..100=off), default=100
+-sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
+ (lossy only)
+-q <float> ............. quality factor (0:small..100:big)
+-m <int> ............... compression method (0=fast, 6=slowest), default=4
+-min_size .............. minimize output size (default:off)
+ lossless compression by default; can be
+ combined with -q, -m, -lossy or -mixed
+ options
+-kmin <int> ............ min distance between key frames
+-kmax <int> ............ max distance between key frames
+-f <int> ............... filter strength (0=off..100)
+-metadata <string> ..... comma separated list of metadata to
+ copy from the input to the output if present
+ Valid values: all, none, icc, xmp (default)
+-loop_compatibility .... use compatibility mode for Chrome
+ version prior to M62 (inclusive)
+-mt .................... use multi-threading if available
+
+-version ............... print version number and exit
+-v ..................... verbose
+-quiet ................. don't print anything
+```
+
+### Building
+
+With the libgif development files installed, gif2webp can be built using
+makefile.unix:
+
+```shell
+$ make -f makefile.unix examples/gif2webp
+```
+
+or using autoconf:
+
+```shell
+$ ./configure --enable-everything
+$ make
+```
+
+## Comparison of animated images
+
+Test utility anim_diff under examples/ can be used to compare two animated
+images (each can be GIF or WebP).
+
+Usage:
+
+```shell
+anim_diff <image1> <image2> [options]
+```
+
+Options:
+
+```
+-dump_frames <folder> dump decoded frames in PAM format
+-min_psnr <float> ... minimum per-frame PSNR
+-raw_comparison ..... if this flag is not used, RGB is
+ premultiplied before comparison
+-max_diff <int> ..... maximum allowed difference per channel
+ between corresponding pixels in subsequent
+ frames
+-h .................. this help
+-version ............ print version number and exit
+```
+
+### Building
+
+With the libgif development files installed, anim_diff can be built using
+makefile.unix:
+
+```shell
+$ make -f makefile.unix examples/anim_diff
+```
+
+or using autoconf:
+
+```shell
+$ ./configure --enable-everything
+$ make
+```
+
+## WebP Mux tool
+
+The examples/ directory contains a tool (webpmux) for manipulating WebP files.
+The webpmux tool can be used to create an extended format WebP file and also to
+extract or strip relevant data from such a file.
+
+A list of options is available using the -help command line flag:
+
+```shell
+> webpmux -help
+Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
+ webpmux -set SET_OPTIONS INPUT -o OUTPUT
+ webpmux -duration DURATION_OPTIONS [-duration ...]
+ INPUT -o OUTPUT
+ webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
+ webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
+ [-bgcolor BACKGROUND_COLOR] -o OUTPUT
+ webpmux -info INPUT
+ webpmux [-h|-help]
+ webpmux -version
+ webpmux argument_file_name
+
+GET_OPTIONS:
+ Extract relevant data:
+ icc get ICC profile
+ exif get EXIF metadata
+ xmp get XMP metadata
+ frame n get nth frame
+
+SET_OPTIONS:
+ Set color profile/metadata/parameters:
+ loop LOOP_COUNT set the loop count
+ bgcolor BACKGROUND_COLOR set the animation background color
+ icc file.icc set ICC profile
+ exif file.exif set EXIF metadata
+ xmp file.xmp set XMP metadata
+ where: 'file.icc' contains the ICC profile to be set,
+ 'file.exif' contains the EXIF metadata to be set
+ 'file.xmp' contains the XMP metadata to be set
+
+DURATION_OPTIONS:
+ Set duration of selected frames:
+ duration set duration for all frames
+ duration,frame set duration of a particular frame
+ duration,start,end set duration of frames in the
+ interval [start,end])
+ where: 'duration' is the duration in milliseconds
+ 'start' is the start frame index
+ 'end' is the inclusive end frame index
+ The special 'end' value '0' means: last frame.
+
+STRIP_OPTIONS:
+ Strip color profile/metadata:
+ icc strip ICC profile
+ exif strip EXIF metadata
+ xmp strip XMP metadata
+
+FRAME_OPTIONS(i):
+ Create animation:
+ file_i +di[+xi+yi[+mi[bi]]]
+ where: 'file_i' is the i'th animation frame (WebP format),
+ 'di' is the pause duration before next frame,
+ 'xi','yi' specify the image offset for this frame,
+ 'mi' is the dispose method for this frame (0 or 1),
+ 'bi' is the blending method for this frame (+b or -b)
+
+LOOP_COUNT:
+ Number of times to repeat the animation.
+ Valid range is 0 to 65535 [Default: 0 (infinite)].
+
+BACKGROUND_COLOR:
+ Background color of the canvas.
+ A,R,G,B
+ where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying
+ the Alpha, Red, Green and Blue component values respectively
+ [Default: 255,255,255,255]
+
+INPUT & OUTPUT are in WebP format.
+
+Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
+valid.
+
+Note: if a single file name is passed as the argument, the arguments will be
+tokenized from this file. The file name must not start with the character '-'.
+```
diff --git a/c-lib/doc/webp-container-spec.txt b/c-lib/doc/webp-container-spec.txt
@@ -4,8 +4,8 @@ Although you may be viewing an alternate representation, this document
is sourced in Markdown, a light-duty markup scheme, and is optimized for
the [kramdown](https://kramdown.gettalong.org/) transformer.
-See the accompanying README. External link targets are referenced at the
-end of this file.
+See the accompanying specs_generation.md. External link targets are referenced
+at the end of this file.
-->
@@ -20,46 +20,48 @@ WebP Container Specification
Introduction
------------
-WebP is an image format that uses either (i) the VP8 key frame encoding
-to compress image data in a lossy way, or (ii) the WebP lossless encoding
-(and possibly other encodings in the future). These encoding schemes should
-make it more efficient than currently used formats. It is optimized for fast
-image transfer over the network (e.g., for websites). The WebP format has
-feature parity (color profile, metadata, animation etc) with other formats as
-well. This document describes the structure of a WebP file.
+WebP is an image format that uses either (i) the VP8 key frame encoding to
+compress image data in a lossy way or (ii) the WebP lossless encoding. These
+encoding schemes should make it more efficient than older formats, such as JPEG,
+GIF, and PNG. It is optimized for fast image transfer over the network (for
+example, for websites). The WebP format has feature parity (color profile,
+metadata, animation, etc.) with other formats as well. This document describes
+the structure of a WebP file.
-The WebP container (i.e., RIFF container for WebP) allows feature support over
-and above the basic use case of WebP (i.e., a file containing a single image
-encoded as a VP8 key frame). The WebP container provides additional support
-for:
+The WebP container (that is, the RIFF container for WebP) allows feature support
+over and above the basic use case of WebP (that is, a file containing a single
+image encoded as a VP8 key frame). The WebP container provides additional
+support for the following:
- * **Lossless compression.** An image can be losslessly compressed, using the
+ * Lossless Compression: An image can be losslessly compressed, using the
WebP Lossless Format.
- * **Metadata.** An image may have metadata stored in EXIF or XMP formats.
+ * Metadata: An image may have metadata stored in Exchangeable Image File
+ Format (Exif) or Extensible Metadata Platform (XMP) format.
- * **Transparency.** An image may have transparency, i.e., an alpha channel.
+ * Transparency: An image may have transparency, that is, an alpha channel.
- * **Color Profile.** An image may have an embedded ICC profile as described
+ * Color Profile: An image may have an embedded ICC profile as described
by the [International Color Consortium][iccspec].
- * **Animation.** An image may have multiple frames with pauses between them,
+ * Animation: An image may have multiple frames with pauses between them,
making it an animation.
-The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
-"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
-document are to be interpreted as described in [RFC 2119][].
-
-Bit numbering in chunk diagrams starts at `0` for the most significant bit
-('MSB 0') as described in [RFC 1166][].
+Terminology & Basics
+--------------------
-Terminology & Basics
-------------------------
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this
+document are to be interpreted as described in BCP 14 [RFC 2119][] [RFC 8174][]
+when, and only when, they appear in all capitals, as shown here.
-A WebP file contains either a still image (i.e., an encoded matrix of pixels)
+A WebP file contains either a still image (that is, an encoded matrix of pixels)
or an [animation](#animation). Optionally, it can also contain transparency
-information, color profile and metadata. In case we need to refer only to the
-matrix of pixels, we will call it the _canvas_ of the image.
+information, a color profile and metadata. We refer to the matrix of pixels as
+the _canvas_ of the image.
+
+Bit numbering in chunk diagrams starts at `0` for the most significant bit
+('MSB 0'), as described in [RFC 1166][].
Below are additional terms used throughout this document:
@@ -82,19 +84,25 @@ _uint32_
_FourCC_
-: A _FourCC_ (four-character code) is a _uint32_ created by concatenating four
- ASCII characters in little-endian order.
+: A four-character code (FourCC) is a _uint32_ created by concatenating four
+ ASCII characters in little-endian order. This means 'aaaa' (0x61616161) and
+ 'AAAA' (0x41414141) are treated as different _FourCCs_.
_1-based_
-: An unsigned integer field storing values offset by `-1`. e.g., Such a field
- would store value _25_ as _24_.
+: An unsigned integer field storing values offset by `-1`, for example, such a
+ field would store value _25_ as _24_.
+
+_ChunkHeader('ABCD')_
+
+: Used to describe the _FourCC_ and _Chunk Size_ header of individual chunks,
+ where 'ABCD' is the FourCC for the chunk. This element's size is 8 bytes.
RIFF File Format
----------------
-The WebP file format is based on the RIFF (resource interchange file format)
+The WebP file format is based on the RIFF (Resource Interchange File Format)
document format.
The basic element of a RIFF file is a _chunk_. It consists of:
@@ -106,7 +114,7 @@ The basic element of a RIFF file is a _chunk_. It consists of:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Chunk Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Chunk Payload |
+ : Chunk Payload :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk FourCC: 32 bits
@@ -115,21 +123,15 @@ Chunk FourCC: 32 bits
Chunk Size: 32 bits (_uint32_)
-: The size of the chunk not including this field, the chunk identifier or
- padding.
+: The size of the chunk in bytes, not including this field, the chunk
+ identifier, or padding.
Chunk Payload: _Chunk Size_ bytes
-: The data payload. If _Chunk Size_ is odd, a single padding byte -- that
- SHOULD be `0` -- is added.
-
-_ChunkHeader('ABCD')_
-
-: This is used to describe the _FourCC_ and _Chunk Size_ header of individual
- chunks, where 'ABCD' is the FourCC for the chunk. This element's
- size is 8 bytes.
+: The data payload. If _Chunk Size_ is odd, a single padding byte -- which MUST
+ be `0` to conform with RIFF -- is added.
-**Note:** RIFF has a convention that all-uppercase chunk FourCCs are standard
+**Note**: RIFF has a convention that all-uppercase chunk FourCCs are standard
chunks that apply to any RIFF file format, while FourCCs specific to a file
format are all lowercase. WebP does not follow this convention.
@@ -149,23 +151,25 @@ WebP File Header
'RIFF': 32 bits
-: The ASCII characters 'R' 'I' 'F' 'F'.
+: The ASCII characters 'R', 'I', 'F', 'F'.
File Size: 32 bits (_uint32_)
-: The size of the file in bytes starting at offset 8. The maximum value of
+: The size of the file in bytes, starting at offset 8. The maximum value of
this field is 2^32 minus 10 bytes and thus the size of the whole file is at
- most 4GiB minus 2 bytes.
+ most 4 GiB minus 2 bytes.
'WEBP': 32 bits
-: The ASCII characters 'W' 'E' 'B' 'P'.
+: The ASCII characters 'W', 'E', 'B', 'P'.
A WebP file MUST begin with a RIFF header with the FourCC 'WEBP'. The file size
in the header is the total size of the chunks that follow plus `4` bytes for
-the 'WEBP' FourCC. The file SHOULD NOT contain anything after it. As the size
-of any chunk is even, the size given by the RIFF header is also even. The
-contents of individual chunks will be described in the following sections.
+the 'WEBP' FourCC. The file SHOULD NOT contain any data after the data
+specified by _File Size_. Readers MAY parse such files, ignoring the trailing
+data. As the size of any chunk is even, the size given by the RIFF header is
+also even. The contents of individual chunks are described in the following
+sections.
Simple File Format (Lossy)
@@ -180,37 +184,43 @@ Simple WebP (lossy) file format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
| WebP file header (12 bytes) |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | VP8 chunk |
+ : 'VP8 ' Chunk :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-VP8 chunk:
+'VP8 ' Chunk:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8 ') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | VP8 data |
+ : VP8 data :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
VP8 data: _Chunk Size_ bytes
: VP8 bitstream data.
-The VP8 bitstream format specification can be found at [VP8 Data Format and
-Decoding Guide][vp8spec]. Note that the VP8 frame header contains the VP8 frame
+Note that the fourth character in the 'VP8 ' FourCC is an ASCII space (0x20).
+
+The VP8 bitstream format specification is described in [VP8 Data Format and
+Decoding Guide][rfc 6386]. Note that the VP8 frame header contains the VP8 frame
width and height. That is assumed to be the width and height of the canvas.
-The VP8 specification describes how to decode the image into Y'CbCr
-format. To convert to RGB, Rec. 601 SHOULD be used.
+The VP8 specification describes how to decode the image into Y'CbCr format. To
+convert to RGB, [Recommendation BT.601][rec601] SHOULD be used. Applications MAY
+use another conversion method, but visual results may differ among decoders.
Simple File Format (Lossless)
-----------------------------
-**Note:** Older readers may not support files using the lossless format.
+**Note**: Older readers may not support files using the lossless format.
This layout SHOULD be used if the image requires _lossless_ encoding (with an
optional transparency channel) and does not require advanced features provided
@@ -221,19 +231,22 @@ Simple WebP (lossless) file format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
| WebP file header (12 bytes) |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | VP8L chunk |
+ : 'VP8L' Chunk :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-VP8L chunk:
+'VP8L' Chunk:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8L') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | VP8L data |
+ : VP8L data :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
VP8L data: _Chunk Size_ bytes
@@ -249,21 +262,21 @@ and height of the canvas.
Extended File Format
--------------------
-**Note:** Older readers may not support files using the extended format.
+**Note**: Older readers may not support files using the extended format.
An extended format file consists of:
- * A 'VP8X' chunk with information about features used in the file.
+ * A 'VP8X' Chunk with information about features used in the file.
- * An optional 'ICCP' chunk with color profile.
+ * An optional 'ICCP' Chunk with a color profile.
- * An optional 'ANIM' chunk with animation control data.
+ * An optional 'ANIM' Chunk with animation control data.
* Image data.
- * An optional 'EXIF' chunk with EXIF metadata.
+ * An optional 'EXIF' Chunk with Exif metadata.
- * An optional 'XMP ' chunk with XMP metadata.
+ * An optional 'XMP ' Chunk with XMP metadata.
* An optional list of [unknown chunks](#unknown-chunks).
@@ -277,15 +290,18 @@ up of:
For an _animated image_, the _image data_ consists of multiple frames. More
details about frames can be found in the [Animation](#animation) section.
-All chunks SHOULD be placed in the same order as listed above. If a chunk
-appears in the wrong place, the file is invalid, but readers MAY parse the
-file, ignoring the chunks that come too late.
+All chunks necessary for reconstruction and color correction, that is, 'VP8X',
+'ICCP', 'ANIM', 'ANMF', 'ALPH', 'VP8 ', and 'VP8L', MUST appear in the order
+described earlier. Readers SHOULD fail when chunks necessary for reconstruction
+and color correction are out of order.
+
+[Metadata](#metadata) and [unknown chunks](#unknown-chunks) MAY appear out of
+order.
-**Rationale:** Setting the order of chunks should allow quicker file
-parsing. For example, if an 'ALPH' chunk does not appear in its required
-position, a decoder can choose to stop searching for it. The rule of
-ignoring late chunks should make programs that need to do a full search
-give the same results as the ones stopping early.
+**Rationale:** The chunks necessary for reconstruction should appear first in
+the file to allow a reader to begin decoding an image before receiving all of
+the data. An application may benefit from varying the order of metadata and
+custom chunks to suit the implementation.
Extended WebP file header:
{:#extended_header}
@@ -293,9 +309,12 @@ Extended WebP file header:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
| WebP file header (12 bytes) |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8X') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Rsv|I|L|E|X|A|R| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -306,20 +325,20 @@ Extended WebP file header:
Reserved (Rsv): 2 bits
-: SHOULD be `0`.
+: MUST be `0`. Readers MUST ignore this field.
ICC profile (I): 1 bit
-: Set if the file contains an ICC profile.
+: Set if the file contains an 'ICCP' Chunk.
Alpha (L): 1 bit
: Set if any of the frames of the image contain transparency information
("alpha").
-EXIF metadata (E): 1 bit
+Exif metadata (E): 1 bit
-: Set if the file contains EXIF metadata.
+: Set if the file contains Exif metadata.
XMP metadata (X): 1 bit
@@ -327,38 +346,38 @@ XMP metadata (X): 1 bit
Animation (A): 1 bit
-: Set if this is an animated image. Data in 'ANIM' and 'ANMF' chunks should be
+: Set if this is an animated image. Data in 'ANIM' and 'ANMF' Chunks should be
used to control the animation.
Reserved (R): 1 bit
-: SHOULD be `0`.
+: MUST be `0`. Readers MUST ignore this field.
Reserved: 24 bits
-: SHOULD be `0`.
+: MUST be `0`. Readers MUST ignore this field.
Canvas Width Minus One: 24 bits
: _1-based_ width of the canvas in pixels.
- The actual canvas width is '1 + Canvas Width Minus One'
+ The actual canvas width is `1 + Canvas Width Minus One`.
Canvas Height Minus One: 24 bits
: _1-based_ height of the canvas in pixels.
- The actual canvas height is '1 + Canvas Height Minus One'
+ The actual canvas height is `1 + Canvas Height Minus One`.
The product of _Canvas Width_ and _Canvas Height_ MUST be at most `2^32 - 1`.
-Future specifications MAY add more fields.
+Future specifications may add more fields. Unknown fields MUST be ignored.
### Chunks
#### Animation
-An animation is controlled by ANIM and ANMF chunks.
+An animation is controlled by 'ANIM' and 'ANMF' Chunks.
-ANIM Chunk:
+'ANIM' Chunk:
{:#anim_chunk}
For an animated image, this chunk contains the _global parameters_ of the
@@ -368,6 +387,7 @@ animation.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ANIM') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Background Color |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -379,14 +399,14 @@ Background Color: 32 bits (_uint32_)
: The default background color of the canvas in \[Blue, Green, Red, Alpha\]
byte order. This color MAY be used to fill the unused space on the canvas
around the frames, as well as the transparent pixels of the first frame.
- Background color is also used when disposal method is `1`.
+ The background color is also used when the Disposal method is `1`.
-**Note**:
+**Notes**:
- * Background color MAY contain a transparency value (alpha), even if the
- _Alpha_ flag in [VP8X chunk](#extended_header) is unset.
+ * The background color MAY contain a non-opaque alpha value, even if the
+ _Alpha_ flag in the ['VP8X' Chunk](#extended_header) is unset.
- * Viewer applications SHOULD treat the background color value as a hint, and
+ * Viewer applications SHOULD treat the background color value as a hint and
are not required to use it.
* The canvas is cleared at the start of each loop. The background color MAY be
@@ -394,13 +414,14 @@ Background Color: 32 bits (_uint32_)
Loop Count: 16 bits (_uint16_)
-: The number of times to loop the animation. `0` means infinitely.
+: The number of times to loop the animation. If it is `0`, this means
+ infinitely.
-This chunk MUST appear if the _Animation_ flag in the VP8X chunk is set.
-If the _Animation_ flag is not set and this chunk is present, it
-SHOULD be ignored.
+This chunk MUST appear if the _Animation_ flag in the 'VP8X' Chunk is set.
+If the _Animation_ flag is not set and this chunk is present, it MUST be
+ignored.
-ANMF chunk:
+'ANMF' Chunk:
For animated images, this chunk contains information about a _single_ frame.
If the _Animation flag_ is not set, then this chunk SHOULD NOT be present.
@@ -409,6 +430,7 @@ If the _Animation flag_ is not set, then this chunk SHOULD NOT be present.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ANMF') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame X | ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -418,47 +440,47 @@ If the _Animation flag_ is not set, then this chunk SHOULD NOT be present.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame Duration | Reserved |B|D|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Frame Data |
+ : Frame Data :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Frame X: 24 bits (_uint24_)
-: The X coordinate of the upper left corner of the frame is `Frame X * 2`
+: The X coordinate of the upper left corner of the frame is `Frame X * 2`.
Frame Y: 24 bits (_uint24_)
-: The Y coordinate of the upper left corner of the frame is `Frame Y * 2`
+: The Y coordinate of the upper left corner of the frame is `Frame Y * 2`.
Frame Width Minus One: 24 bits (_uint24_)
: The _1-based_ width of the frame.
- The frame width is `1 + Frame Width Minus One`
+ The frame width is `1 + Frame Width Minus One`.
Frame Height Minus One: 24 bits (_uint24_)
: The _1-based_ height of the frame.
- The frame height is `1 + Frame Height Minus One`
+ The frame height is `1 + Frame Height Minus One`.
Frame Duration: 24 bits (_uint24_)
-: The time to wait before displaying the next frame, in 1 millisecond units.
- Note the interpretation of frame duration of 0 (and often <= 10) is
- implementation defined. Many tools and browsers assign a minimum duration
- similar to GIF.
+: The time to wait before displaying the next frame, in 1-millisecond units.
+ Note that the interpretation of the Frame Duration of 0 (and often <= 10) is
+ defined by the implementation. Many tools and browsers assign a minimum
+ duration similar to GIF.
Reserved: 6 bits
-: SHOULD be 0.
+: MUST be `0`. Readers MUST ignore this field.
Blending method (B): 1 bit
: Indicates how transparent pixels of _the current frame_ are to be blended
with corresponding pixels of the previous canvas:
- * `0`: Use alpha blending. After disposing of the previous frame, render the
+ * `0`: Use alpha-blending. After disposing of the previous frame, render the
current frame on the canvas using [alpha-blending](#alpha-blending). If
- the current frame does not have an alpha channel, assume alpha value of
- 255, effectively replacing the rectangle.
+ the current frame does not have an alpha channel, assume the alpha value
+ is 255, effectively replacing the rectangle.
* `1`: Do not blend. After disposing of the previous frame, render the
current frame on the canvas by overwriting the rectangle covered by the
@@ -471,20 +493,20 @@ Disposal method (D): 1 bit
* `0`: Do not dispose. Leave the canvas as is.
- * `1`: Dispose to background color. Fill the _rectangle_ on the canvas
- covered by the _current frame_ with background color specified in the
- [ANIM chunk](#anim_chunk).
+ * `1`: Dispose to the background color. Fill the _rectangle_ on the canvas
+ covered by the _current frame_ with the background color specified in the
+ ['ANIM' Chunk](#anim_chunk).
**Notes**:
* The frame disposal only applies to the _frame rectangle_, that is, the
- rectangle defined by _Frame X_, _Frame Y_, _frame width_ and _frame height_.
- It may or may not cover the whole canvas.
+ rectangle defined by _Frame X_, _Frame Y_, _frame width_, and _frame
+ height_. It may or may not cover the whole canvas.
{:#alpha-blending}
- * **Alpha-blending**:
+ * Alpha-blending:
- Given that each of the R, G, B and A channels is 8-bit, and the RGB
+ Given that each of the R, G, B, and A channels is 8 bits, and the RGB
channels are _not premultiplied_ by alpha, the formula for blending
'dst' onto 'src' is:
@@ -493,16 +515,17 @@ Disposal method (D): 1 bit
if blend.A = 0 then
blend.RGB = 0
else
- blend.RGB = (src.RGB * src.A +
- dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
+ blend.RGB =
+ (src.RGB * src.A +
+ dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
~~~~~
* Alpha-blending SHOULD be done in linear color space, by taking into account
the [color profile](#color-profile) of the image. If the color profile is
- not present, sRGB is to be assumed. (Note that sRGB also needs to be
- linearized due to a gamma of ~2.2).
+ not present, standard RGB (sRGB) is to be assumed. (Note that sRGB also
+ needs to be linearized due to a gamma of ~2.2.)
-Frame Data: _Chunk Size_ - `16` bytes
+Frame Data: _Chunk Size_ bytes - `16`
: Consists of:
@@ -512,8 +535,8 @@ Frame Data: _Chunk Size_ - `16` bytes
* An optional list of [unknown chunks](#unknown-chunks).
-**Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual
-_padded_ chunks as described by the [RIFF file format](#riff-file-format).
+**Note**: The 'ANMF' payload, _Frame Data_, consists of individual
+_padded_ chunks, as described by the [RIFF file format](#riff-file-format).
#### Alpha
@@ -521,26 +544,29 @@ _padded_ chunks as described by the [RIFF file format](#riff-file-format).
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ALPH') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Rsv| P | F | C | Alpha Bitstream... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Reserved (Rsv): 2 bits
-: SHOULD be `0`.
+: MUST be `0`. Readers MUST ignore this field.
-Pre-processing (P): 2 bits
+Preprocessing (P): 2 bits
-: These INFORMATIVE bits are used to signal the pre-processing that has
+: These _informative_ bits are used to signal the preprocessing that has
been performed during compression. The decoder can use this information to
- e.g. dither the values or smooth the gradients prior to display.
+ for example, dither the values or smooth the gradients prior to display.
- * `0`: no pre-processing
- * `1`: level reduction
+ * `0`: No preprocessing.
+ * `1`: Level reduction.
+
+Decoders are not required to use this information in any specified way.
Filtering method (F): 2 bits
-: The filtering method used:
+: The filtering methods used are described as follows:
* `0`: None.
* `1`: Horizontal filter.
@@ -564,27 +590,25 @@ made depending on the filtering method:
where `clip(v)` is equal to:
- * 0 if v < 0
- * 255 if v > 255
+ * 0 if v < 0,
+ * 255 if v > 255, or
* v otherwise
The final value is derived by adding the decompressed value `X` to the
-predictor and using modulo-256 arithmetic to wrap the \[256-511\] range
-into the \[0-255\] one:
+predictor and using modulo-256 arithmetic to wrap the \[256..511\] range
+into the \[0..255\] one:
`alpha = (predictor + X) % 256`
-There are special cases for left-most and top-most pixel positions:
+There are special cases for the left-most and top-most pixel positions. For
+example, the top-left value at location (0, 0) uses 0 as the predictor value.
+Otherwise:
- * Top-left value at location (0,0) uses 0 as predictor value. Otherwise,
* For horizontal or gradient filtering methods, the left-most pixels at
location (0, y) are predicted using the location (0, y-1) just above.
* For vertical or gradient filtering methods, the top-most pixels at
location (x, 0) are predicted using the location (x-1, 0) on the left.
-
-Decoders are not required to use this information in any specified way.
-
Compression method (C): 2 bits
: The compression method used:
@@ -592,37 +616,37 @@ Compression method (C): 2 bits
* `0`: No compression.
* `1`: Compressed using the WebP lossless format.
-Alpha bitstream: _Chunk Size_ - `1` bytes
+Alpha bitstream: _Chunk Size_ bytes - `1`
: Encoded alpha bitstream.
This optional chunk contains encoded alpha data for this frame. A frame
-containing a 'VP8L' chunk SHOULD NOT contain this chunk.
+containing a 'VP8L' Chunk SHOULD NOT contain this chunk.
**Rationale**: The transparency information is already part of the 'VP8L'
-chunk.
+Chunk.
-The alpha channel data is stored as uncompressed raw data (when
+The alpha channel data is stored as uncompressed raw data (when the
compression method is '0') or compressed using the lossless format
(when the compression method is '1').
- * Raw data: consists of a byte sequence of length width * height,
+ * Raw data: This consists of a byte sequence of length = width * height,
containing all the 8-bit transparency values in scan order.
- * Lossless format compression: the byte sequence is a compressed
- image-stream (as described in the [WebP Lossless Bitstream Format]
- [webpllspec]) of implicit dimension width x height. That is, this
- image-stream does NOT contain any headers describing the image dimension.
+ * Lossless format compression: The byte sequence is a compressed
+ image-stream (as described in ["WebP Lossless Bitstream Format"]
+ [webpllspec]) of implicit dimensions width x height. That is, this
+ image-stream does NOT contain any headers describing the image dimensions.
- **Rationale**: the dimension is already known from other sources,
- so storing it again would be redundant and error-prone.
+ **Rationale**: The dimensions are already known from other sources,
+ so storing them again would be redundant and prone to error.
- Once the image-stream is decoded into ARGB color values, following
- the process described in the lossless format specification, the
- transparency information must be extracted from the *green* channel
- of the ARGB quadruplet.
+ Once the image-stream is decoded into Alpha, Red, Green, Blue (ARGB) color
+ values, following the process described in the lossless format
+ specification, the transparency information must be extracted from the
+ *green* channel of the ARGB quadruplet.
- **Rationale**: the green channel is allowed extra transformation
+ **Rationale**: The green channel is allowed extra transformation
steps in the specification -- unlike the other channels -- that can
improve compression.
@@ -630,22 +654,23 @@ compression method is '0') or compressed using the lossless format
This chunk contains compressed bitstream data for a single frame.
-A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the
-significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using
-"VP8L" as its tag.
+A bitstream chunk may be either (i) a 'VP8 ' Chunk, using 'VP8 ' (note the
+significant fourth-character space) as its FourCC, _or_ (ii) a 'VP8L' Chunk,
+using 'VP8L' as its FourCC.
-The formats of VP8 and VP8L chunks are as described in sections
+The formats of 'VP8 ' and 'VP8L' Chunks are as described in sections
[Simple File Format (Lossy)](#simple-file-format-lossy)
-and [Simple File Format (Lossless)](#simple-file-format-lossless) respectively.
+and [Simple File Format (Lossless)](#simple-file-format-lossless), respectively.
-#### Color profile
+#### Color Profile
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('ICCP') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Color Profile |
+ : Color Profile :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Color Profile: _Chunk Size_ bytes
@@ -662,114 +687,145 @@ If this chunk is not present, sRGB SHOULD be assumed.
#### Metadata
-Metadata can be stored in 'EXIF' or 'XMP ' chunks.
+Metadata can be stored in 'EXIF' or 'XMP ' Chunks.
There SHOULD be at most one chunk of each type ('EXIF' and 'XMP '). If there
-are more such chunks, readers MAY ignore all except the first one. Also, a file
-may possibly contain both 'EXIF' and 'XMP ' chunks.
+are more such chunks, readers MAY ignore all except the first one.
The chunks are defined as follows:
-EXIF chunk:
+'EXIF' Chunk:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('EXIF') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | EXIF Metadata |
+ : Exif Metadata :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-EXIF Metadata: _Chunk Size_ bytes
+Exif Metadata: _Chunk Size_ bytes
-: image metadata in EXIF format.
+: Image metadata in Exif format.
-XMP chunk:
+'XMP ' Chunk:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('XMP ') |
+ | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | XMP Metadata |
+ : XMP Metadata :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
XMP Metadata: _Chunk Size_ bytes
-: image metadata in XMP format.
+: Image metadata in XMP format.
+
+Note that the fourth character in the 'XMP ' FourCC is an ASCII space (0x20).
Additional guidance about handling metadata can be found in the
-Metadata Working Group's [Guidelines for Handling Metadata][metadata].
+Metadata Working Group's ["Guidelines for Handling Metadata"][metadata].
#### Unknown Chunks
-A RIFF chunk (described in [this](#terminology-amp-basics) section) whose _chunk
-tag_ is different from any of the chunks described in this document, is
+A RIFF chunk (described in the [RIFF File Format](#riff-file-format) section)
+whose FourCC is different from any of the chunks described in this document, is
considered an _unknown chunk_.
**Rationale**: Allowing unknown chunks gives a provision for future extension
-of the format, and also allows storage of any application-specific data.
+of the format and also allows storage of any application-specific data.
A file MAY contain unknown chunks:
- * At the end of the file as described in [Extended WebP file
- header](#extended_header) section.
- * At the end of ANMF chunks as described in the
+ * at the end of the file, as described in [Extended WebP file
+ header](#extended_header) section, or
+ * at the end of 'ANMF' Chunks, as described in the
[Animation](#animation) section.
Readers SHOULD ignore these chunks. Writers SHOULD preserve them in their
original order (unless they specifically intend to modify these chunks).
-### Assembling the Canvas from frames
+### Canvas Assembly from Frames
+
+Here we provide an overview of how a reader MUST assemble a canvas in the case
+of an animated image.
-Here we provide an overview of how a reader should assemble a canvas in the
-case of an animated image. The notation _VP8X.field_ means the field in the
-'VP8X' chunk with the same description.
+The process begins with creating a canvas using the dimensions given in the
+'VP8X' Chunk, `Canvas Width Minus One + 1` pixels wide by `Canvas Height Minus
+One + 1` pixels high. The `Loop Count` field from the 'ANIM' Chunk controls how
+many times the animation process is repeated. This is `Loop Count - 1` for
+nonzero `Loop Count` values or infinite if the `Loop Count` is zero.
-Displaying an _animated image_ canvas MUST be equivalent to the following
-pseudocode:
+At the beginning of each loop iteration, the canvas is filled using the
+background color from the 'ANIM' Chunk or an application-defined color.
+
+'ANMF' Chunks contain individual frames given in display order. Before rendering
+each frame, the previous frame's `Disposal method` is applied.
+
+The rendering of the decoded frame begins at the Cartesian coordinates (`2 *
+Frame X`, `2 * Frame Y`), using the top-left corner of the canvas as the origin.
+`Frame Width Minus One + 1` pixels wide by `Frame Height Minus One + 1` pixels
+high are rendered onto the canvas using the `Blending method`.
+
+The canvas is displayed for `Frame Duration` milliseconds. This continues until
+all frames given by 'ANMF' Chunks have been displayed. A new loop iteration is
+then begun, or the canvas is left in its final state if all iterations have been
+completed.
+
+The following pseudocode illustrates the rendering process. The notation
+_VP8X.field_ means the field in the 'VP8X' Chunk with the same description.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-assert VP8X.flags.hasAnimation
+VP8X.flags.hasAnimation MUST be TRUE
canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
- background color ANIM.background_color.
+ background color ANIM.background_color or
+ application-defined color.
loop_count ← ANIM.loopCount
-dispose_method ← ANIM.disposeMethod
+dispose_method ← Dispose to background color
if loop_count == 0:
- loop_count = ∞
+ loop_count = ∞
frame_params ← nil
-assert next chunk in image_data is ANMF
+next chunk in image_data is ANMF MUST be TRUE
for loop = 0..loop_count - 1
- clear canvas to ANIM.background_color or application defined color
- until eof or non-ANMF chunk
- frame_params.frameX = Frame X
- frame_params.frameY = Frame Y
- frame_params.frameWidth = Frame Width Minus One + 1
- frame_params.frameHeight = Frame Height Minus One + 1
- frame_params.frameDuration = Frame Duration
- frame_right = frame_params.frameX + frame_params.frameWidth
- frame_bottom = frame_params.frameY + frame_params.frameHeight
- assert VP8X.canvasWidth >= frame_right
- assert VP8X.canvasHeight >= frame_bottom
- for subchunk in 'Frame Data':
- if subchunk.tag == "ALPH":
- assert alpha subchunks not found in 'Frame Data' earlier
- frame_params.alpha = alpha_data
- else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
- assert bitstream subchunks not found in 'Frame Data' earlier
- frame_params.bitstream = bitstream_data
- render frame with frame_params.alpha and frame_params.bitstream on
- canvas with top-left corner at (frame_params.frameX,
- frame_params.frameY), using dispose method dispose_method.
- canvas contains the decoded image.
- Show the contents of the canvas for frame_params.frameDuration * 1ms.
+ clear canvas to ANIM.background_color or application-defined color
+ until eof or non-ANMF chunk
+ frame_params.frameX = Frame X
+ frame_params.frameY = Frame Y
+ frame_params.frameWidth = Frame Width Minus One + 1
+ frame_params.frameHeight = Frame Height Minus One + 1
+ frame_params.frameDuration = Frame Duration
+ frame_right = frame_params.frameX + frame_params.frameWidth
+ frame_bottom = frame_params.frameY + frame_params.frameHeight
+ VP8X.canvasWidth >= frame_right MUST be TRUE
+ VP8X.canvasHeight >= frame_bottom MUST be TRUE
+ for subchunk in 'Frame Data':
+ if subchunk.tag == "ALPH":
+ alpha subchunks not found in 'Frame Data' earlier MUST be
+ TRUE
+ frame_params.alpha = alpha_data
+ else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
+ bitstream subchunks not found in 'Frame Data' earlier MUST
+ be TRUE
+ frame_params.bitstream = bitstream_data
+ apply dispose_method.
+ render frame with frame_params.alpha and frame_params.bitstream
+ on canvas with top-left corner at (frame_params.frameX,
+ frame_params.frameY), using Blending method
+ frame_params.blendingMethod.
+ canvas contains the decoded image.
+ Show the contents of the canvas for
+ frame_params.frameDuration * 1 ms.
+ dispose_method = frame_params.disposeMethod
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Example File Layouts
--------------------
-A lossy encoded image with alpha may look as follows:
+A lossy-encoded image with alpha may look as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RIFF/WEBP
@@ -778,16 +834,16 @@ RIFF/WEBP
+- VP8 (bitstream)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-A losslessly encoded image may look as follows:
+A lossless-encoded image may look as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RIFF/WEBP
+- VP8X (descriptions of features used)
-+- XYZW (unknown chunk)
+- VP8L (lossless bitstream)
++- XYZW (unknown chunk)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-A lossless image with ICC profile and XMP metadata may
+A lossless image with an ICC profile and XMP metadata may
look as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -798,7 +854,7 @@ RIFF/WEBP
+- XMP (metadata)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-An animated image with EXIF metadata may look as follows:
+An animated image with Exif metadata may look as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RIFF/WEBP
@@ -811,9 +867,11 @@ RIFF/WEBP
+- EXIF (metadata)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-[vp8spec]: https://datatracker.ietf.org/doc/html/rfc6386
[webpllspec]: https://chromium.googlesource.com/webm/libwebp/+/HEAD/doc/webp-lossless-bitstream-spec.txt
[iccspec]: https://www.color.org/icc_specs2.xalter
[metadata]: https://web.archive.org/web/20180919181934/http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
+[rec601]: https://www.itu.int/rec/R-REC-BT.601
[rfc 1166]: https://datatracker.ietf.org/doc/html/rfc1166
[rfc 2119]: https://datatracker.ietf.org/doc/html/rfc2119
+[rfc 6386]: https://datatracker.ietf.org/doc/html/rfc6386
+[rfc 8174]: https://datatracker.ietf.org/doc/html/rfc8174
diff --git a/c-lib/doc/webp-lossless-bitstream-spec.txt b/c-lib/doc/webp-lossless-bitstream-spec.txt
@@ -1,202 +1,189 @@
<!--
-Although you may be viewing an alternate representation, this document
-is sourced in Markdown, a light-duty markup scheme, and is optimized for
-the [kramdown](https://kramdown.gettalong.org/) transformer.
+Although you may be viewing an alternate representation, this document is
+sourced in Markdown, a light-duty markup scheme, and is optimized for the
+[kramdown](https://kramdown.gettalong.org/) transformer.
-See the accompanying README. External link targets are referenced at the
-end of this file.
+See the accompanying specs_generation.md. External link targets are referenced
+at the end of this file.
-->
Specification for WebP Lossless Bitstream
=========================================
-_Jyrki Alakuijala, Ph.D., Google, Inc., 2012-06-19_
-
-Paragraphs marked as \[AMENDED\] were amended on 2014-09-16.
+_Jyrki Alakuijala, Ph.D., Google, Inc., 2023-03-09_
Abstract
--------
-WebP lossless is an image format for lossless compression of ARGB
-images. The lossless format stores and restores the pixel values
-exactly, including the color values for zero alpha pixels. The
-format uses subresolution images, recursively embedded into the format
-itself, for storing statistical data about the images, such as the used
-entropy codes, spatial predictors, color space conversion, and color
-table. LZ77, Huffman coding, and a color cache are used for compression
-of the bulk data. Decoding speeds faster than PNG have been
-demonstrated, as well as 25% denser compression than can be achieved
-using today's PNG format.
-
+WebP lossless is an image format for lossless compression of ARGB images. The
+lossless format stores and restores the pixel values exactly, including the
+color values for fully transparent pixels. A universal algorithm for sequential
+data compression (LZ77), prefix coding, and a color cache are used for
+compression of the bulk data. Decoding speeds faster than PNG have been
+demonstrated, as well as 25% denser compression than can be achieved using
+today's PNG format.
* TOC placeholder
{:toc}
+1 Introduction
+--------------
-Nomenclature
-------------
+This document describes the compressed data representation of a WebP lossless
+image. It is intended as a detailed reference for the WebP lossless encoder and
+decoder implementation.
+
+In this document, we extensively use C programming language syntax to describe
+the bitstream and assume the existence of a function for reading bits,
+`ReadBits(n)`. The bytes are read in the natural order of the stream containing
+them, and bits of each byte are read in least-significant-bit-first order. When
+multiple bits are read at the same time, the integer is constructed from the
+original data in the original order. The most significant bits of the returned
+integer are also the most significant bits of the original data. Thus, the
+statement
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+b = ReadBits(2);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+is equivalent with the two statements below:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+b = ReadBits(1);
+b |= ReadBits(1) << 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We assume that each color component, that is, alpha, red, blue, and green, is
+represented using an 8-bit byte. We define the corresponding type as uint8. A
+whole ARGB pixel is represented by a type called uint32, which is an unsigned
+integer consisting of 32 bits. In the code showing the behavior of the
+transforms, these values are codified in the following bits: alpha in bits
+31..24, red in bits 23..16, green in bits 15..8, and blue in bits 7..0; however,
+implementations of the format are free to use another representation internally.
+
+Broadly, a WebP lossless image contains header data, transform information, and
+actual image data. Headers contain the width and height of the image. A WebP
+lossless image can go through four different types of transforms before being
+entropy encoded. The transform information in the bitstream contains the data
+required to apply the respective inverse transforms.
+
+2 Nomenclature
+--------------
ARGB
-: A pixel value consisting of alpha, red, green, and blue values.
+: A pixel value consisting of alpha, red, green, and blue values.
ARGB image
-: A two-dimensional array containing ARGB pixels.
+: A two-dimensional array containing ARGB pixels.
color cache
-: A small hash-addressed array to store recently used colors, to be able
- to recall them with shorter codes.
+: A small hash-addressed array to store recently used colors to be able to
+ recall them with shorter codes.
color indexing image
-: A one-dimensional image of colors that can be indexed using a small
- integer (up to 256 within WebP lossless).
+: A one-dimensional image of colors that can be indexed using a small integer
+ (up to 256 within WebP lossless).
color transform image
-: A two-dimensional subresolution image containing data about
- correlations of color components.
+: A two-dimensional subresolution image containing data about correlations of
+ color components.
distance mapping
-: Changes LZ77 distances to have the smallest values for pixels in 2D
- proximity.
+: Changes LZ77 distances to have the smallest values for pixels in
+ two-dimensional proximity.
entropy image
-: A two-dimensional subresolution image indicating which entropy coding
- should be used in a respective square in the image, i.e., each pixel
- is a meta Huffman code.
-
-Huffman code
-: A classic way to do entropy coding where a smaller number of bits are
- used for more frequent codes.
+: A two-dimensional subresolution image indicating which entropy coding should
+ be used in a respective square in the image, that is, each pixel is a meta
+ prefix code.
LZ77
-: Dictionary-based sliding window compression algorithm that either
- emits symbols or describes them as sequences of past symbols.
+: A dictionary-based sliding window compression algorithm that either emits
+ symbols or describes them as sequences of past symbols.
-meta Huffman code
-: A small integer (up to 16 bits) that indexes an element in the meta
- Huffman table.
+meta prefix code
+: A small integer (up to 16 bits) that indexes an element in the meta prefix
+ table.
predictor image
-: A two-dimensional subresolution image indicating which spatial
- predictor is used for a particular square in the image.
+: A two-dimensional subresolution image indicating which spatial predictor is
+ used for a particular square in the image.
+
+prefix code
+: A classic way to do entropy coding where a smaller number of bits are used
+ for more frequent codes.
prefix coding
-: A way to entropy code larger integers that codes a few bits of the
- integer using an entropy code and codifies the remaining bits raw.
- This allows for the descriptions of the entropy codes to remain
- relatively small even when the range of symbols is large.
+: A way to entropy code larger integers, which codes a few bits of the integer
+ using an entropy code and codifies the remaining bits raw. This allows for
+ the descriptions of the entropy codes to remain relatively small even when
+ the range of symbols is large.
scan-line order
-: A processing order of pixels, left-to-right, top-to-bottom, starting
- from the left-hand-top pixel, proceeding to the right. Once a row is
- completed, continue from the left-hand column of the next row.
-
-
-1 Introduction
---------------
-
-This document describes the compressed data representation of a WebP
-lossless image. It is intended as a detailed reference for WebP lossless
-encoder and decoder implementation.
+: A processing order of pixels (left to right and top to bottom), starting
+ from the left-hand-top pixel. Once a row is completed, continue from the
+ left-hand column of the next row.
-In this document, we extensively use C programming language syntax to
-describe the bitstream, and assume the existence of a function for
-reading bits, `ReadBits(n)`. The bytes are read in the natural order of
-the stream containing them, and bits of each byte are read in
-least-significant-bit-first order. When multiple bits are read at the
-same time, the integer is constructed from the original data in the
-original order. The most significant bits of the returned integer are
-also the most significant bits of the original data. Thus the statement
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-b = ReadBits(2);
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-is equivalent with the two statements below:
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-b = ReadBits(1);
-b |= ReadBits(1) << 1;
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We assume that each color component (e.g. alpha, red, blue and green) is
-represented using an 8-bit byte. We define the corresponding type as
-uint8. A whole ARGB pixel is represented by a type called uint32, an
-unsigned integer consisting of 32 bits. In the code showing the behavior
-of the transformations, alpha value is codified in bits 31..24, red in
-bits 23..16, green in bits 15..8 and blue in bits 7..0, but
-implementations of the format are free to use another representation
-internally.
-
-Broadly, a WebP lossless image contains header data, transform
-information and actual image data. Headers contain width and height of
-the image. A WebP lossless image can go through four different types of
-transformation before being entropy encoded. The transform information
-in the bitstream contains the data required to apply the respective
-inverse transforms.
-
-
-2 RIFF Header
+3 RIFF Header
-------------
The beginning of the header has the RIFF container. This consists of the
following 21 bytes:
- 1. String "RIFF"
- 2. A little-endian 32 bit value of the block length, the whole size
- of the block controlled by the RIFF header. Normally this equals
+ 1. String 'RIFF'.
+ 2. A little-endian, 32-bit value of the chunk length, which is the whole size
+ of the chunk controlled by the RIFF header. Normally, this equals
the payload size (file size minus 8 bytes: 4 bytes for the 'RIFF'
identifier and 4 bytes for storing the value itself).
- 3. String "WEBP" (RIFF container name).
- 4. String "VP8L" (chunk tag for lossless encoded image data).
- 5. A little-endian 32-bit value of the number of bytes in the
+ 3. String 'WEBP' (RIFF container name).
+ 4. String 'VP8L' (FourCC for lossless-encoded image data).
+ 5. A little-endian, 32-bit value of the number of bytes in the
lossless stream.
- 6. One byte signature 0x2f.
+ 6. 1-byte signature 0x2f.
-The first 28 bits of the bitstream specify the width and height of the
-image. Width and height are decoded as 14-bit integers as follows:
+The first 28 bits of the bitstream specify the width and height of the image.
+Width and height are decoded as 14-bit integers as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int image_width = ReadBits(14) + 1;
int image_height = ReadBits(14) + 1;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The 14-bit dynamics for image size limit the maximum size of a WebP
-lossless image to 16384✕16384 pixels.
+The 14-bit precision for image width and height limits the maximum size of a
+WebP lossless image to 16384✕16384 pixels.
-The alpha_is_used bit is a hint only, and should not impact decoding.
-It should be set to 0 when all alpha values are 255 in the picture, and
-1 otherwise.
+The alpha_is_used bit is a hint only, and should not impact decoding. It should
+be set to 0 when all alpha values are 255 in the picture, and 1 otherwise.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int alpha_is_used = ReadBits(1);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The version_number is a 3 bit code that must be set to 0. Any other value
-should be treated as an error. \[AMENDED\]
+The version_number is a 3 bit code that must be set to 0. Any other value should
+be treated as an error.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int version_number = ReadBits(3);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+4 Transforms
+------------
-3 Transformations
------------------
-
-Transformations are reversible manipulations of the image data that can
-reduce the remaining symbolic entropy by modeling spatial and color
-correlations. Transformations can make the final compression more dense.
+The transforms are reversible manipulations of the image data that can reduce
+the remaining symbolic entropy by modeling spatial and color correlations. They
+can make the final compression more dense.
-An image can go through four types of transformation. A 1 bit indicates
-the presence of a transform. Each transform is allowed to be used only
-once. The transformations are used only for the main level ARGB image:
-the subresolution images have no transforms, not even the 0 bit
-indicating the end-of-transforms.
+An image can go through four types of transforms. A 1 bit indicates the
+presence of a transform. Each transform is allowed to be used only once. The
+transforms are used only for the main-level ARGB image; the subresolution images
+(color transform image, entropy image, and predictor image) have no transforms,
+not even the 0 bit indicating the end of transforms.
-Typically an encoder would use these transforms to reduce the Shannon
-entropy in the residual image. Also, the transform data can be decided
-based on entropy minimization.
+Typically, an encoder would use these transforms to reduce the Shannon entropy
+in the residual image. Also, the transform data can be decided based on entropy
+minimization.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while (ReadBits(1)) { // Transform present.
@@ -206,69 +193,71 @@ while (ReadBits(1)) { // Transform present.
...
}
-// Decode actual image data (Section 4).
+// Decode actual image data (Section 5).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If a transform is present then the next two bits specify the transform
-type. There are four types of transforms.
+If a transform is present, then the next two bits specify the transform type.
+There are four types of transforms.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum TransformType {
PREDICTOR_TRANSFORM = 0,
COLOR_TRANSFORM = 1,
- SUBTRACT_GREEN = 2,
+ SUBTRACT_GREEN_TRANSFORM = 2,
COLOR_INDEXING_TRANSFORM = 3,
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The transform type is followed by the transform data. Transform data
-contains the information required to apply the inverse transform and
-depends on the transform type. Next we describe the transform data for
-different types.
+The transform type is followed by the transform data. Transform data contains
+the information required to apply the inverse transform and depends on the
+transform type. The inverse transforms are applied in the reverse order that
+they are read from the bitstream, that is, last one first.
+Next, we describe the transform data for different types.
-### Predictor Transform
+### 4.1 Predictor Transform
-The predictor transform can be used to reduce entropy by exploiting the
-fact that neighboring pixels are often correlated. In the predictor
-transform, the current pixel value is predicted from the pixels already
-decoded (in scan-line order) and only the residual value (actual -
-predicted) is encoded. The _prediction mode_ determines the type of
-prediction to use. We divide the image into squares and all the pixels
-in a square use same prediction mode.
+The predictor transform can be used to reduce entropy by exploiting the fact
+that neighboring pixels are often correlated. In the predictor transform, the
+current pixel value is predicted from the pixels already decoded (in scan-line
+order) and only the residual value (actual - predicted) is encoded. The green
+component of a pixel defines which of the 14 predictors is used within a
+particular block of the ARGB image. The _prediction mode_ determines the type of
+prediction to use. We divide the image into squares, and all the pixels in a
+square use the same prediction mode.
-The first 3 bits of prediction data define the block width and height in
-number of bits. The number of block columns, `block_xsize`, is used in
-indexing two-dimensionally.
+The first 3 bits of prediction data define the block width and height in number
+of bits.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int size_bits = ReadBits(3) + 2;
int block_width = (1 << size_bits);
int block_height = (1 << size_bits);
-#define DIV_ROUND_UP(num, den) ((num) + (den) - 1) / (den))
-int block_xsize = DIV_ROUND_UP(image_width, 1 << size_bits);
+#define DIV_ROUND_UP(num, den) (((num) + (den) - 1) / (den))
+int transform_width = DIV_ROUND_UP(image_width, 1 << size_bits);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The transform data contains the prediction mode for each block of the
-image. All the `block_width * block_height` pixels of a block use same
-prediction mode. The prediction modes are treated as pixels of an image
-and encoded using the same techniques described in
-[Chapter 4](#image-data).
+The transform data contains the prediction mode for each block of the image. It
+is a subresolution image where the green component of a pixel defines which of
+the 14 predictors is used for all the `block_width * block_height` pixels within
+a particular block of the ARGB image. This subresolution image is encoded using
+the same techniques described in [Chapter 5](#image-data).
-For a pixel _x, y_, one can compute the respective filter block address
-by:
+The number of block columns, `transform_width`, is used in two-dimensional
+indexing. For a pixel (x, y), one can compute the respective filter block
+address by:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int block_index = (y >> size_bits) * block_xsize +
+int block_index = (y >> size_bits) * transform_width +
(x >> size_bits);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-There are 14 different prediction modes. In each prediction mode, the
-current pixel value is predicted from one or more neighboring pixels
-whose values are already known.
+There are 14 different prediction modes. In each prediction mode, the current
+pixel value is predicted from one or more neighboring pixels whose values are
+already known.
-We choose the neighboring pixels (TL, T, TR, and L) of the current pixel
-(P) as follows:
+We chose the neighboring pixels (TL, T, TR, and L) of the current pixel (P) as
+follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
O O O O O O O O O O O
@@ -279,11 +268,11 @@ X X X X X X X X X X X
X X X X X X X X X X X
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-where TL means top-left, T top, TR top-right, L left pixel.
-At the time of predicting a value for P, all pixels O, TL, T, TR and L
-have been already processed, and pixel P and all pixels X are unknown.
+where TL means top-left, T means top, TR means top-right, and L means left. At
+the time of predicting a value for P, all O, TL, T, TR and L pixels have already
+been processed, and the P pixel and all X pixels are unknown.
-Given the above neighboring pixels, the different prediction modes are
+Given the preceding neighboring pixels, the different prediction modes are
defined as follows.
| Mode | Predicted value of each channel of the current pixel |
@@ -316,7 +305,7 @@ The Select predictor is defined as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
uint32 Select(uint32 L, uint32 T, uint32 TL) {
- // L = left pixel, T = top pixel, TL = top left pixel.
+ // L = left pixel, T = top pixel, TL = top-left pixel.
// ARGB component estimates for prediction.
int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
@@ -331,7 +320,7 @@ uint32 Select(uint32 L, uint32 T, uint32 TL) {
abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
// Return either left or top, the one closer to the prediction.
- if (pL < pT) { // \[AMENDED\]
+ if (pL < pT) {
return L;
} else {
return T;
@@ -339,13 +328,13 @@ uint32 Select(uint32 L, uint32 T, uint32 TL) {
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The functions `ClampAddSubtractFull` and `ClampAddSubtractHalf` are
-performed for each ARGB component as follows:
+The functions `ClampAddSubtractFull` and `ClampAddSubtractHalf` are performed
+for each ARGB component as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Clamp the input value between 0 and 255.
int Clamp(int a) {
- return (a < 0) ? 0 : (a > 255) ? 255 : a;
+ return (a < 0) ? 0 : (a > 255) ? 255 : a;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -362,28 +351,40 @@ int ClampAddSubtractHalf(int a, int b) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are special handling rules for some border pixels. If there is a
-prediction transform, regardless of the mode \[0..13\] for these pixels,
-the predicted value for the left-topmost pixel of the image is
-0xff000000, L-pixel for all pixels on the top row, and T-pixel for all
-pixels on the leftmost column.
+predictor transform, regardless of the mode \[0..13\] for these pixels, the
+predicted value for the left-topmost pixel of the image is 0xff000000, all
+pixels on the top row are L-pixel, and all pixels on the leftmost column are
+T-pixel.
Addressing the TR-pixel for pixels on the rightmost column is
-exceptional. The pixels on the rightmost column are predicted by using
-the modes \[0..13\] just like pixels not on border, but by using the
-leftmost pixel on the same row as the current TR-pixel. The TR-pixel
-offset in memory is the same for border and non-border pixels.
+exceptional. The pixels on the rightmost column are predicted by using the modes
+\[0..13\], just like pixels not on the border, but the leftmost pixel on the
+same row as the current pixel is instead used as the TR-pixel.
+The final pixel value is obtained by adding each channel of the predicted value
+to the encoded residual value.
-### Color Transform
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void PredictorTransformOutput(uint32 residual, uint32 pred,
+ uint8* alpha, uint8* red,
+ uint8* green, uint8* blue) {
+ *alpha = ALPHA(residual) + ALPHA(pred);
+ *red = RED(residual) + RED(pred);
+ *green = GREEN(residual) + GREEN(pred);
+ *blue = BLUE(residual) + BLUE(pred);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The goal of the color transform is to decorrelate the R, G and B values
-of each pixel. Color transform keeps the green (G) value as it is,
-transforms red (R) based on green and transforms blue (B) based on green
-and then based on red.
+### 4.2 Color Transform
-As is the case for the predictor transform, first the image is divided
-into blocks and the same transform mode is used for all the pixels in a
-block. For each block there are three types of color transform elements.
+The goal of the color transform is to decorrelate the R, G, and B values of each
+pixel. The color transform keeps the green (G) value as it is, transforms the
+red (R) value based on the green value, and transforms the blue (B) value based
+on the green value and then on the red value.
+
+As is the case for the predictor transform, first the image is divided into
+blocks, and the same transform mode is used for all the pixels in a block. For
+each block, there are three types of color transform elements.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typedef struct {
@@ -393,11 +394,10 @@ typedef struct {
} ColorTransformElement;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The actual color transformation is done by defining a color transform
-delta. The color transform delta depends on the `ColorTransformElement`,
-which is the same for all the pixels in a particular block. The delta is
-added during color transform. The inverse color transform then is just
-subtracting those deltas.
+The actual color transform is done by defining a color transform delta. The
+color transform delta depends on the `ColorTransformElement`, which is the same
+for all the pixels in a particular block. The delta is subtracted during the
+color transform. The inverse color transform then is just adding those deltas.
The color transform function is defined as follows:
@@ -406,22 +406,22 @@ void ColorTransform(uint8 red, uint8 blue, uint8 green,
ColorTransformElement *trans,
uint8 *new_red, uint8 *new_blue) {
// Transformed values of red and blue components
- uint32 tmp_red = red;
- uint32 tmp_blue = blue;
+ int tmp_red = red;
+ int tmp_blue = blue;
- // Applying transform is just adding the transform deltas
- tmp_red += ColorTransformDelta(trans->green_to_red, green);
- tmp_blue += ColorTransformDelta(trans->green_to_blue, green);
- tmp_blue += ColorTransformDelta(trans->red_to_blue, red);
+ // Applying the transform is just subtracting the transform deltas
+ tmp_red -= ColorTransformDelta(trans->green_to_red, green);
+ tmp_blue -= ColorTransformDelta(trans->green_to_blue, green);
+ tmp_blue -= ColorTransformDelta(trans->red_to_blue, red);
*new_red = tmp_red & 0xff;
*new_blue = tmp_blue & 0xff;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-`ColorTransformDelta` is computed using a signed 8-bit integer
-representing a 3.5-fixed-point number, and a signed 8-bit RGB color
-channel (c) \[-128..127\] and is defined as follows:
+`ColorTransformDelta` is computed using a signed 8-bit integer representing a
+3.5-fixed-point number and a signed 8-bit RGB color channel (c) \[-128..127\]
+and is defined as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int8 ColorTransformDelta(int8 t, int8 c) {
@@ -429,22 +429,20 @@ int8 ColorTransformDelta(int8 t, int8 c) {
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-A conversion from the 8-bit unsigned representation (uint8) to the 8-bit
-signed one (int8) is required before calling ColorTransformDelta().
-It should be performed using 8-bit two's complement (that is: uint8 range
-\[128-255\] is mapped to the \[-128, -1\] range of its converted int8 value).
+A conversion from the 8-bit unsigned representation (uint8) to the 8-bit signed
+one (int8) is required before calling `ColorTransformDelta()`. The signed value
+should be interpreted as an 8-bit two's complement number (that is: uint8 range
+\[128..255\] is mapped to the \[-128..-1\] range of its converted int8 value).
-The multiplication is to be done using more precision (with at least
-16-bit dynamics). The sign extension property of the shift operation
-does not matter here: only the lowest 8 bits are used from the result,
-and there the sign extension shifting and unsigned shifting are
-consistent with each other.
+The multiplication is to be done using more precision (with at least 16-bit
+precision). The sign extension property of the shift operation does not matter
+here; only the lowest 8 bits are used from the result, and in these bits, the
+sign extension shifting and unsigned shifting are consistent with each other.
-Now we describe the contents of color transform data so that decoding
-can apply the inverse color transform and recover the original red and
-blue values. The first 3 bits of the color transform data contain the
-width and height of the image block in number of bits, just like the
-predictor transform:
+Now, we describe the contents of color transform data so that decoding can apply
+the inverse color transform and recover the original red and blue values. The
+first 3 bits of the color transform data contain the width and height of the
+image block in number of bits, just like the predictor transform:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int size_bits = ReadBits(3) + 2;
@@ -452,41 +450,44 @@ int block_width = 1 << size_bits;
int block_height = 1 << size_bits;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The remaining part of the color transform data contains
-`ColorTransformElement` instances corresponding to each block of the
-image. `ColorTransformElement` instances are treated as pixels of an
-image and encoded using the methods described in
-[Chapter 4](#image-data).
+The remaining part of the color transform data contains `ColorTransformElement`
+instances, corresponding to each block of the image. Each
+`ColorTransformElement` `'cte'` is treated as a pixel in a subresolution image
+whose alpha component is `255`, red component is `cte.red_to_blue`, green
+component is `cte.green_to_blue`, and blue component is `cte.green_to_red`.
-During decoding, `ColorTransformElement` instances of the blocks are
-decoded and the inverse color transform is applied on the ARGB values of
-the pixels. As mentioned earlier, that inverse color transform is just
-subtracting `ColorTransformElement` values from the red and blue
-channels.
+During decoding, `ColorTransformElement` instances of the blocks are decoded and
+the inverse color transform is applied on the ARGB values of the pixels. As
+mentioned earlier, that inverse color transform is just adding
+`ColorTransformElement` values to the red and blue channels. The alpha and green
+channels are left as is.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void InverseTransform(uint8 red, uint8 green, uint8 blue,
- ColorTransformElement *p,
+ ColorTransformElement *trans,
uint8 *new_red, uint8 *new_blue) {
- // Applying inverse transform is just subtracting the
+ // Transformed values of red and blue components
+ int tmp_red = red;
+ int tmp_blue = blue;
+
+ // Applying the inverse transform is just adding the
// color transform deltas
- red -= ColorTransformDelta(p->green_to_red_, green);
- blue -= ColorTransformDelta(p->green_to_blue_, green);
- blue -= ColorTransformDelta(p->red_to_blue_, red & 0xff);
+ tmp_red += ColorTransformDelta(trans->green_to_red, green);
+ tmp_blue += ColorTransformDelta(trans->green_to_blue, green);
+ tmp_blue +=
+ ColorTransformDelta(trans->red_to_blue, tmp_red & 0xff);
- *new_red = red & 0xff;
- *new_blue = blue & 0xff;
+ *new_red = tmp_red & 0xff;
+ *new_blue = tmp_blue & 0xff;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+### 4.3 Subtract Green Transform
-### Subtract Green Transform
-
-The subtract green transform subtracts green values from red and blue
-values of each pixel. When this transform is present, the decoder needs
-to add the green value to both red and blue. There is no data associated
-with this transform. The decoder applies the inverse transform as
-follows:
+The subtract green transform subtracts green values from red and blue values of
+each pixel. When this transform is present, the decoder needs to add the green
+value to both the red and blue values. There is no data associated with this
+transform. The decoder applies the inverse transform as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) {
@@ -495,71 +496,62 @@ void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) {
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-This transform is redundant as it can be modeled using the color
-transform, but it is still often useful. Since it can extend the
-dynamics of the color transform and there is no additional data here,
-the subtract green transform can be coded using fewer bits than a
-full-blown color transform.
+This transform is redundant, as it can be modeled using the color transform, but
+since there is no additional data here, the subtract green transform can be
+coded using fewer bits than a full-blown color transform.
+### 4.4 Color Indexing Transform
-### Color Indexing Transform
+If there are not many unique pixel values, it may be more efficient to create a
+color index array and replace the pixel values by the array's indices. The color
+indexing transform achieves this. (In the context of WebP lossless, we
+specifically do not call this a palette transform because a similar but more
+dynamic concept exists in WebP lossless encoding: color cache.)
-If there are not many unique pixel values, it may be more efficient to
-create a color index array and replace the pixel values by the array's
-indices. The color indexing transform achieves this. (In the context of
-WebP lossless, we specifically do not call this a palette transform
-because a similar but more dynamic concept exists in WebP lossless
-encoding: color cache.)
+The color indexing transform checks for the number of unique ARGB values in the
+image. If that number is below a threshold (256), it creates an array of those
+ARGB values, which is then used to replace the pixel values with the
+corresponding index: the green channel of the pixels are replaced with the
+index, all alpha values are set to 255, and all red and blue values to 0.
-The color indexing transform checks for the number of unique ARGB values
-in the image. If that number is below a threshold (256), it creates an
-array of those ARGB values, which is then used to replace the pixel
-values with the corresponding index: the green channel of the pixels are
-replaced with the index; all alpha values are set to 255; all red and
-blue values to 0.
-
-The transform data contains color table size and the entries in the
-color table. The decoder reads the color indexing transform data as
-follows:
+The transform data contains the color table size and the entries in the color
+table. The decoder reads the color indexing transform data as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// 8 bit value for color table size
+// 8-bit value for the color table size
int color_table_size = ReadBits(8) + 1;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The color table is stored using the image storage format itself. The
-color table can be obtained by reading an image, without the RIFF
-header, image size, and transforms, assuming a height of one pixel and
-a width of `color_table_size`. The color table is always
-subtraction-coded to reduce image entropy. The deltas of palette colors
-contain typically much less entropy than the colors themselves, leading
-to significant savings for smaller images. In decoding, every final
-color in the color table can be obtained by adding the previous color
-component values by each ARGB component separately, and storing the
-least significant 8 bits of the result.
+The color table is stored using the image storage format itself. The color table
+can be obtained by reading an image, without the RIFF header, image size, and
+transforms, assuming the height of 1 pixel and the width of `color_table_size`.
+The color table is always subtraction-coded to reduce image entropy. The deltas
+of palette colors contain typically much less entropy than the colors
+themselves, leading to significant savings for smaller images. In decoding,
+every final color in the color table can be obtained by adding the previous
+color component values by each ARGB component separately and storing the least
+significant 8 bits of the result.
-The inverse transform for the image is simply replacing the pixel values
-(which are indices to the color table) with the actual color table
-values. The indexing is done based on the green component of the ARGB
-color.
+The inverse transform for the image is simply replacing the pixel values (which
+are indices to the color table) with the actual color table values. The indexing
+is done based on the green component of the ARGB color.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Inverse transform
argb = color_table[GREEN(argb)];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If the index is equal or larger than color_table_size, the argb color value
-should be set to 0x00000000 (transparent black). \[AMENDED\]
+If the index is equal to or larger than `color_table_size`, the argb color value
+should be set to 0x00000000 (transparent black).
-When the color table is small (equal to or less than 16 colors), several
-pixels are bundled into a single pixel. The pixel bundling packs several
-(2, 4, or 8) pixels into a single pixel, reducing the image width
-respectively. Pixel bundling allows for a more efficient joint
-distribution entropy coding of neighboring pixels, and gives some
-arithmetic coding-like benefits to the entropy code, but it can only be
-used when there are a small number of unique values.
+When the color table is small (equal to or less than 16 colors), several pixels
+are bundled into a single pixel. The pixel bundling packs several (2, 4, or 8)
+pixels into a single pixel, reducing the image width respectively. Pixel
+bundling allows for a more efficient joint distribution entropy coding of
+neighboring pixels and gives some arithmetic coding-like benefits to the
+entropy code, but it can only be used when there are 16 or fewer unique values.
-`color_table_size` specifies how many pixels are combined together:
+`color_table_size` specifies how many pixels are combined:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int width_bits;
@@ -574,59 +566,58 @@ if (color_table_size <= 2) {
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-`width_bits` has a value of 0, 1, 2 or 3. A value of 0 indicates no
-pixel bundling to be done for the image. A value of 1 indicates that two
-pixels are combined together, and each pixel has a range of \[0..15\]. A
-value of 2 indicates that four pixels are combined together, and each
-pixel has a range of \[0..3\]. A value of 3 indicates that eight pixels
-are combined together and each pixel has a range of \[0..1\], i.e., a
-binary value.
+`width_bits` has a value of 0, 1, 2, or 3. A value of 0 indicates no pixel
+bundling is to be done for the image. A value of 1 indicates that two pixels are
+combined, and each pixel has a range of \[0..15\]. A value of 2 indicates that
+four pixels are combined, and each pixel has a range of \[0..3\]. A value of 3
+indicates that eight pixels are combined and each pixel has a range of \[0..1\],
+that is, a binary value.
The values are packed into the green component as follows:
- * `width_bits` = 1: for every x value where x ≡ 0 (mod 2), a green
- value at x is positioned into the 4 least-significant bits of the
- green value at x / 2, a green value at x + 1 is positioned into the
- 4 most-significant bits of the green value at x / 2.
- * `width_bits` = 2: for every x value where x ≡ 0 (mod 4), a green
+ * `width_bits` = 1: For every x value, where x ≡ 0 (mod 2), a green
+ value at x is positioned into the 4 least significant bits of the
+ green value at x / 2, and a green value at x + 1 is positioned into the
+ 4 most significant bits of the green value at x / 2.
+ * `width_bits` = 2: For every x value, where x ≡ 0 (mod 4), a green
value at x is positioned into the 2 least-significant bits of the
- green value at x / 4, green values at x + 1 to x + 3 in order to the
- more significant bits of the green value at x / 4.
- * `width_bits` = 3: for every x value where x ≡ 0 (mod 8), a green
- value at x is positioned into the least-significant bit of the green
- value at x / 8, green values at x + 1 to x + 7 in order to the more
- significant bits of the green value at x / 8.
+ green value at x / 4, and green values at x + 1 to x + 3 are positioned in
+ order to the more significant bits of the green value at x / 4.
+ * `width_bits` = 3: For every x value, where x ≡ 0 (mod 8), a green
+ value at x is positioned into the least significant bit of the green
+ value at x / 8, and green values at x + 1 to x + 7 are positioned in order
+ to the more significant bits of the green value at x / 8.
+
+After reading this transform, `image_width` is subsampled by `width_bits`. This
+affects the size of subsequent transforms. The new size can be calculated using
+`DIV_ROUND_UP`, as defined [earlier](#predictor-transform).
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+image_width = DIV_ROUND_UP(image_width, 1 << width_bits);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-4 Image Data
+5 Image Data
------------
Image data is an array of pixel values in scan-line order.
-### 4.1 Roles of Image Data
+### 5.1 Roles of Image Data
We use image data in five different roles:
1. ARGB image: Stores the actual pixels of the image.
- 1. Entropy image: Stores the
- [meta Huffman codes](#decoding-of-meta-huffman-codes). The red and green
- components of a pixel define the meta Huffman code used in a particular
- block of the ARGB image.
- 1. Predictor image: Stores the metadata for [Predictor
- Transform](#predictor-transform). The green component of a pixel defines
- which of the 14 predictors is used within a particular block of the
- ARGB image.
- 1. Color transform image. It is created by `ColorTransformElement` values
- (defined in [Color Transform](#color-transform)) for different blocks of
- the image. Each `ColorTransformElement` `'cte'` is treated as a pixel whose
- alpha component is `255`, red component is `cte.red_to_blue`, green
- component is `cte.green_to_blue` and blue component is `cte.green_to_red`.
- 1. Color indexing image: An array of of size `color_table_size` (up to 256
- ARGB values) storing the metadata for the
- [Color Indexing Transform](#color-indexing-transform). This is stored as an
- image of width `color_table_size` and height `1`.
-
-### 4.2 Encoding of Image data
+ 1. Entropy image: Stores the meta prefix codes (see
+ ["Decoding of Meta Prefix Codes"](#decoding-of-meta-prefix-codes)).
+ 1. Predictor image: Stores the metadata for the predictor transform (see
+ ["Predictor Transform"](#predictor-transform)).
+ 1. Color transform image: Created by `ColorTransformElement` values
+ (defined in ["Color Transform"](#color-transform)) for different blocks of
+ the image.
+ 1. Color indexing image: An array of the size of `color_table_size` (up to
+ 256 ARGB values) that stores metadata for the color indexing transform (see
+ ["Color Indexing Transform"](#color-indexing-transform)).
+
+### 5.2 Encoding of Image Data
The encoding of image data is independent of its role.
@@ -637,27 +628,28 @@ several blocks may share the same entropy codes.
**Rationale:** Storing an entropy code incurs a cost. This cost can be minimized
if statistically similar blocks share an entropy code, thereby storing that code
only once. For example, an encoder can find similar blocks by clustering them
-using their statistical properties, or by repeatedly joining a pair of randomly
+using their statistical properties or by repeatedly joining a pair of randomly
selected clusters when it reduces the overall amount of bits needed to encode
the image.
Each pixel is encoded using one of the three possible methods:
- 1. Huffman coded literal: each channel (green, red, blue and alpha) is
- entropy-coded independently;
- 2. LZ77 backward reference: a sequence of pixels are copied from elsewhere
- in the image; or
- 3. Color cache code: using a short multiplicative hash code (color cache
+ 1. Prefix-coded literals: Each channel (green, red, blue, and alpha) is
+ entropy-coded independently.
+ 2. LZ77 backward reference: A sequence of pixels are copied from elsewhere in
+ the image.
+ 3. Color cache code: Using a short multiplicative hash code (color cache
index) of a recently seen color.
-The following sub-sections describe each of these in detail.
+The following subsections describe each of these in detail.
-#### 4.2.1 Huffman Coded Literals
+#### 5.2.1 Prefix-Coded Literals
-The pixel is stored as Huffman coded values of green, red, blue and alpha (in
-that order). See [this section](#decoding-entropy-coded-image-data) for details.
+The pixel is stored as prefix-coded values of green, red, blue, and alpha (in
+that order). See [Section 6.2.3](#decoding-entropy-coded-image-data) for
+details.
-#### 4.2.2 LZ77 Backward Reference
+#### 5.2.2 LZ77 Backward Reference
Backward references are tuples of _length_ and _distance code_:
@@ -669,16 +661,16 @@ Backward references are tuples of _length_ and _distance code_:
The length and distance values are stored using **LZ77 prefix coding**.
LZ77 prefix coding divides large integer values into two parts: the _prefix
-code_ and the _extra bits_: the prefix code is stored using an entropy code,
+code_ and the _extra bits_. The prefix code is stored using an entropy code,
while the extra bits are stored as they are (without an entropy code).
**Rationale**: This approach reduces the storage requirement for the entropy
-code. Also, large values are usually rare, and so extra bits would be used for
-very few values in the image. Thus, this approach results in a better
-compression overall.
+code. Also, large values are usually rare, so extra bits would be used for very
+few values in the image. Thus, this approach results in better compression
+overall.
The following table denotes the prefix codes and extra bits used for storing
-different range of values.
+different ranges of values.
Note: The maximum backward reference length is limited to 4096. Hence, only the
first 24 prefix codes (with the respective extra bits) are meaningful for length
@@ -700,8 +692,8 @@ values. For distance values, however, all the 40 prefix codes are valid.
| 524289..786432 | 38 | 18 |
| 786433..1048576 | 39 | 18 |
-The pseudocode to obtain a (length or distance) value from the prefix code is
-as follows:
+The pseudocode to obtain a (length or distance) value from the prefix code is as
+follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if (prefix_code < 4) {
@@ -712,78 +704,80 @@ int offset = (2 + (prefix_code & 1)) << extra_bits;
return offset + ReadBits(extra_bits) + 1;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-**Distance Mapping:**
-{:#distance-mapping}
+##### Distance Mapping
-As noted previously, distance code is a number indicating the position of a
-previously seen pixel, from which the pixels are to be copied. This sub-section
+As noted previously, a distance code is a number indicating the position of a
+previously seen pixel, from which the pixels are to be copied. This subsection
defines the mapping between a distance code and the position of a previous
pixel.
-The distance codes larger than 120 denote the pixel-distance in scan-line
-order, offset by 120.
+Distance codes larger than 120 denote the pixel distance in scan-line order,
+offset by 120.
-The smallest distance codes \[1..120\] are special, and are reserved for a close
+The smallest distance codes \[1..120\] are special and are reserved for a close
neighborhood of the current pixel. This neighborhood consists of 120 pixels:
- * Pixels that are 1 to 7 rows above the current pixel, and are up to 8 columns
+ * Pixels that are 1 to 7 rows above the current pixel and are up to 8 columns
to the left or up to 7 columns to the right of the current pixel. \[Total
such pixels = `7 * (8 + 1 + 7) = 112`\].
- * Pixels that are in same row as the current pixel, and are up to 8 columns to
- the left of the current pixel. \[`8` such pixels\].
-
-The mapping between distance code `i` and the neighboring pixel offset
-`(xi, yi)` is as follows:
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-(0, 1), (1, 0), (1, 1), (-1, 1), (0, 2), (2, 0), (1, 2), (-1, 2),
-(2, 1), (-2, 1), (2, 2), (-2, 2), (0, 3), (3, 0), (1, 3), (-1, 3),
-(3, 1), (-3, 1), (2, 3), (-2, 3), (3, 2), (-3, 2), (0, 4), (4, 0),
-(1, 4), (-1, 4), (4, 1), (-4, 1), (3, 3), (-3, 3), (2, 4), (-2, 4),
-(4, 2), (-4, 2), (0, 5), (3, 4), (-3, 4), (4, 3), (-4, 3), (5, 0),
-(1, 5), (-1, 5), (5, 1), (-5, 1), (2, 5), (-2, 5), (5, 2), (-5, 2),
-(4, 4), (-4, 4), (3, 5), (-3, 5), (5, 3), (-5, 3), (0, 6), (6, 0),
-(1, 6), (-1, 6), (6, 1), (-6, 1), (2, 6), (-2, 6), (6, 2), (-6, 2),
-(4, 5), (-4, 5), (5, 4), (-5, 4), (3, 6), (-3, 6), (6, 3), (-6, 3),
-(0, 7), (7, 0), (1, 7), (-1, 7), (5, 5), (-5, 5), (7, 1), (-7, 1),
-(4, 6), (-4, 6), (6, 4), (-6, 4), (2, 7), (-2, 7), (7, 2), (-7, 2),
-(3, 7), (-3, 7), (7, 3), (-7, 3), (5, 6), (-5, 6), (6, 5), (-6, 5),
-(8, 0), (4, 7), (-4, 7), (7, 4), (-7, 4), (8, 1), (8, 2), (6, 6),
-(-6, 6), (8, 3), (5, 7), (-5, 7), (7, 5), (-7, 5), (8, 4), (6, 7),
-(-6, 7), (7, 6), (-7, 6), (8, 5), (7, 7), (-7, 7), (8, 6), (8, 7)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-For example, distance code `1` indicates offset of `(0, 1)` for the neighboring
-pixel, that is, the pixel above the current pixel (0-pixel difference in
-X-direction and 1 pixel difference in Y-direction). Similarly, distance code
-`3` indicates left-top pixel.
-
-The decoder can convert a distances code 'i' to a scan-line order distance
-'dist' as follows:
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-(xi, yi) = distance_map[i]
-dist = x + y * xsize
+ * Pixels that are in the same row as the current pixel and are up to 8
+ columns to the left of the current pixel. \[`8` such pixels\].
+
+The mapping between distance code `distance_code` and the neighboring pixel
+offset `(xi, yi)` is as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(0, 1), (1, 0), (1, 1), (-1, 1), (0, 2), (2, 0), (1, 2),
+(-1, 2), (2, 1), (-2, 1), (2, 2), (-2, 2), (0, 3), (3, 0),
+(1, 3), (-1, 3), (3, 1), (-3, 1), (2, 3), (-2, 3), (3, 2),
+(-3, 2), (0, 4), (4, 0), (1, 4), (-1, 4), (4, 1), (-4, 1),
+(3, 3), (-3, 3), (2, 4), (-2, 4), (4, 2), (-4, 2), (0, 5),
+(3, 4), (-3, 4), (4, 3), (-4, 3), (5, 0), (1, 5), (-1, 5),
+(5, 1), (-5, 1), (2, 5), (-2, 5), (5, 2), (-5, 2), (4, 4),
+(-4, 4), (3, 5), (-3, 5), (5, 3), (-5, 3), (0, 6), (6, 0),
+(1, 6), (-1, 6), (6, 1), (-6, 1), (2, 6), (-2, 6), (6, 2),
+(-6, 2), (4, 5), (-4, 5), (5, 4), (-5, 4), (3, 6), (-3, 6),
+(6, 3), (-6, 3), (0, 7), (7, 0), (1, 7), (-1, 7), (5, 5),
+(-5, 5), (7, 1), (-7, 1), (4, 6), (-4, 6), (6, 4), (-6, 4),
+(2, 7), (-2, 7), (7, 2), (-7, 2), (3, 7), (-3, 7), (7, 3),
+(-7, 3), (5, 6), (-5, 6), (6, 5), (-6, 5), (8, 0), (4, 7),
+(-4, 7), (7, 4), (-7, 4), (8, 1), (8, 2), (6, 6), (-6, 6),
+(8, 3), (5, 7), (-5, 7), (7, 5), (-7, 5), (8, 4), (6, 7),
+(-6, 7), (7, 6), (-7, 6), (8, 5), (7, 7), (-7, 7), (8, 6),
+(8, 7)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For example, the distance code `1` indicates an offset of `(0, 1)` for the
+neighboring pixel, that is, the pixel above the current pixel (0 pixel
+difference in the X direction and 1 pixel difference in the Y direction).
+Similarly, the distance code `3` indicates the top-left pixel.
+
+The decoder can convert a distance code `distance_code` to a scan-line order
+distance `dist` as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(xi, yi) = distance_map[distance_code - 1]
+dist = xi + yi * image_width
if (dist < 1) {
dist = 1
}
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-where 'distance_map' is the mapping noted above and `xsize` is the width of the
-image in pixels.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+where `distance_map` is the mapping noted above, and `image_width` is the width
+of the image in pixels.
-#### 4.2.3 Color Cache Coding
+#### 5.2.3 Color Cache Coding
+{:#color-cache-code}
Color cache stores a set of colors that have been recently used in the image.
**Rationale:** This way, the recently used colors can sometimes be referred to
-more efficiently than emitting them using other two methods (described in
-[4.2.1](#huffman-coded-literals) and [4.2.2](#lz77-backward-reference)).
+more efficiently than emitting them using the other two methods (described in
+Sections [5.2.1](#prefix-coded-literals) and [5.2.2](#lz77-backward-reference)).
Color cache codes are stored as follows. First, there is a 1-bit value that
indicates if the color cache is used. If this bit is 0, no color cache codes
-exist, and they are not transmitted in the Huffman code that decodes the green
+exist, and they are not transmitted in the prefix code that decodes the green
symbols and the length prefix codes. However, if this bit is 1, the color cache
size is read next:
@@ -792,299 +786,371 @@ int color_cache_code_bits = ReadBits(4);
int color_cache_size = 1 << color_cache_code_bits;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-`color_cache_code_bits` defines the size of the color_cache by (1 <<
-`color_cache_code_bits`). The range of allowed values for
+`color_cache_code_bits` defines the size of the color cache (`1 <<
+color_cache_code_bits`). The range of allowed values for
`color_cache_code_bits` is \[1..11\]. Compliant decoders must indicate a
corrupted bitstream for other values.
-A color cache is an array of size `color_cache_size`. Each entry
-stores one ARGB color. Colors are looked up by indexing them by
-(0x1e35a7bd * `color`) >> (32 - `color_cache_code_bits`). Only one
-lookup is done in a color cache; there is no conflict resolution.
+A color cache is an array of size `color_cache_size`. Each entry stores one ARGB
+color. Colors are looked up by indexing them by `(0x1e35a7bd * color) >> (32 -
+color_cache_code_bits)`. Only one lookup is done in a color cache; there is no
+conflict resolution.
-In the beginning of decoding or encoding of an image, all entries in all
-color cache values are set to zero. The color cache code is converted to
-this color at decoding time. The state of the color cache is maintained
-by inserting every pixel, be it produced by backward referencing or as
-literals, into the cache in the order they appear in the stream.
+In the beginning of decoding or encoding of an image, all entries in all color
+cache values are set to zero. The color cache code is converted to this color at
+decoding time. The state of the color cache is maintained by inserting every
+pixel, be it produced by backward referencing or as literals, into the cache in
+the order they appear in the stream.
-
-5 Entropy Code
+6 Entropy Code
--------------
-### 5.1 Overview
+### 6.1 Overview
-Most of the data is coded using [canonical Huffman code][canonical_huff]. Hence,
-the codes are transmitted by sending the _Huffman code lengths_, as opposed to
-the actual _Huffman codes_.
+Most of the data is coded using a [canonical prefix code][canonical_huff].
+Hence, the codes are transmitted by sending the _prefix code lengths_, as
+opposed to the actual _prefix codes_.
-In particular, the format uses **spatially-variant Huffman coding**. In other
+In particular, the format uses **spatially variant prefix coding**. In other
words, different blocks of the image can potentially use different entropy
codes.
-**Rationale**: Different areas of the image may have different characteristics. So, allowing them to use different entropy codes provides more flexibility and
-potentially a better compression.
+**Rationale**: Different areas of the image may have different characteristics.
+So, allowing them to use different entropy codes provides more flexibility and
+potentially better compression.
-### 5.2 Details
+### 6.2 Details
-The encoded image data consists of two parts:
+The encoded image data consists of several parts:
- 1. Meta Huffman codes
- 1. Entropy-coded image data
+ 1. Decoding and building the prefix codes.
+ 1. Meta prefix codes.
+ 1. Entropy-coded image data.
-#### 5.2.1 Decoding of Meta Huffman Codes
+For any given pixel (x, y), there is a set of five prefix codes associated with
+it. These codes are (in bitstream order):
-As noted earlier, the format allows the use of different Huffman codes for
-different blocks of the image. _Meta Huffman codes_ are indexes identifying
-which Huffman codes to use in different parts of the image.
+ * **Prefix code #1**: Used for green channel, backward-reference length, and
+ color cache.
+ * **Prefix code #2, #3, and #4**: Used for red, blue, and alpha channels,
+ respectively.
+ * **Prefix code #5**: Used for backward-reference distance.
-Meta Huffman codes may be used _only_ when the image is being used in the
-[role](#roles-of-image-data) of an _ARGB image_.
+From here on, we refer to this set as a **prefix code group**.
-There are two possibilities for the meta Huffman codes, indicated by a 1-bit
-value:
+#### 6.2.1 Decoding and Building the Prefix Codes
- * If this bit is zero, there is only one meta Huffman code used everywhere in
- the image. No more data is stored.
- * If this bit is one, the image uses multiple meta Huffman codes. These meta
- Huffman codes are stored as an _entropy image_ (described below).
+This section describes how to read the prefix code lengths from the bitstream.
+
+The prefix code lengths can be coded in two ways. The method used is specified
+by a 1-bit value.
-**Entropy image:**
+ * If this bit is 1, it is a _simple code length code_.
+ * If this bit is 0, it is a _normal code length code_.
-The entropy image defines which Huffman codes are used in different parts of the
-image, as described below.
+In both cases, there can be unused code lengths that are still part of the
+stream. This may be inefficient, but it is allowed by the format.
+The described tree must be a complete binary tree. A single leaf node is
+considered a complete binary tree and can be encoded using either the simple
+code length code or the normal code length code. When coding a single leaf
+node using the _normal code length code_, all but one code length are zeros,
+and the single leaf node value is marked with the length of 1 -- even when no
+bits are consumed when that single leaf node tree is used.
-The first 3-bits contain the `huffman_bits` value. The dimensions of the entropy
-image are derived from 'huffman_bits'.
+##### Simple Code Length Code
+
+This variant is used in the special case when only 1 or 2 prefix symbols are in
+the range \[0..255\] with code length `1`. All other prefix code lengths are
+implicitly zeros.
+
+The first bit indicates the number of symbols:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int huffman_bits = ReadBits(3) + 2;
-int huffman_xsize = DIV_ROUND_UP(xsize, 1 << huffman_bits);
-int huffman_ysize = DIV_ROUND_UP(ysize, 1 << huffman_bits);
+int num_symbols = ReadBits(1) + 1;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-where `DIV_ROUND_UP` is as defined [earlier](#predictor-transform).
+The following are the symbol values.
-Next bits contain an entropy image of width `huffman_xsize` and height
-`huffman_ysize`.
+This first symbol is coded using 1 or 8 bits, depending on the value of
+`is_first_8bits`. The range is \[0..1\] or \[0..255\], respectively. The second
+symbol, if present, is always assumed to be in the range \[0..255\] and coded
+using 8 bits.
-**Interpretation of Meta Huffman Codes:**
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int is_first_8bits = ReadBits(1);
+symbol0 = ReadBits(1 + 7 * is_first_8bits);
+code_lengths[symbol0] = 1;
+if (num_symbols == 2) {
+ symbol1 = ReadBits(8);
+ code_lengths[symbol1] = 1;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-For any given pixel (x, y), there is a set of five Huffman codes associated with
-it. These codes are (in bitstream order):
+The two symbols should be different. Duplicate symbols are allowed, but
+inefficient.
- * **Huffman code #1**: used for green channel, backward-reference length and
- color cache
- * **Huffman code #2, #3 and #4**: used for red, blue and alpha channels
- respectively.
- * **Huffman code #5**: used for backward-reference distance.
+**Note:** Another special case is when _all_ prefix code lengths are _zeros_ (an
+empty prefix code). For example, a prefix code for distance can be empty if
+there are no backward references. Similarly, prefix codes for alpha, red, and
+blue can be empty if all pixels within the same meta prefix code are produced
+using the color cache. However, this case doesn't need special handling, as
+empty prefix codes can be coded as those containing a single symbol `0`.
-From here on, we refer to this set as a **Huffman code group**.
+##### Normal Code Length Code
-The number of Huffman code groups in the ARGB image can be obtained by finding
-the _largest meta Huffman code_ from the entropy image:
+The code lengths of the prefix code fit in 8 bits and are read as follows.
+First, `num_code_lengths` specifies the number of code lengths.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int num_huff_groups = max(entropy image) + 1;
+int num_code_lengths = 4 + ReadBits(4);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-where `max(entropy image)` indicates the largest Huffman code stored in the
-entropy image.
-As each Huffman code groups contains five Huffman codes, the total number of
-Huffman codes is:
+The code lengths are themselves encoded using prefix codes; lower-level code
+lengths, `code_length_code_lengths`, first have to be read. The rest of those
+`code_length_code_lengths` (according to the order in `kCodeLengthCodeOrder`)
+are zeros.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int num_huff_codes = 5 * num_huff_groups;
+int kCodeLengthCodes = 19;
+int kCodeLengthCodeOrder[kCodeLengthCodes] = {
+ 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+int code_length_code_lengths[kCodeLengthCodes] = { 0 }; // All zeros
+for (i = 0; i < num_code_lengths; ++i) {
+ code_length_code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3);
+}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Given a pixel (x, y) in the ARGB image, we can obtain the corresponding Huffman
-codes to be used as follows:
+Next, if `ReadBits(1) == 0`, the maximum number of different read symbols
+(`max_symbol`) for each symbol type (A, R, G, B, and distance) is set to its
+alphabet size:
+
+ * G channel: 256 + 24 + `color_cache_size`
+ * Other literals (A, R, and B): 256
+ * Distance code: 40
+
+Otherwise, it is defined as:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int position = (y >> huffman_bits) * huffman_xsize + (x >> huffman_bits);
-int meta_huff_code = (entropy_image[pos] >> 8) & 0xffff;
-HuffmanCodeGroup huff_group = huffman_code_groups[meta_huff_code];
+int length_nbits = 2 + 2 * ReadBits(3);
+int max_symbol = 2 + ReadBits(length_nbits);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-where, we have assumed the existence of `HuffmanCodeGroup` structure, which
-represents a set of five Huffman codes. Also, `huffman_code_groups` is an array
-of `HuffmanCodeGroup` (of size `num_huff_groups`).
+If `max_symbol` is larger than the size of the alphabet for the symbol type, the
+bitstream is invalid.
-The decoder then uses Huffman code group `huff_group` to decode the pixel
-(x, y) as explained in the [next section](#decoding-entropy-coded-image-data).
+A prefix table is then built from `code_length_code_lengths` and used to read up
+to `max_symbol` code lengths.
-#### 5.2.2 Decoding Entropy-coded Image Data
+ * Code \[0..15\] indicates literal code lengths.
+ * Value 0 means no symbols have been coded.
+ * Values \[1..15\] indicate the bit length of the respective code.
+ * Code 16 repeats the previous nonzero value \[3..6\] times, that is,
+ `3 + ReadBits(2)` times. If code 16 is used before a nonzero
+ value has been emitted, a value of 8 is repeated.
+ * Code 17 emits a streak of zeros of length \[3..10\], that is, `3 +
+ ReadBits(3)` times.
+ * Code 18 emits a streak of zeros of length \[11..138\], that is,
+ `11 + ReadBits(7)` times.
-For the current position (x, y) in the image, the decoder first identifies the
-corresponding Huffman code group (as explained in the last section). Given the
-Huffman code group, the pixel is read and decoded as follows:
+Once code lengths are read, a prefix code for each symbol type (A, R, G, B, and
+distance) is formed using their respective alphabet sizes.
-Read next symbol S from the bitstream using Huffman code #1. \[See
-[next section](#decoding-the-code-lengths) for details on decoding the Huffman
-code lengths\]. Note that S is any integer in the range `0` to
-`(256 + 24 + ` [`color_cache_size`](#color-cache-code)`- 1)`.
+The Normal Code Length Code must code a full decision tree, that is, the sum of
+`2 ^ (-length)` for all non-zero codes must be exactly one. There is however
+one exception to this rule, the single leaf node tree, where the leaf node
+value is marked with value 1 and other values are 0s.
-The interpretation of S depends on its value:
+#### 6.2.2 Decoding of Meta Prefix Codes
- 1. if S < 256
- 1. Use S as the green component
- 1. Read red from the bitstream using Huffman code #2
- 1. Read blue from the bitstream using Huffman code #3
- 1. Read alpha from the bitstream using Huffman code #4
- 1. if S < 256 + 24
- 1. Use S - 256 as a length prefix code
- 1. Read extra bits for length from the bitstream
- 1. Determine backward-reference length L from length prefix code and the
- extra bits read.
- 1. Read distance prefix code from the bitstream using Huffman code #5
- 1. Read extra bits for distance from the bitstream
- 1. Determine backward-reference distance D from distance prefix code and
- the extra bits read.
- 1. Copy the L pixels (in scan-line order) from the sequence of pixels
- prior to them by D pixels.
- 1. if S >= 256 + 24
- 1. Use S - (256 + 24) as the index into the color cache.
- 1. Get ARGB color from the color cache at that index.
+As noted earlier, the format allows the use of different prefix codes for
+different blocks of the image. _Meta prefix codes_ are indexes identifying which
+prefix codes to use in different parts of the image.
+Meta prefix codes may be used _only_ when the image is being used in the
+[role](#roles-of-image-data) of an _ARGB image_.
-**Decoding the Code Lengths:**
-{:#decoding-the-code-lengths}
+There are two possibilities for the meta prefix codes, indicated by a 1-bit
+value:
-This section describes the details about reading a symbol from the bitstream by
-decoding the Huffman code length.
+ * If this bit is zero, there is only one meta prefix code used everywhere in
+ the image. No more data is stored.
+ * If this bit is one, the image uses multiple meta prefix codes. These meta
+ prefix codes are stored as an _entropy image_ (described below).
-The Huffman code lengths can be coded in two ways. The method used is specified
-by a 1-bit value.
+The red and green components of a pixel define a 16-bit meta prefix code used in
+a particular block of the ARGB image.
- * If this bit is 1, it is a _simple code length code_, and
- * If this bit is 0, it is a _normal code length code_.
+##### Entropy Image
-**(i) Simple Code Length Code:**
+The entropy image defines which prefix codes are used in different parts of the
+image.
-This variant is used in the special case when only 1 or 2 Huffman code lengths
-are non-zero, and are in the range of \[0, 255\]. All other Huffman code lengths
-are implicitly zeros.
-
-The first bit indicates the number of non-zero code lengths:
+The first 3 bits contain the `prefix_bits` value. The dimensions of the entropy
+image are derived from `prefix_bits`:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int num_code_lengths = ReadBits(1) + 1;
+int prefix_bits = ReadBits(3) + 2;
+int prefix_image_width =
+ DIV_ROUND_UP(image_width, 1 << prefix_bits);
+int prefix_image_height =
+ DIV_ROUND_UP(image_height, 1 << prefix_bits);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The first code length is stored either using a 1-bit code for values of 0 and 1,
-or using an 8-bit code for values in range \[0, 255\]. The second code length,
-when present, is coded as an 8-bit code.
+where `DIV_ROUND_UP` is as defined [earlier](#predictor-transform).
+
+The next bits contain an entropy image of width `prefix_image_width` and height
+`prefix_image_height`.
+
+##### Interpretation of Meta Prefix Codes
+
+The number of prefix code groups in the ARGB image can be obtained by finding
+the _largest meta prefix code_ from the entropy image:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int is_first_8bits = ReadBits(1);
-code_lengths[0] = ReadBits(1 + 7 * is_first_8bits);
-if (num_code_lengths == 2) {
- code_lengths[1] = ReadBits(8);
-}
+int num_prefix_groups = max(entropy image) + 1;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+where `max(entropy image)` indicates the largest prefix code stored in the
+entropy image.
-**Note:** Another special case is when _all_ Huffman code lengths are _zeros_
-(an empty Huffman code). For example, a Huffman code for distance can be empty
-if there are no backward references. Similarly, Huffman codes for alpha, red,
-and blue can be empty if all pixels within the same meta Huffman code are
-produced using the color cache. However, this case doesn't need a special
-handling, as empty Huffman codes can be coded as those containing a single
-symbol `0`.
+As each prefix code group contains five prefix codes, the total number of prefix
+codes is:
-**(ii) Normal Code Length Code:**
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_prefix_codes = 5 * num_prefix_groups;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The code lengths of a Huffman code are read as follows: `num_code_lengths`
-specifies the number of code lengths; the rest of the code lengths
-(according to the order in `kCodeLengthCodeOrder`) are zeros.
+Given a pixel (x, y) in the ARGB image, we can obtain the corresponding prefix
+codes to be used as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int kCodeLengthCodes = 19;
-int kCodeLengthCodeOrder[kCodeLengthCodes] = {
- 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
-};
-int code_lengths[kCodeLengthCodes] = { 0 }; // All zeros.
-int num_code_lengths = 4 + ReadBits(4);
-for (i = 0; i < num_code_lengths; ++i) {
- code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3);
-}
+int position =
+ (y >> prefix_bits) * prefix_image_width + (x >> prefix_bits);
+int meta_prefix_code = (entropy_image[position] >> 8) & 0xffff;
+PrefixCodeGroup prefix_group = prefix_code_groups[meta_prefix_code];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * Code length code \[0..15\] indicates literal code lengths.
- * Value 0 means no symbols have been coded.
- * Values \[1..15\] indicate the bit length of the respective code.
- * Code 16 repeats the previous non-zero value \[3..6\] times, i.e.,
- 3 + `ReadBits(2)` times. If code 16 is used before a non-zero
- value has been emitted, a value of 8 is repeated.
- * Code 17 emits a streak of zeros \[3..10\], i.e., 3 + `ReadBits(3)`
- times.
- * Code 18 emits a streak of zeros of length \[11..138\], i.e.,
- 11 + `ReadBits(7)` times.
+where we have assumed the existence of `PrefixCodeGroup` structure, which
+represents a set of five prefix codes. Also, `prefix_code_groups` is an array of
+`PrefixCodeGroup` (of size `num_prefix_groups`).
+
+The decoder then uses prefix code group `prefix_group` to decode the pixel
+(x, y), as explained in ["Decoding Entropy-Coded Image
+Data"](#decoding-entropy-coded-image-data).
+
+#### 6.2.3 Decoding Entropy-Coded Image Data
+For the current position (x, y) in the image, the decoder first identifies the
+corresponding prefix code group (as explained in the last section). Given the
+prefix code group, the pixel is read and decoded as follows.
+
+Next, read the symbol S from the bitstream using prefix code #1. Note that S is
+any integer in the range `0` to
+`(256 + 24 + ` [`color_cache_size`](#color-cache-code)` - 1)`.
+
+The interpretation of S depends on its value:
+
+ 1. If S < 256
+ 1. Use S as the green component.
+ 1. Read red from the bitstream using prefix code #2.
+ 1. Read blue from the bitstream using prefix code #3.
+ 1. Read alpha from the bitstream using prefix code #4.
+ 1. If S >= 256 & S < 256 + 24
+ 1. Use S - 256 as a length prefix code.
+ 1. Read extra bits for the length from the bitstream.
+ 1. Determine backward-reference length L from length prefix code and the
+ extra bits read.
+ 1. Read the distance prefix code from the bitstream using prefix code #5.
+ 1. Read extra bits for the distance from the bitstream.
+ 1. Determine backward-reference distance D from the distance prefix code
+ and the extra bits read.
+ 1. Copy L pixels (in scan-line order) from the sequence of pixels starting
+ at the current position minus D pixels.
+ 1. If S >= 256 + 24
+ 1. Use S - (256 + 24) as the index into the color cache.
+ 1. Get ARGB color from the color cache at that index.
-6 Overall Structure of the Format
+7 Overall Structure of the Format
---------------------------------
-Below is a view into the format in Backus-Naur form. It does not cover
-all details. End-of-image (EOI) is only implicitly coded into the number
-of pixels (xsize * ysize).
+Below is a view into the format in Augmented Backus-Naur Form (ABNF)
+[RFC 5234][] [RFC 7405][]. It does not cover all details. The end-of-image (EOI)
+is only implicitly coded into the number of pixels (image_width * image_height).
+Note that `*element` means `element` can be repeated 0 or more times. `5element`
+means `element` is repeated exactly 5 times. `%b` represents a binary value.
-#### Basic Structure
+#### 7.1 Basic Structure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-<format> ::= <RIFF header><image size><image stream>
-<image stream> ::= <optional-transform><spatially-coded image>
+format = RIFF-header image-header image-stream
+RIFF-header = %s"RIFF" 4OCTET %s"WEBPVP8L" 4OCTET
+image-header = %x2F image-size alpha-is-used version
+image-size = 14BIT 14BIT ; width - 1, height - 1
+alpha-is-used = 1BIT
+version = 3BIT ; 0
+image-stream = optional-transform spatially-coded-image
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#### 7.2 Structure of Transforms
-#### Structure of Transforms
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+optional-transform = (%b1 transform optional-transform) / %b0
+transform = predictor-tx / color-tx / subtract-green-tx
+transform =/ color-indexing-tx
+predictor-tx = %b00 predictor-image
+predictor-image = 3BIT ; sub-pixel code
+ entropy-coded-image
+
+color-tx = %b01 color-image
+color-image = 3BIT ; sub-pixel code
+ entropy-coded-image
+
+subtract-green-tx = %b10
+
+color-indexing-tx = %b11 color-indexing-image
+color-indexing-image = 8BIT ; color count
+ entropy-coded-image
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-<optional-transform> ::= (1-bit value 1; <transform> <optional-transform>) |
- 1-bit value 0
-<transform> ::= <predictor-tx> | <color-tx> | <subtract-green-tx> |
- <color-indexing-tx>
-<predictor-tx> ::= 2-bit value 0; <predictor image>
-<predictor image> ::= 3-bit sub-pixel code ; <entropy-coded image>
-<color-tx> ::= 2-bit value 1; <color image>
-<color image> ::= 3-bit sub-pixel code ; <entropy-coded image>
-<subtract-green-tx> ::= 2-bit value 2
-<color-indexing-tx> ::= 2-bit value 3; <color-indexing image>
-<color-indexing image> ::= 8-bit color count; <entropy-coded image>
+
+#### 7.3 Structure of the Image Data
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+spatially-coded-image = color-cache-info meta-prefix data
+entropy-coded-image = color-cache-info data
+color-cache-info = %b0
+color-cache-info =/ (%b1 4BIT) ; 1 followed by color cache size
-#### Structure of the Image Data
+meta-prefix = %b0 / (%b1 entropy-image)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-<spatially-coded image> ::= <meta huffman><entropy-coded image>
-<entropy-coded image> ::= <color cache info><huffman codes><lz77-coded image>
-<meta huffman> ::= 1-bit value 0 |
- (1-bit value 1; <entropy image>)
-<entropy image> ::= 3-bit subsample value; <entropy-coded image>
-<color cache info> ::= 1 bit value 0 |
- (1-bit value 1; 4-bit value for color cache size)
-<huffman codes> ::= <huffman code group> | <huffman code group><huffman codes>
-<huffman code group> ::= <huffman code><huffman code><huffman code>
- <huffman code><huffman code>
- See "Interpretation of Meta Huffman codes" to
- understand what each of these five Huffman codes are
- for.
-<huffman code> ::= <simple huffman code> | <normal huffman code>
-<simple huffman code> ::= see "Simple code length code" for details
-<normal huffman code> ::= <code length code>; encoded code lengths
-<code length code> ::= see section "Normal code length code"
-<lz77-coded image> ::= ((<argb-pixel> | <lz77-copy> | <color-cache-code>)
- <lz77-coded image>) | ""
+data = prefix-codes lz77-coded-image
+entropy-image = 3BIT ; subsample value
+ entropy-coded-image
+
+prefix-codes = prefix-code-group *prefix-codes
+prefix-code-group =
+ 5prefix-code ; See "Interpretation of Meta Prefix Codes" to
+ ; understand what each of these five prefix
+ ; codes are for.
+
+prefix-code = simple-prefix-code / normal-prefix-code
+simple-prefix-code = ; see "Simple Code Length Code" for details
+normal-prefix-code = ; see "Normal Code Length Code" for details
+
+lz77-coded-image =
+ *((argb-pixel / lz77-copy / color-cache-code) lz77-coded-image)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-A possible example sequence:
+The following is a possible example sequence:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-<RIFF header><image size>1-bit value 1<subtract-green-tx>
-1-bit value 1<predictor-tx>1-bit value 0<meta huffman>
-<color cache info><huffman codes>
-<lz77-coded image>
+RIFF-header image-size %b1 subtract-green-tx
+%b1 predictor-tx %b0 color-cache-info
+%b0 prefix-codes lz77-coded-image
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+[RFC 5234]: https://www.rfc-editor.org/rfc/rfc5234
+[RFC 7405]: https://www.rfc-editor.org/rfc/rfc7405
[canonical_huff]: https://en.wikipedia.org/wiki/Canonical_Huffman_code
diff --git a/c-lib/examples/Android.mk b/c-lib/examples/Android.mk
@@ -1,3 +1,5 @@
+# Ignore this file during non-NDK builds.
+ifdef NDK_ROOT
LOCAL_PATH := $(call my-dir)
################################################################################
@@ -91,3 +93,4 @@ LOCAL_STATIC_LIBRARIES := example_util imageio_util webp
LOCAL_MODULE := webpinfo_example
include $(BUILD_EXECUTABLE)
+endif # NDK_ROOT
diff --git a/c-lib/examples/Makefile.am b/c-lib/examples/Makefile.am
@@ -47,7 +47,7 @@ anim_dump_LDADD += ../imageio/libimageenc.la
anim_dump_LDADD += $(PNG_LIBS) $(GIF_LIBS) $(TIFF_LIBS) -lm
cwebp_SOURCES = cwebp.c stopwatch.h
-cwebp_CPPFLAGS = $(AM_CPPFLAGS)
+cwebp_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)
cwebp_LDADD =
cwebp_LDADD += libexample_util.la
cwebp_LDADD += ../imageio/libimageio_util.la
@@ -67,7 +67,7 @@ dwebp_LDADD += ../src/libwebp.la
dwebp_LDADD +=$(PNG_LIBS) $(JPEG_LIBS)
gif2webp_SOURCES = gif2webp.c gifdec.c gifdec.h
-gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES)
+gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES) -I$(top_srcdir)
gif2webp_LDADD =
gif2webp_LDADD += libexample_util.la
gif2webp_LDADD += ../imageio/libimageio_util.la
@@ -92,7 +92,7 @@ webpmux_LDADD += ../src/mux/libwebpmux.la
webpmux_LDADD += ../src/libwebp.la
img2webp_SOURCES = img2webp.c
-img2webp_CPPFLAGS = $(AM_CPPFLAGS)
+img2webp_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)
img2webp_LDADD =
img2webp_LDADD += libexample_util.la
img2webp_LDADD += ../imageio/libimageio_util.la
diff --git a/c-lib/examples/anim_diff.c b/c-lib/examples/anim_diff.c
@@ -16,12 +16,13 @@
#include <assert.h>
#include <limits.h>
#include <stdio.h>
-#include <stdlib.h> // for 'strtod'.
+#include <stdlib.h>
#include <string.h> // for 'strcmp'.
#include "./anim_util.h"
#include "./example_util.h"
#include "./unicode.h"
+#include "webp/types.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
@@ -206,8 +207,9 @@ static void Help(void) {
printf(" -version ............ print version number and exit\n");
}
+// Returns 0 on success, 1 if animation files differ, and 2 for any error.
int main(int argc, const char* argv[]) {
- int return_code = -1;
+ int return_code = 2;
int dump_frames = 0;
const char* dump_folder = NULL;
double min_psnr = 0.;
@@ -269,18 +271,18 @@ int main(int argc, const char* argv[]) {
}
if (parse_error) {
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(return_code);
}
}
if (argc < 3) {
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(return_code);
}
if (!got_input2) {
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(return_code);
}
if (dump_frames) {
@@ -293,7 +295,7 @@ int main(int argc, const char* argv[]) {
if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) {
WFPRINTF(stderr, "Error decoding file: %s\n Aborting.\n",
(const W_CHAR*)files[i]);
- return_code = -2;
+ return_code = 2;
goto End;
} else {
MinimizeAnimationFrames(&images[i], max_diff);
@@ -304,7 +306,7 @@ int main(int argc, const char* argv[]) {
premultiply, min_psnr)) {
WFPRINTF(stderr, "\nFiles %s and %s differ.\n", (const W_CHAR*)files[0],
(const W_CHAR*)files[1]);
- return_code = -3;
+ return_code = 1;
} else {
WPRINTF("\nFiles %s and %s are identical.\n", (const W_CHAR*)files[0],
(const W_CHAR*)files[1]);
diff --git a/c-lib/examples/anim_dump.c b/c-lib/examples/anim_dump.c
@@ -12,12 +12,14 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <stdio.h>
+#include <stdlib.h>
#include <string.h> // for 'strcmp'.
-#include "./anim_util.h"
-#include "webp/decode.h"
#include "../imageio/image_enc.h"
+#include "./anim_util.h"
#include "./unicode.h"
+#include "webp/decode.h"
+#include "webp/types.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
@@ -35,6 +37,7 @@ static void Help(void) {
printf(" -version ............ print version number and exit\n");
}
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
int error = 0;
const W_CHAR* dump_folder = TO_W_CHAR(".");
@@ -47,7 +50,7 @@ int main(int argc, const char* argv[]) {
if (argc < 2) {
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
for (c = 1; !error && c < argc; ++c) {
@@ -73,7 +76,7 @@ int main(int argc, const char* argv[]) {
suffix = TO_W_CHAR("pam");
} else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-version")) {
int dec_version, demux_version;
GetAnimatedImageVersions(&dec_version, &demux_version);
@@ -82,7 +85,7 @@ int main(int argc, const char* argv[]) {
(dec_version >> 0) & 0xff,
(demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
(demux_version >> 0) & 0xff);
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else {
uint32_t i;
AnimatedImage image;
@@ -98,7 +101,11 @@ int main(int argc, const char* argv[]) {
for (i = 0; !error && i < image.num_frames; ++i) {
W_CHAR out_file[1024];
WebPDecBuffer buffer;
- WebPInitDecBuffer(&buffer);
+ if (!WebPInitDecBuffer(&buffer)) {
+ fprintf(stderr, "Cannot init dec buffer\n");
+ error = 1;
+ continue;
+ }
buffer.colorspace = MODE_RGBA;
buffer.is_external_memory = 1;
buffer.width = image.canvas_width;
@@ -117,5 +124,5 @@ int main(int argc, const char* argv[]) {
ClearAnimatedImage(&image);
}
}
- FREE_WARGV_AND_RETURN(error ? 1 : 0);
+ FREE_WARGV_AND_RETURN(error ? EXIT_FAILURE : EXIT_SUCCESS);
}
diff --git a/c-lib/examples/anim_util.c b/c-lib/examples/anim_util.c
@@ -19,13 +19,16 @@
#if defined(WEBP_HAVE_GIF)
#include <gif_lib.h>
#endif
-#include "webp/format_constants.h"
-#include "webp/decode.h"
-#include "webp/demux.h"
+
#include "../imageio/imageio_util.h"
#include "./gifdec.h"
#include "./unicode.h"
#include "./unicode_gif.h"
+#include "webp/decode.h"
+#include "webp/demux.h"
+#include "webp/format_constants.h"
+#include "webp/mux_types.h"
+#include "webp/types.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
@@ -241,7 +244,7 @@ static int ReadAnimatedWebP(const char filename[],
image->bgcolor = anim_info.bgcolor;
// Allocate frames.
- if (!AllocateFrames(image, anim_info.frame_count)) return 0;
+ if (!AllocateFrames(image, anim_info.frame_count)) goto End;
// Decode frames.
while (WebPAnimDecoderHasMoreFrames(dec)) {
@@ -558,7 +561,10 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
}
}
// Allocate frames.
- AllocateFrames(image, frame_count);
+ if (!AllocateFrames(image, frame_count)) {
+ DGifCloseFile(gif, NULL);
+ return 0;
+ }
canvas_width = image->canvas_width;
canvas_height = image->canvas_height;
@@ -768,6 +774,7 @@ void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
*psnr = 99.; // PSNR when images are identical.
} else {
sse /= stride * height;
+ assert(sse != 0.0);
*psnr = 4.3429448 * log(255. * 255. / sse);
}
}
diff --git a/c-lib/examples/cwebp.c b/c-lib/examples/cwebp.c
@@ -27,7 +27,10 @@
#include "../imageio/webpdec.h"
#include "./stopwatch.h"
#include "./unicode.h"
+#include "imageio/metadata.h"
+#include "sharpyuv/sharpyuv.h"
#include "webp/encode.h"
+#include "webp/types.h"
#ifndef WEBP_DLL
#ifdef __cplusplus
@@ -177,8 +180,14 @@ static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
fprintf(stderr, "\n");
}
- fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n",
- stats->histogram_bits, stats->transform_bits, stats->cache_bits);
+ fprintf(stderr, " * Precision Bits: histogram=%d", stats->histogram_bits);
+ if (stats->lossless_features & 1) {
+ fprintf(stderr, " prediction=%d", stats->transform_bits);
+ }
+ if (stats->lossless_features & 2) {
+ fprintf(stderr, " cross-color=%d", stats->cross_color_transform_bits);
+ }
+ fprintf(stderr, " cache=%d\n", stats->cache_bits);
if (stats->palette_size > 0) {
fprintf(stderr, " * Palette size: %d\n", stats->palette_size);
}
@@ -305,6 +314,7 @@ static int MyWriter(const uint8_t* data, size_t data_size,
// Dumps a picture as a PGM file using the IMC4 layout.
static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
int y;
+ int ok = 0;
const int uv_width = (picture->width + 1) / 2;
const int uv_height = (picture->height + 1) / 2;
const int stride = (picture->width + 1) & ~1;
@@ -319,23 +329,26 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
if (f == NULL) return 0;
fprintf(f, "P5\n%d %d\n255\n", stride, height);
for (y = 0; y < picture->height; ++y) {
- if (fwrite(src_y, picture->width, 1, f) != 1) return 0;
+ if (fwrite(src_y, picture->width, 1, f) != 1) goto Error;
if (picture->width & 1) fputc(0, f); // pad
src_y += picture->y_stride;
}
for (y = 0; y < uv_height; ++y) {
- if (fwrite(src_u, uv_width, 1, f) != 1) return 0;
- if (fwrite(src_v, uv_width, 1, f) != 1) return 0;
+ if (fwrite(src_u, uv_width, 1, f) != 1) goto Error;
+ if (fwrite(src_v, uv_width, 1, f) != 1) goto Error;
src_u += picture->uv_stride;
src_v += picture->uv_stride;
}
for (y = 0; y < alpha_height; ++y) {
- if (fwrite(src_a, picture->width, 1, f) != 1) return 0;
+ if (fwrite(src_a, picture->width, 1, f) != 1) goto Error;
if (picture->width & 1) fputc(0, f); // pad
src_a += picture->a_stride;
}
+ ok = 1;
+
+ Error:
fclose(f);
- return 1;
+ return ok;
}
// -----------------------------------------------------------------------------
@@ -505,6 +518,37 @@ static int WriteWebPWithMetadata(FILE* const out,
}
//------------------------------------------------------------------------------
+// Resize
+
+enum {
+ RESIZE_MODE_DOWN_ONLY,
+ RESIZE_MODE_UP_ONLY,
+ RESIZE_MODE_ALWAYS,
+ RESIZE_MODE_DEFAULT = RESIZE_MODE_ALWAYS
+};
+
+static void ApplyResizeMode(const int resize_mode,
+ const WebPPicture* const pic,
+ int* const resize_w, int* const resize_h) {
+ const int src_w = pic->width;
+ const int src_h = pic->height;
+ const int dst_w = *resize_w;
+ const int dst_h = *resize_h;
+
+ if (resize_mode == RESIZE_MODE_DOWN_ONLY) {
+ if ((dst_w == 0 && src_h <= dst_h) ||
+ (dst_h == 0 && src_w <= dst_w) ||
+ (src_w <= dst_w && src_h <= dst_h)) {
+ *resize_w = *resize_h = 0;
+ }
+ } else if (resize_mode == RESIZE_MODE_UP_ONLY) {
+ if (src_w >= dst_w && src_h >= dst_h) {
+ *resize_w = *resize_h = 0;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
static int ProgressReport(int percent, const WebPPicture* const picture) {
fprintf(stderr, "[%s]: %3d %% \r",
@@ -571,7 +615,9 @@ static void HelpLong(void) {
printf(" -qrange <min> <max> .... specifies the permissible quality range\n"
" (default: 0 100)\n");
printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
- printf(" -resize <w> <h> ........ resize picture (after any cropping)\n");
+ printf(" -resize <w> <h> ........ resize picture (*after* any cropping)\n");
+ printf(" -resize_mode <string> .. one of: up_only, down_only,"
+ " always (default)\n");
printf(" -mt .................... use multi-threading if available\n");
printf(" -low_memory ............ reduce memory usage (slower encoding)\n");
printf(" -map <int> ............. print map of extra info\n");
@@ -591,9 +637,8 @@ static void HelpLong(void) {
" green=0xe0 and blue=0xd0\n");
printf(" -noalpha ............... discard any transparency information\n");
printf(" -lossless .............. encode image losslessly, default=off\n");
- printf(" -near_lossless <int> ... use near-lossless image\n"
- " preprocessing (0..100=off), "
- "default=100\n");
+ printf(" -near_lossless <int> ... use near-lossless image preprocessing\n"
+ " (0..100=off), default=100\n");
printf(" -hint <string> ......... specify image characteristics hint,\n");
printf(" one of: photo, picture or graph\n");
@@ -620,6 +665,7 @@ static void HelpLong(void) {
printf(" -af .................... auto-adjust filter strength\n");
printf(" -pre <int> ............. pre-processing filter\n");
printf("\n");
+ printf("Supported input formats:\n %s\n", WebPGetEnabledInputFileFormats());
}
//------------------------------------------------------------------------------
@@ -646,8 +692,9 @@ static const char* const kErrorMessages[VP8_ENC_ERROR_LAST] = {
//------------------------------------------------------------------------------
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
- int return_value = -1;
+ int return_value = EXIT_FAILURE;
const char* in_file = NULL, *out_file = NULL, *dump_file = NULL;
FILE* out = NULL;
int c;
@@ -658,6 +705,7 @@ int main(int argc, const char* argv[]) {
uint32_t background_color = 0xffffffu;
int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
int resize_w = 0, resize_h = 0;
+ int resize_mode = RESIZE_MODE_DEFAULT;
int lossless_preset = 6;
int use_lossless_preset = -1; // -1=unset, 0=don't use, 1=use it
int show_progress = 0;
@@ -681,22 +729,22 @@ int main(int argc, const char* argv[]) {
!WebPPictureInit(&original_picture) ||
!WebPConfigInit(&config)) {
fprintf(stderr, "Error! Version mismatch!\n");
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
if (argc == 1) {
HelpShort();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
for (c = 1; c < argc; ++c) {
int parse_error = 0;
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
HelpShort();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
HelpLong();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-o") && c + 1 < argc) {
out_file = (const char*)GET_WARGV(argv, ++c);
} else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
@@ -825,15 +873,31 @@ int main(int argc, const char* argv[]) {
} else if (!strcmp(argv[c], "-resize") && c + 2 < argc) {
resize_w = ExUtilGetInt(argv[++c], 0, &parse_error);
resize_h = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-resize_mode") && c + 1 < argc) {
+ ++c;
+ if (!strcmp(argv[c], "down_only")) {
+ resize_mode = RESIZE_MODE_DOWN_ONLY;
+ } else if (!strcmp(argv[c], "up_only")) {
+ resize_mode = RESIZE_MODE_UP_ONLY;
+ } else if (!strcmp(argv[c], "always")) {
+ resize_mode = RESIZE_MODE_ALWAYS;
+ } else {
+ fprintf(stderr, "Error! Unrecognized resize mode: %s\n", argv[c]);
+ goto Error;
+ }
#ifndef WEBP_DLL
} else if (!strcmp(argv[c], "-noasm")) {
VP8GetCPUInfo = NULL;
#endif
} else if (!strcmp(argv[c], "-version")) {
const int version = WebPGetEncoderVersion();
+ const int sharpyuv_version = SharpYuvGetVersion();
printf("%d.%d.%d\n",
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
- FREE_WARGV_AND_RETURN(0);
+ printf("libsharpyuv: %d.%d.%d\n",
+ (sharpyuv_version >> 24) & 0xff, (sharpyuv_version >> 16) & 0xffff,
+ sharpyuv_version & 0xff);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-progress")) {
show_progress = 1;
} else if (!strcmp(argv[c], "-quiet")) {
@@ -895,7 +959,7 @@ int main(int argc, const char* argv[]) {
if (i == kNumTokens) {
fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
(int)(token - start), start);
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
start = token + 1;
}
@@ -914,14 +978,14 @@ int main(int argc, const char* argv[]) {
} else if (argv[c][0] == '-') {
fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
HelpLong();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
} else {
in_file = (const char*)GET_WARGV(argv, c);
}
if (parse_error) {
HelpLong();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
}
if (in_file == NULL) {
@@ -1041,6 +1105,7 @@ int main(int argc, const char* argv[]) {
goto Error;
}
}
+ ApplyResizeMode(resize_mode, &picture, &resize_w, &resize_h);
if ((resize_w | resize_h) > 0) {
WebPPicture picture_no_alpha;
if (config.exact) {
@@ -1139,9 +1204,10 @@ int main(int argc, const char* argv[]) {
}
picture.use_argb = 1;
- if (!ReadWebP(memory_writer.mem, memory_writer.size, &picture,
- /*keep_alpha=*/WebPPictureHasTransparency(&picture),
- /*metadata=*/NULL)) {
+ if (!ReadWebP(
+ memory_writer.mem, memory_writer.size, &picture,
+ /*keep_alpha=*/WebPPictureHasTransparency(&original_picture),
+ /*metadata=*/NULL)) {
fprintf(stderr, "Error! Cannot decode encoded WebP bitstream\n");
fprintf(stderr, "Error code: %d (%s)\n", picture.error_code,
kErrorMessages[picture.error_code]);
@@ -1221,7 +1287,7 @@ int main(int argc, const char* argv[]) {
PrintMetadataInfo(&metadata, metadata_written);
}
}
- return_value = 0;
+ return_value = EXIT_SUCCESS;
Error:
WebPMemoryWriterClear(&memory_writer);
diff --git a/c-lib/examples/dwebp.c b/c-lib/examples/dwebp.c
@@ -25,6 +25,8 @@
#include "../imageio/webpdec.h"
#include "./stopwatch.h"
#include "./unicode.h"
+#include "webp/decode.h"
+#include "webp/types.h"
static int verbose = 0;
static int quiet = 0;
@@ -96,7 +98,7 @@ static void Help(void) {
" -alpha_dither use alpha-plane dithering if needed\n"
" -mt .......... use multi-threading\n"
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
- " -resize <w> <h> ......... scale the output (*after* any cropping)\n"
+ " -resize <w> <h> ......... resize output (*after* any cropping)\n"
" -flip ........ flip the output vertically\n"
" -alpha ....... only save the alpha plane\n"
" -incremental . use incremental decoding (useful for tests)\n"
@@ -177,6 +179,7 @@ static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
return external_buffer;
}
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
int ok = 0;
const char* in_file = NULL;
@@ -197,14 +200,14 @@ int main(int argc, const char* argv[]) {
if (!WebPInitDecoderConfig(&config)) {
fprintf(stderr, "Library version mismatch!\n");
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
for (c = 1; c < argc; ++c) {
int parse_error = 0;
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
out_file = (const char*)GET_WARGV(argv, ++c);
} else if (!strcmp(argv[c], "-alpha")) {
@@ -227,7 +230,7 @@ int main(int argc, const char* argv[]) {
const int version = WebPGetDecoderVersion();
printf("%d.%d.%d\n",
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-pgm")) {
format = PGM;
} else if (!strcmp(argv[c], "-yuv")) {
@@ -293,21 +296,21 @@ int main(int argc, const char* argv[]) {
} else if (argv[c][0] == '-') {
fprintf(stderr, "Unknown option '%s'\n", argv[c]);
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
} else {
in_file = (const char*)GET_WARGV(argv, c);
}
if (parse_error) {
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
}
if (in_file == NULL) {
fprintf(stderr, "missing input file!!\n");
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
if (quiet) verbose = 0;
@@ -316,7 +319,7 @@ int main(int argc, const char* argv[]) {
VP8StatusCode status = VP8_STATUS_OK;
size_t data_size = 0;
if (!LoadWebP(in_file, &data, &data_size, bitstream)) {
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
switch (format) {
@@ -415,7 +418,7 @@ int main(int argc, const char* argv[]) {
WebPFreeDecBuffer(output_buffer);
WebPFree((void*)external_buffer);
WebPFree((void*)data);
- FREE_WARGV_AND_RETURN(ok ? 0 : -1);
+ FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
//------------------------------------------------------------------------------
diff --git a/c-lib/examples/example_util.c b/c-lib/examples/example_util.c
@@ -17,8 +17,9 @@
#include <stdlib.h>
#include <string.h>
-#include "webp/mux_types.h"
#include "../imageio/imageio_util.h"
+#include "webp/mux_types.h"
+#include "webp/types.h"
//------------------------------------------------------------------------------
// String parsing
@@ -66,17 +67,17 @@ float ExUtilGetFloat(const char* const v, int* const error) {
static void ResetCommandLineArguments(int argc, const char* argv[],
CommandLineArguments* const args) {
assert(args != NULL);
- args->argc_ = argc;
- args->argv_ = argv;
- args->own_argv_ = 0;
- WebPDataInit(&args->argv_data_);
+ args->argc = argc;
+ args->argv = argv;
+ args->own_argv = 0;
+ WebPDataInit(&args->argv_data);
}
void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args) {
if (args != NULL) {
- if (args->own_argv_) {
- WebPFree((void*)args->argv_);
- WebPDataClear(&args->argv_data_);
+ if (args->own_argv) {
+ WebPFree((void*)args->argv);
+ WebPDataClear(&args->argv_data);
}
ResetCommandLineArguments(0, NULL, args);
}
@@ -98,25 +99,29 @@ int ExUtilInitCommandLineArguments(int argc, const char* argv[],
return 0;
#endif
- if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data_)) {
+ if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data)) {
+ return 0;
+ }
+ args->own_argv = 1;
+ args->argv = (const char**)WebPMalloc(MAX_ARGC * sizeof(*args->argv));
+ if (args->argv == NULL) {
+ ExUtilDeleteCommandLineArguments(args);
return 0;
}
- args->own_argv_ = 1;
- args->argv_ = (const char**)WebPMalloc(MAX_ARGC * sizeof(*args->argv_));
- if (args->argv_ == NULL) return 0;
argc = 0;
- for (cur = strtok((char*)args->argv_data_.bytes, sep);
+ for (cur = strtok((char*)args->argv_data.bytes, sep);
cur != NULL;
cur = strtok(NULL, sep)) {
if (argc == MAX_ARGC) {
fprintf(stderr, "ERROR: Arguments limit %d reached\n", MAX_ARGC);
+ ExUtilDeleteCommandLineArguments(args);
return 0;
}
assert(strlen(cur) != 0);
- args->argv_[argc++] = cur;
+ args->argv[argc++] = cur;
}
- args->argc_ = argc;
+ args->argc = argc;
}
return 1;
}
diff --git a/c-lib/examples/example_util.h b/c-lib/examples/example_util.h
@@ -45,10 +45,10 @@ int ExUtilReadFileToWebPData(const char* const filename,
// Command-line arguments
typedef struct {
- int argc_;
- const char** argv_;
- WebPData argv_data_;
- int own_argv_;
+ int argc;
+ const char** argv;
+ WebPData argv_data;
+ int own_argv;
} CommandLineArguments;
// Initializes the structure from the command-line parameters. If there is
diff --git a/c-lib/examples/gif2webp.c b/c-lib/examples/gif2webp.c
@@ -28,6 +28,7 @@
#endif
#include <gif_lib.h>
+#include "sharpyuv/sharpyuv.h"
#include "webp/encode.h"
#include "webp/mux.h"
#include "../examples/example_util.h"
@@ -70,8 +71,14 @@ static void Help(void) {
printf(" -lossy ................. encode image using lossy compression\n");
printf(" -mixed ................. for each frame in the image, pick lossy\n"
" or lossless compression heuristically\n");
+ printf(" -near_lossless <int> ... use near-lossless image preprocessing\n"
+ " (0..100=off), default=100\n");
+ printf(" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
+ "conversion\n"
+ " (lossy only)\n");
printf(" -q <float> ............. quality factor (0:small..100:big)\n");
- printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
+ printf(" -m <int> ............... compression method (0=fast, 6=slowest), "
+ "default=4\n");
printf(" -min_size .............. minimize output size (default:off)\n"
" lossless compression by default; can be\n"
" combined with -q, -m, -lossy or -mixed\n"
@@ -96,6 +103,7 @@ static void Help(void) {
//------------------------------------------------------------------------------
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
int verbose = 0;
int gif_error = GIF_ERROR;
@@ -140,7 +148,7 @@ int main(int argc, const char* argv[]) {
!WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) ||
!WebPPictureInit(&prev_canvas)) {
fprintf(stderr, "Error! Version mismatch!\n");
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
config.lossless = 1; // Use lossless compression by default.
@@ -150,14 +158,14 @@ int main(int argc, const char* argv[]) {
if (argc == 1) {
Help();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
for (c = 1; c < argc; ++c) {
int parse_error = 0;
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
out_file = GET_WARGV(argv, ++c);
} else if (!strcmp(argv[c], "-lossy")) {
@@ -165,6 +173,10 @@ int main(int argc, const char* argv[]) {
} else if (!strcmp(argv[c], "-mixed")) {
enc_options.allow_mixed = 1;
config.lossless = 0;
+ } else if (!strcmp(argv[c], "-near_lossless") && c < argc - 1) {
+ config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-sharp_yuv")) {
+ config.use_sharp_yuv = 1;
} else if (!strcmp(argv[c], "-loop_compatibility")) {
loop_compatibility = 1;
} else if (!strcmp(argv[c], "-q") && c < argc - 1) {
@@ -216,7 +228,7 @@ int main(int argc, const char* argv[]) {
fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
(int)(token - start), start);
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
start = token + 1;
}
@@ -225,11 +237,14 @@ int main(int argc, const char* argv[]) {
} else if (!strcmp(argv[c], "-version")) {
const int enc_version = WebPGetEncoderVersion();
const int mux_version = WebPGetMuxVersion();
+ const int sharpyuv_version = SharpYuvGetVersion();
printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
(enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
enc_version & 0xff, (mux_version >> 16) & 0xff,
(mux_version >> 8) & 0xff, mux_version & 0xff);
- FREE_WARGV_AND_RETURN(0);
+ printf("libsharpyuv: %d.%d.%d\n", (sharpyuv_version >> 24) & 0xff,
+ (sharpyuv_version >> 16) & 0xffff, sharpyuv_version & 0xff);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-quiet")) {
quiet = 1;
enc_options.verbose = 0;
@@ -242,14 +257,14 @@ int main(int argc, const char* argv[]) {
} else if (argv[c][0] == '-') {
fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
} else {
in_file = GET_WARGV(argv, c);
}
if (parse_error) {
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
}
@@ -314,8 +329,11 @@ int main(int argc, const char* argv[]) {
frame.use_argb = 1;
if (!WebPPictureAlloc(&frame)) goto End;
GIFClearPic(&frame, NULL);
- WebPPictureCopy(&frame, &curr_canvas);
- WebPPictureCopy(&frame, &prev_canvas);
+ if (!(WebPPictureCopy(&frame, &curr_canvas) &&
+ WebPPictureCopy(&frame, &prev_canvas))) {
+ fprintf(stderr, "Error allocating canvas.\n");
+ goto End;
+ }
// Background color.
GIFGetBackgroundColor(gif->SColorMap, gif->SBackGroundColor,
@@ -590,7 +608,7 @@ int main(int argc, const char* argv[]) {
#endif
}
- FREE_WARGV_AND_RETURN(!ok);
+ FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
#else // !WEBP_HAVE_GIF
@@ -598,7 +616,7 @@ int main(int argc, const char* argv[]) {
int main(int argc, const char* argv[]) {
fprintf(stderr, "GIF support not enabled in %s.\n", argv[0]);
(void)argc;
- return 0;
+ return EXIT_FAILURE;
}
#endif
diff --git a/c-lib/examples/gifdec.c b/c-lib/examples/gifdec.c
@@ -19,6 +19,7 @@
#include <string.h>
#include "webp/encode.h"
+#include "webp/types.h"
#include "webp/mux_types.h"
#define GIF_TRANSPARENT_COLOR 0x00000000u
@@ -317,7 +318,7 @@ void GIFDisplayError(const GifFileType* const gif, int gif_error) {
#else // !WEBP_HAVE_GIF
-static void ErrorGIFNotAvailable() {
+static void ErrorGIFNotAvailable(void) {
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
"package before building.\n");
}
diff --git a/c-lib/examples/gifdec.h b/c-lib/examples/gifdec.h
@@ -13,6 +13,7 @@
#define WEBP_EXAMPLES_GIFDEC_H_
#include <stdio.h>
+
#include "webp/types.h"
#ifdef HAVE_CONFIG_H
diff --git a/c-lib/examples/img2webp.c b/c-lib/examples/img2webp.c
@@ -28,24 +28,32 @@
#include "../imageio/imageio_util.h"
#include "./stopwatch.h"
#include "./unicode.h"
+#include "sharpyuv/sharpyuv.h"
#include "webp/encode.h"
#include "webp/mux.h"
+#include "webp/mux_types.h"
+#include "webp/types.h"
//------------------------------------------------------------------------------
static void Help(void) {
printf("Usage:\n\n");
- printf(" img2webp [file_options] [[frame_options] frame_file]...\n");
- printf("\n");
+ printf(" img2webp [file_options] [[frame_options] frame_file]...");
+ printf(" [-o webp_file]\n\n");
printf("File-level options (only used at the start of compression):\n");
printf(" -min_size ............ minimize size\n");
- printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
printf(" -kmax <int> .......... maximum number of frame between key-frames\n"
" (0=only keyframes)\n");
printf(" -kmin <int> .......... minimum number of frame between key-frames\n"
" (0=disable key-frames altogether)\n");
printf(" -mixed ............... use mixed lossy/lossless automatic mode\n");
+ printf(" -near_lossless <int> . use near-lossless image preprocessing\n"
+ " (0..100=off), default=100\n");
+ printf(" -sharp_yuv ........... use sharper (and slower) RGB->YUV "
+ "conversion\n "
+ "(lossy only)\n");
+ printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
printf(" -v ................... verbose mode\n");
printf(" -h ................... this help\n");
printf(" -version ............. print version number and exit\n");
@@ -53,10 +61,15 @@ static void Help(void) {
printf("Per-frame options (only used for subsequent images input):\n");
printf(" -d <int> ............. frame duration in ms (default: 100)\n");
- printf(" -lossless ........... use lossless mode (default)\n");
- printf(" -lossy ... ........... use lossy mode\n");
+ printf(" -lossless ............ use lossless mode (default)\n");
+ printf(" -lossy ............... use lossy mode\n");
printf(" -q <float> ........... quality\n");
- printf(" -m <int> ............. method to use\n");
+ printf(" -m <int> ............. compression method (0=fast, 6=slowest), "
+ "default=4\n");
+ printf(" -exact, -noexact ..... preserve or alter RGB values in transparent "
+ "area\n"
+ " (default: -noexact, may cause artifacts\n"
+ " with lossy animations)\n");
printf("\n");
printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
@@ -65,6 +78,8 @@ static void Help(void) {
"arguments will be\n");
printf("tokenized from this file. The file name must not start with "
"the character '-'.\n");
+ printf("\nSupported input formats:\n %s\n",
+ WebPGetEnabledInputFileFormats());
}
//------------------------------------------------------------------------------
@@ -122,6 +137,7 @@ static int SetLoopCount(int loop_count, WebPData* const webp_data) {
//------------------------------------------------------------------------------
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
const char* output = NULL;
WebPAnimEncoder* enc = NULL;
@@ -137,16 +153,17 @@ int main(int argc, const char* argv[]) {
WebPData webp_data;
int c;
int have_input = 0;
+ int last_input_index = 0;
CommandLineArguments cmd_args;
int ok;
INIT_WARGV(argc, argv);
ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args);
- if (!ok) FREE_WARGV_AND_RETURN(1);
+ if (!ok) FREE_WARGV_AND_RETURN(EXIT_FAILURE);
- argc = cmd_args.argc_;
- argv = cmd_args.argv_;
+ argc = cmd_args.argc;
+ argv = cmd_args.argv;
WebPDataInit(&webp_data);
if (!WebPAnimEncoderOptionsInit(&anim_config) ||
@@ -182,18 +199,26 @@ int main(int argc, const char* argv[]) {
} else if (!strcmp(argv[c], "-mixed")) {
anim_config.allow_mixed = 1;
config.lossless = 0;
+ } else if (!strcmp(argv[c], "-near_lossless") && c + 1 < argc) {
+ argv[c] = NULL;
+ config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-sharp_yuv")) {
+ config.use_sharp_yuv = 1;
} else if (!strcmp(argv[c], "-v")) {
verbose = 1;
} else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help();
- goto End;
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-version")) {
const int enc_version = WebPGetEncoderVersion();
const int mux_version = WebPGetMuxVersion();
+ const int sharpyuv_version = SharpYuvGetVersion();
printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
(enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
enc_version & 0xff, (mux_version >> 16) & 0xff,
(mux_version >> 8) & 0xff, mux_version & 0xff);
+ printf("libsharpyuv: %d.%d.%d\n", (sharpyuv_version >> 24) & 0xff,
+ (sharpyuv_version >> 16) & 0xffff, sharpyuv_version & 0xff);
goto End;
} else {
continue;
@@ -207,6 +232,8 @@ int main(int argc, const char* argv[]) {
}
if (!have_input) {
fprintf(stderr, "No input file(s) for generating animation!\n");
+ ok = 0;
+ Help();
goto End;
}
@@ -231,6 +258,10 @@ int main(int argc, const char* argv[]) {
fprintf(stderr, "Invalid negative duration (%d)\n", duration);
parse_error = 1;
}
+ } else if (!strcmp(argv[c], "-exact")) {
+ config.exact = 1;
+ } else if (!strcmp(argv[c], "-noexact")) {
+ config.exact = 0;
} else {
parse_error = 1; // shouldn't be here.
fprintf(stderr, "Unknown option [%s]\n", argv[c]);
@@ -251,6 +282,7 @@ int main(int argc, const char* argv[]) {
// read next input image
pic.use_argb = 1;
ok = ReadImage((const char*)GET_WARGV_SHIFTED(argv, c), &pic);
+ last_input_index = c;
if (!ok) goto End;
if (enc == NULL) {
@@ -289,6 +321,13 @@ int main(int argc, const char* argv[]) {
++pic_num;
}
+ for (c = last_input_index + 1; c < argc; ++c) {
+ if (argv[c] != NULL) {
+ fprintf(stderr, "Warning: unused option [%s]!"
+ " Frame options go before the input frame.\n", argv[c]);
+ }
+ }
+
// add a last fake frame to signal the last duration
ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
ok = ok && WebPAnimEncoderAssemble(enc, &webp_data);
@@ -319,5 +358,5 @@ int main(int argc, const char* argv[]) {
}
WebPDataClear(&webp_data);
ExUtilDeleteCommandLineArguments(&cmd_args);
- FREE_WARGV_AND_RETURN(ok ? 0 : 1);
+ FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/c-lib/examples/unicode.h b/c-lib/examples/unicode.h
@@ -16,11 +16,15 @@
#ifndef WEBP_EXAMPLES_UNICODE_H_
#define WEBP_EXAMPLES_UNICODE_H_
+#include <stdio.h>
+
#if defined(_WIN32) && defined(_UNICODE)
// wchar_t is used instead of TCHAR because we only perform additional work when
// Unicode is enabled and because the output of CommandLineToArgvW() is wchar_t.
+#include <fcntl.h>
+#include <io.h>
#include <wchar.h>
#include <windows.h>
#include <shellapi.h>
@@ -55,8 +59,16 @@
#define WFOPEN(ARG, OPT) _wfopen((const W_CHAR*)ARG, TO_W_CHAR(OPT))
-#define WPRINTF(STR, ...) wprintf(TO_W_CHAR(STR), __VA_ARGS__)
-#define WFPRINTF(STDERR, STR, ...) fwprintf(STDERR, TO_W_CHAR(STR), __VA_ARGS__)
+#define WFPRINTF(STREAM, STR, ...) \
+ do { \
+ int prev_mode; \
+ fflush(STREAM); \
+ prev_mode = _setmode(_fileno(STREAM), _O_U8TEXT); \
+ fwprintf(STREAM, TO_W_CHAR(STR), __VA_ARGS__); \
+ fflush(STREAM); \
+ (void)_setmode(_fileno(STREAM), prev_mode); \
+ } while (0)
+#define WPRINTF(STR, ...) WFPRINTF(stdout, STR, __VA_ARGS__)
#define WSTRLEN(FILENAME) wcslen((const W_CHAR*)FILENAME)
#define WSTRCMP(FILENAME, STR) wcscmp((const W_CHAR*)FILENAME, TO_W_CHAR(STR))
@@ -65,6 +77,8 @@
#else
+#include <string.h>
+
// Unicode file paths work as is on Unix platforms, and no extra work is done on
// Windows either if Unicode is disabled.
@@ -83,7 +97,7 @@
#define WFOPEN(ARG, OPT) fopen(ARG, OPT)
#define WPRINTF(STR, ...) printf(STR, __VA_ARGS__)
-#define WFPRINTF(STDERR, STR, ...) fprintf(STDERR, STR, __VA_ARGS__)
+#define WFPRINTF(STREAM, STR, ...) fprintf(STREAM, STR, __VA_ARGS__)
#define WSTRLEN(FILENAME) strlen(FILENAME)
#define WSTRCMP(FILENAME, STR) strcmp(FILENAME, STR)
diff --git a/c-lib/examples/unicode_gif.h b/c-lib/examples/unicode_gif.h
@@ -45,18 +45,19 @@ static GifFileType* DGifOpenFileUnicode(const W_CHAR* file_name, int* error) {
}
#if defined(_WIN32) && defined(_UNICODE)
-
- int file_handle = _wopen(file_name, _O_RDONLY | _O_BINARY);
- if (file_handle == -1) {
- if (error != NULL) *error = D_GIF_ERR_OPEN_FAILED;
- return NULL;
- }
+ {
+ int file_handle = _wopen(file_name, _O_RDONLY | _O_BINARY);
+ if (file_handle == -1) {
+ if (error != NULL) *error = D_GIF_ERR_OPEN_FAILED;
+ return NULL;
+ }
#if LOCAL_GIF_PREREQ(5, 0)
- return DGifOpenFileHandle(file_handle, error);
+ return DGifOpenFileHandle(file_handle, error);
#else
- return DGifOpenFileHandle(file_handle);
+ return DGifOpenFileHandle(file_handle);
#endif
+ }
#else
diff --git a/c-lib/examples/vwebp.c b/c-lib/examples/vwebp.c
@@ -18,6 +18,7 @@
#define _POSIX_C_SOURCE 200112L // for setenv
#endif
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -292,6 +293,19 @@ static void PrintString(const char* const text) {
}
}
+static void PrintStringW(const char* const text) {
+#if defined(_WIN32) && defined(_UNICODE)
+ void* const font = GLUT_BITMAP_9_BY_15;
+ const W_CHAR* const wtext = (const W_CHAR*)text;
+ int i;
+ for (i = 0; wtext[i]; ++i) {
+ glutBitmapCharacter(font, wtext[i]);
+ }
+#else
+ PrintString(text);
+#endif
+}
+
static float GetColorf(uint32_t color, int shift) {
return ((color >> shift) & 0xff) / 255.f;
}
@@ -396,7 +410,7 @@ static void HandleDisplay(void) {
glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
glRasterPos2f(-0.95f, 0.90f);
- PrintString(kParams.file_name);
+ PrintStringW(kParams.file_name);
snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height);
glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
@@ -417,10 +431,13 @@ static void HandleDisplay(void) {
#endif
}
-static void StartDisplay(void) {
+static void StartDisplay(const char* filename) {
int width = kParams.canvas_width;
int height = kParams.canvas_height;
int screen_width, screen_height;
+ const char viewername[] = " - WebP viewer";
+ // max linux file len + viewername string
+ char title[4096 + sizeof(viewername)] = "";
// TODO(webp:365) GLUT_DOUBLE results in flickering / old frames to be
// partially displayed with animated webp + alpha.
#if defined(__APPLE__) || defined(_WIN32)
@@ -440,8 +457,9 @@ static void StartDisplay(void) {
height = screen_height;
}
}
+ snprintf(title, sizeof(title), "%s%s", filename, viewername);
glutInitWindowSize(width, height);
- glutCreateWindow("WebP viewer");
+ glutCreateWindow(title);
glutDisplayFunc(HandleDisplay);
glutReshapeFunc(HandleReshape);
glutIdleFunc(NULL);
@@ -480,7 +498,7 @@ static void Help(void) {
}
int main(int argc, char* argv[]) {
- int c;
+ int c, file_name_argv_index = 1;
WebPDecoderConfig* const config = &kParams.config;
WebPIterator* const curr = &kParams.curr_frame;
@@ -488,7 +506,7 @@ int main(int argc, char* argv[]) {
if (!WebPInitDecoderConfig(config)) {
fprintf(stderr, "Library version mismatch!\n");
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
config->options.dithering_strength = 50;
config->options.alpha_dithering_strength = 100;
@@ -500,7 +518,7 @@ int main(int argc, char* argv[]) {
int parse_error = 0;
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-noicc")) {
kParams.use_color_profile = 0;
} else if (!strcmp(argv[c], "-nofancy")) {
@@ -523,30 +541,34 @@ int main(int argc, char* argv[]) {
(dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
dec_version & 0xff, (dmux_version >> 16) & 0xff,
(dmux_version >> 8) & 0xff, dmux_version & 0xff);
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-mt")) {
config->options.use_threads = 1;
} else if (!strcmp(argv[c], "--")) {
- if (c < argc - 1) kParams.file_name = (const char*)GET_WARGV(argv, ++c);
+ if (c < argc - 1) {
+ kParams.file_name = (const char*)GET_WARGV(argv, ++c);
+ file_name_argv_index = c;
+ }
break;
} else if (argv[c][0] == '-') {
printf("Unknown option '%s'\n", argv[c]);
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
} else {
kParams.file_name = (const char*)GET_WARGV(argv, c);
+ file_name_argv_index = c;
}
if (parse_error) {
Help();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
}
if (kParams.file_name == NULL) {
printf("missing input file!!\n");
Help();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
if (!ImgIoUtilReadFile(kParams.file_name,
@@ -600,7 +622,7 @@ int main(int argc, char* argv[]) {
// Position iterator to last frame. Next call to HandleDisplay will wrap over.
// We take this into account by bumping up loop_count.
- WebPDemuxGetFrame(kParams.dmux, 0, curr);
+ if (!WebPDemuxGetFrame(kParams.dmux, 0, curr)) goto Error;
if (kParams.loop_count) ++kParams.loop_count;
#if defined(__unix__) || defined(__CYGWIN__)
@@ -614,18 +636,18 @@ int main(int argc, char* argv[]) {
#ifdef FREEGLUT
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
#endif
- StartDisplay();
+ StartDisplay(argv[file_name_argv_index]);
if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
glutMainLoop();
// Should only be reached when using FREEGLUT:
ClearParams();
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
Error:
ClearParams();
- FREE_WARGV_AND_RETURN(-1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
#else // !WEBP_HAVE_GL
@@ -633,7 +655,7 @@ int main(int argc, char* argv[]) {
int main(int argc, const char* argv[]) {
fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
(void)argc;
- return 0;
+ return EXIT_FAILURE;
}
#endif
diff --git a/c-lib/examples/webpinfo.c b/c-lib/examples/webpinfo.c
@@ -14,6 +14,8 @@
#include <assert.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#ifdef HAVE_CONFIG_H
#include "webp/config.h"
@@ -24,6 +26,7 @@
#include "webp/decode.h"
#include "webp/format_constants.h"
#include "webp/mux_types.h"
+#include "webp/types.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
@@ -31,17 +34,17 @@
#define LOG_ERROR(MESSAGE) \
do { \
- if (webp_info->show_diagnosis_) { \
+ if (webp_info->show_diagnosis) { \
fprintf(stderr, "Error: %s\n", MESSAGE); \
} \
} while (0)
#define LOG_WARN(MESSAGE) \
do { \
- if (webp_info->show_diagnosis_) { \
+ if (webp_info->show_diagnosis) { \
fprintf(stderr, "Warning: %s\n", MESSAGE); \
} \
- ++webp_info->num_warnings_; \
+ ++webp_info->num_warnings; \
} while (0)
static const char* const kFormats[3] = {
@@ -89,36 +92,36 @@ typedef enum ChunkID {
} ChunkID;
typedef struct {
- size_t start_;
- size_t end_;
- const uint8_t* buf_;
+ size_t start;
+ size_t end;
+ const uint8_t* buf;
} MemBuffer;
typedef struct {
- size_t offset_;
- size_t size_;
- const uint8_t* payload_;
- ChunkID id_;
+ size_t offset;
+ size_t size;
+ const uint8_t* payload;
+ ChunkID id;
} ChunkData;
typedef struct WebPInfo {
- int canvas_width_;
- int canvas_height_;
- int loop_count_;
- int num_frames_;
- int chunk_counts_[CHUNK_TYPES];
- int anmf_subchunk_counts_[3]; // 0 VP8; 1 VP8L; 2 ALPH.
- uint32_t bgcolor_;
- int feature_flags_;
- int has_alpha_;
+ int canvas_width;
+ int canvas_height;
+ int loop_count;
+ int num_frames;
+ int chunk_counts[CHUNK_TYPES];
+ int anmf_subchunk_counts[3]; // 0 VP8; 1 VP8L; 2 ALPH.
+ uint32_t bgcolor;
+ int feature_flags;
+ int has_alpha;
// Used for parsing ANMF chunks.
- int frame_width_, frame_height_;
- size_t anim_frame_data_size_;
- int is_processing_anim_frame_, seen_alpha_subchunk_, seen_image_subchunk_;
+ int frame_width, frame_height;
+ size_t anim_frame_data_size;
+ int is_processing_anim_frame, seen_alpha_subchunk, seen_image_subchunk;
// Print output control.
- int quiet_, show_diagnosis_, show_summary_;
- int num_warnings_;
- int parse_bitstream_;
+ int quiet, show_diagnosis, show_summary;
+ int num_warnings;
+ int parse_bitstream;
} WebPInfo;
static void WebPInfoInit(WebPInfo* const webp_info) {
@@ -184,25 +187,25 @@ static int ReadFileToWebPData(const char* const filename,
// MemBuffer object.
static void InitMemBuffer(MemBuffer* const mem, const WebPData* webp_data) {
- mem->buf_ = webp_data->bytes;
- mem->start_ = 0;
- mem->end_ = webp_data->size;
+ mem->buf = webp_data->bytes;
+ mem->start = 0;
+ mem->end = webp_data->size;
}
static size_t MemDataSize(const MemBuffer* const mem) {
- return (mem->end_ - mem->start_);
+ return (mem->end - mem->start);
}
static const uint8_t* GetBuffer(MemBuffer* const mem) {
- return mem->buf_ + mem->start_;
+ return mem->buf + mem->start;
}
static void Skip(MemBuffer* const mem, size_t size) {
- mem->start_ += size;
+ mem->start += size;
}
static uint32_t ReadMemBufLE32(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);
assert(MemDataSize(mem) >= 4);
Skip(mem, 4);
@@ -333,8 +336,8 @@ static WebPInfoStatus ParseLossyFilterHeader(const WebPInfo* const webp_info,
static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
const WebPInfo* const webp_info) {
- const uint8_t* data = chunk_data->payload_;
- size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+ const uint8_t* data = chunk_data->payload;
+ size_t data_size = chunk_data->size - CHUNK_HEADER_SIZE;
const uint32_t bits = (uint32_t)data[0] | (data[1] << 8) | (data[2] << 16);
const int key_frame = !(bits & 1);
const int profile = (bits >> 1) & 7;
@@ -346,7 +349,7 @@ static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
int colorspace, clamp_type;
printf(" Parsing lossy bitstream...\n");
// Calling WebPGetFeatures() in ProcessImageChunk() should ensure this.
- assert(chunk_data->size_ >= CHUNK_HEADER_SIZE + 10);
+ assert(chunk_data->size >= CHUNK_HEADER_SIZE + 10);
if (profile > 3) {
LOG_ERROR("Unknown profile.");
return WEBP_INFO_BITSTREAM_ERROR;
@@ -357,12 +360,12 @@ static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
}
data += 3;
data_size -= 3;
- printf(" Key frame: %s\n"
- " Profile: %d\n"
- " Display: %s\n"
- " Part. 0 length: %d\n",
- key_frame ? "Yes" : "No", profile,
- display ? "Yes" : "No", partition0_length);
+ printf(
+ " Key frame: %s\n"
+ " Profile: %d\n"
+ " Display: Yes\n"
+ " Part. 0 length: %d\n",
+ key_frame ? "Yes" : "No", profile, partition0_length);
if (key_frame) {
if (!(data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a)) {
LOG_ERROR("Invalid lossy bitstream signature.");
@@ -504,8 +507,8 @@ static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
- const uint8_t* data = chunk_data->payload_;
- size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+ const uint8_t* data = chunk_data->payload;
+ size_t data_size = chunk_data->size - CHUNK_HEADER_SIZE;
uint64_t bit_position = 0;
uint64_t* const bit_pos = &bit_position;
WebPInfoStatus status;
@@ -540,8 +543,8 @@ static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data,
static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
- const uint8_t* data = chunk_data->payload_;
- size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+ const uint8_t* data = chunk_data->payload;
+ size_t data_size = chunk_data->size - CHUNK_HEADER_SIZE;
if (data_size <= ALPHA_HEADER_LEN) {
LOG_ERROR("Truncated ALPH chunk.");
return WEBP_INFO_TRUNCATED_DATA;
@@ -606,14 +609,14 @@ static WebPInfoStatus ParseRIFFHeader(WebPInfo* const webp_info,
return WEBP_INFO_PARSE_ERROR;
}
riff_size += CHUNK_HEADER_SIZE;
- if (!webp_info->quiet_) {
+ if (!webp_info->quiet) {
printf("RIFF HEADER:\n");
printf(" File size: %6d\n", (int)riff_size);
}
- if (riff_size < mem->end_) {
+ if (riff_size < mem->end) {
LOG_WARN("RIFF size is smaller than the file size.");
- mem->end_ = riff_size;
- } else if (riff_size > mem->end_) {
+ mem->end = riff_size;
+ } else if (riff_size > mem->end) {
LOG_ERROR("Truncated data detected when parsing RIFF payload.");
return WEBP_INFO_TRUNCATED_DATA;
}
@@ -629,7 +632,7 @@ static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
LOG_ERROR("Truncated data detected when parsing chunk header.");
return WEBP_INFO_TRUNCATED_DATA;
} else {
- const size_t chunk_start_offset = mem->start_;
+ const size_t chunk_start_offset = mem->start;
const uint32_t fourcc = ReadMemBufLE32(mem);
const uint32_t payload_size = ReadMemBufLE32(mem);
const uint32_t payload_size_padded = payload_size + (payload_size & 1);
@@ -646,11 +649,11 @@ static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
for (i = 0; i < CHUNK_TYPES; ++i) {
if (kWebPChunkTags[i] == fourcc) break;
}
- chunk_data->offset_ = chunk_start_offset;
- chunk_data->size_ = chunk_size;
- chunk_data->id_ = (ChunkID)i;
- chunk_data->payload_ = GetBuffer(mem);
- if (chunk_data->id_ == CHUNK_ANMF) {
+ chunk_data->offset = chunk_start_offset;
+ chunk_data->size = chunk_size;
+ chunk_data->id = (ChunkID)i;
+ chunk_data->payload = GetBuffer(mem);
+ if (chunk_data->id == CHUNK_ANMF) {
if (payload_size != payload_size_padded) {
LOG_ERROR("ANMF chunk size should always be even.");
return WEBP_INFO_PARSE_ERROR;
@@ -669,39 +672,39 @@ static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
- const uint8_t* data = chunk_data->payload_;
- if (webp_info->chunk_counts_[CHUNK_VP8] ||
- webp_info->chunk_counts_[CHUNK_VP8L] ||
- webp_info->chunk_counts_[CHUNK_VP8X]) {
+ const uint8_t* data = chunk_data->payload;
+ if (webp_info->chunk_counts[CHUNK_VP8] ||
+ webp_info->chunk_counts[CHUNK_VP8L] ||
+ webp_info->chunk_counts[CHUNK_VP8X]) {
LOG_ERROR("Already seen a VP8/VP8L/VP8X chunk when parsing VP8X chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- if (chunk_data->size_ != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
+ if (chunk_data->size != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
LOG_ERROR("Corrupted VP8X chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- ++webp_info->chunk_counts_[CHUNK_VP8X];
- webp_info->feature_flags_ = *data;
+ ++webp_info->chunk_counts[CHUNK_VP8X];
+ webp_info->feature_flags = *data;
data += 4;
- webp_info->canvas_width_ = 1 + ReadLE24(&data);
- webp_info->canvas_height_ = 1 + ReadLE24(&data);
- if (!webp_info->quiet_) {
+ webp_info->canvas_width = 1 + ReadLE24(&data);
+ webp_info->canvas_height = 1 + ReadLE24(&data);
+ if (!webp_info->quiet) {
printf(" ICCP: %d\n Alpha: %d\n EXIF: %d\n XMP: %d\n Animation: %d\n",
- (webp_info->feature_flags_ & ICCP_FLAG) != 0,
- (webp_info->feature_flags_ & ALPHA_FLAG) != 0,
- (webp_info->feature_flags_ & EXIF_FLAG) != 0,
- (webp_info->feature_flags_ & XMP_FLAG) != 0,
- (webp_info->feature_flags_ & ANIMATION_FLAG) != 0);
+ (webp_info->feature_flags & ICCP_FLAG) != 0,
+ (webp_info->feature_flags & ALPHA_FLAG) != 0,
+ (webp_info->feature_flags & EXIF_FLAG) != 0,
+ (webp_info->feature_flags & XMP_FLAG) != 0,
+ (webp_info->feature_flags & ANIMATION_FLAG) != 0);
printf(" Canvas size %d x %d\n",
- webp_info->canvas_width_, webp_info->canvas_height_);
+ webp_info->canvas_width, webp_info->canvas_height);
}
- if (webp_info->canvas_width_ > MAX_CANVAS_SIZE) {
+ if (webp_info->canvas_width > MAX_CANVAS_SIZE) {
LOG_WARN("Canvas width is out of range in VP8X chunk.");
}
- if (webp_info->canvas_height_ > MAX_CANVAS_SIZE) {
+ if (webp_info->canvas_height > MAX_CANVAS_SIZE) {
LOG_WARN("Canvas height is out of range in VP8X chunk.");
}
- if ((uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
+ if ((uint64_t)webp_info->canvas_width * webp_info->canvas_height >
MAX_IMAGE_AREA) {
LOG_WARN("Canvas area is out of range in VP8X chunk.");
}
@@ -710,27 +713,27 @@ static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data,
static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
- const uint8_t* data = chunk_data->payload_;
- if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+ const uint8_t* data = chunk_data->payload;
+ if (!webp_info->chunk_counts[CHUNK_VP8X]) {
LOG_ERROR("ANIM chunk detected before VP8X chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- if (chunk_data->size_ != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
+ if (chunk_data->size != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
LOG_ERROR("Corrupted ANIM chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- webp_info->bgcolor_ = ReadLE32(&data);
- webp_info->loop_count_ = ReadLE16(&data);
- ++webp_info->chunk_counts_[CHUNK_ANIM];
- if (!webp_info->quiet_) {
+ webp_info->bgcolor = ReadLE32(&data);
+ webp_info->loop_count = ReadLE16(&data);
+ ++webp_info->chunk_counts[CHUNK_ANIM];
+ if (!webp_info->quiet) {
printf(" Background color:(ARGB) %02x %02x %02x %02x\n",
- (webp_info->bgcolor_ >> 24) & 0xff,
- (webp_info->bgcolor_ >> 16) & 0xff,
- (webp_info->bgcolor_ >> 8) & 0xff,
- webp_info->bgcolor_ & 0xff);
- printf(" Loop count : %d\n", webp_info->loop_count_);
+ (webp_info->bgcolor >> 24) & 0xff,
+ (webp_info->bgcolor >> 16) & 0xff,
+ (webp_info->bgcolor >> 8) & 0xff,
+ webp_info->bgcolor & 0xff);
+ printf(" Loop count : %d\n", webp_info->loop_count);
}
- if (webp_info->loop_count_ > MAX_LOOP_COUNT) {
+ if (webp_info->loop_count > MAX_LOOP_COUNT) {
LOG_WARN("Loop count is out of range in ANIM chunk.");
}
return WEBP_INFO_OK;
@@ -738,17 +741,17 @@ static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data,
static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
- const uint8_t* data = chunk_data->payload_;
+ const uint8_t* data = chunk_data->payload;
int offset_x, offset_y, width, height, duration, blend, dispose, temp;
- if (webp_info->is_processing_anim_frame_) {
+ if (webp_info->is_processing_anim_frame) {
LOG_ERROR("ANMF chunk detected within another ANMF chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- if (!webp_info->chunk_counts_[CHUNK_ANIM]) {
+ if (!webp_info->chunk_counts[CHUNK_ANIM]) {
LOG_ERROR("ANMF chunk detected before ANIM chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- if (chunk_data->size_ <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) {
+ if (chunk_data->size <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) {
LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
return WEBP_INFO_TRUNCATED_DATA;
}
@@ -760,8 +763,8 @@ static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
temp = *data;
dispose = temp & 1;
blend = (temp >> 1) & 1;
- ++webp_info->chunk_counts_[CHUNK_ANMF];
- if (!webp_info->quiet_) {
+ ++webp_info->chunk_counts[CHUNK_ANMF];
+ if (!webp_info->quiet) {
printf(" Offset_X: %d\n Offset_Y: %d\n Width: %d\n Height: %d\n"
" Duration: %d\n Dispose: %d\n Blend: %d\n",
offset_x, offset_y, width, height, duration, dispose, blend);
@@ -774,92 +777,92 @@ static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
LOG_ERROR("Invalid offset parameters in ANMF chunk.");
return WEBP_INFO_INVALID_PARAM;
}
- if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width_ ||
- (uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height_) {
+ if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width ||
+ (uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height) {
LOG_ERROR("Frame exceeds canvas in ANMF chunk.");
return WEBP_INFO_INVALID_PARAM;
}
- webp_info->is_processing_anim_frame_ = 1;
- webp_info->seen_alpha_subchunk_ = 0;
- webp_info->seen_image_subchunk_ = 0;
- webp_info->frame_width_ = width;
- webp_info->frame_height_ = height;
- webp_info->anim_frame_data_size_ =
- chunk_data->size_ - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE;
+ webp_info->is_processing_anim_frame = 1;
+ webp_info->seen_alpha_subchunk = 0;
+ webp_info->seen_image_subchunk = 0;
+ webp_info->frame_width = width;
+ webp_info->frame_height = height;
+ webp_info->anim_frame_data_size =
+ chunk_data->size - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE;
return WEBP_INFO_OK;
}
static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
- const uint8_t* data = chunk_data->payload_ - CHUNK_HEADER_SIZE;
+ const uint8_t* data = chunk_data->payload - CHUNK_HEADER_SIZE;
WebPBitstreamFeatures features;
const VP8StatusCode vp8_status =
- WebPGetFeatures(data, chunk_data->size_, &features);
+ WebPGetFeatures(data, chunk_data->size, &features);
if (vp8_status != VP8_STATUS_OK) {
LOG_ERROR("VP8/VP8L bitstream error.");
return WEBP_INFO_BITSTREAM_ERROR;
}
- if (!webp_info->quiet_) {
+ if (!webp_info->quiet) {
assert(features.format >= 0 && features.format <= 2);
printf(" Width: %d\n Height: %d\n Alpha: %d\n Animation: %d\n"
" Format: %s (%d)\n",
features.width, features.height, features.has_alpha,
features.has_animation, kFormats[features.format], features.format);
}
- if (webp_info->is_processing_anim_frame_) {
- ++webp_info->anmf_subchunk_counts_[chunk_data->id_ == CHUNK_VP8 ? 0 : 1];
- if (chunk_data->id_ == CHUNK_VP8L && webp_info->seen_alpha_subchunk_) {
+ if (webp_info->is_processing_anim_frame) {
+ ++webp_info->anmf_subchunk_counts[chunk_data->id == CHUNK_VP8 ? 0 : 1];
+ if (chunk_data->id == CHUNK_VP8L && webp_info->seen_alpha_subchunk) {
LOG_ERROR("Both VP8L and ALPH sub-chunks are present in an ANMF chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- if (webp_info->frame_width_ != features.width ||
- webp_info->frame_height_ != features.height) {
+ if (webp_info->frame_width != features.width ||
+ webp_info->frame_height != features.height) {
LOG_ERROR("Frame size in VP8/VP8L sub-chunk differs from ANMF header.");
return WEBP_INFO_PARSE_ERROR;
}
- if (webp_info->seen_image_subchunk_) {
+ if (webp_info->seen_image_subchunk) {
LOG_ERROR("Consecutive VP8/VP8L sub-chunks in an ANMF chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- webp_info->seen_image_subchunk_ = 1;
+ webp_info->seen_image_subchunk = 1;
} else {
- if (webp_info->chunk_counts_[CHUNK_VP8] ||
- webp_info->chunk_counts_[CHUNK_VP8L]) {
+ if (webp_info->chunk_counts[CHUNK_VP8] ||
+ webp_info->chunk_counts[CHUNK_VP8L]) {
LOG_ERROR("Multiple VP8/VP8L chunks detected.");
return WEBP_INFO_PARSE_ERROR;
}
- if (chunk_data->id_ == CHUNK_VP8L &&
- webp_info->chunk_counts_[CHUNK_ALPHA]) {
+ if (chunk_data->id == CHUNK_VP8L &&
+ webp_info->chunk_counts[CHUNK_ALPHA]) {
LOG_WARN("Both VP8L and ALPH chunks are detected.");
}
- if (webp_info->chunk_counts_[CHUNK_ANIM] ||
- webp_info->chunk_counts_[CHUNK_ANMF]) {
+ if (webp_info->chunk_counts[CHUNK_ANIM] ||
+ webp_info->chunk_counts[CHUNK_ANMF]) {
LOG_ERROR("VP8/VP8L chunk and ANIM/ANMF chunk are both detected.");
return WEBP_INFO_PARSE_ERROR;
}
- if (webp_info->chunk_counts_[CHUNK_VP8X]) {
- if (webp_info->canvas_width_ != features.width ||
- webp_info->canvas_height_ != features.height) {
+ if (webp_info->chunk_counts[CHUNK_VP8X]) {
+ if (webp_info->canvas_width != features.width ||
+ webp_info->canvas_height != features.height) {
LOG_ERROR("Image size in VP8/VP8L chunk differs from VP8X chunk.");
return WEBP_INFO_PARSE_ERROR;
}
} else {
- webp_info->canvas_width_ = features.width;
- webp_info->canvas_height_ = features.height;
- if (webp_info->canvas_width_ < 1 || webp_info->canvas_height_ < 1 ||
- webp_info->canvas_width_ > MAX_CANVAS_SIZE ||
- webp_info->canvas_height_ > MAX_CANVAS_SIZE ||
- (uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
+ webp_info->canvas_width = features.width;
+ webp_info->canvas_height = features.height;
+ if (webp_info->canvas_width < 1 || webp_info->canvas_height < 1 ||
+ webp_info->canvas_width > MAX_CANVAS_SIZE ||
+ webp_info->canvas_height > MAX_CANVAS_SIZE ||
+ (uint64_t)webp_info->canvas_width * webp_info->canvas_height >
MAX_IMAGE_AREA) {
LOG_WARN("Invalid parameters in VP8/VP8L chunk.");
}
}
- ++webp_info->chunk_counts_[chunk_data->id_];
+ ++webp_info->chunk_counts[chunk_data->id];
}
- ++webp_info->num_frames_;
- webp_info->has_alpha_ |= features.has_alpha;
- if (webp_info->parse_bitstream_) {
- const int is_lossy = (chunk_data->id_ == CHUNK_VP8);
+ ++webp_info->num_frames;
+ webp_info->has_alpha |= features.has_alpha;
+ if (webp_info->parse_bitstream) {
+ const int is_lossy = (chunk_data->id == CHUNK_VP8);
const WebPInfoStatus status =
is_lossy ? ParseLossyHeader(chunk_data, webp_info)
: ParseLosslessHeader(chunk_data, webp_info);
@@ -870,41 +873,41 @@ static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data,
static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
- if (webp_info->is_processing_anim_frame_) {
- ++webp_info->anmf_subchunk_counts_[2];
- if (webp_info->seen_alpha_subchunk_) {
+ if (webp_info->is_processing_anim_frame) {
+ ++webp_info->anmf_subchunk_counts[2];
+ if (webp_info->seen_alpha_subchunk) {
LOG_ERROR("Consecutive ALPH sub-chunks in an ANMF chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- webp_info->seen_alpha_subchunk_ = 1;
+ webp_info->seen_alpha_subchunk = 1;
- if (webp_info->seen_image_subchunk_) {
+ if (webp_info->seen_image_subchunk) {
LOG_ERROR("ALPHA sub-chunk detected after VP8 sub-chunk "
"in an ANMF chunk.");
return WEBP_INFO_PARSE_ERROR;
}
} else {
- if (webp_info->chunk_counts_[CHUNK_ANIM] ||
- webp_info->chunk_counts_[CHUNK_ANMF]) {
+ if (webp_info->chunk_counts[CHUNK_ANIM] ||
+ webp_info->chunk_counts[CHUNK_ANMF]) {
LOG_ERROR("ALPHA chunk and ANIM/ANMF chunk are both detected.");
return WEBP_INFO_PARSE_ERROR;
}
- if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+ if (!webp_info->chunk_counts[CHUNK_VP8X]) {
LOG_ERROR("ALPHA chunk detected before VP8X chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- if (webp_info->chunk_counts_[CHUNK_VP8]) {
+ if (webp_info->chunk_counts[CHUNK_VP8]) {
LOG_ERROR("ALPHA chunk detected after VP8 chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- if (webp_info->chunk_counts_[CHUNK_ALPHA]) {
+ if (webp_info->chunk_counts[CHUNK_ALPHA]) {
LOG_ERROR("Multiple ALPHA chunks detected.");
return WEBP_INFO_PARSE_ERROR;
}
- ++webp_info->chunk_counts_[CHUNK_ALPHA];
+ ++webp_info->chunk_counts[CHUNK_ALPHA];
}
- webp_info->has_alpha_ = 1;
- if (webp_info->parse_bitstream_) {
+ webp_info->has_alpha = 1;
+ if (webp_info->parse_bitstream) {
const WebPInfoStatus status = ParseAlphaHeader(chunk_data, webp_info);
if (status != WEBP_INFO_OK) return status;
}
@@ -914,41 +917,41 @@ static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data,
static WebPInfoStatus ProcessICCPChunk(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
(void)chunk_data;
- if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+ if (!webp_info->chunk_counts[CHUNK_VP8X]) {
LOG_ERROR("ICCP chunk detected before VP8X chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- if (webp_info->chunk_counts_[CHUNK_VP8] ||
- webp_info->chunk_counts_[CHUNK_VP8L] ||
- webp_info->chunk_counts_[CHUNK_ANIM]) {
+ if (webp_info->chunk_counts[CHUNK_VP8] ||
+ webp_info->chunk_counts[CHUNK_VP8L] ||
+ webp_info->chunk_counts[CHUNK_ANIM]) {
LOG_ERROR("ICCP chunk detected after image data.");
return WEBP_INFO_PARSE_ERROR;
}
- ++webp_info->chunk_counts_[CHUNK_ICCP];
+ ++webp_info->chunk_counts[CHUNK_ICCP];
return WEBP_INFO_OK;
}
static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
WebPInfo* const webp_info) {
WebPInfoStatus status = WEBP_INFO_OK;
- ChunkID id = chunk_data->id_;
- if (chunk_data->id_ == CHUNK_UNKNOWN) {
+ ChunkID id = chunk_data->id;
+ if (chunk_data->id == CHUNK_UNKNOWN) {
char error_message[50];
snprintf(error_message, 50, "Unknown chunk at offset %6d, length %6d",
- (int)chunk_data->offset_, (int)chunk_data->size_);
+ (int)chunk_data->offset, (int)chunk_data->size);
LOG_WARN(error_message);
} else {
- if (!webp_info->quiet_) {
+ if (!webp_info->quiet) {
char tag[4];
- uint32_t fourcc = kWebPChunkTags[chunk_data->id_];
+ uint32_t fourcc = kWebPChunkTags[chunk_data->id];
#ifdef WORDS_BIGENDIAN
fourcc = (fourcc >> 24) | ((fourcc >> 8) & 0xff00) |
((fourcc << 8) & 0xff0000) | (fourcc << 24);
#endif
memcpy(tag, &fourcc, sizeof(tag));
printf("Chunk %c%c%c%c at offset %6d, length %6d\n",
- tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset_,
- (int)chunk_data->size_);
+ tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset,
+ (int)chunk_data->size);
}
}
switch (id) {
@@ -973,21 +976,21 @@ static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
break;
case CHUNK_EXIF:
case CHUNK_XMP:
- ++webp_info->chunk_counts_[id];
+ ++webp_info->chunk_counts[id];
break;
case CHUNK_UNKNOWN:
default:
break;
}
- if (webp_info->is_processing_anim_frame_ && id != CHUNK_ANMF) {
- if (webp_info->anim_frame_data_size_ == chunk_data->size_) {
- if (!webp_info->seen_image_subchunk_) {
+ if (webp_info->is_processing_anim_frame && id != CHUNK_ANMF) {
+ if (webp_info->anim_frame_data_size == chunk_data->size) {
+ if (!webp_info->seen_image_subchunk) {
LOG_ERROR("No VP8/VP8L chunk detected in an ANMF chunk.");
return WEBP_INFO_PARSE_ERROR;
}
- webp_info->is_processing_anim_frame_ = 0;
- } else if (webp_info->anim_frame_data_size_ > chunk_data->size_) {
- webp_info->anim_frame_data_size_ -= chunk_data->size_;
+ webp_info->is_processing_anim_frame = 0;
+ } else if (webp_info->anim_frame_data_size > chunk_data->size) {
+ webp_info->anim_frame_data_size -= chunk_data->size;
} else {
LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
return WEBP_INFO_TRUNCATED_DATA;
@@ -997,55 +1000,55 @@ static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
}
static WebPInfoStatus Validate(WebPInfo* const webp_info) {
- if (webp_info->num_frames_ < 1) {
+ if (webp_info->num_frames < 1) {
LOG_ERROR("No image/frame detected.");
return WEBP_INFO_MISSING_DATA;
}
- if (webp_info->chunk_counts_[CHUNK_VP8X]) {
- const int iccp = !!(webp_info->feature_flags_ & ICCP_FLAG);
- const int exif = !!(webp_info->feature_flags_ & EXIF_FLAG);
- const int xmp = !!(webp_info->feature_flags_ & XMP_FLAG);
- const int animation = !!(webp_info->feature_flags_ & ANIMATION_FLAG);
- const int alpha = !!(webp_info->feature_flags_ & ALPHA_FLAG);
- if (!alpha && webp_info->has_alpha_) {
+ if (webp_info->chunk_counts[CHUNK_VP8X]) {
+ const int iccp = !!(webp_info->feature_flags & ICCP_FLAG);
+ const int exif = !!(webp_info->feature_flags & EXIF_FLAG);
+ const int xmp = !!(webp_info->feature_flags & XMP_FLAG);
+ const int animation = !!(webp_info->feature_flags & ANIMATION_FLAG);
+ const int alpha = !!(webp_info->feature_flags & ALPHA_FLAG);
+ if (!alpha && webp_info->has_alpha) {
LOG_ERROR("Unexpected alpha data detected.");
return WEBP_INFO_PARSE_ERROR;
}
- if (alpha && !webp_info->has_alpha_) {
+ if (alpha && !webp_info->has_alpha) {
LOG_WARN("Alpha flag is set with no alpha data present.");
}
- if (iccp && !webp_info->chunk_counts_[CHUNK_ICCP]) {
+ if (iccp && !webp_info->chunk_counts[CHUNK_ICCP]) {
LOG_ERROR("Missing ICCP chunk.");
return WEBP_INFO_MISSING_DATA;
}
- if (exif && !webp_info->chunk_counts_[CHUNK_EXIF]) {
+ if (exif && !webp_info->chunk_counts[CHUNK_EXIF]) {
LOG_ERROR("Missing EXIF chunk.");
return WEBP_INFO_MISSING_DATA;
}
- if (xmp && !webp_info->chunk_counts_[CHUNK_XMP]) {
+ if (xmp && !webp_info->chunk_counts[CHUNK_XMP]) {
LOG_ERROR("Missing XMP chunk.");
return WEBP_INFO_MISSING_DATA;
}
- if (!iccp && webp_info->chunk_counts_[CHUNK_ICCP]) {
+ if (!iccp && webp_info->chunk_counts[CHUNK_ICCP]) {
LOG_ERROR("Unexpected ICCP chunk detected.");
return WEBP_INFO_PARSE_ERROR;
}
- if (!exif && webp_info->chunk_counts_[CHUNK_EXIF]) {
+ if (!exif && webp_info->chunk_counts[CHUNK_EXIF]) {
LOG_ERROR("Unexpected EXIF chunk detected.");
return WEBP_INFO_PARSE_ERROR;
}
- if (!xmp && webp_info->chunk_counts_[CHUNK_XMP]) {
+ if (!xmp && webp_info->chunk_counts[CHUNK_XMP]) {
LOG_ERROR("Unexpected XMP chunk detected.");
return WEBP_INFO_PARSE_ERROR;
}
// Incomplete animation frame.
- if (webp_info->is_processing_anim_frame_) return WEBP_INFO_MISSING_DATA;
- if (!animation && webp_info->num_frames_ > 1) {
+ if (webp_info->is_processing_anim_frame) return WEBP_INFO_MISSING_DATA;
+ if (!animation && webp_info->num_frames > 1) {
LOG_ERROR("More than 1 frame detected in non-animation file.");
return WEBP_INFO_PARSE_ERROR;
}
- if (animation && (!webp_info->chunk_counts_[CHUNK_ANIM] ||
- !webp_info->chunk_counts_[CHUNK_ANMF])) {
+ if (animation && (!webp_info->chunk_counts[CHUNK_ANIM] ||
+ !webp_info->chunk_counts[CHUNK_ANMF])) {
LOG_ERROR("No ANIM/ANMF chunk detected in animation file.");
return WEBP_INFO_PARSE_ERROR;
}
@@ -1056,17 +1059,17 @@ static WebPInfoStatus Validate(WebPInfo* const webp_info) {
static void ShowSummary(const WebPInfo* const webp_info) {
int i;
printf("Summary:\n");
- printf("Number of frames: %d\n", webp_info->num_frames_);
+ printf("Number of frames: %d\n", webp_info->num_frames);
printf("Chunk type : VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP "
"EXIF XMP\n");
printf("Chunk counts: ");
for (i = 0; i < CHUNK_TYPES; ++i) {
- printf("%4d ", webp_info->chunk_counts_[i]);
+ printf("%4d ", webp_info->chunk_counts[i]);
if (i == CHUNK_ANMF) {
printf("%4d %4d %4d ",
- webp_info->anmf_subchunk_counts_[0],
- webp_info->anmf_subchunk_counts_[1],
- webp_info->anmf_subchunk_counts_[2]);
+ webp_info->anmf_subchunk_counts[0],
+ webp_info->anmf_subchunk_counts[1],
+ webp_info->anmf_subchunk_counts[2]);
}
}
printf("\n");
@@ -1089,20 +1092,20 @@ static WebPInfoStatus AnalyzeWebP(WebPInfo* const webp_info,
webp_info_status = ProcessChunk(&chunk_data, webp_info);
}
if (webp_info_status != WEBP_INFO_OK) goto Error;
- if (webp_info->show_summary_) ShowSummary(webp_info);
+ if (webp_info->show_summary) ShowSummary(webp_info);
// Final check.
webp_info_status = Validate(webp_info);
Error:
- if (!webp_info->quiet_) {
+ if (!webp_info->quiet) {
if (webp_info_status == WEBP_INFO_OK) {
printf("No error detected.\n");
} else {
printf("Errors detected.\n");
}
- if (webp_info->num_warnings_ > 0) {
- printf("There were %d warning(s).\n", webp_info->num_warnings_);
+ if (webp_info->num_warnings > 0) {
+ printf("There were %d warning(s).\n", webp_info->num_warnings);
}
}
return webp_info_status;
@@ -1120,6 +1123,7 @@ static void Help(void) {
" -bitstream_info .... Parse bitstream header.\n");
}
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
int c, quiet = 0, show_diag = 0, show_summary = 0;
int parse_bitstream = 0;
@@ -1130,7 +1134,7 @@ int main(int argc, const char* argv[]) {
if (argc == 1) {
Help();
- FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
// Parse command-line input.
@@ -1138,7 +1142,7 @@ int main(int argc, const char* argv[]) {
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help") ||
!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
Help();
- FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else if (!strcmp(argv[c], "-quiet")) {
quiet = 1;
} else if (!strcmp(argv[c], "-diag")) {
@@ -1151,7 +1155,7 @@ int main(int argc, const char* argv[]) {
const int version = WebPGetDecoderVersion();
printf("WebP Decoder version: %d.%d.%d\n",
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else { // Assume the remaining are all input files.
break;
}
@@ -1159,7 +1163,7 @@ int main(int argc, const char* argv[]) {
if (c == argc) {
Help();
- FREE_WARGV_AND_RETURN(WEBP_INFO_INVALID_COMMAND);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
// Process input files one by one.
@@ -1167,10 +1171,10 @@ int main(int argc, const char* argv[]) {
WebPData webp_data;
const W_CHAR* in_file = NULL;
WebPInfoInit(&webp_info);
- webp_info.quiet_ = quiet;
- webp_info.show_diagnosis_ = show_diag;
- webp_info.show_summary_ = show_summary;
- webp_info.parse_bitstream_ = parse_bitstream;
+ webp_info.quiet = quiet;
+ webp_info.show_diagnosis = show_diag;
+ webp_info.show_summary = show_summary;
+ webp_info.parse_bitstream = parse_bitstream;
in_file = GET_WARGV(argv, c);
if (in_file == NULL ||
!ReadFileToWebPData((const char*)in_file, &webp_data)) {
@@ -1178,9 +1182,10 @@ int main(int argc, const char* argv[]) {
WFPRINTF(stderr, "Failed to open input file %s.\n", in_file);
continue;
}
- if (!webp_info.quiet_) WPRINTF("File: %s\n", in_file);
+ if (!webp_info.quiet) WPRINTF("File: %s\n", in_file);
webp_info_status = AnalyzeWebP(&webp_info, &webp_data);
WebPDataClear(&webp_data);
}
- FREE_WARGV_AND_RETURN(webp_info_status);
+ FREE_WARGV_AND_RETURN((webp_info_status == WEBP_INFO_OK) ? EXIT_SUCCESS
+ : EXIT_FAILURE);
}
diff --git a/c-lib/examples/webpmux.c b/c-lib/examples/webpmux.c
@@ -59,11 +59,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "webp/decode.h"
-#include "webp/mux.h"
+
#include "../examples/example_util.h"
#include "../imageio/imageio_util.h"
#include "./unicode.h"
+#include "webp/decode.h"
+#include "webp/mux.h"
+#include "webp/mux_types.h"
+#include "webp/types.h"
//------------------------------------------------------------------------------
// Config object to parse command-line arguments.
@@ -86,9 +89,9 @@ typedef enum {
} FeatureSubType;
typedef struct {
- FeatureSubType subtype_;
- const char* filename_;
- const char* params_;
+ FeatureSubType subtype;
+ const char* filename;
+ const char* params;
} FeatureArg;
typedef enum {
@@ -113,14 +116,14 @@ static const char* const kDescriptions[LAST_FEATURE] = {
};
typedef struct {
- CommandLineArguments cmd_args_;
-
- ActionType action_type_;
- const char* input_;
- const char* output_;
- FeatureType type_;
- FeatureArg* args_;
- int arg_count_;
+ CommandLineArguments cmd_args;
+
+ ActionType action_type;
+ const char* input;
+ const char* output;
+ FeatureType type;
+ FeatureArg* args;
+ int arg_count;
} Config;
//------------------------------------------------------------------------------
@@ -131,8 +134,8 @@ static int CountOccurrences(const CommandLineArguments* const args,
int i;
int num_occurences = 0;
- for (i = 0; i < args->argc_; ++i) {
- if (!strcmp(args->argv_[i], arg)) {
+ for (i = 0; i < args->argc; ++i) {
+ if (!strcmp(args->argv[i], arg)) {
++num_occurences;
}
}
@@ -150,16 +153,20 @@ static const char* ErrorString(WebPMuxError err) {
}
#define RETURN_IF_ERROR(ERR_MSG) \
- if (err != WEBP_MUX_OK) { \
- fprintf(stderr, ERR_MSG); \
- return err; \
- }
+ do { \
+ if (err != WEBP_MUX_OK) { \
+ fprintf(stderr, ERR_MSG); \
+ return err; \
+ } \
+ } while (0)
#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
- if (err != WEBP_MUX_OK) { \
- fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
- return err; \
- }
+ do { \
+ if (err != WEBP_MUX_OK) { \
+ fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
+ return err; \
+ } \
+ } while (0)
#define ERROR_GOTO1(ERR_MSG, LABEL) \
do { \
@@ -329,7 +336,7 @@ static void PrintHelp(void) {
printf("\n");
printf("DURATION_OPTIONS:\n");
printf(" Set duration of selected frames:\n");
- printf(" duration set duration for each frames\n");
+ printf(" duration set duration for all frames\n");
printf(" duration,frame set duration of a particular frame\n");
printf(" duration,start,end set duration of frames in the\n");
printf(" interval [start,end])\n");
@@ -348,7 +355,7 @@ static void PrintHelp(void) {
printf("\n");
printf("FRAME_OPTIONS(i):\n");
printf(" Create animation:\n");
- printf(" file_i +di+[xi+yi[+mi[bi]]]\n");
+ printf(" file_i +di[+xi+yi[+mi[bi]]]\n");
printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
printf(" 'di' is the pause duration before next frame,\n");
printf(" 'xi','yi' specify the image offset for this frame,\n");
@@ -460,7 +467,8 @@ static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
if (err == WEBP_MUX_OK && metadata.size > 0) {
err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
if (err != WEBP_MUX_OK) {
- ERROR_GOTO1("Error transferring metadata in DuplicateMux().", End);
+ ERROR_GOTO1("Error transferring metadata in DuplicateMuxHeader().",
+ End);
}
}
}
@@ -521,8 +529,8 @@ static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
static void DeleteConfig(Config* const config) {
if (config != NULL) {
- free(config->args_);
- ExUtilDeleteCommandLineArguments(&config->cmd_args_);
+ free(config->args);
+ ExUtilDeleteCommandLineArguments(&config->cmd_args);
memset(config, 0, sizeof(*config));
}
}
@@ -599,25 +607,31 @@ static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
return ok;
}
-#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
+#define ACTION_IS_NIL (config->action_type == NIL_ACTION)
-#define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE)
+#define FEATURETYPE_IS_NIL (config->type == NIL_FEATURE)
#define CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL) \
- if (argc < i + (NUM)) { \
- fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
- goto LABEL; \
- }
+ do { \
+ if (argc < i + (NUM)) { \
+ fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
+ goto LABEL; \
+ } \
+ } while (0)
#define CHECK_NUM_ARGS_AT_MOST(NUM, LABEL) \
- if (argc > i + (NUM)) { \
- fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
- goto LABEL; \
- }
+ do { \
+ if (argc > i + (NUM)) { \
+ fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
+ goto LABEL; \
+ } \
+ } while (0)
#define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \
- CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \
- CHECK_NUM_ARGS_AT_MOST(NUM, LABEL);
+ do { \
+ CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \
+ CHECK_NUM_ARGS_AT_MOST(NUM, LABEL); \
+ } while (0)
// Parses command-line arguments to fill up config object. Also performs some
// semantic checks. unicode_argv contains wchar_t arguments or is null.
@@ -625,98 +639,97 @@ static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
int i = 0;
int feature_arg_index = 0;
int ok = 1;
- int argc = config->cmd_args_.argc_;
- const char* const* argv = config->cmd_args_.argv_;
+ int argc = config->cmd_args.argc;
+ const char* const* argv = config->cmd_args.argv;
// Unicode file paths will be used if available.
const char* const* wargv =
(unicode_argv != NULL) ? (const char**)(unicode_argv + 1) : argv;
while (i < argc) {
- FeatureArg* const arg = &config->args_[feature_arg_index];
+ FeatureArg* const arg = &config->args[feature_arg_index];
if (argv[i][0] == '-') { // One of the action types or output.
if (!strcmp(argv[i], "-set")) {
if (ACTION_IS_NIL) {
- config->action_type_ = ACTION_SET;
+ config->action_type = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
++i;
} else if (!strcmp(argv[i], "-duration")) {
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
- if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
- config->action_type_ = ACTION_DURATION;
+ if (ACTION_IS_NIL || config->action_type == ACTION_DURATION) {
+ config->action_type = ACTION_DURATION;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
- if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) {
- config->type_ = FEATURE_DURATION;
+ if (FEATURETYPE_IS_NIL || config->type == FEATURE_DURATION) {
+ config->type = FEATURE_DURATION;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
- arg->params_ = argv[i + 1];
+ arg->params = argv[i + 1];
++feature_arg_index;
i += 2;
} else if (!strcmp(argv[i], "-get")) {
if (ACTION_IS_NIL) {
- config->action_type_ = ACTION_GET;
+ config->action_type = ACTION_GET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
++i;
} else if (!strcmp(argv[i], "-strip")) {
if (ACTION_IS_NIL) {
- config->action_type_ = ACTION_STRIP;
- config->arg_count_ = 0;
+ config->action_type = ACTION_STRIP;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
++i;
} else if (!strcmp(argv[i], "-frame")) {
CHECK_NUM_ARGS_AT_LEAST(3, ErrParse);
- if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
- config->action_type_ = ACTION_SET;
+ if (ACTION_IS_NIL || config->action_type == ACTION_SET) {
+ config->action_type = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
- if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
- config->type_ = FEATURE_ANMF;
+ if (FEATURETYPE_IS_NIL || config->type == FEATURE_ANMF) {
+ config->type = FEATURE_ANMF;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
- arg->subtype_ = SUBTYPE_ANMF;
- arg->filename_ = argv[i + 1];
- arg->params_ = argv[i + 2];
+ arg->subtype = SUBTYPE_ANMF;
+ arg->filename = wargv[i + 1];
+ arg->params = argv[i + 2];
++feature_arg_index;
i += 3;
} else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
- if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
- config->action_type_ = ACTION_SET;
+ if (ACTION_IS_NIL || config->action_type == ACTION_SET) {
+ config->action_type = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
- if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
- config->type_ = FEATURE_ANMF;
+ if (FEATURETYPE_IS_NIL || config->type == FEATURE_ANMF) {
+ config->type = FEATURE_ANMF;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
- arg->subtype_ =
+ arg->subtype =
!strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
- arg->params_ = argv[i + 1];
+ arg->params = argv[i + 1];
++feature_arg_index;
i += 2;
} else if (!strcmp(argv[i], "-o")) {
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
- config->output_ = wargv[i + 1];
+ config->output = wargv[i + 1];
i += 2;
} else if (!strcmp(argv[i], "-info")) {
CHECK_NUM_ARGS_EXACTLY(2, ErrParse);
- if (config->action_type_ != NIL_ACTION) {
+ if (config->action_type != NIL_ACTION) {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
} else {
- config->action_type_ = ACTION_INFO;
- config->arg_count_ = 0;
- config->input_ = wargv[i + 1];
+ config->action_type = ACTION_INFO;
+ config->arg_count = 0;
+ config->input = wargv[i + 1];
}
i += 2;
} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
@@ -734,8 +747,8 @@ static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
} else if (!strcmp(argv[i], "--")) {
if (i < argc - 1) {
++i;
- if (config->input_ == NULL) {
- config->input_ = wargv[i];
+ if (config->input == NULL) {
+ config->input = wargv[i];
} else {
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
argv[i], ErrParse);
@@ -746,50 +759,65 @@ static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
}
} else { // One of the feature types or input.
+ // After consuming the arguments to -get/-set/-strip, treat any remaining
+ // arguments as input. This allows files that are named the same as the
+ // keywords used with these options.
+ int is_input = feature_arg_index == config->arg_count;
if (ACTION_IS_NIL) {
ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
ErrParse);
}
- if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
- !strcmp(argv[i], "xmp")) {
- if (FEATURETYPE_IS_NIL) {
- config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
- (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
- } else {
- ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
- }
- if (config->action_type_ == ACTION_SET) {
+ if (!is_input) {
+ if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
+ !strcmp(argv[i], "xmp")) {
+ if (FEATURETYPE_IS_NIL) {
+ config->type = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
+ (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+ }
+ if (config->action_type == ACTION_SET) {
+ CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+ arg->filename = wargv[i + 1];
+ ++feature_arg_index;
+ i += 2;
+ } else {
+ // Note: 'arg->params' is not used in this case. 'arg_count' is
+ // used as a flag to indicate the -get/-strip feature has already
+ // been consumed, allowing input types to be named the same as the
+ // feature type.
+ config->arg_count = 0;
+ ++i;
+ }
+ } else if (!strcmp(argv[i], "frame") &&
+ (config->action_type == ACTION_GET)) {
+ CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+ config->type = FEATURE_ANMF;
+ arg->params = argv[i + 1];
+ ++feature_arg_index;
+ i += 2;
+ } else if (!strcmp(argv[i], "loop") &&
+ (config->action_type == ACTION_SET)) {
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
- arg->filename_ = wargv[i + 1];
+ config->type = FEATURE_LOOP;
+ arg->params = argv[i + 1];
+ ++feature_arg_index;
+ i += 2;
+ } else if (!strcmp(argv[i], "bgcolor") &&
+ (config->action_type == ACTION_SET)) {
+ CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+ config->type = FEATURE_BGCOLOR;
+ arg->params = argv[i + 1];
++feature_arg_index;
i += 2;
} else {
- ++i;
+ is_input = 1;
}
- } else if (!strcmp(argv[i], "frame") &&
- (config->action_type_ == ACTION_GET)) {
- CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
- config->type_ = FEATURE_ANMF;
- arg->params_ = argv[i + 1];
- ++feature_arg_index;
- i += 2;
- } else if (!strcmp(argv[i], "loop") &&
- (config->action_type_ == ACTION_SET)) {
- CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
- config->type_ = FEATURE_LOOP;
- arg->params_ = argv[i + 1];
- ++feature_arg_index;
- i += 2;
- } else if (!strcmp(argv[i], "bgcolor") &&
- (config->action_type_ == ACTION_SET)) {
- CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
- config->type_ = FEATURE_BGCOLOR;
- arg->params_ = argv[i + 1];
- ++feature_arg_index;
- i += 2;
- } else { // Assume input file.
- if (config->input_ == NULL) {
- config->input_ = wargv[i];
+ }
+
+ if (is_input) {
+ if (config->input == NULL) {
+ config->input = wargv[i];
} else {
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
argv[i], ErrParse);
@@ -812,21 +840,21 @@ static int ValidateConfig(Config* const config) {
}
// Feature type.
- if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
+ if (FEATURETYPE_IS_NIL && config->action_type != ACTION_INFO) {
ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
}
// Input file.
- if (config->input_ == NULL) {
- if (config->action_type_ != ACTION_SET) {
+ if (config->input == NULL) {
+ if (config->action_type != ACTION_SET) {
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
- } else if (config->type_ != FEATURE_ANMF) {
+ } else if (config->type != FEATURE_ANMF) {
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
}
}
// Output file.
- if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
+ if (config->output == NULL && config->action_type != ACTION_INFO) {
ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
}
@@ -842,17 +870,17 @@ static int InitializeConfig(int argc, const char* argv[], Config* const config,
memset(config, 0, sizeof(*config));
- ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_);
+ ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args);
if (!ok) return 0;
// Validate command-line arguments.
- if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) {
+ if (!ValidateCommandLine(&config->cmd_args, &num_feature_args)) {
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
}
- config->arg_count_ = num_feature_args;
- config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_));
- if (config->args_ == NULL) {
+ config->arg_count = num_feature_args;
+ config->args = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args));
+ if (config->args == NULL) {
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
}
@@ -884,7 +912,7 @@ static int GetFrame(const WebPMux* mux, const Config* config) {
WebPMuxFrameInfo info;
WebPDataInit(&info.bitstream);
- num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
+ num = ExUtilGetInt(config->args[0].params, 10, &parse_error);
if (num < 0) {
ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
}
@@ -909,7 +937,7 @@ static int GetFrame(const WebPMux* mux, const Config* config) {
ErrorString(err), ErrGet);
}
- ok = WriteWebP(mux_single, config->output_);
+ ok = WriteWebP(mux_single, config->output);
ErrGet:
WebPDataClear(&info.bitstream);
@@ -924,11 +952,11 @@ static int Process(const Config* config) {
WebPMuxError err = WEBP_MUX_OK;
int ok = 1;
- switch (config->action_type_) {
+ switch (config->action_type) {
case ACTION_GET: {
- ok = CreateMux(config->input_, &mux);
+ ok = CreateMux(config->input, &mux);
if (!ok) goto Err2;
- switch (config->type_) {
+ switch (config->type) {
case FEATURE_ANMF:
ok = GetFrame(mux, config);
break;
@@ -936,12 +964,12 @@ static int Process(const Config* config) {
case FEATURE_ICCP:
case FEATURE_EXIF:
case FEATURE_XMP:
- err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk);
+ err = WebPMuxGetChunk(mux, kFourccList[config->type], &chunk);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
- ErrorString(err), kDescriptions[config->type_], Err2);
+ ErrorString(err), kDescriptions[config->type], Err2);
}
- ok = WriteData(config->output_, &chunk);
+ ok = WriteData(config->output, &chunk);
break;
default:
@@ -951,7 +979,7 @@ static int Process(const Config* config) {
break;
}
case ACTION_SET: {
- switch (config->type_) {
+ switch (config->type) {
case FEATURE_ANMF: {
int i;
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
@@ -960,11 +988,11 @@ static int Process(const Config* config) {
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
}
- for (i = 0; i < config->arg_count_; ++i) {
- switch (config->args_[i].subtype_) {
+ for (i = 0; i < config->arg_count; ++i) {
+ switch (config->args[i].subtype) {
case SUBTYPE_BGCOLOR: {
uint32_t bgcolor;
- ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor);
+ ok = ParseBgcolorArgs(config->args[i].params, &bgcolor);
if (!ok) {
ERROR_GOTO1("ERROR: Could not parse the background color \n",
Err2);
@@ -975,7 +1003,7 @@ static int Process(const Config* config) {
case SUBTYPE_LOOP: {
int parse_error = 0;
const int loop_count =
- ExUtilGetInt(config->args_[i].params_, 10, &parse_error);
+ ExUtilGetInt(config->args[i].params, 10, &parse_error);
if (loop_count < 0 || loop_count > 65535) {
// Note: This is only a 'necessary' condition for loop_count
// to be valid. The 'sufficient' conditioned in checked in
@@ -991,10 +1019,10 @@ static int Process(const Config* config) {
case SUBTYPE_ANMF: {
WebPMuxFrameInfo frame;
frame.id = WEBP_CHUNK_ANMF;
- ok = ExUtilReadFileToWebPData(config->args_[i].filename_,
+ ok = ExUtilReadFileToWebPData(config->args[i].filename,
&frame.bitstream);
if (!ok) goto Err2;
- ok = ParseFrameArgs(config->args_[i].params_, &frame);
+ ok = ParseFrameArgs(config->args[i].params, &frame);
if (!ok) {
WebPDataClear(&frame.bitstream);
ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
@@ -1025,15 +1053,15 @@ static int Process(const Config* config) {
case FEATURE_ICCP:
case FEATURE_EXIF:
case FEATURE_XMP: {
- ok = CreateMux(config->input_, &mux);
+ ok = CreateMux(config->input, &mux);
if (!ok) goto Err2;
- ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk);
+ ok = ExUtilReadFileToWebPData(config->args[0].filename, &chunk);
if (!ok) goto Err2;
- err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1);
+ err = WebPMuxSetChunk(mux, kFourccList[config->type], &chunk, 1);
WebPDataClear(&chunk);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
- ErrorString(err), kDescriptions[config->type_], Err2);
+ ErrorString(err), kDescriptions[config->type], Err2);
}
break;
}
@@ -1041,12 +1069,12 @@ static int Process(const Config* config) {
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
int parse_error = 0;
const int loop_count =
- ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
+ ExUtilGetInt(config->args[0].params, 10, &parse_error);
if (loop_count < 0 || loop_count > 65535 || parse_error) {
ERROR_GOTO1("ERROR: Loop count must be in the range 0 to 65535.\n",
Err2);
}
- ok = CreateMux(config->input_, &mux);
+ ok = CreateMux(config->input, &mux);
if (!ok) goto Err2;
ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK);
if (!ok) {
@@ -1065,12 +1093,12 @@ static int Process(const Config* config) {
case FEATURE_BGCOLOR: {
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
uint32_t bgcolor;
- ok = ParseBgcolorArgs(config->args_[0].params_, &bgcolor);
+ ok = ParseBgcolorArgs(config->args[0].params, &bgcolor);
if (!ok) {
ERROR_GOTO1("ERROR: Could not parse the background color.\n",
Err2);
}
- ok = CreateMux(config->input_, &mux);
+ ok = CreateMux(config->input, &mux);
if (!ok) goto Err2;
ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK);
if (!ok) {
@@ -1091,12 +1119,12 @@ static int Process(const Config* config) {
break;
}
}
- ok = WriteWebP(mux, config->output_);
+ ok = WriteWebP(mux, config->output);
break;
}
case ACTION_DURATION: {
int num_frames;
- ok = CreateMux(config->input_, &mux);
+ ok = CreateMux(config->input, &mux);
if (!ok) goto Err2;
err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
ok = (err == WEBP_MUX_OK);
@@ -1106,7 +1134,7 @@ static int Process(const Config* config) {
if (num_frames == 0) {
fprintf(stderr, "Doesn't look like the source is animated. "
"Skipping duration setting.\n");
- ok = WriteWebP(mux, config->output_);
+ ok = WriteWebP(mux, config->output);
if (!ok) goto Err2;
} else {
int i;
@@ -1118,11 +1146,11 @@ static int Process(const Config* config) {
for (i = 0; i < num_frames; ++i) durations[i] = -1;
// Parse intervals to process.
- for (i = 0; i < config->arg_count_; ++i) {
+ for (i = 0; i < config->arg_count; ++i) {
int k;
int args[3];
int duration, start, end;
- const int nb_args = ExUtilGetInts(config->args_[i].params_,
+ const int nb_args = ExUtilGetInts(config->args[i].params,
10, 3, args);
ok = (nb_args >= 1);
if (!ok) goto Err3;
@@ -1166,7 +1194,7 @@ static int Process(const Config* config) {
WebPDataClear(&frame.bitstream);
}
WebPMuxDelete(mux);
- ok = WriteWebP(new_mux, config->output_);
+ ok = WriteWebP(new_mux, config->output);
mux = new_mux; // transfer for the WebPMuxDelete() call
new_mux = NULL;
@@ -1178,24 +1206,24 @@ static int Process(const Config* config) {
break;
}
case ACTION_STRIP: {
- ok = CreateMux(config->input_, &mux);
+ ok = CreateMux(config->input, &mux);
if (!ok) goto Err2;
- if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF ||
- config->type_ == FEATURE_XMP) {
- err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]);
+ if (config->type == FEATURE_ICCP || config->type == FEATURE_EXIF ||
+ config->type == FEATURE_XMP) {
+ err = WebPMuxDeleteChunk(mux, kFourccList[config->type]);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
- ErrorString(err), kDescriptions[config->type_], Err2);
+ ErrorString(err), kDescriptions[config->type], Err2);
}
} else {
ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
break;
}
- ok = WriteWebP(mux, config->output_);
+ ok = WriteWebP(mux, config->output);
break;
}
case ACTION_INFO: {
- ok = CreateMux(config->input_, &mux);
+ ok = CreateMux(config->input, &mux);
if (!ok) goto Err2;
ok = (DisplayInfo(mux) == WEBP_MUX_OK);
break;
@@ -1214,6 +1242,7 @@ static int Process(const Config* config) {
//------------------------------------------------------------------------------
// Main.
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
Config config;
int ok;
@@ -1227,7 +1256,7 @@ int main(int argc, const char* argv[]) {
PrintHelp();
}
DeleteConfig(&config);
- FREE_WARGV_AND_RETURN(!ok);
+ FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
//------------------------------------------------------------------------------
diff --git a/c-lib/extras/Makefile.am b/c-lib/extras/Makefile.am
@@ -7,6 +7,7 @@ noinst_HEADERS += ../src/webp/types.h
libwebpextras_la_SOURCES =
libwebpextras_la_SOURCES += extras.c extras.h quality_estimate.c
+libwebpextras_la_SOURCES += sharpyuv_risk_table.c sharpyuv_risk_table.h
libwebpextras_la_CPPFLAGS = $(AM_CPPFLAGS)
libwebpextras_la_LDFLAGS = -lm
diff --git a/c-lib/extras/extras.c b/c-lib/extras/extras.c
@@ -11,15 +11,22 @@
//
#include "extras/extras.h"
-#include "webp/format_constants.h"
-#include "src/dsp/dsp.h"
#include <assert.h>
+#include <limits.h>
#include <string.h>
+#include "extras/sharpyuv_risk_table.h"
+#include "sharpyuv/sharpyuv.h"
+#include "src/dsp/dsp.h"
+#include "src/utils/utils.h"
+#include "src/webp/encode.h"
+#include "webp/format_constants.h"
+#include "webp/types.h"
+
#define XTRA_MAJ_VERSION 1
-#define XTRA_MIN_VERSION 2
-#define XTRA_REV_VERSION 2
+#define XTRA_MIN_VERSION 6
+#define XTRA_REV_VERSION 0
//------------------------------------------------------------------------------
@@ -160,3 +167,159 @@ int WebPUnmultiplyARGB(WebPPicture* pic) {
}
//------------------------------------------------------------------------------
+// 420 risk metric
+
+#define YUV_FIX 16 // fixed-point precision for RGB->YUV
+static const int kYuvHalf = 1 << (YUV_FIX - 1);
+
+// Maps a value in [0, (256 << YUV_FIX) - 1] to [0,
+// precomputed_scores_table_sampling - 1]. It is important that the extremal
+// values are preserved and 1:1 mapped:
+// ConvertValue(0) = 0
+// ConvertValue((256 << 16) - 1) = rgb_sampling_size - 1
+static int SharpYuvConvertValueToSampledIdx(int v, int rgb_sampling_size) {
+ v = (v + kYuvHalf) >> YUV_FIX;
+ v = (v < 0) ? 0 : (v > 255) ? 255 : v;
+ return (v * (rgb_sampling_size - 1)) / 255;
+}
+
+#undef YUV_FIX
+
+// For each pixel, computes the index to look up that color in a precomputed
+// risk score table where the YUV space is subsampled to a size of
+// precomputed_scores_table_sampling^3 (see sharpyuv_risk_table.h)
+static int SharpYuvConvertToYuvSharpnessIndex(
+ int r, int g, int b, const SharpYuvConversionMatrix* matrix,
+ int precomputed_scores_table_sampling) {
+ const int y = SharpYuvConvertValueToSampledIdx(
+ matrix->rgb_to_y[0] * r + matrix->rgb_to_y[1] * g +
+ matrix->rgb_to_y[2] * b + matrix->rgb_to_y[3],
+ precomputed_scores_table_sampling);
+ const int u = SharpYuvConvertValueToSampledIdx(
+ matrix->rgb_to_u[0] * r + matrix->rgb_to_u[1] * g +
+ matrix->rgb_to_u[2] * b + matrix->rgb_to_u[3],
+ precomputed_scores_table_sampling);
+ const int v = SharpYuvConvertValueToSampledIdx(
+ matrix->rgb_to_v[0] * r + matrix->rgb_to_v[1] * g +
+ matrix->rgb_to_v[2] * b + matrix->rgb_to_v[3],
+ precomputed_scores_table_sampling);
+ return y + u * precomputed_scores_table_sampling +
+ v * precomputed_scores_table_sampling *
+ precomputed_scores_table_sampling;
+}
+
+static void SharpYuvRowToYuvSharpnessIndex(
+ const uint8_t* r_ptr, const uint8_t* g_ptr, const uint8_t* b_ptr,
+ int rgb_step, int rgb_bit_depth, int width, uint16_t* dst,
+ const SharpYuvConversionMatrix* matrix,
+ int precomputed_scores_table_sampling) {
+ int i;
+ assert(rgb_bit_depth == 8);
+ (void)rgb_bit_depth; // Unused for now.
+ for (i = 0; i < width;
+ ++i, r_ptr += rgb_step, g_ptr += rgb_step, b_ptr += rgb_step) {
+ dst[i] =
+ SharpYuvConvertToYuvSharpnessIndex(r_ptr[0], g_ptr[0], b_ptr[0], matrix,
+ precomputed_scores_table_sampling);
+ }
+}
+
+#define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((uint64_t)(W) * (H), sizeof(T)))
+
+static int DoEstimateRisk(const uint8_t* r_ptr, const uint8_t* g_ptr,
+ const uint8_t* b_ptr, int rgb_step, int rgb_stride,
+ int rgb_bit_depth, int width, int height,
+ const SharpYuvOptions* options,
+ const uint8_t precomputed_scores_table[],
+ int precomputed_scores_table_sampling,
+ float* score_out) {
+ const int sampling3 = precomputed_scores_table_sampling *
+ precomputed_scores_table_sampling *
+ precomputed_scores_table_sampling;
+ const int kNoiseLevel = 4;
+ double total_score = 0;
+ double count = 0;
+ // Rows of indices in
+ uint16_t* row1 = SAFE_ALLOC(width, 1, uint16_t);
+ uint16_t* row2 = SAFE_ALLOC(width, 1, uint16_t);
+ uint16_t* tmp;
+ int i, j;
+
+ if (row1 == NULL || row2 == NULL) {
+ WebPFree(row1);
+ WebPFree(row2);
+ return 0;
+ }
+
+ // Convert the first row ahead.
+ SharpYuvRowToYuvSharpnessIndex(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth,
+ width, row2, options->yuv_matrix,
+ precomputed_scores_table_sampling);
+
+ for (j = 1; j < height; ++j) {
+ r_ptr += rgb_stride;
+ g_ptr += rgb_stride;
+ b_ptr += rgb_stride;
+ // Swap row 1 and row 2.
+ tmp = row1;
+ row1 = row2;
+ row2 = tmp;
+ // Convert the row below.
+ SharpYuvRowToYuvSharpnessIndex(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth,
+ width, row2, options->yuv_matrix,
+ precomputed_scores_table_sampling);
+ for (i = 0; i < width - 1; ++i) {
+ const int idx0 = row1[i + 0];
+ const int idx1 = row1[i + 1];
+ const int idx2 = row2[i + 0];
+ const int score = precomputed_scores_table[idx0 + sampling3 * idx1] +
+ precomputed_scores_table[idx0 + sampling3 * idx2] +
+ precomputed_scores_table[idx1 + sampling3 * idx2];
+ if (score > kNoiseLevel) {
+ total_score += score;
+ count += 1.0;
+ }
+ }
+ }
+ if (count > 0.) total_score /= count;
+
+ // If less than 1% of pixels were evaluated -> below noise level.
+ if (100. * count / (width * height) < 1.) total_score = 0.;
+
+ // Rescale to [0:100]
+ total_score = (total_score > 25.) ? 100. : total_score * 100. / 25.;
+
+ WebPFree(row1);
+ WebPFree(row2);
+
+ *score_out = (float)total_score;
+ return 1;
+}
+
+#undef SAFE_ALLOC
+
+int SharpYuvEstimate420Risk(const void* r_ptr, const void* g_ptr,
+ const void* b_ptr, int rgb_step, int rgb_stride,
+ int rgb_bit_depth, int width, int height,
+ const SharpYuvOptions* options, float* score) {
+ if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX ||
+ r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || options == NULL ||
+ score == NULL) {
+ return 0;
+ }
+ if (rgb_bit_depth != 8) {
+ return 0;
+ }
+
+ if (width <= 4 || height <= 4) {
+ *score = 0.0f; // too small, no real risk.
+ return 1;
+ }
+
+ return DoEstimateRisk(
+ (const uint8_t*)r_ptr, (const uint8_t*)g_ptr, (const uint8_t*)b_ptr,
+ rgb_step, rgb_stride, rgb_bit_depth, width, height, options,
+ kSharpYuvPrecomputedRisk, kSharpYuvPrecomputedRiskYuvSampling, score);
+}
+
+//------------------------------------------------------------------------------
diff --git a/c-lib/extras/extras.h b/c-lib/extras/extras.h
@@ -11,15 +11,18 @@
#ifndef WEBP_EXTRAS_EXTRAS_H_
#define WEBP_EXTRAS_EXTRAS_H_
+#include <stddef.h>
+
#include "webp/types.h"
#ifdef __cplusplus
extern "C" {
#endif
+#include "sharpyuv/sharpyuv.h"
#include "webp/encode.h"
-#define WEBP_EXTRAS_ABI_VERSION 0x0002 // MAJOR(8b) + MINOR(8b)
+#define WEBP_EXTRAS_ABI_VERSION 0x0003 // MAJOR(8b) + MINOR(8b)
//------------------------------------------------------------------------------
@@ -70,6 +73,38 @@ WEBP_EXTERN int VP8EstimateQuality(const uint8_t* const data, size_t size);
//------------------------------------------------------------------------------
+// Computes a score between 0 and 100 which represents the risk of having visual
+// quality loss from converting an RGB image to YUV420.
+// A low score, typically < 40, means there is a low risk of artifacts from
+// chroma subsampling and a simple averaging algorithm can be used instead of
+// the more expensive SharpYuvConvert function.
+// A medium score, typically >= 40 and < 70, means that simple chroma
+// subsampling will produce artifacts and it may be advisable to use the more
+// costly SharpYuvConvert for YUV420 conversion.
+// A high score, typically >= 70, means there is a very high risk of artifacts
+// from chroma subsampling even with SharpYuvConvert, and best results might be
+// achieved by using YUV444.
+// If not using SharpYuvConvert, a threshold of about 50 can be used to decide
+// between (simple averaging) 420 and 444.
+// r_ptr, g_ptr, b_ptr: pointers to the source r, g and b channels. Should point
+// to uint8_t buffers if rgb_bit_depth is 8, or uint16_t buffers otherwise.
+// rgb_step: distance in bytes between two horizontally adjacent pixels on the
+// r, g and b channels. If rgb_bit_depth is > 8, it should be a
+// multiple of 2.
+// rgb_stride: distance in bytes between two vertically adjacent pixels on the
+// r, g, and b channels. If rgb_bit_depth is > 8, it should be a
+// multiple of 2.
+// rgb_bit_depth: number of bits for each r/g/b value. Only a value of 8 is
+// currently supported.
+// width, height: width and height of the image in pixels
+// Returns 0 on failure.
+WEBP_EXTERN int SharpYuvEstimate420Risk(
+ const void* r_ptr, const void* g_ptr, const void* b_ptr, int rgb_step,
+ int rgb_stride, int rgb_bit_depth, int width, int height,
+ const SharpYuvOptions* options, float* score);
+
+//------------------------------------------------------------------------------
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/c-lib/extras/get_disto.c b/c-lib/extras/get_disto.c
@@ -23,10 +23,11 @@
#include <stdlib.h>
#include <string.h>
-#include "webp/encode.h"
+#include "../examples/unicode.h"
#include "imageio/image_dec.h"
#include "imageio/imageio_util.h"
-#include "../examples/unicode.h"
+#include "src/webp/types.h"
+#include "webp/encode.h"
static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
int keep_alpha) {
@@ -223,13 +224,15 @@ static void Help(void) {
" -o <file> . save the diff map as a WebP lossless file\n"
" -scale .... scale the difference map to fit [0..255] range\n"
" -gray ..... use grayscale for difference map (-scale)\n"
- " Also handles PNG, JPG and TIFF files, in addition to WebP.\n");
+ "\nSupported input formats:\n %s\n",
+ WebPGetEnabledInputFileFormats());
}
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
WebPPicture pic1, pic2;
size_t size1 = 0, size2 = 0;
- int ret = 1;
+ int ret = EXIT_FAILURE;
float disto[5];
int type = 0;
int c;
@@ -245,7 +248,7 @@ int main(int argc, const char* argv[]) {
if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) {
fprintf(stderr, "Can't init pictures\n");
- FREE_WARGV_AND_RETURN(1);
+ FREE_WARGV_AND_RETURN(EXIT_FAILURE);
}
for (c = 1; c < argc; ++c) {
@@ -261,7 +264,7 @@ int main(int argc, const char* argv[]) {
use_gray = 1;
} else if (!strcmp(argv[c], "-h")) {
help = 1;
- ret = 0;
+ ret = EXIT_SUCCESS;
} else if (!strcmp(argv[c], "-o")) {
if (++c == argc) {
fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]);
@@ -336,7 +339,8 @@ int main(int argc, const char* argv[]) {
fprintf(stderr, "Error during lossless encoding.\n");
goto End;
}
- ret = ImgIoUtilWriteFile(output, data, data_size) ? 0 : 1;
+ ret = ImgIoUtilWriteFile(output, data, data_size) ? EXIT_SUCCESS
+ : EXIT_FAILURE;
WebPFree(data);
if (ret) goto End;
#else
@@ -344,9 +348,10 @@ int main(int argc, const char* argv[]) {
(void)data_size;
fprintf(stderr, "Cannot save the difference map. Please recompile "
"without the WEBP_REDUCE_CSP flag.\n");
+ goto End;
#endif // WEBP_REDUCE_CSP
}
- ret = 0;
+ ret = EXIT_SUCCESS;
End:
WebPPictureFree(&pic1);
diff --git a/c-lib/extras/quality_estimate.c b/c-lib/extras/quality_estimate.c
@@ -11,11 +11,13 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <math.h>
+#include <stddef.h>
+
#include "extras/extras.h"
+#include "src/webp/types.h"
#include "webp/decode.h"
-#include <math.h>
-
//------------------------------------------------------------------------------
#define INVALID_BIT_POS (1ull << 63)
@@ -76,7 +78,7 @@ int VP8EstimateQuality(const uint8_t* const data, size_t size) {
GET_BIT(2); // colorspace + clamp type
// Segment header
- if (GET_BIT(1)) { // use_segment_
+ if (GET_BIT(1)) { // use_segment
int s;
const int update_map = GET_BIT(1);
if (GET_BIT(1)) { // update data
diff --git a/c-lib/extras/sharpyuv_risk_table.c b/c-lib/extras/sharpyuv_risk_table.c
@@ -0,0 +1,6210 @@
+// Copyright 2023 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Precomputed data for 420 risk estimation.
+
+#include "src/webp/types.h"
+
+const int kSharpYuvPrecomputedRiskYuvSampling = 7;
+
+const uint8_t kSharpYuvPrecomputedRisk[] = {
+ 0, 2, 2, 3, 3, 2, 2, 1, 2, 2, 3, 2, 2, 1, 2, 1, 2, 2, 2,
+ 1, 2, 2, 1, 1, 2, 2, 3, 4, 4, 4, 4, 4, 5, 4, 4, 8, 8, 8,
+ 7, 5, 4, 4, 14, 12, 9, 8, 6, 4, 4, 2, 1, 2, 2, 2, 2, 1, 2,
+ 0, 2, 2, 2, 1, 1, 2, 1, 1, 2, 2, 1, 3, 2, 2, 2, 2, 2, 3,
+ 4, 5, 5, 5, 5, 5, 4, 4, 11, 9, 9, 8, 6, 5, 4, 18, 14, 11, 9,
+ 7, 5, 4, 2, 1, 1, 1, 1, 3, 6, 2, 2, 1, 1, 1, 3, 6, 3, 2,
+ 2, 2, 3, 3, 7, 6, 3, 5, 6, 6, 6, 8, 9, 7, 9, 9, 9, 8, 8,
+ 14, 12, 13, 12, 11, 10, 8, 20, 18, 16, 14, 13, 11, 10, 3, 6, 6, 7, 7,
+ 7, 10, 6, 7, 7, 8, 8, 8, 10, 10, 7, 9, 9, 10, 10, 10, 13, 10, 12,
+ 13, 13, 13, 12, 13, 15, 17, 17, 17, 16, 15, 14, 22, 21, 20, 19, 18, 17, 20,
+ 27, 25, 23, 20, 19, 19, 14, 15, 14, 14, 15, 14, 9, 16, 16, 16, 16, 16, 15,
+ 9, 16, 17, 18, 18, 18, 17, 14, 16, 21, 21, 22, 22, 20, 17, 19, 26, 27, 26,
+ 25, 24, 18, 23, 30, 32, 29, 28, 25, 20, 27, 33, 35, 32, 30, 28, 22, 24, 25,
+ 24, 24, 21, 17, 12, 23, 27, 25, 26, 22, 17, 14, 24, 30, 29, 28, 24, 20, 17,
+ 24, 31, 32, 32, 28, 24, 20, 27, 34, 37, 36, 32, 27, 22, 30, 36, 42, 41, 34,
+ 29, 23, 33, 39, 44, 44, 38, 31, 26, 32, 37, 36, 29, 23, 19, 15, 31, 38, 39,
+ 31, 25, 20, 16, 31, 38, 41, 34, 28, 24, 20, 31, 38, 44, 38, 33, 27, 24, 34,
+ 41, 46, 45, 37, 31, 25, 36, 43, 47, 49, 40, 33, 27, 39, 44, 49, 49, 42, 36,
+ 29, 2, 0, 2, 2, 2, 2, 1, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2,
+ 1, 2, 3, 2, 2, 2, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 9, 9,
+ 9, 8, 6, 5, 5, 15, 14, 10, 8, 7, 5, 5, 2, 2, 1, 2, 1, 1, 4,
+ 2, 2, 0, 2, 1, 1, 4, 2, 2, 1, 1, 1, 3, 5, 3, 3, 3, 3, 3,
+ 4, 6, 7, 6, 7, 6, 6, 5, 6, 12, 11, 10, 9, 8, 7, 6, 19, 16, 12,
+ 10, 9, 8, 6, 2, 2, 3, 3, 3, 5, 7, 3, 2, 3, 4, 4, 5, 7, 7,
+ 2, 4, 6, 6, 6, 9, 10, 4, 8, 9, 9, 9, 11, 13, 8, 11, 13, 13, 12,
+ 11, 17, 14, 15, 16, 15, 14, 10, 21, 20, 18, 18, 16, 15, 13, 7, 7, 9, 9,
+ 10, 8, 11, 11, 8, 10, 11, 11, 11, 11, 13, 8, 11, 13, 13, 14, 13, 16, 11,
+ 16, 17, 17, 17, 15, 17, 17, 20, 21, 21, 20, 18, 18, 23, 25, 24, 23, 21, 20,
+ 21, 29, 28, 26, 25, 23, 22, 16, 17, 17, 17, 18, 17, 11, 20, 18, 19, 19, 19,
+ 18, 11, 19, 20, 20, 22, 21, 20, 16, 19, 24, 26, 25, 26, 24, 22, 21, 29, 31,
+ 30, 31, 27, 22, 26, 33, 35, 33, 31, 30, 25, 30, 36, 39, 36, 35, 31, 26, 27,
+ 28, 28, 27, 23, 20, 15, 26, 30, 29, 29, 25, 21, 17, 27, 32, 32, 32, 28, 24,
+ 21, 26, 35, 38, 37, 32, 29, 25, 30, 38, 41, 41, 36, 31, 25, 34, 40, 47, 45,
+ 39, 33, 27, 37, 44, 48, 49, 41, 35, 29, 35, 42, 40, 33, 27, 22, 18, 36, 41,
+ 42, 34, 29, 24, 21, 36, 41, 46, 38, 32, 28, 24, 35, 43, 49, 42, 37, 32, 28,
+ 38, 45, 51, 48, 42, 36, 30, 40, 47, 53, 51, 44, 38, 32, 44, 49, 54, 55, 47,
+ 39, 34, 2, 2, 0, 2, 2, 1, 2, 3, 2, 1, 2, 2, 1, 3, 3, 2, 1,
+ 1, 2, 3, 5, 3, 3, 3, 3, 3, 4, 5, 6, 6, 7, 6, 6, 5, 6, 11,
+ 10, 10, 9, 7, 5, 5, 17, 15, 12, 10, 8, 5, 6, 3, 2, 2, 1, 1, 3,
+ 6, 3, 2, 2, 1, 1, 3, 6, 4, 3, 2, 2, 3, 5, 7, 7, 4, 4, 5,
+ 6, 6, 8, 11, 7, 8, 9, 9, 6, 9, 14, 12, 12, 11, 11, 10, 8, 20, 17,
+ 14, 12, 13, 11, 9, 4, 3, 4, 5, 6, 6, 8, 8, 3, 4, 6, 7, 6, 9,
+ 11, 3, 5, 9, 10, 8, 11, 14, 5, 9, 12, 13, 12, 13, 17, 10, 12, 16, 17,
+ 16, 13, 21, 15, 17, 20, 19, 17, 12, 23, 20, 20, 21, 20, 19, 16, 11, 8, 12,
+ 13, 12, 10, 13, 15, 9, 12, 14, 14, 14, 14, 18, 10, 13, 16, 17, 17, 16, 20,
+ 14, 18, 20, 21, 21, 18, 21, 20, 22, 24, 25, 24, 21, 21, 25, 27, 28, 27, 26,
+ 25, 23, 31, 30, 30, 28, 27, 26, 19, 19, 20, 21, 21, 20, 13, 23, 21, 21, 22,
+ 23, 21, 14, 23, 22, 23, 25, 25, 25, 19, 23, 26, 28, 29, 29, 28, 25, 24, 31,
+ 34, 34, 34, 32, 26, 28, 36, 40, 38, 37, 34, 28, 32, 40, 43, 40, 39, 35, 29,
+ 29, 32, 31, 30, 26, 22, 17, 29, 33, 32, 32, 29, 25, 21, 29, 36, 36, 36, 32,
+ 28, 24, 29, 38, 42, 40, 36, 32, 28, 32, 42, 45, 44, 40, 35, 30, 37, 45, 50,
+ 49, 42, 37, 31, 40, 47, 52, 52, 46, 39, 33, 38, 45, 42, 36, 30, 25, 21, 39,
+ 45, 45, 38, 32, 28, 24, 39, 45, 49, 42, 37, 32, 28, 38, 46, 52, 46, 41, 35,
+ 31, 42, 49, 55, 52, 45, 40, 34, 45, 51, 57, 57, 48, 42, 36, 48, 53, 58, 57,
+ 51, 44, 38, 3, 2, 2, 0, 0, 2, 4, 3, 2, 2, 0, 1, 3, 6, 3, 3,
+ 2, 2, 2, 4, 7, 4, 4, 4, 5, 4, 5, 7, 8, 7, 8, 8, 7, 6, 7,
+ 12, 11, 12, 10, 8, 6, 7, 18, 16, 13, 11, 9, 6, 8, 3, 2, 2, 2, 3,
+ 5, 7, 4, 3, 2, 2, 3, 6, 8, 8, 3, 2, 3, 6, 7, 9, 11, 4, 5,
+ 6, 9, 8, 11, 14, 8, 9, 10, 12, 8, 11, 17, 13, 13, 13, 14, 12, 11, 22,
+ 18, 15, 13, 16, 15, 11, 8, 3, 5, 8, 8, 8, 10, 12, 3, 5, 8, 10, 8,
+ 11, 15, 5, 6, 11, 13, 9, 13, 18, 9, 10, 15, 16, 16, 16, 21, 12, 14, 19,
+ 19, 19, 16, 24, 17, 18, 21, 22, 21, 16, 26, 22, 20, 23, 23, 22, 19, 15, 9,
+ 13, 15, 16, 12, 15, 18, 11, 14, 16, 17, 17, 17, 22, 13, 15, 19, 21, 21, 19,
+ 26, 17, 20, 24, 23, 25, 21, 26, 22, 24, 27, 28, 27, 24, 25, 27, 30, 31, 31,
+ 29, 27, 25, 33, 33, 33, 32, 31, 30, 23, 20, 22, 24, 24, 23, 16, 26, 22, 25,
+ 25, 26, 25, 17, 26, 25, 26, 28, 30, 28, 22, 26, 29, 31, 33, 33, 32, 27, 28,
+ 34, 37, 36, 38, 36, 30, 30, 39, 42, 42, 41, 37, 32, 34, 43, 46, 44, 42, 39,
+ 33, 31, 34, 33, 33, 30, 26, 20, 31, 36, 35, 35, 32, 28, 24, 32, 37, 39, 39,
+ 36, 31, 28, 32, 40, 45, 42, 40, 37, 32, 34, 44, 50, 48, 44, 40, 34, 39, 47,
+ 54, 52, 47, 40, 35, 43, 48, 56, 56, 49, 43, 37, 43, 46, 45, 39, 32, 29, 25,
+ 42, 47, 47, 40, 35, 31, 28, 41, 47, 49, 44, 39, 36, 31, 42, 48, 55, 50, 44,
+ 40, 34, 45, 52, 57, 55, 49, 43, 38, 48, 54, 59, 60, 53, 44, 39, 51, 57, 61,
+ 60, 54, 47, 41, 3, 2, 2, 0, 0, 2, 4, 3, 2, 2, 1, 0, 3, 6, 3,
+ 2, 2, 2, 2, 4, 6, 4, 4, 4, 4, 4, 5, 7, 8, 7, 8, 8, 7, 6,
+ 8, 12, 12, 11, 11, 8, 6, 8, 18, 16, 13, 11, 9, 6, 8, 3, 2, 2, 2,
+ 3, 5, 7, 4, 3, 2, 2, 3, 6, 8, 8, 3, 2, 3, 6, 7, 9, 12, 5,
+ 5, 6, 9, 8, 11, 14, 8, 9, 10, 12, 8, 10, 17, 13, 13, 13, 14, 13, 11,
+ 22, 18, 15, 13, 15, 15, 11, 8, 3, 5, 8, 8, 8, 10, 12, 3, 5, 9, 10,
+ 8, 11, 16, 5, 6, 11, 13, 9, 13, 17, 8, 9, 15, 16, 15, 15, 22, 12, 14,
+ 19, 20, 20, 16, 24, 17, 18, 21, 22, 21, 16, 26, 22, 20, 23, 24, 22, 19, 15,
+ 9, 13, 15, 15, 12, 16, 18, 11, 14, 16, 17, 17, 17, 21, 13, 15, 19, 21, 21,
+ 19, 26, 17, 20, 23, 24, 25, 20, 25, 21, 24, 27, 27, 27, 24, 24, 27, 29, 30,
+ 30, 29, 27, 26, 33, 32, 33, 32, 31, 30, 23, 21, 23, 24, 23, 22, 15, 26, 22,
+ 24, 24, 26, 25, 17, 26, 24, 25, 28, 30, 27, 22, 26, 29, 30, 33, 33, 32, 27,
+ 28, 34, 36, 38, 37, 35, 30, 30, 38, 43, 42, 41, 37, 32, 33, 42, 46, 44, 42,
+ 39, 34, 32, 34, 33, 33, 29, 26, 20, 32, 37, 35, 35, 32, 28, 24, 31, 38, 38,
+ 39, 36, 32, 28, 31, 40, 44, 44, 39, 36, 32, 35, 42, 49, 48, 44, 40, 33, 39,
+ 47, 54, 53, 46, 40, 36, 44, 50, 54, 55, 48, 43, 37, 40, 47, 44, 37, 33, 28,
+ 25, 41, 48, 47, 40, 35, 31, 28, 40, 47, 50, 44, 40, 35, 31, 41, 49, 55, 50,
+ 44, 39, 35, 44, 52, 58, 55, 47, 43, 38, 48, 55, 60, 59, 50, 45, 39, 50, 57,
+ 62, 62, 53, 47, 42, 2, 2, 1, 2, 2, 0, 3, 2, 2, 1, 2, 2, 1, 4,
+ 2, 2, 1, 2, 4, 3, 5, 3, 3, 4, 4, 6, 6, 6, 6, 7, 7, 7, 9,
+ 8, 6, 11, 11, 11, 10, 9, 8, 6, 18, 15, 13, 11, 10, 7, 6, 2, 2, 2,
+ 1, 1, 3, 5, 3, 2, 2, 1, 1, 4, 6, 6, 2, 2, 3, 4, 5, 7, 10,
+ 4, 5, 6, 7, 6, 9, 11, 8, 9, 9, 10, 7, 8, 16, 13, 13, 12, 12, 11,
+ 9, 21, 18, 15, 13, 14, 13, 9, 6, 2, 4, 6, 6, 6, 8, 10, 2, 4, 7,
+ 8, 6, 9, 14, 3, 5, 9, 10, 7, 11, 16, 6, 9, 13, 14, 13, 13, 18, 11,
+ 12, 17, 18, 17, 13, 22, 16, 17, 20, 20, 18, 13, 25, 22, 20, 22, 22, 20, 17,
+ 14, 7, 12, 13, 13, 9, 13, 17, 9, 12, 14, 15, 14, 14, 20, 12, 13, 17, 18,
+ 19, 16, 22, 15, 18, 22, 22, 22, 18, 22, 20, 22, 25, 26, 25, 21, 23, 25, 28,
+ 28, 28, 27, 24, 24, 31, 31, 30, 30, 29, 27, 22, 19, 21, 21, 21, 20, 13, 24,
+ 20, 22, 22, 23, 22, 14, 24, 22, 23, 26, 26, 25, 19, 25, 26, 28, 29, 32, 29,
+ 25, 25, 32, 34, 35, 35, 32, 27, 28, 36, 41, 39, 37, 34, 30, 32, 40, 43, 43,
+ 39, 36, 30, 29, 32, 32, 30, 26, 22, 17, 29, 35, 32, 32, 29, 25, 22, 29, 35,
+ 35, 36, 33, 29, 25, 29, 38, 41, 41, 37, 33, 29, 33, 41, 47, 45, 42, 37, 30,
+ 38, 44, 52, 52, 44, 38, 32, 41, 47, 53, 53, 47, 40, 34, 39, 46, 42, 36, 30,
+ 26, 22, 39, 45, 45, 38, 33, 29, 25, 39, 44, 48, 42, 36, 33, 28, 39, 46, 52,
+ 47, 41, 37, 33, 42, 50, 54, 52, 45, 39, 36, 45, 52, 58, 56, 49, 43, 35, 49,
+ 54, 58, 59, 52, 44, 37, 1, 1, 2, 4, 4, 3, 0, 2, 1, 2, 4, 4, 3,
+ 2, 2, 1, 1, 4, 5, 4, 4, 2, 3, 3, 5, 7, 7, 6, 5, 6, 7, 8,
+ 10, 8, 7, 10, 10, 11, 11, 10, 8, 7, 17, 15, 12, 11, 11, 8, 7, 2, 1,
+ 1, 3, 2, 2, 3, 2, 2, 1, 2, 2, 2, 4, 2, 2, 1, 3, 3, 3, 5,
+ 6, 3, 4, 5, 5, 4, 6, 9, 7, 8, 8, 8, 6, 6, 13, 12, 12, 11, 9,
+ 8, 6, 20, 18, 13, 12, 11, 9, 6, 2, 2, 3, 3, 3, 5, 6, 6, 2, 3,
+ 4, 4, 4, 6, 11, 2, 4, 7, 7, 5, 8, 15, 4, 8, 10, 10, 10, 10, 16,
+ 9, 11, 14, 14, 14, 10, 19, 14, 16, 18, 16, 15, 10, 23, 21, 19, 19, 18, 16,
+ 14, 11, 7, 9, 10, 10, 7, 11, 15, 7, 10, 11, 12, 11, 11, 17, 9, 12, 14,
+ 15, 15, 12, 19, 13, 16, 18, 18, 18, 14, 19, 17, 20, 22, 23, 22, 18, 20, 23,
+ 26, 26, 25, 23, 21, 22, 29, 29, 27, 26, 25, 23, 19, 17, 18, 18, 18, 17, 11,
+ 22, 18, 20, 19, 20, 19, 11, 22, 20, 21, 23, 23, 22, 17, 22, 24, 26, 27, 27,
+ 26, 22, 23, 30, 31, 32, 32, 28, 24, 25, 34, 37, 36, 34, 30, 26, 30, 37, 41,
+ 37, 36, 33, 27, 27, 29, 27, 27, 24, 20, 15, 26, 31, 30, 29, 26, 22, 18, 27,
+ 33, 32, 33, 30, 26, 22, 26, 35, 39, 36, 34, 29, 25, 31, 37, 43, 42, 38, 33,
+ 28, 35, 44, 48, 47, 40, 34, 30, 38, 45, 49, 49, 42, 37, 31, 35, 41, 38, 32,
+ 26, 23, 18, 36, 42, 41, 35, 29, 25, 22, 36, 40, 43, 38, 33, 29, 25, 36, 42,
+ 50, 43, 37, 34, 29, 39, 45, 52, 47, 42, 37, 31, 42, 47, 53, 54, 45, 39, 33,
+ 46, 51, 54, 54, 46, 40, 35, 1, 2, 3, 3, 3, 2, 2, 0, 2, 2, 3, 3,
+ 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 3, 4,
+ 4, 5, 4, 4, 7, 7, 7, 7, 5, 4, 4, 13, 12, 9, 7, 6, 4, 4, 1,
+ 2, 2, 3, 2, 2, 1, 2, 1, 2, 2, 2, 1, 0, 2, 0, 2, 2, 2, 1,
+ 2, 2, 2, 2, 2, 2, 3, 4, 5, 5, 5, 5, 5, 4, 4, 10, 9, 9, 8,
+ 6, 4, 4, 17, 14, 11, 8, 6, 5, 4, 2, 1, 1, 2, 2, 2, 5, 2, 1,
+ 1, 1, 1, 2, 6, 2, 2, 1, 2, 2, 2, 6, 5, 3, 4, 4, 5, 5, 7,
+ 8, 7, 8, 8, 8, 7, 7, 13, 12, 12, 11, 10, 9, 7, 20, 17, 15, 13, 11,
+ 10, 9, 2, 5, 6, 6, 6, 6, 9, 5, 6, 6, 7, 7, 7, 9, 8, 7, 8,
+ 8, 9, 9, 9, 11, 9, 11, 12, 12, 12, 11, 12, 15, 15, 16, 15, 15, 14, 14,
+ 20, 20, 20, 18, 17, 16, 19, 27, 24, 21, 20, 18, 17, 13, 14, 13, 14, 14, 13,
+ 9, 15, 15, 14, 15, 15, 14, 9, 15, 17, 17, 17, 17, 16, 12, 15, 21, 20, 20,
+ 21, 19, 16, 18, 25, 25, 24, 24, 23, 17, 22, 29, 30, 29, 26, 24, 19, 26, 32,
+ 33, 31, 29, 26, 21, 23, 24, 23, 22, 20, 15, 12, 23, 27, 25, 25, 21, 16, 13,
+ 23, 28, 28, 26, 23, 19, 15, 23, 30, 32, 31, 28, 22, 19, 26, 32, 36, 36, 31,
+ 26, 20, 29, 34, 41, 39, 34, 28, 23, 31, 38, 41, 42, 35, 30, 25, 31, 37, 35,
+ 29, 23, 18, 14, 31, 37, 38, 30, 25, 19, 15, 30, 36, 40, 33, 27, 23, 18, 30,
+ 37, 44, 37, 31, 26, 23, 33, 39, 45, 43, 36, 30, 24, 36, 42, 48, 47, 39, 31,
+ 27, 38, 43, 48, 49, 42, 34, 28, 2, 1, 2, 2, 2, 2, 1, 2, 0, 2, 3,
+ 2, 2, 1, 2, 1, 1, 2, 2, 1, 3, 2, 2, 2, 2, 3, 3, 5, 5, 5,
+ 5, 5, 5, 5, 4, 9, 8, 8, 8, 6, 5, 4, 15, 13, 10, 8, 7, 5, 4,
+ 2, 1, 2, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 3, 2, 2, 1, 1, 1,
+ 2, 5, 2, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 4, 6, 11, 10, 10,
+ 9, 7, 5, 6, 19, 15, 12, 9, 8, 7, 6, 2, 2, 2, 2, 2, 4, 7, 3,
+ 2, 3, 3, 3, 5, 7, 5, 2, 4, 5, 5, 5, 8, 9, 3, 7, 8, 8, 8,
+ 9, 12, 8, 11, 11, 12, 11, 10, 16, 13, 15, 15, 14, 12, 10, 21, 19, 18, 17,
+ 15, 14, 13, 5, 7, 8, 8, 9, 8, 11, 9, 7, 9, 10, 10, 10, 11, 12, 8,
+ 11, 11, 12, 12, 12, 16, 11, 15, 16, 16, 15, 14, 16, 17, 19, 19, 20, 19, 17,
+ 16, 23, 24, 23, 22, 20, 19, 21, 29, 27, 25, 23, 22, 20, 16, 16, 17, 16, 17,
+ 16, 10, 18, 18, 18, 18, 18, 17, 11, 18, 19, 20, 20, 21, 19, 16, 18, 24, 24,
+ 25, 25, 23, 20, 21, 29, 29, 28, 28, 26, 21, 24, 32, 34, 33, 31, 28, 24, 29,
+ 36, 38, 34, 33, 30, 25, 26, 28, 26, 26, 22, 18, 14, 25, 31, 28, 28, 23, 19,
+ 16, 26, 32, 31, 31, 27, 23, 19, 26, 33, 36, 33, 32, 26, 22, 29, 36, 40, 38,
+ 35, 30, 25, 33, 39, 45, 44, 36, 31, 27, 35, 42, 45, 46, 41, 33, 29, 35, 41,
+ 38, 31, 26, 21, 17, 35, 40, 41, 34, 27, 23, 19, 35, 41, 44, 37, 30, 26, 22,
+ 34, 41, 47, 41, 35, 31, 26, 37, 43, 50, 47, 40, 34, 29, 39, 46, 52, 51, 42,
+ 37, 30, 42, 49, 53, 53, 44, 39, 32, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0,
+ 2, 2, 1, 3, 3, 2, 1, 1, 2, 2, 4, 3, 3, 3, 3, 3, 4, 5, 5,
+ 6, 6, 6, 6, 5, 5, 10, 10, 9, 8, 7, 5, 5, 17, 15, 11, 9, 7, 5,
+ 5, 3, 2, 1, 1, 1, 3, 5, 3, 2, 2, 1, 1, 3, 6, 3, 2, 2, 2,
+ 2, 4, 7, 6, 4, 4, 4, 5, 5, 8, 9, 7, 7, 8, 8, 5, 8, 13, 12,
+ 11, 11, 10, 9, 8, 20, 17, 13, 11, 11, 10, 8, 3, 2, 4, 5, 5, 6, 8,
+ 6, 3, 4, 6, 6, 6, 8, 10, 3, 5, 8, 8, 7, 10, 13, 4, 9, 11, 12,
+ 11, 12, 16, 9, 12, 15, 15, 15, 12, 20, 14, 16, 18, 17, 16, 12, 22, 20, 19,
+ 20, 19, 17, 16, 10, 7, 11, 11, 11, 9, 12, 13, 8, 11, 12, 13, 13, 13, 16,
+ 10, 13, 16, 16, 16, 15, 18, 13, 17, 19, 19, 19, 17, 20, 18, 22, 23, 24, 22,
+ 19, 20, 24, 27, 27, 25, 25, 22, 23, 30, 30, 28, 27, 26, 25, 19, 18, 19, 19,
+ 20, 18, 12, 21, 19, 20, 21, 21, 20, 13, 22, 21, 23, 24, 25, 23, 18, 21, 26,
+ 27, 28, 28, 27, 23, 24, 31, 33, 32, 33, 30, 25, 27, 36, 38, 36, 34, 31, 26,
+ 31, 39, 41, 38, 37, 34, 28, 28, 31, 30, 29, 25, 21, 17, 28, 33, 32, 31, 28,
+ 23, 19, 27, 35, 35, 34, 31, 26, 23, 28, 36, 40, 38, 35, 30, 27, 32, 41, 45,
+ 44, 39, 34, 29, 35, 43, 51, 48, 42, 36, 30, 39, 46, 51, 50, 44, 38, 32, 37,
+ 44, 41, 34, 29, 24, 20, 39, 44, 44, 37, 32, 26, 23, 38, 43, 47, 40, 36, 31,
+ 26, 38, 45, 50, 45, 39, 35, 30, 40, 48, 54, 50, 43, 37, 32, 44, 51, 56, 53,
+ 46, 40, 34, 46, 52, 57, 57, 49, 42, 37, 3, 2, 2, 0, 0, 2, 4, 3, 3,
+ 2, 0, 1, 2, 5, 3, 3, 2, 2, 2, 3, 6, 4, 4, 4, 4, 4, 5, 7,
+ 7, 7, 7, 7, 7, 6, 7, 12, 11, 11, 10, 8, 6, 7, 17, 15, 12, 10, 9,
+ 6, 7, 3, 3, 2, 2, 2, 5, 7, 3, 3, 2, 2, 2, 5, 8, 7, 3, 2,
+ 3, 5, 6, 9, 10, 4, 5, 6, 8, 7, 10, 13, 8, 9, 9, 12, 8, 10, 16,
+ 13, 13, 12, 13, 12, 10, 22, 18, 14, 13, 15, 14, 10, 7, 3, 5, 7, 7, 8,
+ 10, 11, 3, 5, 8, 9, 8, 11, 14, 4, 6, 11, 12, 9, 12, 17, 7, 9, 14,
+ 15, 15, 15, 19, 11, 13, 18, 19, 18, 15, 24, 16, 18, 20, 21, 20, 15, 25, 21,
+ 20, 22, 22, 21, 18, 14, 8, 13, 14, 14, 11, 15, 17, 10, 13, 15, 17, 16, 16,
+ 20, 12, 14, 18, 20, 20, 18, 22, 16, 19, 22, 23, 23, 19, 23, 21, 24, 26, 27,
+ 27, 23, 24, 26, 29, 30, 30, 28, 27, 25, 32, 32, 31, 31, 29, 29, 22, 20, 22,
+ 23, 23, 22, 15, 27, 21, 24, 24, 25, 24, 16, 25, 23, 26, 28, 28, 27, 21, 25,
+ 29, 30, 32, 33, 31, 27, 27, 34, 36, 37, 37, 34, 29, 30, 38, 42, 41, 38, 35,
+ 31, 32, 41, 45, 42, 42, 38, 33, 32, 33, 34, 32, 29, 25, 19, 31, 36, 35, 34,
+ 32, 27, 24, 31, 38, 37, 38, 34, 30, 27, 31, 39, 43, 42, 39, 35, 31, 35, 43,
+ 50, 47, 44, 39, 33, 39, 47, 55, 52, 46, 39, 34, 43, 50, 54, 55, 49, 42, 36,
+ 41, 46, 44, 38, 32, 27, 24, 41, 46, 46, 40, 34, 31, 27, 40, 48, 50, 44, 40,
+ 35, 30, 41, 47, 55, 48, 43, 39, 34, 44, 50, 58, 54, 48, 42, 37, 47, 54, 60,
+ 58, 52, 45, 37, 50, 55, 61, 60, 54, 47, 39, 2, 2, 2, 1, 1, 2, 4, 3,
+ 2, 2, 1, 0, 2, 5, 3, 2, 2, 2, 2, 4, 6, 4, 4, 4, 4, 4, 5,
+ 7, 8, 7, 7, 7, 7, 6, 7, 12, 11, 11, 10, 8, 6, 7, 17, 15, 13, 10,
+ 9, 6, 7, 3, 2, 2, 1, 3, 5, 8, 4, 2, 2, 2, 3, 5, 8, 7, 3,
+ 2, 3, 6, 6, 9, 11, 4, 5, 6, 9, 7, 10, 14, 8, 8, 9, 12, 8, 10,
+ 17, 13, 13, 12, 14, 12, 10, 22, 18, 14, 13, 15, 14, 10, 7, 3, 5, 8, 8,
+ 9, 11, 12, 3, 5, 8, 9, 8, 11, 15, 5, 5, 11, 12, 9, 13, 18, 8, 9,
+ 15, 16, 15, 15, 21, 12, 13, 18, 19, 19, 15, 25, 16, 18, 21, 21, 21, 15, 25,
+ 22, 20, 23, 23, 22, 19, 15, 8, 13, 15, 15, 12, 16, 18, 10, 14, 16, 17, 17,
+ 17, 21, 13, 14, 19, 20, 21, 18, 25, 17, 19, 23, 23, 24, 20, 25, 21, 24, 27,
+ 28, 27, 23, 25, 26, 29, 31, 30, 28, 26, 25, 32, 32, 32, 31, 31, 29, 23, 21,
+ 22, 24, 24, 23, 16, 26, 22, 23, 24, 26, 25, 17, 26, 24, 26, 28, 30, 28, 21,
+ 26, 28, 31, 33, 33, 32, 27, 27, 35, 36, 36, 38, 35, 30, 29, 38, 41, 42, 39,
+ 36, 31, 34, 41, 45, 43, 42, 38, 33, 32, 34, 34, 32, 30, 25, 20, 31, 35, 35,
+ 35, 32, 28, 23, 31, 38, 38, 38, 35, 31, 28, 31, 40, 44, 43, 39, 35, 32, 34,
+ 43, 49, 47, 43, 40, 32, 40, 46, 54, 52, 46, 39, 35, 43, 50, 55, 55, 48, 42,
+ 36, 42, 47, 45, 39, 33, 28, 24, 41, 47, 48, 40, 35, 31, 27, 41, 47, 50, 45,
+ 40, 35, 31, 42, 48, 56, 49, 44, 39, 35, 45, 51, 57, 55, 48, 42, 37, 48, 53,
+ 61, 59, 51, 44, 39, 50, 57, 61, 61, 54, 48, 40, 2, 1, 1, 3, 3, 1, 3,
+ 2, 1, 1, 2, 2, 0, 3, 2, 2, 1, 1, 2, 2, 4, 2, 3, 3, 3, 4,
+ 4, 5, 5, 6, 6, 6, 7, 6, 5, 10, 10, 9, 9, 8, 6, 5, 16, 14, 11,
+ 9, 8, 6, 5, 2, 2, 1, 1, 2, 4, 6, 2, 2, 1, 1, 2, 3, 5, 4,
+ 2, 1, 2, 3, 4, 6, 9, 3, 4, 4, 6, 5, 7, 11, 7, 7, 8, 9, 5,
+ 7, 14, 11, 11, 11, 11, 9, 7, 19, 16, 13, 11, 12, 11, 7, 5, 2, 4, 6,
+ 7, 7, 9, 9, 2, 4, 7, 7, 6, 8, 13, 2, 5, 9, 9, 6, 10, 15, 5,
+ 8, 12, 12, 12, 12, 18, 9, 11, 16, 16, 15, 12, 21, 14, 16, 19, 18, 17, 12,
+ 23, 20, 18, 20, 19, 18, 16, 13, 7, 11, 12, 13, 11, 14, 16, 8, 12, 14, 15,
+ 14, 13, 19, 10, 13, 16, 17, 18, 15, 21, 14, 17, 20, 20, 20, 16, 22, 19, 21,
+ 24, 24, 24, 20, 21, 24, 26, 27, 26, 26, 23, 22, 30, 29, 28, 28, 27, 26, 20,
+ 19, 20, 20, 22, 21, 14, 23, 20, 22, 23, 23, 21, 13, 23, 22, 24, 25, 26, 24,
+ 19, 24, 26, 28, 29, 29, 28, 23, 25, 31, 34, 33, 34, 32, 25, 28, 36, 39, 37,
+ 36, 33, 28, 30, 39, 41, 39, 38, 34, 30, 29, 32, 31, 31, 28, 23, 19, 28, 33,
+ 33, 32, 29, 24, 20, 28, 37, 35, 37, 32, 28, 24, 28, 37, 41, 40, 37, 33, 28,
+ 31, 39, 46, 43, 40, 35, 29, 37, 43, 51, 48, 42, 36, 31, 39, 46, 51, 51, 44,
+ 39, 33, 39, 44, 42, 36, 30, 26, 22, 39, 44, 45, 38, 32, 28, 24, 39, 44, 47,
+ 41, 35, 31, 27, 38, 47, 51, 45, 40, 35, 31, 41, 48, 54, 51, 44, 39, 33, 46,
+ 51, 56, 57, 47, 40, 35, 47, 53, 58, 58, 49, 43, 37, 1, 1, 3, 6, 6, 4,
+ 2, 1, 1, 3, 5, 5, 3, 0, 2, 1, 2, 3, 4, 3, 2, 2, 2, 2, 3,
+ 5, 5, 4, 4, 5, 5, 6, 8, 6, 4, 9, 8, 8, 8, 8, 6, 4, 14, 12,
+ 10, 8, 9, 6, 4, 2, 1, 2, 4, 3, 3, 5, 2, 1, 1, 3, 3, 2, 3,
+ 2, 1, 1, 1, 1, 2, 4, 3, 3, 3, 3, 3, 3, 4, 8, 6, 6, 6, 6,
+ 4, 4, 12, 10, 10, 9, 7, 6, 5, 18, 14, 11, 10, 8, 7, 5, 2, 1, 3,
+ 4, 5, 6, 8, 4, 2, 3, 5, 5, 5, 6, 9, 2, 4, 6, 6, 4, 6, 12,
+ 3, 7, 9, 9, 9, 8, 16, 8, 10, 12, 13, 12, 8, 18, 13, 14, 15, 14, 13,
+ 8, 20, 18, 16, 17, 16, 15, 13, 8, 6, 9, 10, 11, 9, 11, 13, 7, 10, 11,
+ 12, 12, 11, 15, 8, 11, 13, 13, 13, 11, 17, 11, 15, 16, 16, 16, 13, 19, 16,
+ 19, 21, 21, 20, 16, 18, 21, 23, 24, 23, 22, 19, 20, 27, 26, 25, 24, 23, 21,
+ 17, 17, 18, 18, 19, 18, 12, 21, 18, 19, 19, 20, 18, 11, 20, 20, 21, 22, 22,
+ 21, 15, 20, 24, 25, 25, 26, 24, 19, 22, 28, 29, 29, 30, 28, 22, 24, 33, 36,
+ 35, 32, 29, 24, 28, 36, 39, 36, 35, 31, 26, 27, 29, 27, 27, 24, 21, 17, 26,
+ 32, 30, 29, 25, 21, 16, 26, 33, 33, 31, 27, 23, 20, 25, 36, 36, 34, 32, 28,
+ 23, 29, 37, 42, 41, 34, 31, 26, 32, 40, 46, 44, 38, 33, 27, 37, 41, 47, 46,
+ 40, 34, 29, 36, 41, 39, 32, 27, 24, 19, 36, 41, 41, 35, 29, 24, 20, 35, 40,
+ 44, 37, 32, 27, 24, 36, 43, 47, 42, 37, 32, 27, 38, 44, 50, 47, 40, 34, 29,
+ 41, 47, 52, 49, 42, 37, 31, 43, 49, 52, 52, 46, 39, 33, 2, 2, 3, 3, 3,
+ 2, 2, 1, 2, 3, 3, 3, 2, 2, 0, 2, 2, 3, 2, 2, 1, 1, 1, 1,
+ 2, 2, 2, 3, 3, 3, 3, 4, 4, 3, 3, 7, 7, 7, 6, 5, 3, 3, 13,
+ 11, 9, 7, 5, 3, 3, 0, 2, 2, 3, 2, 2, 2, 1, 2, 2, 3, 2, 2,
+ 1, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 2, 2, 2, 3, 4, 4, 4, 4,
+ 4, 3, 4, 9, 8, 8, 7, 5, 4, 4, 16, 13, 10, 8, 6, 4, 3, 2, 1,
+ 2, 2, 2, 1, 4, 2, 1, 1, 1, 1, 1, 4, 2, 1, 1, 0, 0, 1, 5,
+ 3, 2, 3, 3, 3, 3, 6, 7, 6, 6, 6, 7, 6, 6, 12, 11, 11, 10, 8,
+ 7, 6, 19, 17, 13, 12, 10, 9, 8, 2, 4, 4, 5, 5, 5, 8, 3, 5, 5,
+ 6, 6, 6, 8, 7, 6, 6, 7, 7, 7, 8, 10, 9, 10, 10, 10, 11, 10, 10,
+ 14, 14, 14, 14, 13, 13, 13, 19, 19, 18, 16, 15, 14, 18, 25, 22, 20, 18, 16,
+ 16, 12, 12, 12, 12, 12, 12, 8, 14, 14, 14, 13, 14, 13, 8, 14, 16, 15, 15,
+ 15, 14, 11, 14, 19, 19, 19, 19, 18, 14, 17, 23, 23, 23, 23, 20, 16, 21, 27,
+ 28, 27, 26, 23, 18, 25, 31, 32, 29, 28, 25, 20, 21, 23, 22, 21, 18, 14, 10,
+ 21, 25, 23, 23, 20, 16, 11, 21, 27, 26, 25, 22, 18, 14, 21, 27, 30, 29, 25,
+ 21, 18, 24, 30, 34, 34, 30, 25, 19, 27, 33, 39, 37, 32, 26, 21, 30, 36, 40,
+ 40, 35, 28, 23, 29, 34, 33, 27, 22, 17, 13, 29, 35, 37, 29, 23, 19, 14, 29,
+ 35, 39, 32, 25, 22, 17, 30, 35, 42, 35, 30, 25, 21, 31, 37, 44, 40, 35, 29,
+ 23, 33, 41, 46, 46, 36, 31, 25, 36, 43, 47, 46, 39, 32, 26, 1, 2, 2, 2,
+ 2, 2, 1, 2, 1, 2, 3, 2, 2, 1, 2, 0, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 3, 4, 4, 4, 4, 4, 5, 4, 4, 8, 8, 8, 7, 5, 4, 4,
+ 14, 12, 9, 8, 6, 4, 4, 2, 0, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2,
+ 1, 2, 2, 2, 1, 2, 2, 1, 4, 2, 2, 2, 3, 2, 3, 5, 6, 5, 5,
+ 5, 5, 4, 5, 11, 10, 9, 8, 6, 5, 5, 18, 15, 11, 9, 7, 5, 5, 2,
+ 2, 1, 2, 1, 3, 6, 2, 2, 2, 2, 2, 4, 6, 4, 2, 3, 3, 4, 4,
+ 7, 7, 3, 6, 6, 6, 7, 9, 11, 7, 9, 10, 10, 10, 8, 15, 12, 14, 13,
+ 12, 11, 8, 20, 18, 17, 15, 14, 12, 11, 4, 6, 7, 7, 8, 7, 10, 8, 7,
+ 8, 8, 9, 9, 10, 11, 8, 10, 10, 11, 11, 11, 14, 10, 14, 14, 14, 14, 13,
+ 14, 16, 18, 18, 18, 17, 16, 15, 21, 22, 21, 20, 18, 18, 20, 28, 26, 24, 22,
+ 20, 19, 14, 15, 15, 15, 16, 15, 10, 17, 17, 16, 16, 17, 15, 10, 16, 18, 19,
+ 19, 19, 18, 14, 17, 22, 23, 23, 23, 21, 18, 19, 27, 27, 27, 27, 24, 20, 24,
+ 31, 34, 32, 29, 27, 21, 28, 34, 36, 33, 31, 28, 23, 24, 26, 24, 24, 21, 17,
+ 13, 24, 29, 27, 26, 23, 18, 14, 24, 30, 29, 28, 25, 21, 17, 24, 31, 34, 32,
+ 29, 25, 22, 29, 35, 38, 37, 34, 29, 23, 32, 37, 44, 42, 36, 31, 25, 34, 40,
+ 45, 44, 39, 33, 26, 33, 38, 37, 30, 24, 20, 16, 32, 39, 40, 32, 26, 21, 18,
+ 32, 38, 42, 35, 30, 25, 21, 33, 40, 46, 39, 33, 28, 24, 34, 41, 48, 43, 38,
+ 32, 27, 37, 43, 49, 49, 41, 35, 28, 40, 46, 49, 50, 44, 36, 30, 2, 1, 1,
+ 2, 2, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 2, 1, 2, 3, 2,
+ 3, 2, 3, 3, 4, 5, 5, 5, 6, 5, 6, 5, 5, 10, 9, 9, 8, 6, 5,
+ 5, 16, 14, 11, 8, 7, 5, 4, 2, 2, 1, 2, 1, 2, 5, 2, 2, 1, 1,
+ 1, 2, 5, 3, 2, 1, 1, 1, 3, 6, 4, 3, 3, 3, 4, 4, 7, 8, 6,
+ 7, 7, 7, 5, 7, 12, 11, 10, 10, 9, 7, 7, 19, 16, 13, 11, 10, 9, 7,
+ 2, 2, 4, 4, 5, 6, 8, 4, 2, 4, 5, 5, 6, 8, 8, 2, 5, 7, 7,
+ 6, 9, 11, 4, 8, 10, 10, 10, 11, 14, 8, 11, 14, 14, 14, 11, 18, 14, 16,
+ 17, 16, 15, 11, 21, 20, 18, 19, 18, 16, 14, 8, 7, 10, 11, 11, 9, 12, 11,
+ 8, 11, 11, 12, 12, 12, 14, 8, 12, 14, 14, 15, 14, 17, 12, 17, 18, 18, 17,
+ 15, 18, 17, 21, 21, 22, 21, 19, 18, 23, 26, 25, 24, 23, 21, 22, 30, 29, 27,
+ 26, 24, 23, 17, 18, 18, 18, 19, 18, 12, 19, 19, 19, 20, 20, 19, 12, 20, 20,
+ 21, 23, 23, 22, 17, 20, 25, 27, 26, 27, 26, 21, 22, 30, 32, 31, 31, 29, 23,
+ 26, 35, 37, 35, 33, 30, 25, 30, 37, 40, 37, 36, 32, 27, 28, 29, 28, 28, 25,
+ 20, 17, 27, 32, 31, 30, 26, 22, 18, 27, 34, 34, 34, 29, 25, 21, 27, 35, 38,
+ 37, 34, 30, 25, 31, 38, 43, 41, 38, 33, 27, 34, 41, 49, 47, 39, 33, 29, 38,
+ 44, 49, 49, 43, 36, 30, 36, 43, 41, 34, 28, 24, 20, 37, 43, 44, 36, 30, 25,
+ 21, 36, 43, 46, 38, 34, 29, 24, 37, 44, 49, 44, 38, 34, 29, 40, 46, 53, 49,
+ 42, 36, 30, 42, 49, 55, 54, 45, 38, 33, 45, 51, 55, 55, 48, 41, 34, 2, 2,
+ 1, 2, 2, 2, 4, 2, 2, 1, 2, 2, 1, 3, 3, 2, 2, 0, 1, 2, 4,
+ 3, 3, 3, 3, 2, 3, 5, 6, 6, 6, 6, 5, 4, 5, 10, 10, 9, 8, 6,
+ 4, 5, 16, 14, 11, 9, 7, 4, 5, 2, 2, 1, 2, 3, 5, 7, 3, 2, 2,
+ 2, 3, 4, 6, 4, 2, 2, 2, 3, 5, 7, 7, 3, 4, 4, 6, 6, 8, 10,
+ 7, 7, 8, 9, 6, 8, 14, 11, 11, 10, 11, 10, 8, 19, 16, 13, 11, 13, 11,
+ 8, 4, 2, 5, 7, 8, 9, 11, 8, 3, 4, 7, 8, 8, 10, 11, 3, 5, 9,
+ 10, 8, 11, 14, 5, 8, 12, 13, 13, 13, 18, 9, 12, 16, 17, 16, 13, 21, 14,
+ 16, 19, 19, 18, 13, 22, 20, 18, 21, 20, 19, 17, 11, 8, 12, 14, 14, 12, 16,
+ 15, 8, 13, 14, 15, 16, 15, 18, 10, 14, 17, 18, 17, 16, 20, 14, 18, 20, 21,
+ 21, 18, 21, 19, 23, 24, 25, 24, 21, 21, 24, 27, 28, 27, 27, 24, 23, 30, 31,
+ 29, 29, 27, 27, 20, 19, 21, 22, 23, 22, 16, 23, 21, 23, 23, 24, 22, 15, 23,
+ 23, 24, 26, 26, 25, 20, 23, 27, 29, 30, 31, 29, 25, 25, 32, 33, 34, 34, 32,
+ 27, 27, 36, 39, 38, 36, 34, 29, 31, 39, 43, 41, 39, 37, 30, 30, 33, 33, 32,
+ 29, 25, 20, 29, 34, 34, 34, 30, 25, 21, 29, 37, 37, 37, 33, 28, 25, 30, 39,
+ 41, 40, 37, 33, 29, 33, 42, 47, 45, 41, 38, 30, 36, 44, 51, 49, 44, 38, 33,
+ 40, 47, 52, 53, 46, 38, 34, 41, 46, 44, 37, 32, 28, 23, 40, 45, 46, 40, 33,
+ 29, 25, 40, 46, 49, 42, 37, 33, 28, 40, 47, 53, 47, 41, 36, 32, 43, 50, 55,
+ 52, 46, 40, 35, 45, 52, 57, 56, 49, 43, 36, 48, 54, 57, 58, 52, 44, 38, 2,
+ 1, 2, 2, 2, 4, 5, 2, 2, 2, 2, 2, 2, 4, 3, 2, 2, 1, 0, 2,
+ 5, 3, 3, 3, 2, 2, 3, 6, 7, 6, 6, 5, 5, 4, 6, 11, 9, 9, 8,
+ 6, 4, 6, 16, 13, 10, 8, 7, 4, 6, 3, 2, 2, 3, 4, 7, 9, 3, 2,
+ 2, 3, 4, 6, 8, 5, 2, 2, 2, 4, 5, 8, 9, 4, 4, 4, 7, 6, 9,
+ 13, 7, 7, 7, 10, 6, 9, 16, 11, 11, 10, 12, 11, 9, 20, 16, 12, 11, 13,
+ 12, 9, 5, 2, 5, 9, 9, 10, 13, 9, 3, 5, 9, 10, 9, 11, 13, 3, 5,
+ 10, 11, 8, 11, 15, 6, 8, 13, 14, 14, 14, 20, 11, 12, 16, 18, 18, 13, 23,
+ 15, 16, 19, 20, 18, 14, 23, 19, 18, 20, 21, 20, 18, 13, 8, 13, 15, 16, 14,
+ 18, 16, 9, 14, 16, 17, 17, 17, 19, 11, 14, 18, 18, 19, 17, 23, 15, 18, 21,
+ 22, 22, 19, 24, 20, 23, 25, 26, 25, 22, 24, 24, 27, 28, 28, 28, 25, 23, 30,
+ 31, 30, 30, 29, 27, 21, 20, 23, 23, 25, 24, 18, 25, 22, 24, 25, 26, 24, 17,
+ 24, 24, 27, 28, 28, 26, 21, 24, 28, 30, 31, 31, 29, 26, 26, 32, 35, 36, 36,
+ 33, 28, 28, 37, 40, 39, 39, 35, 29, 30, 40, 45, 42, 40, 37, 32, 31, 34, 33,
+ 34, 31, 27, 22, 31, 36, 35, 36, 32, 28, 23, 30, 38, 38, 38, 34, 30, 26, 30,
+ 41, 42, 42, 39, 33, 29, 34, 43, 48, 47, 42, 37, 31, 36, 45, 53, 51, 44, 40,
+ 34, 41, 47, 53, 54, 47, 40, 35, 41, 47, 45, 39, 34, 30, 26, 41, 47, 47, 41,
+ 35, 30, 26, 42, 48, 50, 44, 37, 34, 30, 42, 48, 54, 47, 42, 38, 33, 43, 51,
+ 56, 52, 47, 40, 35, 47, 53, 59, 57, 49, 42, 37, 50, 55, 59, 59, 52, 46, 39,
+ 1, 2, 2, 4, 4, 3, 4, 2, 1, 2, 4, 4, 2, 3, 2, 2, 2, 2, 3,
+ 0, 3, 2, 2, 1, 1, 2, 2, 3, 4, 4, 4, 4, 5, 4, 4, 8, 8, 7,
+ 7, 6, 4, 4, 13, 11, 9, 7, 6, 4, 4, 2, 1, 2, 3, 4, 6, 7, 2,
+ 1, 2, 3, 4, 5, 6, 3, 2, 1, 1, 2, 3, 5, 6, 3, 2, 3, 4, 4,
+ 6, 10, 5, 5, 6, 7, 4, 6, 12, 9, 9, 8, 9, 7, 6, 17, 14, 10, 9,
+ 10, 9, 6, 2, 2, 5, 7, 8, 9, 11, 7, 2, 4, 8, 8, 8, 9, 11, 2,
+ 4, 7, 8, 6, 8, 14, 4, 7, 10, 10, 11, 10, 17, 8, 10, 14, 15, 14, 10,
+ 19, 12, 14, 16, 16, 15, 10, 20, 17, 15, 17, 18, 17, 15, 11, 7, 12, 14, 14,
+ 12, 15, 14, 8, 12, 15, 16, 15, 15, 17, 9, 12, 15, 16, 16, 14, 19, 12, 16,
+ 18, 19, 18, 15, 21, 17, 20, 22, 23, 22, 18, 20, 22, 24, 25, 25, 23, 22, 20,
+ 26, 26, 27, 26, 25, 24, 18, 18, 21, 22, 23, 22, 16, 21, 20, 22, 23, 24, 23,
+ 15, 22, 21, 24, 25, 24, 23, 18, 22, 26, 26, 28, 28, 27, 23, 23, 30, 31, 32,
+ 33, 29, 24, 26, 34, 38, 36, 34, 31, 26, 27, 36, 39, 37, 37, 34, 27, 28, 31,
+ 32, 32, 29, 25, 20, 29, 33, 33, 33, 29, 25, 21, 27, 36, 35, 34, 30, 27, 23,
+ 28, 38, 39, 37, 34, 29, 26, 31, 39, 43, 42, 38, 32, 28, 34, 41, 48, 46, 41,
+ 34, 30, 37, 44, 49, 49, 42, 37, 31, 39, 44, 42, 36, 31, 27, 23, 39, 45, 45,
+ 38, 33, 28, 23, 39, 45, 47, 40, 34, 30, 26, 40, 45, 52, 44, 38, 34, 30, 41,
+ 47, 52, 49, 42, 37, 33, 43, 48, 55, 53, 45, 39, 33, 46, 51, 56, 55, 48, 40,
+ 35, 2, 3, 5, 6, 6, 5, 4, 1, 2, 4, 6, 6, 4, 2, 1, 2, 3, 4,
+ 5, 3, 0, 1, 1, 1, 3, 4, 3, 2, 3, 3, 3, 4, 6, 4, 2, 7, 6,
+ 6, 6, 6, 4, 2, 11, 9, 7, 6, 6, 4, 2, 1, 2, 3, 5, 4, 5, 6,
+ 1, 2, 3, 4, 4, 4, 5, 2, 1, 2, 3, 2, 2, 3, 2, 1, 1, 1, 1,
+ 2, 4, 5, 4, 4, 4, 3, 2, 3, 9, 8, 7, 7, 5, 4, 4, 14, 11, 8,
+ 7, 7, 6, 4, 2, 1, 5, 5, 6, 8, 9, 2, 1, 4, 6, 6, 6, 8, 5,
+ 1, 4, 6, 6, 5, 6, 10, 2, 5, 7, 7, 7, 7, 13, 6, 8, 10, 11, 10,
+ 7, 15, 11, 11, 13, 13, 11, 7, 16, 14, 13, 14, 14, 13, 12, 6, 6, 11, 11,
+ 12, 11, 13, 11, 7, 11, 12, 13, 13, 12, 13, 7, 11, 13, 13, 13, 11, 16, 10,
+ 14, 15, 14, 15, 12, 17, 15, 17, 19, 19, 18, 15, 16, 19, 20, 21, 21, 20, 18,
+ 16, 22, 23, 23, 23, 21, 20, 15, 17, 18, 19, 20, 19, 13, 18, 18, 20, 20, 21,
+ 20, 12, 18, 19, 21, 21, 21, 19, 15, 18, 22, 24, 24, 24, 22, 18, 20, 27, 28,
+ 28, 28, 26, 20, 21, 30, 33, 31, 29, 28, 22, 24, 33, 34, 33, 32, 28, 24, 27,
+ 30, 29, 29, 26, 22, 17, 26, 31, 30, 31, 27, 22, 18, 26, 34, 33, 31, 27, 23,
+ 19, 26, 34, 35, 33, 31, 27, 22, 28, 36, 40, 38, 34, 29, 24, 31, 37, 43, 42,
+ 36, 30, 26, 33, 39, 45, 44, 39, 33, 27, 37, 41, 39, 34, 29, 25, 21, 36, 41,
+ 42, 36, 30, 25, 21, 37, 41, 45, 37, 31, 26, 22, 36, 42, 46, 41, 35, 31, 26,
+ 37, 43, 48, 44, 38, 31, 28, 39, 45, 50, 48, 41, 34, 29, 41, 46, 51, 51, 45,
+ 36, 29, 2, 2, 3, 4, 4, 3, 2, 2, 2, 3, 4, 4, 3, 2, 1, 2, 2,
+ 3, 3, 2, 1, 0, 2, 2, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 3, 7,
+ 6, 7, 6, 4, 3, 3, 12, 11, 8, 7, 5, 3, 3, 1, 2, 3, 3, 2, 2,
+ 2, 0, 2, 2, 3, 2, 2, 1, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 2,
+ 2, 1, 3, 4, 3, 4, 4, 4, 3, 3, 9, 8, 8, 7, 5, 3, 3, 16, 13,
+ 10, 8, 6, 4, 3, 1, 2, 2, 2, 2, 2, 3, 2, 1, 1, 2, 2, 2, 3,
+ 2, 0, 1, 1, 1, 1, 3, 2, 2, 2, 2, 2, 2, 5, 6, 5, 5, 5, 5,
+ 4, 5, 12, 10, 10, 9, 7, 6, 5, 18, 17, 13, 10, 9, 7, 6, 2, 3, 3,
+ 4, 4, 4, 7, 2, 4, 4, 5, 5, 5, 8, 5, 5, 5, 6, 6, 6, 7, 8,
+ 8, 8, 9, 9, 9, 9, 9, 13, 13, 12, 13, 12, 11, 12, 19, 17, 16, 15, 14,
+ 13, 18, 24, 21, 18, 17, 15, 15, 11, 11, 11, 11, 11, 11, 8, 13, 13, 12, 13,
+ 13, 12, 8, 12, 15, 15, 14, 14, 13, 10, 13, 18, 18, 17, 18, 16, 13, 16, 23,
+ 22, 22, 22, 20, 15, 20, 26, 28, 26, 25, 21, 17, 24, 29, 30, 27, 27, 23, 18,
+ 20, 23, 21, 21, 17, 13, 10, 21, 24, 23, 22, 19, 15, 10, 21, 25, 26, 24, 21,
+ 17, 12, 21, 27, 28, 27, 25, 20, 17, 23, 29, 33, 33, 29, 23, 19, 25, 32, 38,
+ 36, 30, 26, 20, 29, 35, 39, 39, 34, 27, 21, 28, 34, 33, 25, 21, 17, 12, 28,
+ 34, 34, 29, 22, 17, 13, 28, 34, 38, 30, 25, 20, 16, 29, 34, 40, 33, 29, 24,
+ 20, 30, 37, 42, 38, 33, 26, 22, 33, 39, 44, 43, 36, 29, 24, 35, 41, 44, 44,
+ 38, 32, 26, 1, 2, 3, 4, 4, 3, 3, 2, 2, 3, 4, 4, 3, 2, 1, 2,
+ 3, 3, 3, 2, 1, 2, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 7, 6, 6, 6, 4, 3, 3, 13, 11, 8, 6, 5, 3, 3, 2, 2, 3, 3, 3,
+ 3, 3, 2, 1, 2, 3, 3, 2, 3, 2, 1, 2, 2, 2, 1, 1, 2, 1, 1,
+ 2, 1, 1, 3, 4, 4, 4, 4, 4, 3, 3, 9, 8, 8, 7, 5, 3, 3, 16,
+ 13, 10, 7, 5, 4, 3, 2, 1, 2, 2, 3, 4, 6, 2, 2, 2, 3, 3, 4,
+ 6, 2, 2, 2, 2, 3, 3, 6, 5, 2, 4, 4, 4, 5, 7, 8, 6, 8, 8,
+ 8, 7, 6, 13, 11, 12, 11, 10, 9, 7, 19, 16, 15, 13, 11, 10, 9, 2, 6,
+ 7, 8, 8, 7, 10, 5, 6, 8, 8, 9, 9, 10, 9, 7, 8, 9, 9, 9, 10,
+ 12, 9, 12, 12, 12, 12, 11, 12, 14, 15, 15, 16, 15, 14, 13, 20, 20, 19, 18,
+ 16, 15, 18, 26, 24, 21, 19, 18, 18, 13, 14, 15, 15, 16, 15, 10, 14, 16, 15,
+ 16, 17, 16, 10, 15, 18, 17, 17, 17, 16, 12, 15, 21, 21, 21, 21, 19, 16, 18,
+ 25, 25, 25, 25, 22, 17, 21, 28, 30, 28, 26, 24, 19, 25, 32, 34, 31, 30, 26,
+ 21, 24, 24, 25, 25, 21, 17, 13, 23, 27, 26, 25, 23, 18, 14, 23, 29, 28, 27,
+ 23, 19, 15, 23, 30, 31, 31, 27, 23, 19, 26, 32, 36, 35, 31, 26, 21, 29, 35,
+ 41, 41, 34, 28, 23, 31, 38, 42, 43, 35, 30, 24, 32, 38, 36, 30, 25, 20, 16,
+ 31, 38, 39, 32, 26, 21, 17, 31, 38, 41, 33, 27, 23, 19, 32, 37, 44, 36, 31,
+ 26, 23, 34, 39, 45, 42, 36, 30, 25, 35, 41, 47, 46, 39, 33, 26, 38, 44, 48,
+ 49, 42, 34, 29, 1, 2, 3, 4, 4, 4, 3, 2, 2, 3, 4, 4, 3, 2, 1,
+ 2, 2, 3, 3, 1, 1, 2, 2, 0, 2, 2, 2, 3, 4, 3, 3, 3, 3, 3,
+ 3, 7, 7, 7, 6, 4, 3, 3, 13, 11, 8, 6, 5, 3, 3, 2, 2, 2, 3,
+ 4, 4, 6, 2, 2, 2, 3, 3, 3, 5, 2, 1, 2, 2, 2, 2, 4, 2, 2,
+ 2, 1, 1, 2, 5, 5, 5, 4, 4, 4, 3, 5, 10, 9, 8, 7, 6, 4, 5,
+ 17, 14, 10, 8, 7, 6, 5, 2, 2, 4, 5, 5, 7, 9, 2, 2, 4, 5, 6,
+ 7, 8, 5, 2, 4, 5, 5, 6, 7, 8, 3, 6, 7, 7, 7, 9, 12, 6, 9,
+ 10, 11, 10, 9, 15, 11, 13, 14, 13, 12, 9, 19, 17, 16, 16, 14, 13, 12, 4,
+ 7, 10, 11, 12, 10, 13, 9, 7, 11, 12, 12, 12, 13, 12, 8, 11, 12, 12, 12,
+ 12, 14, 10, 14, 15, 15, 15, 13, 16, 15, 18, 18, 20, 18, 16, 15, 21, 23, 22,
+ 21, 20, 19, 19, 27, 26, 24, 22, 21, 20, 15, 17, 18, 19, 19, 18, 13, 17, 18,
+ 19, 20, 21, 19, 13, 18, 20, 21, 21, 20, 19, 15, 17, 24, 24, 24, 24, 23, 18,
+ 20, 27, 28, 29, 28, 26, 21, 23, 32, 34, 32, 30, 27, 22, 27, 35, 37, 35, 32,
+ 29, 23, 26, 29, 28, 28, 25, 21, 17, 27, 31, 30, 30, 26, 21, 17, 27, 33, 33,
+ 31, 27, 22, 19, 27, 34, 36, 34, 31, 26, 22, 28, 36, 41, 40, 36, 28, 24, 33,
+ 38, 45, 43, 37, 32, 26, 35, 41, 45, 45, 40, 34, 28, 35, 41, 40, 33, 29, 24,
+ 20, 35, 42, 43, 35, 29, 25, 20, 36, 41, 45, 37, 31, 26, 22, 36, 41, 47, 40,
+ 35, 30, 25, 37, 43, 48, 46, 40, 33, 27, 39, 44, 51, 51, 42, 35, 30, 43, 47,
+ 52, 52, 46, 38, 31, 2, 2, 3, 5, 5, 4, 5, 2, 2, 3, 4, 4, 3, 3,
+ 2, 2, 3, 3, 2, 1, 3, 2, 2, 2, 0, 2, 1, 3, 4, 4, 3, 3, 3,
+ 2, 3, 8, 7, 7, 6, 4, 2, 4, 13, 11, 8, 6, 5, 2, 3, 2, 2, 3,
+ 3, 5, 6, 8, 2, 2, 2, 3, 4, 5, 7, 2, 2, 2, 2, 3, 4, 6, 4,
+ 2, 2, 2, 3, 4, 6, 8, 5, 5, 5, 6, 4, 7, 11, 9, 9, 8, 8, 7,
+ 7, 17, 13, 10, 8, 10, 8, 7, 2, 2, 5, 7, 8, 10, 12, 4, 2, 5, 8,
+ 9, 9, 11, 8, 2, 5, 8, 8, 7, 9, 11, 3, 6, 10, 10, 10, 10, 15, 7,
+ 9, 13, 14, 13, 11, 18, 12, 13, 16, 15, 15, 11, 19, 17, 16, 17, 17, 16, 15,
+ 8, 8, 12, 14, 15, 13, 16, 12, 8, 13, 14, 16, 15, 15, 15, 8, 13, 15, 16,
+ 15, 15, 17, 12, 16, 17, 18, 18, 16, 19, 16, 20, 22, 22, 22, 18, 18, 22, 24,
+ 25, 24, 23, 22, 19, 26, 27, 26, 26, 24, 24, 17, 19, 21, 21, 23, 22, 16, 20,
+ 20, 22, 24, 24, 23, 16, 20, 22, 23, 24, 24, 22, 18, 20, 25, 27, 27, 27, 26,
+ 22, 22, 30, 31, 32, 31, 29, 24, 25, 34, 36, 36, 34, 30, 26, 29, 37, 40, 37,
+ 36, 33, 27, 28, 32, 32, 31, 29, 25, 20, 28, 34, 33, 33, 29, 25, 21, 28, 36,
+ 36, 35, 30, 25, 22, 29, 37, 39, 37, 33, 30, 25, 31, 39, 44, 43, 38, 33, 27,
+ 34, 41, 49, 46, 41, 35, 29, 39, 43, 49, 49, 42, 37, 31, 39, 46, 43, 37, 31,
+ 27, 23, 39, 45, 46, 39, 33, 28, 23, 39, 46, 49, 41, 34, 29, 25, 39, 45, 50,
+ 44, 37, 34, 29, 40, 47, 52, 49, 42, 37, 31, 43, 49, 54, 53, 46, 39, 32, 45,
+ 51, 54, 55, 49, 42, 34, 2, 3, 3, 4, 4, 6, 7, 2, 2, 3, 4, 3, 4,
+ 5, 2, 2, 3, 2, 2, 2, 4, 3, 2, 2, 2, 0, 2, 5, 5, 4, 4, 3,
+ 3, 3, 5, 9, 8, 7, 6, 4, 3, 5, 13, 10, 8, 6, 5, 3, 5, 2, 2,
+ 3, 4, 6, 9, 10, 3, 2, 3, 4, 6, 7, 9, 3, 2, 2, 3, 4, 6, 8,
+ 6, 3, 2, 2, 5, 5, 8, 10, 5, 5, 5, 8, 6, 8, 13, 9, 8, 8, 10,
+ 9, 8, 16, 13, 9, 8, 11, 11, 8, 3, 2, 6, 9, 11, 12, 14, 7, 2, 6,
+ 10, 11, 11, 13, 10, 3, 5, 9, 11, 10, 12, 13, 4, 7, 11, 12, 13, 12, 18,
+ 9, 10, 14, 16, 15, 13, 21, 12, 13, 17, 18, 17, 12, 20, 16, 15, 18, 19, 19,
+ 17, 10, 8, 14, 16, 17, 15, 18, 13, 8, 14, 17, 18, 18, 18, 16, 10, 14, 17,
+ 19, 19, 17, 20, 13, 17, 20, 21, 21, 18, 21, 18, 21, 23, 25, 24, 21, 21, 22,
+ 25, 26, 27, 25, 24, 20, 26, 27, 28, 28, 27, 26, 19, 20, 23, 25, 26, 25, 19,
+ 22, 22, 25, 26, 27, 25, 18, 23, 23, 26, 27, 27, 26, 21, 22, 26, 29, 30, 31,
+ 29, 24, 24, 31, 34, 34, 34, 31, 27, 26, 34, 38, 37, 36, 34, 28, 28, 36, 41,
+ 38, 38, 35, 30, 31, 35, 35, 35, 31, 28, 23, 31, 37, 36, 36, 33, 28, 24, 30,
+ 39, 38, 37, 33, 29, 25, 31, 38, 41, 40, 36, 32, 28, 32, 41, 46, 44, 40, 35,
+ 29, 35, 43, 52, 49, 42, 37, 32, 37, 45, 51, 51, 45, 39, 33, 40, 48, 45, 39,
+ 34, 31, 26, 42, 48, 48, 41, 36, 30, 26, 42, 48, 50, 43, 37, 32, 28, 41, 48,
+ 53, 46, 39, 35, 31, 43, 49, 54, 52, 45, 39, 33, 44, 52, 57, 56, 47, 42, 36,
+ 46, 53, 57, 58, 50, 43, 37, 3, 4, 4, 5, 5, 6, 7, 2, 3, 4, 5, 5,
+ 4, 5, 2, 3, 4, 3, 3, 2, 3, 2, 2, 2, 2, 2, 0, 3, 3, 2, 2,
+ 2, 3, 2, 3, 6, 5, 5, 4, 3, 2, 3, 9, 7, 5, 4, 3, 2, 3, 2,
+ 3, 4, 5, 6, 8, 9, 2, 3, 3, 5, 6, 6, 7, 2, 2, 3, 3, 4, 5,
+ 6, 4, 2, 1, 1, 3, 3, 5, 8, 3, 3, 4, 5, 3, 5, 10, 6, 6, 5,
+ 7, 6, 5, 12, 9, 6, 5, 8, 7, 5, 2, 2, 6, 8, 9, 11, 12, 5, 2,
+ 5, 9, 9, 9, 11, 9, 2, 5, 8, 9, 8, 10, 13, 2, 5, 8, 9, 9, 9,
+ 15, 6, 8, 11, 12, 12, 9, 16, 8, 10, 13, 14, 13, 9, 15, 12, 11, 15, 15,
+ 15, 13, 9, 7, 13, 15, 15, 14, 17, 14, 8, 13, 15, 16, 16, 16, 16, 8, 13,
+ 15, 16, 16, 15, 18, 10, 14, 17, 17, 17, 14, 19, 15, 18, 20, 20, 20, 17, 16,
+ 18, 21, 22, 22, 22, 20, 15, 21, 22, 24, 24, 23, 22, 17, 19, 22, 23, 24, 23,
+ 16, 20, 20, 23, 24, 25, 23, 16, 20, 21, 24, 25, 24, 23, 19, 20, 24, 25, 26,
+ 27, 25, 21, 21, 29, 30, 29, 30, 28, 23, 22, 30, 33, 33, 31, 29, 24, 22, 31,
+ 36, 35, 33, 31, 26, 28, 32, 32, 32, 30, 26, 21, 28, 34, 34, 34, 30, 25, 21,
+ 29, 36, 35, 35, 31, 26, 22, 28, 36, 38, 36, 32, 28, 24, 29, 38, 44, 40, 36,
+ 32, 26, 32, 40, 46, 43, 38, 33, 28, 33, 42, 46, 46, 41, 35, 30, 39, 46, 44,
+ 37, 32, 28, 24, 40, 45, 46, 40, 33, 29, 24, 40, 46, 48, 40, 33, 29, 24, 40,
+ 45, 50, 43, 37, 33, 28, 40, 45, 51, 47, 40, 34, 29, 41, 47, 52, 51, 43, 37,
+ 31, 43, 48, 53, 53, 46, 39, 32, 4, 5, 6, 7, 8, 6, 7, 4, 4, 5, 7,
+ 7, 5, 4, 3, 4, 4, 5, 6, 3, 2, 3, 3, 3, 3, 5, 3, 0, 2, 1,
+ 1, 2, 4, 2, 0, 3, 2, 2, 2, 3, 2, 0, 5, 3, 2, 2, 3, 2, 0,
+ 4, 4, 5, 6, 7, 8, 8, 3, 4, 4, 5, 6, 6, 6, 2, 4, 4, 4, 4,
+ 4, 4, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 2, 2, 1, 3, 4, 3, 2,
+ 2, 2, 2, 3, 8, 5, 3, 2, 4, 3, 3, 3, 4, 6, 7, 9, 10, 11, 2,
+ 3, 6, 8, 8, 8, 9, 5, 3, 6, 7, 7, 6, 8, 9, 2, 4, 7, 6, 7,
+ 6, 12, 3, 5, 7, 8, 8, 6, 13, 4, 6, 9, 9, 9, 6, 11, 8, 6, 9,
+ 10, 10, 10, 5, 7, 11, 12, 13, 13, 14, 10, 8, 12, 13, 14, 14, 13, 13, 8,
+ 12, 13, 14, 14, 12, 15, 8, 12, 14, 14, 14, 11, 15, 10, 13, 15, 16, 15, 14,
+ 13, 12, 15, 17, 17, 17, 16, 11, 15, 17, 18, 19, 18, 18, 15, 17, 20, 20, 21,
+ 21, 14, 18, 18, 21, 21, 21, 20, 13, 18, 20, 22, 22, 22, 20, 16, 18, 20, 22,
+ 22, 23, 21, 17, 16, 23, 26, 24, 24, 22, 18, 16, 24, 27, 28, 26, 24, 20, 17,
+ 25, 30, 28, 28, 26, 21, 27, 29, 30, 29, 26, 23, 18, 27, 32, 31, 31, 27, 22,
+ 18, 27, 33, 33, 32, 27, 23, 19, 27, 33, 35, 32, 29, 24, 19, 26, 33, 37, 35,
+ 31, 26, 22, 27, 34, 39, 37, 32, 28, 23, 27, 34, 40, 40, 35, 29, 24, 37, 42,
+ 41, 34, 29, 25, 21, 37, 42, 43, 35, 30, 25, 21, 37, 43, 44, 36, 31, 27, 22,
+ 37, 42, 46, 40, 32, 27, 23, 36, 41, 46, 41, 35, 28, 24, 36, 42, 46, 45, 37,
+ 31, 26, 36, 41, 46, 46, 39, 33, 27, 4, 5, 6, 8, 8, 6, 5, 4, 5, 6,
+ 7, 8, 6, 4, 3, 4, 5, 6, 7, 4, 3, 3, 3, 4, 4, 4, 3, 2, 0,
+ 2, 2, 2, 3, 2, 2, 4, 4, 4, 3, 3, 2, 2, 10, 8, 6, 4, 3, 2,
+ 2, 3, 4, 5, 6, 5, 5, 5, 3, 4, 5, 6, 6, 5, 4, 2, 4, 4, 5,
+ 4, 4, 3, 2, 2, 3, 3, 3, 2, 1, 2, 2, 2, 2, 2, 2, 1, 6, 5,
+ 6, 5, 2, 1, 1, 13, 11, 7, 5, 3, 1, 1, 2, 3, 4, 4, 5, 5, 5,
+ 2, 3, 4, 4, 5, 4, 5, 2, 2, 4, 3, 4, 4, 4, 2, 1, 1, 1, 1,
+ 1, 2, 3, 3, 2, 2, 3, 1, 2, 9, 8, 7, 6, 4, 3, 2, 15, 14, 10,
+ 7, 6, 4, 3, 2, 3, 5, 5, 6, 6, 8, 1, 4, 5, 6, 6, 6, 8, 3,
+ 5, 6, 6, 6, 6, 8, 6, 6, 6, 6, 6, 7, 7, 6, 10, 9, 10, 10, 9,
+ 8, 9, 15, 14, 13, 12, 11, 10, 15, 21, 18, 15, 14, 12, 12, 11, 11, 11, 12,
+ 13, 11, 8, 12, 13, 13, 13, 14, 12, 8, 12, 14, 14, 14, 15, 13, 9, 12, 16,
+ 15, 15, 15, 14, 10, 14, 19, 19, 19, 19, 17, 12, 17, 23, 24, 23, 21, 19, 13,
+ 21, 26, 28, 25, 23, 21, 15, 20, 22, 21, 22, 19, 14, 10, 20, 24, 23, 24, 20,
+ 16, 11, 20, 26, 25, 25, 21, 16, 12, 20, 25, 26, 25, 22, 18, 14, 20, 26, 30,
+ 29, 25, 20, 15, 22, 29, 36, 33, 28, 23, 17, 25, 31, 35, 36, 30, 25, 19, 28,
+ 35, 32, 26, 22, 17, 13, 28, 35, 36, 29, 24, 18, 14, 28, 34, 38, 30, 25, 19,
+ 14, 28, 35, 38, 31, 26, 21, 17, 26, 33, 38, 36, 30, 26, 19, 29, 36, 40, 39,
+ 33, 28, 20, 32, 37, 40, 40, 33, 29, 23, 4, 5, 6, 7, 7, 7, 6, 3, 5,
+ 6, 7, 7, 6, 5, 3, 4, 5, 6, 6, 4, 3, 3, 3, 3, 4, 4, 2, 1,
+ 2, 0, 1, 2, 3, 2, 2, 4, 4, 4, 3, 2, 2, 1, 10, 8, 5, 4, 2,
+ 2, 1, 3, 4, 5, 7, 7, 7, 6, 3, 4, 5, 6, 6, 5, 5, 2, 3, 5,
+ 5, 5, 4, 3, 2, 2, 2, 2, 2, 2, 0, 2, 2, 1, 2, 1, 1, 0, 7,
+ 6, 5, 4, 2, 1, 0, 13, 11, 7, 5, 3, 1, 0, 3, 3, 4, 5, 6, 7,
+ 8, 2, 3, 4, 5, 6, 6, 7, 2, 2, 4, 4, 4, 4, 6, 2, 2, 2, 2,
+ 3, 3, 5, 5, 3, 5, 5, 5, 4, 5, 10, 8, 9, 8, 7, 6, 5, 15, 13,
+ 12, 10, 8, 7, 6, 2, 6, 7, 8, 9, 9, 11, 2, 7, 8, 8, 9, 9, 10,
+ 6, 7, 8, 9, 9, 9, 10, 9, 7, 9, 9, 9, 9, 9, 9, 11, 12, 12, 13,
+ 12, 11, 10, 17, 17, 16, 14, 13, 13, 16, 23, 20, 18, 16, 15, 14, 13, 14, 14,
+ 15, 16, 15, 11, 13, 16, 16, 16, 17, 16, 11, 14, 17, 17, 17, 17, 16, 12, 14,
+ 18, 17, 18, 18, 16, 13, 15, 22, 22, 21, 22, 19, 14, 19, 26, 27, 26, 24, 21,
+ 16, 23, 30, 31, 28, 26, 23, 18, 23, 25, 24, 24, 22, 17, 13, 23, 27, 26, 25,
+ 23, 18, 14, 23, 29, 29, 26, 23, 18, 14, 23, 28, 29, 28, 24, 20, 16, 23, 29,
+ 33, 32, 28, 23, 18, 26, 32, 39, 37, 31, 25, 19, 29, 33, 38, 39, 34, 27, 21,
+ 31, 37, 37, 29, 24, 20, 16, 32, 38, 39, 31, 26, 21, 17, 31, 37, 41, 33, 27,
+ 21, 17, 31, 36, 43, 34, 28, 24, 20, 30, 37, 42, 40, 32, 26, 21, 31, 38, 44,
+ 44, 35, 29, 23, 35, 40, 46, 46, 39, 31, 24, 4, 5, 6, 8, 8, 7, 6, 4,
+ 5, 6, 7, 7, 6, 5, 3, 4, 5, 6, 6, 4, 3, 3, 3, 3, 3, 4, 2,
+ 1, 2, 1, 0, 2, 2, 1, 1, 4, 4, 4, 3, 2, 1, 1, 10, 8, 5, 3,
+ 2, 1, 1, 4, 4, 5, 7, 7, 8, 8, 3, 4, 5, 6, 7, 6, 7, 3, 4,
+ 5, 5, 5, 4, 5, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 1, 0, 3,
+ 7, 6, 5, 4, 2, 1, 3, 13, 10, 7, 5, 4, 2, 3, 3, 4, 6, 7, 8,
+ 10, 10, 2, 3, 6, 7, 8, 8, 9, 3, 3, 6, 6, 6, 7, 8, 6, 2, 4,
+ 5, 5, 6, 7, 8, 4, 6, 7, 7, 7, 7, 12, 8, 10, 10, 9, 8, 7, 15,
+ 13, 13, 12, 10, 10, 8, 2, 7, 10, 11, 11, 12, 13, 6, 8, 10, 11, 12, 12,
+ 13, 10, 8, 11, 11, 12, 12, 12, 12, 8, 11, 12, 12, 12, 11, 12, 12, 14, 15,
+ 16, 14, 14, 12, 18, 20, 19, 17, 17, 15, 15, 23, 22, 21, 19, 18, 16, 14, 17,
+ 18, 18, 19, 18, 14, 15, 19, 19, 19, 19, 19, 13, 16, 19, 20, 20, 20, 19, 15,
+ 16, 21, 22, 21, 21, 20, 16, 16, 24, 25, 24, 24, 22, 17, 19, 27, 30, 29, 27,
+ 24, 19, 24, 32, 34, 31, 29, 26, 20, 26, 29, 28, 28, 25, 21, 16, 26, 31, 29,
+ 30, 26, 21, 16, 25, 32, 32, 30, 26, 21, 17, 26, 31, 33, 32, 27, 23, 19, 25,
+ 32, 37, 35, 31, 26, 20, 28, 34, 41, 39, 34, 27, 23, 32, 36, 42, 42, 36, 30,
+ 24, 35, 41, 39, 33, 28, 24, 19, 35, 41, 42, 35, 29, 24, 19, 35, 42, 45, 36,
+ 30, 24, 19, 35, 40, 46, 38, 31, 26, 22, 33, 40, 46, 43, 37, 29, 24, 35, 43,
+ 47, 47, 39, 32, 26, 39, 45, 49, 48, 41, 34, 29, 4, 5, 6, 8, 8, 7, 8,
+ 4, 5, 6, 7, 7, 6, 6, 3, 5, 6, 6, 5, 4, 4, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 0, 2, 1, 2, 5, 4, 4, 3, 2, 1, 2, 9, 7, 5,
+ 3, 2, 1, 2, 4, 5, 5, 7, 8, 10, 10, 3, 4, 5, 6, 7, 8, 8, 3,
+ 4, 5, 5, 5, 6, 6, 3, 2, 3, 3, 3, 4, 5, 4, 2, 2, 2, 3, 3,
+ 5, 8, 6, 5, 5, 5, 4, 5, 12, 9, 6, 5, 6, 5, 5, 3, 4, 7, 9,
+ 10, 12, 13, 3, 3, 7, 9, 9, 10, 11, 6, 3, 6, 8, 8, 8, 10, 9, 2,
+ 5, 7, 8, 8, 9, 11, 4, 7, 10, 10, 10, 9, 14, 8, 10, 12, 12, 11, 9,
+ 14, 11, 11, 14, 13, 12, 11, 6, 8, 12, 13, 14, 14, 16, 10, 9, 13, 14, 15,
+ 15, 16, 13, 9, 13, 14, 15, 15, 15, 15, 9, 13, 15, 15, 16, 14, 15, 13, 16,
+ 18, 18, 18, 17, 14, 18, 21, 21, 20, 19, 18, 14, 21, 23, 23, 22, 21, 20, 16,
+ 19, 20, 21, 22, 22, 16, 18, 20, 22, 23, 23, 22, 16, 18, 21, 23, 23, 24, 22,
+ 18, 18, 23, 24, 24, 25, 23, 19, 18, 27, 28, 27, 27, 25, 20, 21, 30, 33, 31,
+ 30, 27, 22, 24, 31, 35, 33, 32, 29, 23, 27, 32, 31, 31, 28, 24, 20, 28, 34,
+ 32, 33, 29, 24, 20, 27, 35, 36, 34, 29, 25, 20, 28, 35, 37, 36, 31, 27, 22,
+ 27, 34, 40, 39, 35, 29, 23, 31, 38, 44, 43, 37, 31, 25, 34, 39, 45, 46, 40,
+ 33, 27, 38, 45, 43, 37, 31, 26, 22, 39, 44, 46, 39, 33, 27, 23, 39, 45, 47,
+ 40, 33, 28, 23, 39, 45, 49, 41, 35, 30, 26, 37, 43, 49, 46, 39, 33, 27, 38,
+ 45, 51, 50, 43, 35, 29, 41, 46, 51, 51, 44, 38, 31, 5, 5, 6, 7, 7, 9,
+ 10, 5, 5, 6, 7, 7, 7, 8, 4, 5, 6, 5, 5, 5, 6, 4, 3, 3, 3,
+ 3, 3, 4, 3, 3, 2, 2, 0, 1, 4, 4, 3, 3, 2, 1, 1, 4, 6, 5,
+ 3, 2, 2, 2, 4, 4, 5, 6, 7, 9, 11, 12, 4, 5, 5, 7, 9, 10, 10,
+ 4, 4, 5, 5, 7, 8, 9, 5, 3, 3, 3, 5, 6, 7, 6, 3, 2, 3, 6,
+ 5, 7, 8, 4, 4, 4, 7, 7, 7, 10, 7, 4, 4, 8, 8, 7, 3, 5, 8,
+ 11, 12, 14, 15, 5, 4, 7, 11, 12, 12, 14, 8, 4, 7, 10, 11, 11, 13, 12,
+ 3, 5, 10, 11, 12, 11, 13, 4, 7, 12, 13, 13, 12, 14, 6, 8, 12, 14, 14,
+ 12, 14, 9, 9, 13, 16, 16, 15, 8, 9, 14, 17, 18, 17, 20, 12, 10, 15, 17,
+ 19, 19, 19, 15, 10, 16, 18, 19, 19, 18, 17, 10, 15, 18, 18, 19, 17, 16, 14,
+ 17, 20, 21, 21, 20, 15, 16, 20, 22, 23, 23, 22, 14, 18, 21, 23, 24, 24, 24,
+ 19, 21, 24, 25, 26, 26, 19, 21, 22, 25, 26, 28, 26, 19, 21, 24, 26, 28, 27,
+ 25, 21, 21, 25, 26, 27, 28, 27, 22, 20, 28, 30, 30, 31, 29, 24, 21, 29, 34,
+ 33, 32, 30, 25, 21, 30, 37, 35, 34, 33, 27, 30, 34, 35, 34, 32, 28, 24, 30,
+ 37, 37, 37, 33, 28, 24, 30, 39, 39, 37, 33, 29, 25, 31, 38, 40, 39, 35, 30,
+ 25, 30, 39, 43, 41, 36, 32, 27, 31, 40, 47, 44, 39, 33, 29, 32, 41, 45, 47,
+ 41, 36, 31, 42, 48, 47, 40, 35, 31, 26, 43, 49, 49, 42, 36, 32, 27, 42, 48,
+ 51, 44, 37, 32, 27, 43, 48, 53, 44, 39, 33, 29, 41, 47, 53, 49, 41, 35, 31,
+ 42, 48, 52, 50, 44, 38, 32, 43, 48, 53, 53, 46, 40, 33, 4, 5, 5, 6, 6,
+ 8, 8, 4, 5, 5, 6, 6, 6, 6, 3, 4, 5, 4, 4, 4, 4, 3, 3, 3,
+ 2, 3, 2, 3, 2, 2, 1, 1, 1, 0, 2, 3, 2, 2, 2, 1, 0, 2, 5,
+ 3, 2, 1, 1, 0, 2, 4, 4, 5, 6, 8, 10, 10, 3, 4, 5, 6, 7, 8,
+ 9, 3, 3, 4, 4, 5, 6, 7, 4, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2,
+ 4, 3, 5, 7, 3, 2, 2, 5, 5, 5, 9, 5, 3, 2, 6, 6, 5, 3, 4,
+ 7, 9, 11, 12, 14, 5, 3, 7, 10, 10, 10, 12, 9, 3, 6, 9, 9, 9, 10,
+ 13, 2, 5, 8, 9, 10, 9, 13, 3, 5, 9, 10, 11, 9, 16, 5, 6, 10, 12,
+ 12, 9, 14, 8, 7, 11, 13, 13, 13, 9, 8, 13, 15, 16, 15, 17, 13, 8, 13,
+ 16, 16, 17, 17, 16, 8, 13, 16, 17, 16, 16, 18, 9, 14, 16, 17, 17, 14, 18,
+ 12, 15, 18, 19, 18, 17, 15, 13, 17, 19, 20, 20, 20, 14, 16, 19, 21, 21, 21,
+ 21, 16, 19, 22, 23, 24, 24, 18, 20, 20, 22, 24, 25, 23, 17, 20, 21, 25, 25,
+ 25, 24, 19, 20, 24, 25, 26, 26, 24, 20, 18, 25, 27, 28, 28, 25, 21, 18, 26,
+ 30, 30, 29, 27, 23, 18, 27, 32, 32, 31, 30, 25, 29, 33, 33, 32, 30, 26, 21,
+ 28, 35, 34, 34, 31, 26, 21, 29, 38, 37, 35, 31, 27, 22, 28, 36, 38, 37, 32,
+ 28, 23, 28, 36, 41, 39, 34, 29, 25, 29, 36, 42, 42, 35, 30, 26, 30, 37, 43,
+ 43, 38, 33, 27, 40, 47, 45, 37, 33, 28, 24, 40, 45, 46, 40, 34, 29, 25, 41,
+ 46, 49, 41, 34, 29, 25, 40, 45, 50, 42, 36, 31, 26, 39, 44, 49, 46, 38, 33,
+ 28, 39, 45, 50, 47, 40, 34, 30, 39, 46, 50, 49, 43, 36, 30, 4, 5, 6, 8,
+ 8, 6, 6, 4, 4, 5, 7, 7, 5, 4, 3, 4, 5, 5, 6, 4, 2, 3, 3,
+ 3, 3, 5, 3, 0, 2, 1, 1, 2, 4, 2, 0, 2, 2, 2, 2, 3, 2, 0,
+ 4, 3, 2, 2, 3, 2, 0, 4, 4, 5, 6, 6, 8, 8, 3, 4, 5, 5, 6,
+ 6, 6, 3, 4, 4, 4, 4, 4, 5, 2, 2, 2, 2, 2, 2, 3, 2, 2, 1,
+ 1, 1, 1, 3, 4, 2, 2, 2, 2, 2, 3, 7, 5, 2, 2, 3, 3, 3, 3,
+ 4, 6, 8, 9, 10, 11, 2, 3, 6, 8, 8, 8, 9, 5, 3, 6, 7, 7, 7,
+ 8, 9, 2, 4, 7, 6, 7, 6, 11, 2, 4, 7, 8, 8, 6, 13, 4, 5, 8,
+ 9, 9, 6, 10, 8, 6, 9, 10, 10, 10, 5, 7, 11, 12, 13, 13, 14, 10, 7,
+ 11, 13, 14, 14, 13, 13, 8, 12, 13, 14, 13, 12, 15, 7, 12, 14, 14, 14, 11,
+ 16, 10, 13, 15, 15, 15, 14, 12, 12, 15, 16, 17, 17, 16, 10, 15, 17, 18, 18,
+ 18, 18, 15, 17, 20, 20, 21, 20, 14, 18, 18, 21, 21, 22, 20, 13, 18, 19, 21,
+ 22, 22, 20, 17, 17, 21, 23, 23, 23, 21, 16, 16, 22, 25, 25, 25, 23, 18, 16,
+ 24, 27, 27, 25, 24, 20, 17, 25, 30, 28, 28, 26, 21, 25, 29, 30, 30, 26, 23,
+ 18, 27, 32, 32, 30, 27, 23, 18, 27, 34, 34, 33, 28, 23, 19, 27, 33, 34, 32,
+ 29, 25, 20, 26, 33, 37, 34, 30, 26, 21, 27, 33, 41, 38, 32, 28, 23, 27, 34,
+ 39, 41, 35, 29, 24, 36, 42, 39, 34, 29, 25, 21, 37, 43, 42, 36, 31, 26, 22,
+ 37, 43, 45, 38, 31, 26, 22, 38, 42, 47, 39, 32, 28, 23, 37, 40, 45, 42, 34,
+ 29, 24, 35, 41, 46, 44, 36, 30, 26, 36, 42, 46, 45, 40, 33, 27, 8, 9, 10,
+ 12, 12, 11, 10, 8, 9, 10, 12, 12, 10, 9, 7, 8, 9, 10, 11, 8, 7, 7,
+ 7, 7, 8, 8, 6, 3, 4, 4, 4, 5, 4, 3, 2, 0, 2, 2, 2, 3, 3,
+ 2, 6, 5, 2, 2, 2, 3, 2, 7, 8, 10, 11, 11, 11, 10, 7, 8, 9, 10,
+ 10, 10, 9, 6, 7, 9, 9, 9, 8, 7, 6, 6, 6, 7, 6, 5, 2, 3, 3,
+ 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 7, 4, 2, 1, 2, 2,
+ 6, 8, 9, 9, 10, 11, 10, 6, 7, 8, 9, 9, 9, 9, 5, 6, 7, 7, 8,
+ 8, 7, 5, 5, 5, 5, 5, 5, 3, 2, 1, 1, 2, 2, 2, 3, 5, 4, 3,
+ 2, 0, 2, 3, 11, 10, 6, 4, 2, 2, 3, 6, 7, 8, 9, 11, 12, 11, 5,
+ 7, 8, 10, 10, 10, 10, 4, 7, 8, 8, 8, 9, 8, 5, 6, 5, 6, 6, 6,
+ 6, 3, 6, 6, 6, 7, 7, 7, 5, 11, 10, 9, 8, 8, 8, 11, 16, 13, 12,
+ 10, 9, 10, 9, 10, 11, 11, 12, 12, 12, 11, 12, 12, 12, 13, 12, 10, 11, 13,
+ 14, 13, 13, 12, 8, 11, 15, 14, 14, 14, 13, 9, 11, 16, 16, 15, 15, 15, 10,
+ 12, 18, 21, 20, 18, 16, 11, 17, 21, 24, 21, 20, 18, 13, 18, 20, 20, 21, 18,
+ 14, 11, 19, 23, 23, 22, 19, 14, 10, 19, 24, 25, 24, 20, 15, 10, 18, 24, 25,
+ 26, 22, 16, 11, 18, 24, 27, 27, 23, 17, 13, 19, 24, 30, 29, 24, 20, 15, 21,
+ 27, 31, 31, 27, 21, 16, 27, 33, 33, 26, 21, 16, 12, 27, 33, 35, 27, 22, 17,
+ 13, 27, 33, 36, 29, 23, 18, 13, 28, 33, 37, 30, 25, 19, 15, 26, 33, 36, 33,
+ 26, 22, 16, 27, 32, 35, 34, 28, 23, 19, 28, 31, 36, 36, 31, 25, 20, 8, 9,
+ 10, 11, 11, 11, 10, 7, 8, 10, 11, 11, 10, 8, 7, 8, 9, 10, 9, 8, 6,
+ 6, 6, 7, 7, 7, 5, 2, 4, 4, 4, 4, 4, 2, 2, 2, 0, 2, 2, 2,
+ 2, 2, 6, 4, 2, 2, 2, 2, 2, 7, 8, 9, 11, 11, 11, 11, 6, 8, 9,
+ 10, 11, 9, 9, 6, 7, 8, 9, 8, 7, 6, 6, 5, 6, 6, 6, 5, 2, 3,
+ 3, 3, 3, 2, 2, 2, 3, 2, 2, 2, 1, 2, 2, 9, 7, 3, 2, 1, 2,
+ 2, 6, 7, 8, 9, 11, 11, 12, 6, 6, 8, 9, 10, 10, 10, 6, 6, 8, 8,
+ 8, 8, 8, 5, 5, 6, 6, 5, 5, 5, 3, 2, 3, 3, 3, 3, 4, 5, 5,
+ 5, 4, 3, 4, 5, 11, 9, 8, 6, 5, 5, 5, 6, 9, 9, 11, 12, 14, 13,
+ 5, 8, 10, 11, 12, 12, 11, 5, 8, 9, 9, 9, 10, 9, 8, 7, 8, 8, 8,
+ 9, 8, 6, 8, 9, 9, 10, 10, 10, 6, 13, 13, 12, 11, 11, 11, 11, 18, 16,
+ 14, 12, 12, 13, 12, 13, 14, 14, 15, 15, 13, 12, 15, 15, 16, 16, 14, 11, 13,
+ 16, 17, 16, 16, 15, 11, 13, 17, 17, 16, 17, 16, 12, 12, 19, 19, 18, 18, 17,
+ 13, 14, 22, 23, 21, 20, 18, 14, 19, 24, 26, 24, 22, 20, 16, 22, 24, 24, 24,
+ 21, 17, 13, 22, 26, 25, 25, 22, 17, 12, 22, 28, 28, 27, 22, 17, 13, 22, 27,
+ 29, 27, 24, 19, 14, 22, 27, 30, 30, 25, 20, 16, 21, 28, 34, 33, 27, 21, 17,
+ 25, 30, 34, 35, 30, 24, 18, 31, 37, 36, 29, 23, 19, 15, 30, 36, 39, 32, 25,
+ 20, 16, 30, 37, 40, 32, 26, 21, 17, 31, 35, 41, 33, 27, 22, 17, 29, 35, 41,
+ 36, 29, 23, 19, 29, 35, 41, 38, 32, 26, 20, 30, 37, 40, 42, 34, 27, 22, 8,
+ 9, 10, 12, 11, 11, 10, 7, 8, 10, 11, 11, 9, 8, 7, 8, 9, 9, 9, 7,
+ 6, 6, 6, 7, 7, 7, 5, 2, 4, 4, 3, 4, 3, 2, 2, 2, 2, 0, 2,
+ 2, 2, 2, 5, 3, 2, 1, 2, 2, 2, 7, 8, 9, 10, 11, 11, 12, 7, 8,
+ 9, 10, 11, 10, 10, 6, 7, 8, 8, 8, 7, 7, 6, 6, 6, 6, 5, 4, 4,
+ 3, 3, 3, 3, 2, 2, 4, 3, 2, 2, 1, 2, 2, 3, 7, 5, 3, 1, 1,
+ 2, 3, 6, 7, 9, 11, 12, 14, 14, 6, 7, 10, 11, 12, 12, 12, 6, 7, 9,
+ 10, 9, 9, 9, 5, 5, 7, 7, 7, 7, 6, 6, 2, 4, 5, 5, 5, 6, 8,
+ 4, 7, 7, 6, 6, 6, 8, 7, 8, 9, 7, 7, 8, 6, 10, 11, 12, 14, 15,
+ 15, 5, 9, 11, 12, 13, 13, 13, 8, 9, 11, 11, 12, 11, 11, 10, 8, 11, 11,
+ 11, 12, 10, 9, 9, 11, 12, 12, 12, 13, 8, 13, 15, 15, 14, 14, 14, 8, 16,
+ 17, 16, 15, 15, 15, 13, 16, 17, 17, 18, 18, 15, 15, 17, 18, 18, 19, 17, 13,
+ 15, 18, 20, 19, 19, 18, 14, 15, 20, 20, 20, 20, 19, 14, 13, 22, 22, 21, 21,
+ 20, 15, 16, 24, 26, 24, 23, 21, 17, 18, 25, 28, 27, 25, 23, 19, 24, 27, 27,
+ 27, 24, 20, 16, 26, 29, 29, 29, 25, 21, 16, 25, 31, 31, 30, 25, 21, 17, 24,
+ 31, 32, 30, 26, 22, 17, 24, 30, 34, 32, 28, 23, 18, 24, 31, 36, 36, 30, 24,
+ 20, 27, 33, 38, 39, 33, 27, 21, 35, 39, 39, 32, 27, 22, 18, 34, 41, 42, 34,
+ 28, 23, 19, 35, 41, 44, 36, 29, 24, 19, 34, 40, 44, 37, 30, 25, 21, 33, 39,
+ 44, 39, 32, 26, 22, 32, 39, 43, 43, 34, 29, 23, 34, 40, 45, 45, 39, 30, 25,
+ 7, 8, 9, 10, 10, 10, 11, 7, 7, 8, 10, 10, 9, 8, 6, 7, 8, 9, 8,
+ 7, 6, 6, 6, 6, 6, 6, 4, 3, 3, 3, 3, 3, 2, 1, 2, 2, 2, 2,
+ 0, 2, 2, 2, 4, 3, 2, 1, 1, 1, 2, 7, 7, 8, 9, 10, 12, 13, 6,
+ 7, 8, 9, 10, 10, 11, 6, 7, 7, 7, 8, 8, 8, 5, 5, 5, 5, 5, 5,
+ 5, 3, 2, 2, 2, 2, 3, 5, 3, 2, 2, 2, 2, 3, 4, 6, 4, 2, 2,
+ 3, 3, 4, 6, 7, 10, 11, 13, 14, 15, 5, 6, 9, 12, 12, 12, 12, 4, 6,
+ 9, 10, 10, 10, 10, 8, 4, 6, 8, 8, 8, 8, 10, 2, 4, 7, 8, 8, 8,
+ 11, 3, 5, 8, 9, 9, 8, 10, 7, 6, 10, 10, 10, 11, 5, 10, 13, 13, 15,
+ 15, 17, 8, 9, 12, 14, 14, 14, 15, 11, 9, 13, 13, 14, 14, 14, 13, 8, 13,
+ 14, 14, 15, 13, 13, 10, 14, 15, 16, 16, 16, 11, 12, 16, 16, 17, 16, 17, 10,
+ 15, 17, 18, 18, 18, 18, 15, 18, 20, 20, 22, 21, 17, 17, 20, 22, 22, 22, 21,
+ 15, 17, 20, 23, 22, 22, 22, 17, 17, 22, 23, 23, 23, 22, 17, 16, 24, 25, 25,
+ 25, 23, 19, 16, 25, 28, 27, 26, 24, 20, 17, 26, 30, 28, 29, 26, 22, 27, 31,
+ 31, 31, 27, 24, 19, 27, 32, 33, 33, 28, 24, 19, 27, 35, 35, 34, 29, 24, 20,
+ 27, 33, 35, 34, 29, 25, 21, 26, 33, 38, 36, 31, 26, 22, 27, 32, 39, 39, 33,
+ 27, 23, 28, 34, 39, 40, 35, 30, 25, 38, 44, 42, 36, 31, 26, 21, 38, 45, 45,
+ 38, 31, 27, 22, 37, 44, 47, 39, 32, 27, 22, 38, 43, 48, 40, 34, 28, 23, 37,
+ 42, 48, 43, 35, 30, 25, 36, 43, 47, 45, 37, 32, 27, 37, 42, 47, 47, 41, 33,
+ 28, 6, 6, 7, 8, 8, 9, 10, 5, 6, 7, 8, 8, 8, 8, 5, 6, 6, 6,
+ 6, 6, 6, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2, 1, 1, 3, 3, 2,
+ 2, 2, 0, 1, 3, 5, 4, 2, 2, 1, 1, 3, 5, 6, 7, 8, 10, 12, 13,
+ 5, 5, 6, 8, 10, 10, 10, 4, 5, 6, 6, 7, 8, 8, 4, 3, 3, 4, 5,
+ 6, 7, 5, 3, 2, 2, 4, 4, 7, 6, 3, 3, 2, 5, 5, 7, 8, 5, 3,
+ 3, 6, 6, 7, 4, 5, 8, 11, 13, 14, 15, 4, 4, 8, 11, 12, 12, 14, 8,
+ 4, 8, 10, 10, 11, 12, 11, 3, 5, 9, 10, 11, 11, 12, 3, 5, 10, 11, 12,
+ 11, 13, 4, 7, 11, 13, 13, 11, 12, 8, 7, 12, 13, 14, 14, 8, 9, 14, 16,
+ 17, 17, 19, 11, 9, 14, 17, 18, 17, 18, 14, 9, 15, 17, 18, 18, 17, 16, 10,
+ 14, 17, 18, 19, 16, 16, 12, 16, 18, 20, 19, 19, 14, 14, 18, 20, 21, 21, 21,
+ 12, 16, 20, 22, 22, 22, 22, 17, 21, 23, 24, 25, 25, 19, 20, 21, 24, 25, 26,
+ 25, 18, 20, 22, 26, 26, 27, 25, 21, 20, 25, 26, 27, 27, 26, 21, 18, 26, 28,
+ 29, 29, 27, 22, 18, 27, 31, 32, 30, 29, 24, 19, 29, 33, 33, 33, 30, 25, 30,
+ 33, 34, 34, 31, 27, 23, 30, 37, 36, 35, 32, 27, 23, 30, 38, 38, 37, 33, 28,
+ 23, 30, 38, 39, 37, 33, 29, 24, 28, 37, 42, 39, 35, 30, 25, 30, 37, 44, 42,
+ 36, 32, 27, 31, 38, 43, 44, 38, 33, 29, 42, 48, 45, 40, 34, 30, 26, 41, 48,
+ 48, 41, 35, 30, 26, 42, 47, 50, 42, 36, 31, 27, 41, 47, 51, 44, 37, 32, 27,
+ 40, 45, 51, 46, 39, 34, 29, 40, 46, 50, 49, 42, 36, 30, 40, 46, 51, 51, 44,
+ 37, 32, 4, 5, 5, 6, 6, 8, 8, 4, 5, 5, 6, 6, 6, 6, 3, 4, 5,
+ 4, 4, 4, 4, 3, 3, 3, 2, 3, 2, 2, 2, 2, 1, 1, 2, 0, 2, 3,
+ 2, 2, 1, 2, 0, 2, 5, 3, 2, 1, 1, 0, 2, 4, 4, 5, 6, 8, 9,
+ 10, 3, 4, 5, 6, 8, 8, 8, 3, 4, 4, 4, 6, 6, 7, 4, 2, 2, 2,
+ 4, 4, 5, 6, 2, 2, 2, 4, 3, 5, 7, 3, 2, 2, 5, 5, 5, 9, 5,
+ 3, 2, 6, 6, 5, 3, 4, 7, 9, 10, 12, 14, 5, 3, 7, 10, 10, 10, 12,
+ 9, 3, 6, 8, 9, 9, 10, 13, 2, 5, 8, 9, 10, 9, 13, 3, 5, 9, 11,
+ 11, 9, 17, 5, 6, 10, 11, 12, 9, 14, 9, 7, 11, 13, 13, 13, 9, 8, 13,
+ 15, 15, 15, 17, 15, 8, 13, 16, 17, 17, 16, 16, 8, 14, 15, 16, 17, 16, 19,
+ 9, 13, 17, 17, 17, 14, 18, 12, 15, 18, 18, 19, 17, 15, 13, 17, 19, 20, 20,
+ 20, 15, 16, 19, 21, 21, 21, 22, 17, 19, 22, 23, 25, 23, 18, 20, 20, 23, 24,
+ 26, 23, 17, 20, 21, 25, 25, 26, 24, 19, 20, 23, 25, 25, 26, 24, 20, 19, 25,
+ 27, 28, 28, 26, 22, 18, 26, 30, 30, 29, 27, 23, 19, 27, 32, 32, 31, 28, 25,
+ 28, 32, 32, 33, 30, 26, 21, 29, 35, 35, 34, 31, 26, 22, 29, 37, 36, 36, 31,
+ 26, 22, 28, 36, 39, 37, 33, 28, 23, 29, 35, 40, 40, 33, 29, 24, 28, 36, 42,
+ 41, 36, 30, 26, 30, 37, 43, 44, 38, 33, 28, 40, 46, 44, 38, 32, 29, 25, 40,
+ 46, 47, 39, 33, 29, 24, 42, 45, 49, 41, 35, 30, 25, 39, 46, 50, 42, 36, 30,
+ 26, 39, 44, 49, 46, 39, 32, 28, 39, 45, 50, 48, 40, 34, 29, 39, 45, 50, 50,
+ 43, 37, 31, 4, 5, 6, 7, 7, 6, 6, 4, 4, 5, 7, 7, 5, 4, 3, 4,
+ 5, 5, 6, 4, 2, 3, 3, 3, 3, 5, 3, 0, 2, 1, 1, 2, 4, 2, 0,
+ 2, 2, 2, 2, 3, 2, 0, 4, 3, 2, 2, 3, 2, 0, 4, 4, 5, 6, 7,
+ 8, 8, 3, 4, 4, 5, 6, 6, 7, 3, 4, 4, 4, 4, 4, 4, 2, 2, 2,
+ 2, 2, 2, 3, 2, 2, 1, 1, 1, 1, 3, 4, 2, 2, 2, 2, 2, 3, 7,
+ 5, 2, 2, 4, 3, 3, 3, 4, 6, 8, 9, 10, 11, 2, 3, 7, 8, 8, 8,
+ 9, 5, 3, 6, 7, 6, 6, 8, 9, 2, 4, 7, 6, 6, 6, 11, 2, 4, 7,
+ 8, 8, 6, 12, 4, 5, 8, 9, 9, 6, 10, 8, 6, 9, 10, 10, 10, 5, 7,
+ 11, 13, 13, 13, 14, 10, 7, 11, 13, 14, 14, 13, 13, 8, 12, 13, 14, 13, 12,
+ 15, 8, 12, 14, 14, 14, 11, 15, 10, 13, 15, 15, 15, 14, 13, 12, 15, 16, 17,
+ 16, 17, 10, 15, 17, 18, 18, 18, 18, 15, 17, 19, 20, 21, 20, 15, 18, 19, 20,
+ 21, 22, 21, 13, 18, 20, 22, 22, 22, 20, 16, 18, 22, 22, 22, 23, 22, 17, 16,
+ 23, 24, 24, 25, 23, 18, 16, 24, 27, 27, 26, 23, 20, 17, 25, 30, 28, 28, 25,
+ 21, 27, 30, 29, 30, 27, 23, 19, 27, 32, 32, 31, 27, 23, 18, 26, 34, 34, 32,
+ 27, 23, 19, 27, 33, 34, 33, 29, 24, 19, 25, 33, 37, 35, 31, 26, 21, 27, 33,
+ 40, 38, 32, 26, 22, 27, 34, 40, 40, 34, 29, 24, 37, 43, 40, 34, 29, 26, 21,
+ 37, 42, 44, 36, 31, 25, 22, 36, 41, 46, 37, 30, 26, 22, 37, 42, 47, 39, 32,
+ 28, 23, 36, 41, 46, 42, 36, 29, 24, 36, 40, 45, 44, 36, 31, 25, 35, 41, 45,
+ 46, 40, 33, 28, 14, 15, 16, 18, 18, 17, 16, 13, 15, 16, 18, 17, 16, 14, 13,
+ 14, 16, 16, 16, 13, 11, 12, 13, 13, 13, 13, 9, 5, 10, 10, 10, 9, 6, 5,
+ 5, 6, 6, 5, 4, 5, 5, 4, 0, 2, 2, 3, 4, 5, 4, 13, 14, 16, 17,
+ 17, 17, 16, 12, 14, 15, 16, 17, 16, 14, 12, 13, 15, 15, 14, 13, 11, 11, 12,
+ 12, 12, 11, 9, 4, 9, 8, 8, 8, 5, 4, 4, 5, 4, 4, 3, 3, 4, 4,
+ 3, 2, 2, 2, 3, 4, 4, 12, 13, 15, 16, 17, 17, 17, 11, 13, 14, 15, 16,
+ 15, 15, 12, 12, 13, 13, 13, 12, 11, 11, 11, 10, 10, 10, 8, 4, 7, 7, 7,
+ 7, 3, 4, 4, 2, 2, 2, 2, 3, 3, 4, 5, 4, 1, 2, 3, 4, 5, 11,
+ 13, 14, 16, 17, 18, 18, 11, 13, 14, 16, 17, 16, 16, 10, 13, 14, 14, 14, 14,
+ 12, 7, 12, 11, 11, 11, 9, 9, 4, 8, 7, 7, 5, 5, 6, 3, 6, 6, 6,
+ 7, 6, 7, 5, 11, 8, 8, 8, 8, 8, 12, 12, 14, 15, 17, 18, 19, 10, 12,
+ 14, 15, 16, 16, 16, 10, 12, 13, 13, 13, 13, 12, 10, 12, 12, 12, 12, 10, 7,
+ 8, 13, 13, 13, 14, 13, 8, 8, 14, 16, 16, 16, 15, 10, 11, 17, 19, 18, 18,
+ 17, 11, 16, 18, 18, 19, 16, 17, 18, 16, 20, 20, 21, 17, 15, 15, 16, 22, 22,
+ 21, 17, 12, 11, 16, 21, 23, 23, 19, 14, 10, 15, 20, 25, 24, 20, 17, 11, 15,
+ 22, 27, 25, 23, 18, 13, 17, 23, 27, 28, 24, 20, 15, 25, 31, 30, 24, 18, 16,
+ 17, 25, 31, 32, 26, 20, 15, 14, 25, 31, 33, 27, 21, 16, 11, 25, 30, 35, 28,
+ 23, 18, 13, 24, 30, 32, 28, 24, 21, 15, 24, 28, 31, 30, 25, 21, 17, 24, 28,
+ 31, 31, 27, 23, 19, 12, 13, 15, 16, 16, 16, 15, 12, 13, 14, 15, 15, 14, 12,
+ 11, 12, 14, 14, 13, 11, 9, 11, 11, 11, 11, 10, 7, 3, 8, 8, 8, 7, 5,
+ 3, 3, 5, 4, 3, 3, 3, 3, 3, 2, 0, 2, 2, 3, 3, 3, 11, 12, 14,
+ 15, 16, 16, 15, 11, 12, 13, 15, 15, 14, 12, 11, 12, 13, 13, 12, 11, 9, 11,
+ 10, 10, 10, 9, 7, 3, 8, 7, 7, 7, 3, 3, 3, 4, 3, 3, 2, 2, 3,
+ 3, 3, 2, 2, 2, 2, 2, 3, 10, 12, 13, 14, 15, 16, 17, 10, 11, 13, 14,
+ 14, 14, 14, 11, 11, 12, 12, 12, 11, 10, 10, 10, 10, 10, 9, 7, 5, 5, 6,
+ 7, 6, 4, 4, 5, 3, 2, 3, 3, 3, 4, 5, 3, 2, 3, 3, 3, 4, 4,
+ 11, 13, 14, 15, 17, 18, 18, 9, 13, 14, 15, 16, 16, 15, 7, 13, 14, 13, 13,
+ 13, 11, 8, 11, 11, 11, 10, 9, 9, 6, 7, 8, 8, 8, 8, 8, 3, 8, 9,
+ 9, 9, 9, 10, 3, 11, 11, 11, 10, 11, 11, 12, 13, 14, 15, 17, 18, 17, 11,
+ 13, 14, 15, 16, 16, 15, 11, 14, 15, 15, 15, 14, 11, 11, 15, 15, 15, 15, 14,
+ 10, 10, 17, 17, 16, 17, 15, 12, 11, 17, 20, 19, 18, 17, 13, 13, 18, 22, 20,
+ 21, 19, 14, 20, 22, 22, 22, 19, 18, 17, 20, 24, 23, 24, 20, 16, 15, 20, 26,
+ 26, 25, 20, 16, 12, 20, 26, 27, 25, 22, 17, 13, 19, 24, 28, 28, 24, 19, 14,
+ 19, 25, 31, 30, 25, 20, 16, 20, 25, 31, 33, 27, 22, 17, 28, 34, 33, 27, 22,
+ 18, 17, 29, 36, 36, 29, 23, 18, 14, 28, 35, 39, 30, 25, 19, 14, 29, 34, 39,
+ 31, 25, 20, 16, 27, 33, 38, 34, 28, 22, 18, 28, 33, 37, 36, 30, 25, 19, 27,
+ 34, 38, 39, 33, 26, 21, 9, 11, 12, 13, 13, 13, 12, 9, 10, 11, 13, 12, 11,
+ 9, 9, 9, 11, 11, 10, 9, 7, 8, 8, 8, 8, 8, 5, 2, 6, 6, 5, 5,
+ 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 9, 9,
+ 11, 12, 12, 13, 13, 9, 9, 10, 12, 12, 11, 11, 8, 9, 10, 10, 9, 9, 8,
+ 8, 7, 7, 7, 7, 5, 3, 5, 5, 4, 4, 2, 2, 3, 2, 2, 1, 1, 2,
+ 2, 3, 4, 2, 2, 1, 2, 2, 3, 8, 9, 11, 12, 13, 15, 15, 8, 8, 11,
+ 12, 13, 13, 13, 8, 8, 11, 11, 10, 10, 10, 6, 7, 8, 8, 8, 7, 6, 5,
+ 3, 5, 6, 4, 5, 6, 6, 2, 4, 5, 5, 5, 6, 5, 4, 5, 6, 6, 6,
+ 7, 8, 11, 12, 13, 15, 16, 16, 5, 11, 12, 14, 14, 14, 14, 7, 11, 12, 12,
+ 12, 12, 11, 10, 9, 10, 10, 10, 10, 10, 8, 7, 10, 11, 11, 12, 12, 6, 10,
+ 13, 12, 13, 13, 13, 5, 13, 14, 14, 14, 14, 15, 12, 15, 16, 16, 17, 18, 16,
+ 14, 16, 17, 18, 18, 17, 14, 13, 18, 19, 19, 19, 17, 13, 13, 19, 18, 19, 18,
+ 18, 14, 13, 20, 20, 20, 20, 19, 15, 13, 21, 22, 22, 22, 20, 16, 16, 22, 26,
+ 24, 24, 22, 18, 23, 26, 25, 25, 23, 19, 16, 24, 28, 28, 28, 24, 19, 15, 24,
+ 30, 30, 29, 24, 20, 15, 23, 29, 30, 29, 25, 21, 16, 23, 29, 33, 31, 27, 22,
+ 17, 24, 29, 35, 34, 29, 23, 19, 23, 29, 34, 36, 32, 26, 21, 33, 39, 38, 31,
+ 25, 21, 18, 33, 40, 41, 33, 27, 22, 18, 33, 39, 43, 35, 29, 22, 18, 33, 39,
+ 44, 36, 29, 24, 19, 32, 37, 43, 38, 30, 25, 20, 31, 36, 43, 41, 33, 27, 22,
+ 31, 37, 43, 42, 36, 30, 24, 7, 8, 10, 11, 11, 11, 11, 7, 8, 9, 11, 10,
+ 9, 8, 7, 8, 9, 9, 8, 7, 6, 6, 6, 6, 6, 6, 4, 2, 4, 3, 3,
+ 3, 2, 2, 2, 2, 2, 1, 1, 2, 1, 2, 3, 2, 1, 0, 2, 1, 2, 7,
+ 8, 8, 10, 11, 12, 13, 7, 7, 8, 9, 10, 10, 10, 6, 7, 8, 8, 8, 8,
+ 8, 6, 6, 5, 5, 5, 5, 4, 3, 3, 2, 3, 2, 3, 4, 3, 2, 2, 1,
+ 2, 2, 4, 6, 3, 2, 2, 2, 3, 4, 6, 7, 10, 11, 13, 14, 14, 6, 7,
+ 10, 11, 12, 12, 12, 4, 6, 9, 10, 10, 10, 9, 6, 5, 7, 8, 8, 7, 8,
+ 8, 2, 4, 7, 7, 7, 8, 9, 3, 5, 8, 8, 8, 8, 8, 6, 6, 9, 9,
+ 9, 10, 5, 10, 12, 13, 14, 15, 16, 7, 9, 12, 13, 14, 14, 14, 10, 9, 12,
+ 13, 13, 13, 13, 13, 8, 12, 13, 13, 14, 12, 12, 9, 13, 14, 15, 15, 15, 9,
+ 12, 15, 16, 16, 16, 16, 8, 15, 17, 18, 17, 16, 18, 14, 18, 19, 19, 21, 20,
+ 16, 16, 19, 20, 21, 21, 20, 14, 16, 20, 22, 22, 21, 20, 16, 16, 22, 23, 22,
+ 23, 21, 17, 15, 23, 24, 24, 23, 22, 18, 15, 23, 27, 26, 25, 24, 19, 17, 24,
+ 29, 27, 27, 26, 20, 26, 30, 29, 30, 27, 22, 18, 26, 31, 31, 31, 28, 22, 18,
+ 27, 34, 33, 33, 28, 23, 19, 26, 32, 35, 34, 29, 24, 19, 27, 33, 36, 35, 30,
+ 25, 21, 25, 33, 40, 38, 33, 26, 22, 27, 33, 40, 39, 34, 29, 24, 37, 43, 41,
+ 34, 29, 25, 21, 37, 43, 43, 37, 30, 26, 21, 36, 43, 46, 38, 32, 26, 21, 38,
+ 41, 48, 40, 33, 27, 23, 35, 41, 47, 42, 35, 29, 24, 35, 42, 47, 44, 37, 31,
+ 26, 35, 41, 47, 46, 39, 32, 27, 6, 7, 8, 9, 9, 10, 11, 6, 7, 7, 9,
+ 9, 8, 8, 5, 6, 7, 7, 7, 6, 6, 5, 5, 5, 5, 5, 3, 3, 3, 2,
+ 2, 2, 2, 1, 3, 3, 2, 2, 1, 1, 1, 3, 4, 3, 2, 2, 0, 1, 3,
+ 5, 6, 7, 8, 10, 12, 13, 5, 6, 7, 8, 10, 10, 10, 4, 6, 6, 7, 8,
+ 8, 8, 4, 4, 4, 4, 5, 6, 6, 4, 2, 2, 2, 3, 3, 6, 5, 3, 2,
+ 2, 4, 4, 6, 7, 4, 3, 2, 5, 5, 6, 5, 6, 9, 11, 12, 14, 14, 4,
+ 5, 9, 11, 12, 12, 13, 7, 5, 8, 10, 10, 10, 11, 10, 3, 6, 9, 9, 9,
+ 10, 12, 3, 5, 9, 10, 10, 10, 13, 4, 6, 10, 11, 11, 10, 11, 8, 7, 11,
+ 12, 13, 13, 6, 9, 13, 15, 16, 16, 18, 10, 9, 13, 15, 17, 17, 17, 14, 9,
+ 14, 15, 16, 17, 16, 16, 9, 14, 16, 17, 17, 15, 15, 11, 15, 17, 18, 18, 18,
+ 13, 13, 17, 19, 19, 19, 19, 12, 16, 18, 20, 20, 21, 21, 16, 19, 22, 23, 24,
+ 23, 18, 19, 20, 23, 24, 25, 23, 17, 19, 22, 25, 25, 25, 23, 19, 18, 24, 25,
+ 26, 26, 25, 20, 17, 26, 27, 27, 27, 26, 21, 17, 25, 29, 30, 29, 27, 23, 19,
+ 28, 33, 31, 30, 29, 24, 29, 32, 33, 32, 30, 25, 21, 29, 35, 35, 34, 31, 26,
+ 22, 28, 37, 36, 35, 30, 26, 22, 29, 36, 38, 37, 32, 27, 22, 28, 35, 40, 38,
+ 34, 29, 23, 29, 36, 42, 41, 36, 31, 26, 30, 36, 43, 44, 38, 32, 27, 40, 46,
+ 44, 38, 33, 28, 24, 41, 46, 47, 40, 34, 29, 24, 40, 46, 50, 41, 35, 29, 25,
+ 39, 46, 49, 42, 36, 30, 26, 39, 45, 49, 46, 38, 33, 27, 38, 45, 49, 48, 40,
+ 34, 29, 39, 45, 49, 48, 43, 36, 30, 4, 5, 5, 6, 6, 8, 8, 4, 5, 5,
+ 6, 6, 6, 6, 3, 4, 5, 4, 4, 4, 4, 3, 3, 3, 2, 3, 2, 2, 2,
+ 2, 1, 1, 2, 0, 2, 3, 2, 2, 2, 1, 0, 2, 5, 3, 2, 1, 1, 0,
+ 2, 4, 4, 5, 6, 8, 10, 10, 3, 4, 5, 6, 8, 8, 8, 3, 4, 4, 4,
+ 5, 6, 7, 4, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2, 4, 3, 5, 7, 3,
+ 2, 2, 5, 4, 5, 10, 5, 3, 2, 6, 6, 5, 3, 4, 7, 9, 10, 12, 14,
+ 5, 3, 7, 10, 10, 10, 12, 9, 3, 6, 8, 9, 9, 10, 13, 2, 5, 8, 9,
+ 10, 9, 14, 3, 5, 9, 11, 10, 9, 15, 5, 6, 10, 12, 11, 9, 14, 8, 7,
+ 11, 13, 13, 13, 10, 8, 13, 15, 16, 15, 18, 14, 8, 13, 15, 17, 17, 16, 16,
+ 9, 14, 16, 16, 17, 16, 18, 9, 14, 16, 17, 17, 14, 18, 12, 15, 18, 18, 19,
+ 17, 16, 13, 17, 19, 20, 20, 19, 14, 16, 19, 20, 21, 21, 21, 17, 19, 22, 23,
+ 24, 23, 18, 19, 20, 23, 24, 26, 23, 17, 20, 21, 25, 25, 25, 24, 19, 20, 23,
+ 25, 26, 26, 24, 20, 18, 26, 28, 28, 28, 26, 21, 18, 26, 31, 31, 29, 27, 23,
+ 19, 27, 33, 32, 32, 28, 24, 28, 33, 33, 32, 30, 26, 22, 29, 34, 35, 35, 31,
+ 27, 22, 28, 38, 37, 35, 31, 26, 23, 29, 37, 37, 37, 32, 27, 23, 28, 35, 40,
+ 39, 34, 29, 24, 29, 35, 43, 41, 36, 31, 27, 29, 38, 43, 44, 38, 32, 27, 40,
+ 46, 44, 37, 32, 28, 24, 39, 45, 46, 40, 33, 29, 24, 41, 45, 49, 41, 34, 30,
+ 25, 40, 45, 50, 42, 35, 31, 26, 39, 44, 49, 45, 38, 33, 28, 39, 46, 50, 47,
+ 41, 35, 29, 40, 44, 50, 50, 43, 36, 31, 4, 5, 6, 8, 8, 6, 6, 4, 4,
+ 5, 7, 7, 5, 4, 3, 4, 5, 5, 6, 4, 2, 3, 3, 3, 4, 5, 3, 0,
+ 2, 2, 1, 2, 4, 2, 0, 2, 2, 2, 2, 3, 2, 0, 4, 3, 2, 2, 3,
+ 2, 0, 4, 4, 5, 6, 6, 8, 8, 3, 4, 4, 5, 6, 6, 7, 3, 4, 4,
+ 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 3, 2, 2, 1, 1, 1, 1, 3, 4,
+ 3, 2, 2, 2, 2, 3, 7, 5, 2, 2, 3, 3, 3, 3, 4, 6, 8, 9, 10,
+ 11, 2, 3, 6, 8, 8, 8, 9, 5, 3, 6, 7, 6, 6, 8, 9, 2, 4, 7,
+ 6, 6, 6, 11, 2, 4, 7, 8, 8, 6, 12, 4, 5, 8, 9, 9, 6, 10, 8,
+ 6, 9, 10, 10, 10, 5, 7, 11, 12, 13, 13, 14, 10, 7, 12, 13, 13, 14, 13,
+ 14, 8, 12, 13, 13, 13, 12, 14, 8, 12, 14, 14, 14, 11, 15, 10, 13, 15, 15,
+ 15, 14, 14, 12, 15, 16, 17, 16, 16, 10, 15, 17, 18, 18, 18, 18, 15, 18, 19,
+ 20, 21, 20, 14, 18, 18, 20, 21, 22, 20, 13, 18, 20, 22, 22, 21, 20, 16, 18,
+ 22, 23, 22, 23, 22, 17, 16, 24, 24, 24, 24, 22, 18, 16, 24, 27, 27, 26, 24,
+ 20, 17, 25, 30, 29, 28, 26, 21, 26, 29, 30, 29, 26, 22, 18, 26, 32, 30, 30,
+ 27, 23, 19, 27, 33, 33, 32, 27, 23, 19, 26, 33, 34, 32, 28, 24, 20, 27, 34,
+ 36, 35, 31, 25, 21, 27, 34, 40, 38, 33, 27, 23, 28, 34, 39, 39, 35, 29, 24,
+ 36, 42, 40, 34, 29, 25, 21, 37, 43, 43, 36, 30, 25, 21, 36, 42, 45, 37, 32,
+ 27, 21, 37, 41, 46, 38, 32, 27, 23, 35, 40, 45, 41, 35, 28, 24, 36, 42, 46,
+ 44, 37, 32, 25, 37, 41, 46, 46, 38, 33, 27, 2, 2, 3, 3, 3, 2, 2, 1,
+ 2, 3, 3, 3, 2, 2, 0, 2, 2, 3, 3, 2, 1, 1, 2, 2, 2, 2, 2,
+ 4, 3, 3, 3, 4, 4, 3, 4, 7, 7, 7, 6, 5, 4, 4, 13, 11, 9, 7,
+ 5, 4, 4, 0, 2, 2, 3, 2, 2, 1, 1, 2, 2, 3, 2, 2, 1, 2, 1,
+ 2, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 3, 4, 4, 4, 5, 4, 4, 4,
+ 9, 9, 9, 7, 5, 4, 4, 16, 14, 10, 8, 6, 4, 4, 2, 1, 2, 1, 2,
+ 1, 4, 2, 1, 2, 1, 1, 1, 4, 2, 1, 0, 0, 1, 0, 5, 3, 2, 3,
+ 3, 3, 3, 6, 7, 6, 6, 6, 7, 6, 7, 12, 11, 11, 10, 9, 7, 7, 19,
+ 17, 14, 12, 10, 9, 8, 2, 4, 5, 5, 5, 5, 8, 3, 5, 5, 6, 6, 6,
+ 8, 7, 6, 6, 7, 7, 8, 8, 10, 9, 10, 10, 10, 11, 10, 10, 14, 14, 14,
+ 14, 14, 12, 13, 20, 19, 17, 16, 15, 14, 19, 26, 22, 20, 18, 16, 15, 12, 13,
+ 12, 13, 12, 12, 8, 13, 14, 14, 14, 14, 13, 8, 14, 16, 16, 15, 16, 15, 11,
+ 13, 20, 19, 19, 19, 18, 14, 17, 25, 24, 22, 23, 21, 16, 21, 27, 30, 26, 25,
+ 23, 17, 25, 31, 32, 29, 27, 25, 20, 21, 23, 22, 21, 18, 15, 11, 21, 26, 24,
+ 23, 20, 16, 11, 22, 27, 26, 25, 22, 18, 14, 22, 28, 30, 29, 25, 22, 18, 25,
+ 31, 35, 34, 30, 25, 20, 27, 33, 40, 38, 33, 26, 22, 31, 37, 41, 41, 35, 28,
+ 23, 29, 35, 34, 27, 22, 17, 13, 29, 35, 36, 29, 23, 19, 15, 30, 35, 38, 31,
+ 26, 21, 17, 30, 36, 41, 35, 30, 25, 21, 31, 37, 43, 40, 35, 28, 24, 34, 40,
+ 46, 45, 38, 31, 25, 37, 42, 46, 46, 39, 33, 27, 1, 2, 2, 2, 2, 2, 1,
+ 2, 1, 2, 3, 2, 2, 1, 2, 0, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2,
+ 3, 4, 4, 4, 4, 5, 5, 4, 4, 8, 8, 8, 7, 6, 4, 4, 14, 13, 9,
+ 8, 6, 4, 4, 2, 0, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 2,
+ 2, 1, 2, 2, 1, 4, 2, 2, 2, 3, 2, 3, 5, 6, 5, 5, 6, 5, 4,
+ 5, 11, 10, 10, 8, 6, 5, 5, 18, 15, 11, 9, 7, 5, 5, 2, 2, 1, 1,
+ 1, 3, 6, 2, 2, 2, 2, 2, 4, 6, 3, 2, 3, 3, 4, 4, 7, 7, 3,
+ 6, 6, 7, 7, 9, 11, 7, 10, 10, 11, 9, 9, 15, 12, 14, 13, 12, 11, 9,
+ 20, 18, 17, 16, 14, 12, 12, 4, 6, 7, 8, 8, 7, 10, 7, 7, 8, 8, 9,
+ 9, 10, 11, 8, 10, 10, 11, 11, 11, 14, 10, 13, 14, 14, 14, 13, 14, 16, 18,
+ 18, 18, 17, 15, 15, 22, 23, 21, 21, 19, 18, 20, 28, 26, 24, 22, 20, 19, 15,
+ 15, 15, 15, 16, 14, 10, 17, 17, 17, 17, 17, 15, 10, 17, 19, 19, 19, 19, 18,
+ 14, 17, 22, 23, 23, 23, 22, 18, 20, 27, 28, 26, 28, 25, 20, 24, 30, 34, 32,
+ 29, 27, 21, 29, 34, 35, 33, 31, 28, 22, 25, 26, 24, 25, 21, 17, 13, 24, 29,
+ 27, 26, 23, 18, 14, 25, 31, 30, 29, 25, 22, 17, 25, 32, 34, 33, 29, 25, 22,
+ 28, 34, 38, 38, 34, 28, 23, 31, 37, 45, 42, 36, 30, 25, 34, 39, 45, 45, 38,
+ 32, 27, 33, 38, 36, 30, 25, 20, 16, 33, 39, 40, 32, 26, 21, 18, 34, 39, 43,
+ 34, 30, 26, 21, 33, 39, 46, 39, 33, 29, 25, 35, 40, 48, 44, 38, 31, 27, 38,
+ 44, 50, 48, 42, 34, 29, 40, 46, 51, 51, 44, 37, 30, 2, 1, 2, 2, 2, 2,
+ 1, 2, 2, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 3, 2, 3, 2, 3,
+ 3, 4, 5, 5, 5, 6, 5, 6, 5, 5, 10, 9, 9, 8, 7, 5, 5, 16, 13,
+ 11, 8, 7, 5, 5, 2, 2, 0, 2, 1, 2, 4, 3, 2, 1, 2, 1, 2, 5,
+ 3, 2, 1, 1, 1, 3, 6, 4, 3, 3, 3, 3, 4, 7, 8, 7, 7, 7, 7,
+ 5, 7, 12, 11, 11, 10, 9, 7, 7, 20, 16, 13, 11, 10, 9, 7, 3, 2, 4,
+ 4, 4, 5, 7, 5, 2, 4, 4, 5, 5, 8, 8, 3, 5, 7, 7, 6, 9, 11,
+ 4, 8, 10, 10, 10, 11, 15, 9, 11, 14, 14, 13, 11, 18, 14, 16, 17, 16, 15,
+ 11, 21, 19, 19, 19, 18, 16, 15, 8, 7, 10, 10, 10, 8, 11, 12, 8, 11, 11,
+ 12, 12, 12, 15, 9, 12, 14, 14, 15, 14, 17, 12, 17, 18, 18, 18, 15, 18, 17,
+ 20, 21, 22, 22, 19, 18, 24, 26, 25, 23, 23, 21, 22, 30, 28, 28, 25, 24, 23,
+ 17, 18, 19, 18, 19, 17, 12, 20, 19, 20, 19, 20, 19, 12, 20, 20, 22, 23, 23,
+ 22, 17, 20, 24, 26, 27, 27, 26, 22, 23, 29, 31, 31, 31, 29, 24, 25, 33, 36,
+ 35, 33, 30, 25, 30, 38, 40, 38, 36, 31, 27, 28, 29, 28, 27, 24, 21, 15, 27,
+ 32, 31, 30, 26, 22, 18, 27, 33, 33, 32, 30, 25, 22, 28, 35, 39, 37, 33, 29,
+ 25, 31, 38, 43, 42, 38, 32, 27, 35, 42, 48, 46, 40, 34, 28, 38, 44, 49, 49,
+ 43, 36, 30, 37, 42, 39, 34, 27, 23, 19, 37, 42, 43, 36, 30, 25, 21, 37, 42,
+ 46, 39, 33, 29, 25, 37, 44, 50, 42, 38, 32, 28, 39, 45, 52, 49, 42, 36, 31,
+ 43, 48, 54, 54, 46, 39, 33, 44, 50, 55, 55, 48, 41, 33, 2, 2, 1, 2, 2,
+ 1, 3, 3, 2, 2, 2, 1, 1, 4, 3, 2, 2, 2, 3, 3, 5, 3, 3, 4,
+ 3, 4, 5, 6, 6, 6, 7, 7, 7, 6, 6, 11, 11, 11, 10, 8, 6, 6, 17,
+ 15, 12, 10, 8, 6, 6, 3, 2, 2, 0, 1, 4, 6, 3, 2, 2, 1, 1, 4,
+ 6, 5, 3, 2, 3, 3, 5, 8, 8, 4, 4, 5, 7, 6, 9, 11, 7, 8, 9,
+ 10, 7, 9, 15, 12, 12, 12, 12, 10, 9, 21, 18, 14, 12, 13, 12, 9, 5, 3,
+ 5, 6, 6, 7, 8, 8, 3, 5, 7, 7, 6, 9, 11, 3, 5, 9, 10, 8, 11,
+ 15, 5, 9, 13, 13, 13, 13, 18, 10, 12, 17, 18, 16, 13, 21, 16, 17, 20, 19,
+ 18, 13, 24, 21, 20, 21, 21, 19, 17, 11, 8, 12, 12, 13, 10, 13, 15, 9, 12,
+ 14, 15, 14, 15, 18, 11, 14, 17, 18, 19, 16, 20, 14, 18, 20, 21, 21, 18, 21,
+ 20, 22, 24, 25, 24, 21, 22, 25, 29, 28, 26, 27, 24, 23, 31, 31, 31, 29, 29,
+ 27, 20, 19, 20, 21, 21, 20, 13, 24, 20, 21, 23, 23, 22, 14, 23, 22, 24, 26,
+ 27, 25, 20, 24, 26, 29, 30, 30, 29, 25, 24, 33, 35, 35, 35, 31, 27, 28, 37,
+ 40, 39, 37, 33, 28, 32, 40, 44, 40, 40, 37, 32, 29, 32, 31, 32, 27, 23, 18,
+ 29, 35, 33, 33, 30, 26, 22, 29, 36, 36, 37, 32, 28, 25, 29, 37, 41, 41, 37,
+ 33, 29, 33, 41, 47, 46, 42, 36, 31, 36, 45, 52, 50, 44, 38, 31, 40, 46, 53,
+ 53, 46, 40, 34, 40, 45, 43, 37, 30, 26, 22, 40, 46, 45, 38, 32, 28, 25, 39,
+ 45, 49, 43, 38, 32, 28, 39, 46, 53, 46, 41, 36, 33, 44, 49, 54, 52, 46, 40,
+ 35, 45, 52, 58, 57, 48, 43, 36, 48, 54, 60, 59, 52, 44, 38, 2, 1, 1, 3,
+ 3, 1, 2, 2, 2, 1, 2, 3, 2, 3, 2, 2, 1, 3, 4, 4, 4, 2, 3,
+ 4, 5, 6, 6, 6, 6, 7, 7, 8, 10, 8, 6, 11, 11, 11, 10, 10, 8, 7,
+ 17, 15, 13, 11, 10, 8, 7, 2, 2, 1, 1, 0, 2, 4, 2, 2, 1, 1, 1,
+ 3, 5, 5, 2, 2, 2, 3, 4, 6, 9, 3, 4, 5, 6, 5, 8, 11, 7, 8,
+ 9, 10, 6, 8, 15, 13, 13, 12, 11, 10, 8, 21, 18, 15, 13, 13, 12, 8, 5,
+ 2, 4, 5, 5, 5, 7, 10, 2, 4, 6, 7, 5, 8, 13, 2, 5, 9, 10, 6,
+ 10, 15, 5, 8, 12, 13, 12, 12, 17, 10, 12, 16, 17, 16, 12, 21, 16, 17, 19,
+ 19, 17, 12, 24, 21, 20, 21, 20, 19, 16, 13, 7, 11, 12, 11, 8, 11, 16, 9,
+ 11, 12, 14, 13, 13, 19, 11, 13, 16, 17, 17, 15, 21, 14, 17, 20, 20, 21, 17,
+ 21, 19, 22, 24, 23, 24, 21, 22, 25, 27, 28, 27, 26, 23, 24, 31, 31, 30, 28,
+ 27, 26, 21, 18, 19, 20, 20, 19, 11, 24, 19, 21, 21, 22, 22, 13, 25, 21, 23,
+ 25, 26, 24, 19, 24, 26, 27, 30, 29, 28, 24, 25, 31, 33, 34, 35, 33, 26, 27,
+ 36, 39, 38, 36, 34, 28, 32, 41, 42, 41, 39, 35, 30, 28, 30, 30, 28, 25, 22,
+ 16, 29, 33, 31, 31, 28, 24, 20, 28, 35, 34, 35, 31, 28, 24, 29, 37, 40, 39,
+ 37, 31, 28, 32, 40, 45, 45, 41, 35, 29, 36, 44, 51, 48, 42, 37, 32, 39, 48,
+ 51, 50, 45, 39, 33, 38, 43, 41, 34, 28, 24, 21, 38, 43, 43, 37, 31, 28, 24,
+ 37, 44, 46, 39, 36, 31, 28, 37, 45, 51, 45, 39, 36, 31, 41, 49, 54, 51, 45,
+ 39, 33, 44, 51, 57, 56, 47, 41, 34, 47, 52, 57, 58, 50, 43, 37, 2, 1, 3,
+ 5, 5, 3, 2, 2, 1, 3, 4, 6, 4, 3, 2, 1, 2, 5, 7, 6, 5, 2,
+ 3, 4, 6, 8, 8, 8, 5, 6, 7, 9, 11, 10, 8, 11, 11, 11, 12, 12, 10,
+ 8, 17, 15, 13, 12, 12, 10, 8, 2, 1, 2, 4, 2, 0, 2, 2, 2, 2, 3,
+ 3, 1, 4, 2, 2, 1, 4, 4, 3, 5, 5, 3, 4, 6, 6, 6, 6, 8, 7,
+ 8, 9, 9, 7, 7, 14, 13, 12, 12, 10, 8, 6, 21, 18, 15, 12, 10, 9, 7,
+ 2, 2, 3, 2, 2, 3, 5, 6, 2, 3, 3, 4, 4, 6, 10, 2, 4, 6, 7,
+ 5, 7, 13, 3, 8, 10, 10, 10, 10, 15, 9, 12, 14, 14, 13, 10, 19, 15, 17,
+ 17, 16, 15, 10, 23, 22, 19, 19, 17, 16, 14, 10, 7, 9, 9, 9, 6, 8, 14,
+ 7, 10, 10, 11, 11, 10, 16, 9, 11, 13, 14, 15, 12, 19, 12, 16, 18, 18, 18,
+ 14, 18, 17, 20, 22, 22, 21, 17, 19, 24, 25, 25, 25, 24, 21, 23, 30, 29, 28,
+ 26, 24, 23, 19, 16, 17, 17, 17, 16, 9, 23, 18, 18, 18, 20, 18, 10, 23, 20,
+ 21, 22, 23, 21, 16, 22, 24, 26, 27, 27, 26, 22, 22, 29, 31, 31, 31, 29, 23,
+ 25, 35, 37, 36, 34, 31, 25, 31, 38, 39, 37, 35, 32, 26, 27, 28, 27, 26, 23,
+ 18, 14, 28, 31, 27, 27, 25, 21, 18, 26, 32, 32, 32, 29, 25, 21, 26, 34, 37,
+ 36, 32, 29, 25, 30, 38, 43, 42, 39, 31, 28, 35, 42, 49, 46, 39, 33, 28, 39,
+ 44, 49, 49, 42, 38, 30, 35, 41, 36, 31, 25, 21, 18, 34, 39, 39, 32, 27, 25,
+ 20, 36, 40, 41, 36, 33, 29, 25, 35, 42, 47, 42, 36, 32, 29, 39, 44, 52, 49,
+ 41, 37, 31, 42, 48, 54, 52, 44, 38, 32, 45, 50, 53, 54, 47, 41, 34, 1, 4,
+ 5, 7, 7, 5, 3, 1, 3, 5, 7, 8, 6, 5, 1, 2, 5, 7, 9, 8, 6,
+ 2, 3, 6, 8, 10, 9, 8, 5, 6, 8, 10, 12, 10, 8, 10, 10, 12, 13, 12,
+ 10, 8, 16, 15, 13, 13, 13, 10, 8, 1, 2, 4, 5, 4, 2, 0, 2, 1, 4,
+ 5, 4, 3, 2, 2, 1, 3, 6, 5, 4, 4, 2, 3, 5, 7, 7, 6, 6, 6,
+ 7, 8, 10, 10, 8, 6, 12, 12, 12, 13, 10, 8, 6, 20, 17, 14, 13, 11, 9,
+ 6, 2, 1, 1, 1, 2, 2, 3, 2, 2, 1, 1, 1, 2, 4, 5, 2, 2, 3,
+ 3, 3, 5, 10, 3, 6, 7, 6, 7, 7, 12, 8, 10, 11, 11, 10, 7, 17, 14,
+ 15, 14, 12, 11, 7, 22, 20, 18, 16, 14, 13, 11, 5, 6, 6, 6, 6, 5, 7,
+ 11, 6, 7, 7, 8, 8, 7, 14, 7, 9, 10, 11, 11, 9, 16, 10, 14, 14, 14,
+ 15, 11, 16, 16, 18, 18, 18, 18, 15, 17, 23, 23, 22, 21, 19, 18, 22, 30, 26,
+ 24, 22, 21, 20, 16, 14, 14, 13, 14, 13, 7, 18, 15, 15, 15, 16, 15, 7, 18,
+ 18, 18, 19, 19, 17, 13, 19, 22, 22, 23, 23, 22, 18, 20, 27, 28, 28, 28, 25,
+ 20, 24, 31, 34, 32, 29, 26, 22, 29, 34, 37, 34, 32, 29, 23, 24, 24, 23, 23,
+ 19, 15, 11, 24, 28, 25, 24, 21, 18, 14, 24, 30, 28, 27, 26, 21, 18, 24, 31,
+ 33, 32, 29, 26, 22, 27, 34, 39, 36, 33, 29, 23, 32, 38, 44, 42, 35, 30, 25,
+ 34, 41, 45, 45, 39, 33, 26, 30, 37, 33, 27, 22, 18, 14, 32, 36, 35, 29, 24,
+ 21, 18, 32, 36, 38, 32, 28, 25, 21, 31, 37, 44, 38, 33, 29, 25, 34, 40, 46,
+ 42, 38, 32, 27, 38, 44, 48, 48, 41, 34, 29, 42, 48, 50, 50, 43, 35, 30, 2,
+ 2, 3, 4, 4, 3, 2, 2, 2, 3, 4, 4, 2, 2, 1, 2, 2, 3, 3, 2,
+ 1, 0, 2, 2, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 3, 7, 6, 7, 6,
+ 5, 3, 3, 13, 11, 9, 7, 5, 3, 3, 1, 2, 3, 3, 2, 2, 2, 0, 2,
+ 2, 3, 2, 2, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 3,
+ 4, 4, 4, 4, 4, 3, 3, 9, 8, 8, 7, 5, 4, 3, 16, 13, 10, 8, 6,
+ 4, 3, 1, 2, 2, 2, 2, 2, 3, 2, 2, 2, 1, 2, 1, 3, 2, 1, 1,
+ 1, 1, 1, 4, 2, 2, 2, 2, 2, 2, 6, 6, 6, 5, 5, 6, 5, 5, 12,
+ 11, 10, 9, 7, 6, 5, 18, 16, 13, 11, 9, 7, 7, 2, 3, 4, 4, 4, 4,
+ 8, 2, 5, 4, 5, 5, 5, 8, 5, 5, 6, 6, 6, 7, 8, 8, 8, 9, 9,
+ 9, 10, 9, 9, 13, 13, 13, 13, 12, 12, 12, 18, 17, 16, 15, 14, 13, 18, 24,
+ 21, 19, 16, 15, 15, 11, 11, 12, 12, 12, 11, 7, 13, 13, 13, 13, 13, 12, 8,
+ 13, 15, 14, 15, 14, 13, 10, 13, 19, 18, 18, 18, 17, 14, 16, 23, 22, 21, 23,
+ 20, 15, 20, 26, 27, 26, 24, 22, 17, 24, 29, 31, 28, 26, 24, 19, 20, 22, 21,
+ 21, 18, 14, 9, 21, 24, 23, 22, 19, 15, 10, 21, 26, 25, 25, 21, 17, 13, 21,
+ 27, 29, 28, 25, 20, 17, 23, 28, 33, 32, 29, 23, 18, 26, 31, 38, 37, 32, 25,
+ 20, 29, 35, 38, 39, 34, 28, 22, 28, 35, 33, 26, 21, 17, 12, 28, 34, 35, 28,
+ 23, 18, 14, 28, 34, 38, 30, 25, 20, 16, 28, 34, 40, 35, 29, 24, 20, 30, 36,
+ 43, 39, 32, 28, 23, 32, 38, 43, 42, 36, 29, 24, 34, 40, 44, 46, 38, 32, 26,
+ 0, 2, 2, 3, 3, 2, 2, 1, 2, 2, 3, 2, 2, 1, 2, 1, 2, 2, 2,
+ 1, 2, 2, 1, 2, 2, 2, 3, 4, 4, 4, 4, 4, 5, 4, 4, 8, 8, 8,
+ 7, 5, 4, 4, 14, 12, 9, 7, 6, 4, 4, 2, 1, 2, 3, 2, 2, 1, 2,
+ 0, 2, 2, 2, 1, 1, 2, 1, 2, 2, 2, 1, 3, 2, 2, 2, 2, 2, 3,
+ 4, 5, 5, 5, 5, 5, 4, 4, 10, 9, 9, 8, 6, 4, 4, 17, 15, 11, 9,
+ 7, 5, 4, 2, 1, 1, 1, 1, 2, 6, 2, 2, 1, 1, 1, 3, 6, 2, 2,
+ 2, 2, 2, 3, 7, 6, 3, 5, 5, 5, 6, 8, 9, 7, 9, 9, 9, 8, 8,
+ 14, 12, 13, 12, 11, 10, 8, 20, 18, 16, 14, 12, 11, 10, 3, 6, 6, 6, 7,
+ 6, 9, 6, 7, 7, 8, 8, 8, 9, 9, 7, 9, 9, 10, 10, 10, 12, 10, 12,
+ 13, 13, 13, 12, 13, 15, 16, 17, 17, 16, 14, 14, 21, 21, 20, 19, 17, 16, 20,
+ 28, 24, 22, 20, 19, 18, 14, 15, 14, 14, 15, 13, 9, 16, 17, 16, 15, 16, 15,
+ 10, 16, 17, 18, 18, 18, 17, 13, 15, 22, 21, 21, 22, 20, 17, 19, 26, 27, 26,
+ 26, 23, 19, 23, 31, 32, 30, 28, 25, 20, 27, 33, 35, 31, 29, 27, 21, 24, 25,
+ 24, 24, 20, 16, 12, 23, 27, 25, 25, 22, 18, 14, 24, 30, 28, 27, 25, 20, 17,
+ 23, 30, 33, 31, 28, 24, 20, 27, 33, 38, 36, 32, 28, 22, 30, 36, 42, 41, 34,
+ 29, 24, 33, 39, 43, 42, 37, 31, 26, 32, 37, 36, 29, 23, 19, 15, 32, 37, 38,
+ 30, 25, 20, 17, 31, 36, 40, 33, 28, 23, 20, 32, 38, 44, 38, 32, 28, 24, 34,
+ 41, 46, 44, 37, 31, 25, 36, 43, 48, 49, 40, 34, 28, 39, 45, 49, 50, 42, 35,
+ 30, 2, 0, 2, 2, 2, 2, 1, 2, 1, 2, 2, 2, 1, 1, 2, 2, 2, 2,
+ 2, 2, 3, 2, 2, 2, 2, 3, 3, 4, 5, 5, 5, 5, 5, 5, 4, 9, 9,
+ 9, 8, 6, 5, 4, 15, 13, 10, 8, 7, 5, 4, 2, 2, 1, 2, 1, 1, 4,
+ 2, 2, 0, 2, 1, 1, 4, 2, 2, 1, 1, 1, 2, 5, 3, 3, 3, 3, 3,
+ 4, 6, 7, 6, 6, 6, 6, 4, 6, 11, 11, 11, 9, 7, 6, 6, 19, 16, 12,
+ 10, 9, 7, 6, 3, 2, 3, 3, 3, 5, 7, 3, 2, 3, 4, 4, 5, 7, 7,
+ 2, 4, 6, 6, 5, 8, 10, 3, 7, 9, 9, 9, 10, 13, 8, 11, 12, 13, 12,
+ 10, 17, 13, 15, 16, 15, 13, 10, 21, 19, 18, 17, 16, 15, 14, 7, 7, 9, 9,
+ 10, 8, 11, 11, 8, 10, 10, 11, 11, 11, 13, 8, 11, 12, 13, 13, 12, 16, 11,
+ 15, 16, 16, 17, 14, 17, 17, 20, 20, 20, 19, 18, 18, 23, 24, 24, 23, 21, 20,
+ 21, 30, 27, 25, 24, 22, 22, 17, 17, 17, 17, 18, 16, 11, 19, 18, 18, 18, 18,
+ 18, 11, 19, 20, 21, 21, 22, 20, 16, 19, 23, 25, 25, 25, 24, 20, 21, 30, 30,
+ 29, 29, 26, 22, 25, 34, 35, 34, 31, 29, 24, 30, 36, 39, 35, 34, 31, 26, 26,
+ 28, 27, 27, 23, 19, 15, 26, 31, 29, 28, 25, 21, 17, 26, 32, 32, 31, 28, 24,
+ 20, 26, 34, 37, 35, 31, 28, 23, 30, 36, 41, 40, 36, 30, 26, 33, 41, 46, 45,
+ 38, 32, 27, 37, 42, 48, 48, 41, 35, 28, 35, 41, 39, 32, 26, 22, 18, 35, 41,
+ 42, 34, 29, 24, 20, 36, 42, 45, 37, 33, 27, 23, 36, 42, 49, 41, 37, 31, 27,
+ 38, 45, 50, 47, 42, 35, 29, 41, 48, 53, 52, 44, 37, 31, 44, 50, 53, 54, 46,
+ 40, 32, 2, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 2, 1, 3, 3, 2, 1,
+ 2, 3, 3, 4, 3, 3, 3, 3, 4, 5, 5, 6, 6, 7, 6, 7, 6, 5, 10,
+ 10, 10, 9, 8, 6, 5, 17, 15, 12, 10, 8, 6, 5, 3, 2, 2, 1, 1, 3,
+ 5, 3, 2, 2, 0, 1, 3, 6, 3, 2, 2, 2, 2, 4, 7, 6, 4, 4, 5,
+ 5, 5, 8, 10, 7, 8, 8, 9, 6, 8, 14, 12, 12, 11, 10, 9, 8, 20, 17,
+ 14, 12, 12, 11, 8, 3, 2, 4, 5, 5, 6, 8, 7, 3, 4, 6, 6, 6, 9,
+ 10, 3, 5, 8, 9, 7, 10, 14, 5, 9, 12, 12, 12, 12, 16, 9, 12, 15, 16,
+ 15, 12, 20, 15, 16, 19, 18, 17, 12, 23, 21, 20, 21, 19, 18, 16, 10, 7, 11,
+ 12, 12, 9, 13, 14, 8, 12, 13, 14, 13, 13, 17, 10, 13, 16, 16, 17, 15, 19,
+ 13, 17, 20, 20, 20, 17, 20, 18, 22, 24, 24, 23, 20, 20, 24, 26, 28, 26, 25,
+ 23, 23, 30, 30, 29, 27, 26, 25, 19, 19, 20, 20, 20, 19, 13, 23, 20, 21, 22,
+ 22, 21, 13, 22, 22, 23, 25, 26, 24, 19, 22, 26, 28, 28, 29, 28, 24, 24, 32,
+ 33, 33, 33, 31, 25, 27, 37, 39, 38, 35, 32, 27, 31, 40, 42, 39, 39, 35, 29,
+ 28, 31, 30, 29, 27, 22, 17, 29, 33, 32, 31, 28, 24, 20, 28, 35, 34, 35, 32,
+ 27, 23, 28, 37, 40, 39, 36, 31, 27, 32, 41, 46, 44, 41, 34, 29, 36, 43, 50,
+ 49, 42, 37, 32, 40, 47, 51, 51, 45, 38, 32, 38, 44, 41, 35, 28, 25, 20, 38,
+ 44, 44, 37, 32, 28, 24, 39, 44, 47, 41, 36, 31, 27, 38, 45, 52, 46, 40, 36,
+ 31, 41, 48, 55, 52, 44, 39, 34, 45, 51, 57, 56, 47, 40, 35, 48, 53, 56, 58,
+ 51, 43, 37, 2, 1, 1, 3, 3, 1, 2, 2, 1, 1, 2, 3, 2, 3, 2, 2,
+ 1, 3, 4, 4, 4, 2, 3, 3, 4, 6, 6, 6, 5, 6, 7, 7, 9, 8, 6,
+ 11, 11, 11, 10, 10, 8, 6, 17, 15, 12, 10, 9, 8, 6, 2, 2, 1, 1, 1,
+ 3, 4, 2, 2, 1, 1, 0, 3, 5, 4, 2, 1, 2, 2, 4, 6, 8, 3, 4,
+ 5, 5, 5, 7, 10, 7, 8, 9, 9, 6, 7, 14, 12, 12, 12, 11, 9, 7, 21,
+ 17, 14, 12, 12, 11, 7, 4, 2, 4, 5, 5, 6, 8, 9, 2, 4, 6, 6, 5,
+ 8, 13, 2, 5, 8, 9, 6, 10, 15, 5, 8, 12, 12, 12, 12, 17, 10, 12, 16,
+ 17, 16, 12, 21, 15, 17, 19, 19, 17, 12, 24, 21, 19, 20, 19, 18, 16, 13, 7,
+ 11, 12, 11, 9, 12, 16, 8, 11, 12, 14, 13, 13, 19, 11, 12, 15, 17, 17, 14,
+ 21, 14, 17, 20, 20, 20, 16, 20, 19, 22, 23, 25, 24, 19, 22, 25, 27, 27, 27,
+ 25, 24, 23, 31, 30, 30, 28, 27, 26, 21, 17, 19, 20, 20, 19, 12, 24, 19, 21,
+ 21, 22, 21, 13, 24, 21, 23, 24, 25, 24, 18, 24, 26, 27, 28, 30, 28, 24, 25,
+ 32, 32, 34, 33, 32, 25, 27, 34, 39, 38, 36, 33, 27, 32, 39, 43, 39, 38, 36,
+ 29, 28, 29, 29, 29, 26, 22, 16, 28, 33, 31, 31, 27, 24, 20, 28, 36, 35, 33,
+ 31, 27, 24, 28, 37, 39, 38, 35, 31, 27, 32, 39, 45, 44, 40, 34, 30, 35, 43,
+ 51, 47, 41, 36, 31, 39, 48, 52, 52, 45, 38, 33, 38, 43, 40, 34, 29, 24, 21,
+ 38, 43, 43, 37, 32, 27, 24, 37, 43, 46, 40, 36, 32, 27, 37, 44, 50, 44, 40,
+ 34, 31, 41, 48, 53, 51, 43, 39, 33, 45, 51, 55, 56, 48, 40, 34, 47, 53, 55,
+ 57, 51, 41, 37, 1, 1, 4, 6, 5, 4, 2, 1, 1, 3, 5, 5, 3, 2, 2,
+ 1, 2, 4, 6, 5, 4, 2, 2, 3, 5, 7, 6, 6, 5, 5, 6, 8, 10, 8,
+ 6, 10, 9, 9, 10, 10, 8, 6, 15, 14, 11, 10, 10, 8, 6, 2, 1, 2, 4,
+ 3, 1, 3, 2, 1, 1, 3, 3, 0, 3, 2, 2, 1, 3, 3, 2, 4, 4, 3,
+ 4, 5, 5, 4, 5, 7, 6, 7, 8, 7, 6, 5, 12, 11, 11, 10, 8, 6, 5,
+ 19, 16, 12, 10, 9, 8, 5, 2, 1, 3, 2, 3, 4, 6, 4, 2, 3, 3, 4,
+ 3, 5, 8, 2, 4, 6, 6, 4, 6, 13, 3, 7, 9, 9, 9, 8, 14, 8, 11,
+ 13, 13, 12, 8, 18, 14, 15, 16, 15, 13, 8, 21, 19, 18, 18, 16, 15, 13, 9,
+ 6, 9, 9, 9, 7, 10, 13, 7, 9, 10, 10, 10, 9, 16, 8, 11, 13, 13, 13,
+ 11, 18, 11, 15, 16, 17, 17, 13, 18, 17, 19, 20, 21, 19, 16, 18, 23, 25, 23,
+ 23, 21, 19, 21, 28, 27, 26, 24, 23, 22, 18, 16, 17, 16, 17, 17, 10, 21, 18,
+ 18, 18, 19, 17, 9, 21, 19, 20, 22, 21, 20, 15, 21, 23, 24, 25, 25, 24, 20,
+ 22, 29, 30, 30, 30, 27, 22, 25, 33, 35, 34, 32, 29, 24, 29, 37, 38, 35, 34,
+ 31, 26, 26, 28, 27, 26, 22, 19, 14, 26, 30, 28, 27, 25, 21, 17, 25, 32, 31,
+ 30, 28, 24, 20, 26, 34, 36, 35, 32, 28, 24, 28, 36, 41, 40, 36, 31, 26, 33,
+ 41, 47, 43, 38, 31, 27, 37, 41, 47, 47, 40, 34, 29, 35, 40, 37, 30, 26, 22,
+ 18, 34, 40, 40, 33, 27, 24, 20, 35, 40, 43, 36, 31, 27, 24, 34, 42, 46, 41,
+ 36, 32, 28, 37, 43, 48, 46, 40, 35, 30, 41, 46, 52, 51, 42, 36, 31, 44, 49,
+ 53, 52, 45, 39, 32, 1, 5, 6, 8, 8, 6, 4, 0, 3, 6, 8, 8, 5, 3,
+ 1, 2, 5, 6, 8, 6, 5, 1, 3, 5, 7, 9, 7, 6, 4, 5, 7, 8, 10,
+ 9, 6, 8, 8, 10, 11, 11, 9, 7, 14, 12, 11, 11, 10, 8, 7, 1, 2, 5,
+ 7, 5, 4, 2, 1, 1, 4, 6, 5, 3, 0, 2, 1, 3, 5, 4, 3, 2, 2,
+ 2, 4, 5, 5, 4, 4, 5, 6, 7, 8, 7, 6, 4, 11, 10, 10, 10, 8, 6,
+ 4, 17, 14, 12, 10, 8, 6, 4, 1, 1, 1, 2, 3, 3, 5, 2, 1, 0, 1,
+ 2, 2, 3, 3, 1, 2, 2, 2, 2, 4, 7, 2, 5, 5, 5, 5, 5, 11, 7,
+ 9, 9, 9, 8, 5, 15, 12, 13, 12, 11, 9, 5, 19, 17, 15, 14, 12, 10, 9,
+ 3, 5, 6, 6, 7, 6, 8, 8, 6, 7, 7, 8, 8, 7, 11, 7, 8, 9, 9,
+ 9, 8, 14, 9, 13, 13, 13, 13, 10, 15, 14, 17, 16, 17, 16, 13, 15, 20, 21,
+ 20, 19, 17, 16, 20, 26, 24, 22, 21, 19, 18, 14, 14, 14, 14, 15, 14, 8, 17,
+ 16, 15, 15, 16, 14, 7, 17, 17, 18, 18, 18, 17, 12, 16, 21, 22, 21, 22, 20,
+ 16, 19, 27, 27, 26, 26, 24, 18, 22, 30, 31, 30, 27, 25, 21, 27, 33, 35, 32,
+ 30, 27, 22, 24, 25, 24, 23, 20, 17, 12, 24, 27, 25, 24, 21, 17, 12, 23, 29,
+ 27, 27, 24, 20, 16, 24, 31, 31, 30, 27, 24, 20, 26, 33, 37, 35, 32, 27, 21,
+ 30, 36, 43, 39, 33, 29, 24, 33, 39, 43, 41, 36, 29, 25, 31, 35, 33, 27, 23,
+ 19, 15, 32, 36, 35, 28, 24, 20, 16, 32, 37, 38, 32, 27, 24, 20, 30, 37, 41,
+ 36, 31, 27, 23, 34, 39, 44, 41, 36, 30, 25, 37, 41, 47, 46, 39, 32, 28, 39,
+ 45, 48, 47, 41, 34, 29, 2, 2, 4, 8, 8, 6, 2, 2, 2, 3, 7, 8, 4,
+ 2, 2, 2, 3, 4, 5, 3, 2, 1, 2, 2, 2, 3, 2, 2, 2, 2, 3, 3,
+ 3, 3, 3, 6, 6, 6, 6, 4, 3, 2, 12, 10, 8, 6, 4, 3, 3, 2, 2,
+ 3, 5, 5, 2, 2, 1, 2, 3, 4, 4, 2, 2, 0, 2, 2, 2, 2, 2, 1,
+ 1, 1, 1, 2, 1, 1, 3, 3, 3, 4, 4, 3, 3, 3, 8, 7, 8, 7, 4,
+ 3, 3, 15, 13, 10, 7, 5, 3, 3, 0, 2, 2, 2, 2, 2, 1, 2, 2, 2,
+ 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 4, 5,
+ 5, 4, 4, 5, 3, 4, 11, 10, 9, 8, 6, 4, 4, 17, 16, 12, 9, 7, 6,
+ 5, 2, 2, 2, 3, 3, 3, 7, 2, 3, 3, 4, 4, 4, 7, 4, 4, 4, 5,
+ 5, 5, 7, 7, 7, 7, 7, 8, 8, 8, 7, 11, 11, 11, 12, 11, 10, 11, 17,
+ 16, 15, 14, 12, 12, 18, 22, 19, 17, 16, 14, 13, 10, 10, 10, 10, 11, 10, 7,
+ 12, 12, 12, 11, 12, 11, 7, 12, 14, 13, 13, 13, 12, 8, 12, 17, 16, 17, 16,
+ 15, 12, 15, 21, 20, 20, 20, 19, 13, 18, 24, 26, 24, 24, 21, 15, 22, 28, 29,
+ 26, 25, 23, 17, 19, 21, 20, 20, 16, 12, 8, 19, 23, 22, 22, 18, 13, 9, 20,
+ 25, 24, 24, 20, 15, 11, 19, 25, 27, 27, 23, 19, 15, 21, 28, 31, 31, 28, 22,
+ 17, 25, 30, 36, 35, 30, 24, 18, 27, 33, 38, 36, 31, 27, 21, 27, 32, 31, 25,
+ 20, 16, 11, 27, 33, 34, 27, 22, 16, 12, 27, 32, 36, 29, 24, 19, 15, 27, 33,
+ 38, 32, 27, 23, 19, 28, 35, 40, 36, 32, 26, 21, 31, 36, 41, 40, 33, 28, 22,
+ 34, 37, 42, 41, 35, 31, 24, 1, 2, 3, 3, 3, 2, 2, 0, 2, 2, 3, 3,
+ 2, 1, 1, 2, 2, 2, 2, 2, 1, 2, 1, 1, 2, 2, 2, 4, 4, 3, 4,
+ 4, 4, 4, 4, 7, 7, 7, 7, 5, 4, 4, 13, 11, 9, 7, 6, 4, 4, 1,
+ 2, 2, 3, 2, 2, 1, 2, 1, 2, 2, 2, 1, 1, 2, 0, 2, 2, 2, 1,
+ 1, 2, 2, 2, 2, 2, 2, 4, 5, 4, 4, 5, 4, 4, 3, 9, 9, 8, 8,
+ 5, 4, 4, 17, 14, 10, 8, 6, 4, 3, 2, 0, 1, 1, 2, 2, 5, 2, 2,
+ 1, 1, 1, 2, 5, 2, 2, 1, 1, 1, 1, 6, 4, 2, 4, 4, 4, 4, 7,
+ 8, 6, 7, 8, 8, 7, 7, 13, 11, 12, 11, 9, 8, 7, 19, 17, 15, 13, 11,
+ 10, 9, 2, 5, 5, 6, 6, 5, 8, 5, 6, 6, 7, 7, 7, 9, 8, 7, 7,
+ 8, 8, 8, 9, 11, 9, 11, 11, 11, 11, 11, 11, 14, 15, 15, 15, 14, 13, 13,
+ 20, 20, 19, 18, 16, 15, 19, 26, 23, 20, 19, 17, 17, 12, 13, 13, 13, 14, 12,
+ 8, 14, 15, 15, 15, 15, 13, 8, 14, 17, 16, 16, 16, 15, 12, 14, 21, 20, 19,
+ 20, 19, 15, 18, 26, 24, 24, 25, 22, 17, 21, 29, 30, 29, 26, 23, 19, 26, 31,
+ 33, 30, 29, 26, 20, 22, 24, 22, 22, 19, 15, 11, 23, 26, 24, 23, 21, 16, 12,
+ 22, 28, 27, 26, 22, 19, 15, 22, 28, 31, 30, 26, 22, 19, 26, 32, 35, 35, 31,
+ 26, 21, 28, 34, 40, 39, 32, 28, 23, 32, 37, 42, 41, 36, 29, 25, 30, 36, 35,
+ 28, 22, 18, 14, 30, 36, 37, 29, 23, 19, 15, 30, 36, 39, 32, 27, 22, 19, 31,
+ 38, 42, 36, 31, 26, 23, 32, 39, 44, 42, 34, 30, 24, 34, 40, 47, 45, 38, 32,
+ 27, 37, 42, 48, 48, 41, 34, 27, 1, 1, 2, 2, 2, 2, 1, 2, 1, 2, 2,
+ 2, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2, 2, 2, 2, 3, 4, 4, 5,
+ 4, 5, 5, 4, 4, 9, 8, 8, 7, 6, 4, 4, 15, 13, 10, 8, 6, 4, 4,
+ 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 2, 1, 1, 3, 2, 2, 0, 1, 2,
+ 1, 5, 2, 3, 2, 3, 3, 3, 5, 6, 6, 6, 6, 5, 4, 5, 11, 10, 10,
+ 9, 6, 5, 5, 18, 15, 12, 9, 8, 6, 5, 2, 2, 2, 2, 2, 5, 7, 2,
+ 2, 3, 3, 3, 5, 7, 5, 2, 4, 4, 5, 5, 8, 8, 3, 7, 7, 8, 8,
+ 9, 12, 7, 10, 11, 11, 10, 9, 15, 13, 15, 15, 13, 12, 9, 21, 19, 17, 16,
+ 15, 13, 12, 5, 7, 8, 9, 9, 8, 11, 9, 7, 9, 10, 10, 10, 11, 11, 8,
+ 11, 11, 12, 12, 12, 14, 10, 15, 15, 15, 15, 13, 15, 16, 18, 19, 19, 18, 17,
+ 16, 22, 23, 22, 21, 20, 19, 21, 28, 27, 25, 23, 22, 21, 15, 16, 16, 16, 17,
+ 16, 11, 17, 18, 18, 18, 18, 17, 11, 17, 19, 20, 21, 21, 19, 15, 17, 23, 24,
+ 23, 24, 22, 19, 20, 29, 28, 28, 28, 25, 21, 24, 33, 34, 33, 31, 27, 23, 29,
+ 35, 37, 33, 32, 30, 24, 26, 27, 26, 26, 23, 18, 14, 25, 30, 28, 27, 24, 20,
+ 15, 26, 31, 31, 31, 26, 23, 19, 25, 33, 34, 35, 31, 26, 22, 28, 35, 40, 38,
+ 36, 29, 24, 32, 39, 45, 43, 37, 32, 26, 36, 41, 46, 46, 40, 34, 28, 34, 41,
+ 38, 32, 26, 22, 17, 34, 41, 41, 34, 27, 22, 19, 34, 40, 43, 37, 31, 26, 22,
+ 34, 41, 47, 41, 35, 30, 26, 36, 43, 49, 44, 40, 33, 28, 39, 45, 52, 51, 43,
+ 35, 30, 42, 48, 52, 51, 45, 38, 32, 2, 1, 2, 4, 3, 3, 3, 2, 1, 2,
+ 3, 3, 2, 1, 2, 2, 1, 2, 2, 1, 3, 2, 2, 2, 2, 3, 3, 4, 5,
+ 5, 5, 5, 5, 4, 4, 9, 9, 8, 8, 6, 4, 4, 15, 13, 10, 8, 7, 4,
+ 4, 2, 2, 1, 3, 3, 4, 6, 2, 2, 1, 2, 2, 3, 5, 2, 2, 1, 0,
+ 0, 3, 6, 4, 3, 3, 3, 3, 4, 7, 8, 6, 6, 7, 6, 4, 7, 12, 10,
+ 10, 9, 8, 7, 7, 19, 16, 12, 10, 10, 9, 7, 2, 2, 4, 5, 6, 7, 9,
+ 4, 2, 4, 6, 6, 6, 8, 7, 2, 4, 6, 7, 6, 9, 11, 3, 7, 10, 10,
+ 10, 10, 14, 8, 11, 14, 14, 13, 10, 18, 13, 15, 17, 15, 15, 10, 21, 18, 18,
+ 18, 17, 16, 14, 7, 7, 10, 11, 12, 10, 14, 11, 8, 11, 12, 13, 13, 13, 14,
+ 8, 12, 13, 14, 14, 13, 17, 12, 16, 17, 18, 18, 15, 17, 17, 20, 21, 22, 21,
+ 19, 18, 23, 26, 24, 23, 22, 21, 21, 29, 28, 27, 25, 23, 24, 17, 18, 19, 19,
+ 20, 20, 14, 20, 19, 21, 21, 21, 20, 13, 19, 20, 22, 23, 23, 22, 17, 20, 25,
+ 26, 27, 27, 26, 21, 22, 31, 30, 31, 32, 29, 23, 25, 35, 36, 35, 33, 30, 25,
+ 30, 37, 40, 37, 36, 32, 26, 27, 30, 29, 29, 26, 22, 18, 27, 32, 31, 30, 27,
+ 23, 18, 28, 34, 34, 33, 30, 25, 21, 27, 36, 39, 38, 33, 29, 25, 30, 39, 42,
+ 41, 37, 32, 27, 34, 41, 47, 46, 40, 34, 29, 38, 42, 49, 50, 43, 35, 30, 36,
+ 44, 41, 34, 29, 25, 20, 38, 43, 43, 36, 30, 26, 21, 38, 44, 47, 40, 34, 29,
+ 25, 37, 44, 50, 44, 38, 33, 29, 39, 45, 52, 50, 42, 36, 30, 42, 49, 54, 54,
+ 46, 37, 33, 47, 51, 56, 55, 49, 41, 34, 2, 1, 3, 6, 6, 4, 3, 2, 1,
+ 2, 5, 6, 3, 1, 2, 2, 1, 3, 4, 2, 2, 2, 2, 2, 3, 4, 4, 4,
+ 4, 5, 5, 5, 7, 5, 4, 9, 8, 8, 8, 7, 5, 4, 14, 12, 10, 7, 8,
+ 5, 4, 2, 2, 1, 3, 3, 4, 5, 2, 2, 1, 2, 2, 3, 4, 2, 2, 1,
+ 0, 0, 2, 4, 4, 3, 3, 3, 3, 3, 5, 8, 6, 6, 6, 6, 3, 5, 12,
+ 10, 10, 9, 8, 7, 5, 18, 15, 11, 9, 9, 8, 5, 2, 2, 4, 5, 5, 6,
+ 8, 5, 2, 3, 6, 6, 6, 7, 8, 2, 4, 6, 7, 4, 7, 12, 3, 7, 10,
+ 10, 9, 9, 15, 8, 10, 13, 14, 13, 9, 18, 12, 14, 17, 16, 14, 9, 21, 18,
+ 16, 18, 17, 16, 14, 9, 6, 10, 11, 12, 10, 13, 12, 7, 10, 12, 13, 12, 12,
+ 15, 8, 12, 13, 14, 14, 12, 16, 11, 15, 17, 17, 17, 14, 18, 16, 19, 21, 22,
+ 21, 17, 18, 22, 24, 24, 24, 22, 21, 20, 27, 27, 26, 25, 24, 23, 17, 17, 19,
+ 19, 20, 19, 13, 20, 19, 20, 20, 21, 20, 12, 20, 20, 22, 23, 23, 22, 16, 20,
+ 23, 26, 26, 27, 25, 21, 22, 29, 31, 31, 30, 28, 23, 25, 34, 37, 35, 33, 29,
+ 25, 29, 37, 40, 37, 35, 32, 26, 27, 30, 29, 29, 26, 22, 17, 26, 32, 31, 30,
+ 27, 22, 18, 26, 34, 34, 33, 29, 25, 21, 27, 35, 39, 37, 33, 29, 25, 30, 39,
+ 44, 41, 37, 32, 26, 34, 41, 48, 46, 39, 33, 29, 37, 44, 48, 48, 43, 36, 30,
+ 37, 42, 40, 33, 29, 25, 21, 37, 42, 42, 36, 29, 25, 21, 37, 42, 45, 38, 34,
+ 29, 24, 37, 44, 49, 43, 36, 33, 28, 38, 46, 50, 48, 42, 35, 30, 42, 49, 53,
+ 52, 45, 38, 32, 45, 49, 55, 55, 47, 40, 34, 1, 3, 5, 7, 7, 5, 3, 1,
+ 1, 4, 6, 6, 4, 2, 2, 1, 3, 5, 5, 3, 2, 2, 1, 2, 4, 6, 5,
+ 4, 4, 4, 4, 6, 8, 6, 4, 8, 7, 7, 8, 8, 6, 4, 13, 11, 9, 8,
+ 8, 6, 4, 1, 1, 3, 5, 4, 3, 4, 1, 1, 2, 4, 4, 2, 3, 2, 1,
+ 1, 3, 2, 0, 3, 2, 2, 2, 3, 3, 2, 4, 6, 5, 5, 6, 5, 4, 4,
+ 11, 9, 9, 8, 6, 4, 3, 16, 13, 10, 8, 7, 6, 3, 2, 1, 3, 4, 4,
+ 6, 7, 2, 2, 4, 4, 5, 5, 6, 5, 2, 3, 4, 4, 3, 5, 10, 3, 6,
+ 7, 7, 7, 7, 13, 7, 9, 11, 11, 10, 7, 16, 12, 13, 14, 12, 12, 7, 19,
+ 17, 15, 15, 14, 13, 12, 5, 6, 9, 10, 11, 9, 11, 11, 7, 9, 11, 11, 11,
+ 10, 14, 7, 11, 11, 11, 11, 10, 16, 10, 14, 14, 15, 15, 11, 17, 15, 18, 19,
+ 19, 18, 14, 16, 20, 22, 22, 20, 20, 18, 18, 25, 25, 24, 23, 21, 20, 15, 16,
+ 17, 18, 18, 17, 11, 19, 17, 19, 19, 20, 18, 10, 19, 19, 20, 20, 20, 18, 14,
+ 19, 22, 23, 23, 24, 22, 18, 21, 28, 28, 28, 28, 25, 20, 23, 31, 34, 32, 30,
+ 27, 23, 26, 34, 36, 35, 32, 29, 24, 26, 28, 27, 27, 24, 19, 16, 25, 30, 28,
+ 28, 25, 21, 16, 25, 32, 30, 29, 25, 22, 18, 25, 33, 34, 34, 30, 26, 22, 28,
+ 35, 40, 38, 34, 29, 23, 31, 40, 45, 41, 36, 30, 26, 35, 40, 44, 45, 39, 33,
+ 27, 36, 40, 37, 31, 25, 23, 19, 35, 40, 40, 33, 28, 23, 19, 35, 40, 42, 35,
+ 30, 26, 22, 34, 41, 45, 39, 32, 30, 26, 37, 43, 48, 44, 37, 33, 28, 38, 44,
+ 50, 49, 41, 35, 29, 42, 47, 49, 50, 44, 37, 31, 3, 5, 7, 9, 9, 7, 5,
+ 2, 5, 7, 8, 9, 6, 4, 1, 4, 6, 7, 8, 5, 3, 1, 1, 4, 6, 8,
+ 6, 5, 3, 3, 5, 6, 9, 7, 4, 7, 6, 7, 8, 8, 7, 4, 10, 9, 8,
+ 8, 8, 7, 5, 1, 4, 6, 8, 7, 5, 4, 1, 3, 5, 7, 6, 4, 2, 1,
+ 1, 5, 6, 4, 3, 0, 2, 1, 3, 4, 3, 3, 2, 4, 4, 4, 6, 5, 4,
+ 2, 9, 8, 7, 8, 6, 4, 2, 14, 11, 8, 8, 6, 4, 2, 1, 1, 3, 3,
+ 4, 5, 6, 2, 1, 3, 3, 3, 4, 5, 2, 1, 2, 2, 2, 2, 3, 4, 2,
+ 4, 4, 3, 3, 4, 9, 6, 7, 7, 7, 6, 4, 12, 10, 10, 9, 9, 8, 4,
+ 16, 13, 12, 11, 10, 9, 8, 2, 6, 7, 8, 8, 8, 9, 5, 6, 8, 8, 9,
+ 9, 8, 9, 6, 8, 9, 9, 9, 7, 12, 8, 11, 11, 10, 11, 8, 13, 13, 15,
+ 14, 15, 14, 12, 12, 17, 18, 17, 17, 15, 14, 16, 22, 21, 19, 19, 17, 16, 13,
+ 14, 15, 15, 16, 15, 9, 15, 15, 16, 17, 17, 16, 8, 15, 17, 18, 17, 17, 15,
+ 12, 15, 20, 20, 20, 20, 18, 15, 17, 25, 24, 25, 24, 21, 16, 20, 27, 29, 27,
+ 26, 23, 18, 23, 29, 32, 29, 28, 25, 20, 23, 25, 24, 23, 21, 17, 14, 24, 27,
+ 26, 25, 23, 18, 13, 23, 29, 28, 26, 22, 18, 14, 23, 30, 31, 29, 26, 22, 18,
+ 26, 32, 36, 33, 29, 25, 20, 29, 35, 39, 37, 32, 26, 21, 31, 36, 40, 39, 33,
+ 28, 23, 31, 37, 34, 28, 23, 20, 17, 32, 36, 35, 29, 25, 20, 16, 32, 37, 39,
+ 31, 25, 22, 17, 32, 36, 40, 35, 29, 26, 21, 33, 38, 43, 39, 33, 28, 23, 36,
+ 40, 43, 44, 36, 30, 25, 38, 42, 45, 45, 39, 32, 26, 2, 3, 7, 11, 11, 10,
+ 6, 2, 2, 6, 10, 11, 9, 3, 2, 2, 4, 7, 9, 6, 2, 2, 2, 2, 4,
+ 6, 4, 2, 2, 2, 2, 3, 5, 4, 2, 6, 6, 6, 5, 4, 4, 2, 11, 11,
+ 8, 6, 4, 4, 2, 2, 2, 4, 8, 9, 5, 2, 1, 2, 3, 6, 8, 3, 2,
+ 1, 2, 2, 4, 4, 2, 2, 0, 2, 2, 2, 2, 1, 2, 3, 3, 3, 3, 3,
+ 2, 2, 8, 7, 8, 7, 4, 3, 2, 15, 13, 10, 7, 5, 3, 2, 1, 2, 2,
+ 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 1, 2, 0, 2,
+ 1, 1, 1, 1, 1, 2, 5, 4, 4, 4, 4, 3, 2, 11, 10, 9, 8, 6, 3,
+ 2, 17, 16, 12, 9, 6, 5, 3, 1, 1, 1, 1, 1, 2, 6, 1, 2, 3, 3,
+ 3, 3, 6, 3, 3, 3, 4, 4, 4, 6, 6, 6, 6, 6, 7, 7, 8, 6, 10,
+ 10, 10, 10, 10, 9, 11, 15, 15, 14, 12, 11, 10, 17, 21, 18, 16, 15, 13, 12,
+ 9, 9, 9, 10, 10, 9, 6, 11, 11, 11, 11, 11, 10, 6, 11, 12, 12, 12, 13,
+ 11, 7, 11, 16, 15, 15, 15, 14, 10, 14, 20, 19, 19, 20, 18, 12, 17, 23, 25,
+ 23, 22, 20, 14, 21, 27, 28, 26, 24, 22, 16, 18, 19, 19, 20, 16, 12, 7, 18,
+ 22, 21, 21, 18, 13, 9, 18, 23, 23, 23, 19, 15, 10, 18, 24, 27, 25, 22, 18,
+ 14, 20, 27, 30, 27, 26, 21, 16, 23, 28, 34, 31, 28, 24, 18, 27, 31, 35, 34,
+ 30, 25, 20, 26, 32, 29, 24, 20, 15, 10, 26, 31, 32, 26, 21, 16, 11, 26, 32,
+ 33, 28, 23, 18, 14, 25, 31, 36, 29, 26, 22, 18, 26, 31, 37, 34, 29, 25, 20,
+ 30, 34, 38, 36, 30, 26, 22, 31, 36, 37, 37, 32, 29, 24, 2, 3, 4, 4, 4,
+ 4, 3, 2, 3, 4, 4, 4, 3, 3, 1, 2, 3, 4, 4, 3, 1, 1, 1, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 6, 6, 6, 5, 3, 2, 2, 11,
+ 10, 7, 6, 4, 2, 2, 1, 3, 3, 4, 3, 3, 3, 1, 2, 3, 4, 3, 3,
+ 2, 1, 2, 3, 3, 3, 2, 1, 2, 0, 1, 2, 2, 1, 2, 3, 3, 3, 3,
+ 3, 2, 2, 8, 7, 7, 6, 4, 2, 2, 15, 12, 9, 7, 5, 3, 2, 1, 2,
+ 2, 3, 3, 3, 5, 2, 1, 2, 3, 3, 3, 5, 2, 1, 2, 2, 2, 2, 4,
+ 2, 1, 2, 2, 2, 2, 5, 6, 5, 5, 5, 5, 5, 5, 11, 10, 10, 9, 7,
+ 6, 5, 17, 15, 12, 10, 9, 7, 6, 2, 4, 5, 5, 6, 6, 9, 2, 5, 6,
+ 7, 7, 7, 8, 6, 6, 7, 7, 7, 8, 9, 9, 8, 9, 9, 9, 9, 9, 9,
+ 13, 13, 13, 13, 13, 11, 11, 18, 17, 16, 15, 14, 13, 17, 25, 20, 19, 17, 15,
+ 14, 12, 12, 13, 13, 14, 12, 9, 13, 14, 14, 14, 15, 13, 9, 13, 15, 16, 16,
+ 16, 14, 10, 13, 18, 18, 18, 18, 17, 13, 16, 23, 22, 22, 22, 20, 15, 20, 26,
+ 27, 26, 24, 22, 16, 24, 29, 31, 27, 26, 24, 17, 22, 23, 22, 22, 20, 15, 11,
+ 21, 25, 24, 24, 21, 16, 12, 21, 27, 26, 26, 21, 17, 13, 22, 27, 29, 27, 24,
+ 20, 16, 23, 29, 34, 32, 28, 23, 18, 26, 32, 39, 37, 32, 26, 20, 29, 35, 40,
+ 40, 34, 27, 22, 29, 36, 35, 28, 22, 18, 14, 29, 36, 38, 30, 24, 19, 15, 30,
+ 37, 39, 31, 25, 20, 17, 30, 34, 41, 34, 29, 24, 20, 29, 37, 42, 39, 34, 28,
+ 22, 32, 38, 44, 43, 37, 30, 24, 35, 41, 45, 46, 39, 32, 25, 2, 3, 4, 5,
+ 5, 5, 4, 2, 3, 4, 5, 5, 4, 3, 2, 2, 3, 4, 4, 2, 1, 2, 1,
+ 2, 2, 2, 1, 2, 3, 2, 2, 3, 3, 2, 2, 6, 6, 6, 5, 3, 2, 2,
+ 12, 10, 7, 5, 4, 2, 2, 2, 2, 3, 4, 4, 4, 5, 2, 2, 3, 4, 4,
+ 3, 4, 2, 2, 2, 3, 3, 2, 2, 2, 2, 0, 1, 2, 1, 3, 4, 3, 3,
+ 3, 3, 2, 4, 9, 8, 7, 6, 4, 2, 4, 16, 13, 9, 7, 5, 3, 3, 2,
+ 2, 3, 4, 5, 6, 8, 2, 2, 3, 4, 5, 5, 8, 2, 2, 3, 4, 4, 4,
+ 6, 5, 2, 4, 5, 5, 5, 7, 9, 5, 8, 8, 8, 7, 7, 12, 10, 12, 11,
+ 10, 9, 7, 17, 16, 15, 13, 11, 10, 9, 2, 6, 8, 9, 10, 9, 11, 5, 7,
+ 9, 10, 11, 11, 12, 9, 8, 10, 10, 10, 11, 11, 12, 9, 12, 12, 12, 13, 11,
+ 12, 14, 15, 15, 16, 16, 14, 13, 20, 21, 20, 18, 17, 16, 18, 26, 24, 22, 19,
+ 18, 17, 13, 15, 16, 17, 18, 17, 11, 15, 17, 17, 18, 19, 17, 11, 15, 18, 19,
+ 18, 19, 18, 13, 15, 22, 21, 20, 21, 20, 16, 17, 26, 26, 25, 25, 23, 17, 22,
+ 30, 31, 30, 27, 24, 19, 26, 33, 34, 32, 29, 26, 21, 25, 27, 26, 26, 23, 20,
+ 15, 24, 29, 28, 27, 24, 20, 16, 25, 31, 30, 30, 25, 20, 16, 24, 31, 32, 32,
+ 28, 23, 19, 26, 32, 36, 36, 32, 26, 21, 29, 35, 42, 41, 35, 28, 23, 33, 38,
+ 42, 42, 37, 30, 25, 33, 40, 37, 32, 27, 22, 18, 32, 39, 40, 33, 27, 22, 18,
+ 34, 40, 43, 35, 28, 23, 19, 33, 38, 44, 38, 32, 28, 23, 34, 40, 45, 43, 36,
+ 31, 25, 36, 43, 48, 47, 40, 33, 26, 38, 44, 49, 49, 42, 35, 28, 2, 3, 5,
+ 6, 6, 6, 4, 2, 3, 4, 6, 6, 4, 3, 2, 2, 3, 4, 4, 2, 1, 2,
+ 2, 1, 2, 2, 1, 2, 3, 2, 2, 3, 3, 2, 2, 7, 6, 6, 5, 4, 2,
+ 2, 12, 10, 8, 5, 4, 2, 2, 2, 3, 3, 5, 5, 6, 7, 2, 2, 3, 5,
+ 5, 4, 5, 2, 2, 3, 3, 3, 3, 4, 2, 2, 2, 0, 0, 2, 5, 5, 4,
+ 3, 4, 3, 3, 4, 9, 8, 7, 7, 5, 4, 5, 15, 13, 9, 8, 7, 5, 5,
+ 2, 2, 5, 6, 7, 8, 10, 2, 2, 5, 6, 7, 7, 9, 4, 2, 5, 6, 6,
+ 6, 8, 8, 2, 5, 7, 7, 7, 8, 11, 6, 8, 10, 11, 10, 8, 15, 11, 12,
+ 13, 12, 11, 8, 17, 15, 15, 16, 14, 13, 11, 4, 7, 11, 11, 12, 11, 14, 8,
+ 7, 11, 12, 13, 13, 14, 11, 8, 12, 13, 13, 13, 13, 14, 10, 14, 14, 15, 14,
+ 13, 14, 14, 17, 18, 18, 18, 16, 15, 20, 22, 21, 20, 19, 18, 17, 26, 25, 24,
+ 22, 21, 20, 15, 17, 18, 19, 20, 19, 14, 17, 19, 20, 21, 21, 20, 14, 17, 20,
+ 21, 22, 22, 20, 16, 17, 23, 24, 24, 23, 23, 19, 19, 27, 28, 28, 28, 26, 20,
+ 22, 31, 33, 32, 30, 27, 22, 27, 34, 37, 34, 32, 29, 23, 27, 29, 29, 28, 26,
+ 22, 18, 27, 32, 30, 30, 27, 22, 18, 27, 33, 33, 32, 28, 23, 18, 27, 33, 35,
+ 34, 30, 25, 21, 28, 36, 40, 38, 35, 29, 24, 31, 38, 45, 43, 36, 30, 26, 34,
+ 40, 45, 46, 39, 34, 27, 37, 42, 41, 35, 29, 25, 20, 36, 42, 43, 37, 30, 25,
+ 21, 36, 43, 45, 38, 31, 26, 22, 37, 42, 48, 41, 35, 30, 25, 37, 43, 49, 46,
+ 39, 33, 28, 38, 46, 51, 50, 42, 35, 30, 41, 47, 52, 53, 45, 38, 32, 2, 3,
+ 6, 9, 9, 7, 5, 2, 3, 5, 8, 9, 6, 3, 2, 2, 3, 6, 7, 4, 1,
+ 2, 1, 1, 3, 5, 3, 2, 3, 2, 2, 3, 5, 3, 2, 6, 6, 6, 5, 5,
+ 4, 2, 11, 9, 7, 5, 5, 3, 2, 2, 2, 3, 7, 6, 6, 7, 2, 2, 3,
+ 5, 6, 5, 5, 2, 2, 3, 3, 3, 3, 4, 2, 2, 2, 0, 0, 1, 4, 5,
+ 3, 3, 3, 3, 2, 4, 9, 7, 7, 6, 5, 4, 4, 14, 11, 8, 7, 6, 5,
+ 4, 1, 2, 5, 6, 7, 8, 9, 2, 2, 5, 6, 7, 7, 8, 5, 2, 4, 6,
+ 6, 5, 7, 8, 2, 5, 7, 7, 7, 7, 12, 5, 8, 10, 11, 10, 7, 15, 11,
+ 11, 13, 12, 11, 7, 16, 14, 13, 14, 14, 13, 11, 5, 6, 10, 11, 12, 11, 13,
+ 9, 7, 11, 12, 13, 13, 12, 11, 7, 11, 12, 13, 13, 11, 14, 9, 13, 14, 14,
+ 14, 12, 15, 14, 17, 19, 19, 18, 15, 14, 18, 21, 22, 21, 19, 18, 16, 23, 23,
+ 22, 22, 21, 20, 15, 16, 18, 19, 20, 19, 14, 18, 18, 20, 21, 21, 20, 13, 18,
+ 19, 21, 22, 21, 19, 15, 17, 22, 24, 24, 23, 22, 18, 19, 26, 28, 27, 28, 25,
+ 20, 21, 30, 32, 31, 30, 27, 22, 24, 34, 36, 34, 32, 29, 23, 26, 29, 29, 29,
+ 26, 22, 17, 26, 31, 31, 30, 27, 22, 17, 26, 33, 33, 32, 27, 22, 18, 26, 33,
+ 35, 34, 30, 25, 21, 28, 35, 39, 38, 35, 29, 24, 31, 37, 44, 42, 37, 31, 25,
+ 34, 38, 44, 45, 39, 34, 27, 36, 42, 41, 33, 29, 25, 20, 37, 43, 43, 36, 30,
+ 25, 21, 36, 42, 45, 38, 31, 26, 22, 36, 42, 46, 41, 34, 29, 25, 37, 43, 48,
+ 45, 39, 33, 27, 38, 46, 51, 50, 42, 35, 29, 40, 47, 51, 51, 43, 36, 31, 3,
+ 4, 6, 8, 8, 6, 4, 3, 3, 5, 7, 7, 5, 3, 2, 3, 4, 6, 6, 4,
+ 2, 1, 1, 2, 4, 5, 3, 2, 2, 2, 2, 4, 6, 4, 2, 6, 5, 5, 5,
+ 6, 4, 2, 9, 7, 5, 5, 6, 4, 2, 2, 3, 4, 6, 5, 6, 6, 2, 3,
+ 4, 5, 5, 4, 4, 1, 2, 3, 4, 3, 2, 3, 1, 1, 1, 2, 1, 0, 3,
+ 4, 3, 3, 3, 3, 2, 3, 8, 6, 5, 5, 3, 2, 2, 11, 8, 6, 5, 5,
+ 4, 3, 1, 2, 5, 5, 6, 8, 9, 2, 2, 5, 6, 6, 6, 7, 3, 1, 5,
+ 5, 5, 5, 6, 8, 1, 4, 5, 5, 5, 5, 11, 5, 7, 9, 9, 8, 5, 12,
+ 9, 9, 11, 10, 9, 5, 13, 11, 10, 12, 11, 11, 10, 3, 6, 10, 11, 11, 11,
+ 12, 8, 7, 11, 12, 12, 12, 11, 11, 7, 11, 12, 12, 12, 10, 14, 8, 12, 13,
+ 13, 13, 10, 15, 13, 15, 17, 17, 16, 13, 12, 16, 18, 19, 18, 18, 16, 13, 19,
+ 21, 20, 19, 19, 18, 14, 16, 18, 19, 20, 19, 13, 17, 18, 19, 20, 20, 19, 12,
+ 17, 18, 21, 20, 20, 19, 15, 17, 21, 22, 22, 23, 21, 17, 19, 26, 26, 26, 25,
+ 24, 18, 20, 28, 30, 29, 27, 25, 20, 21, 29, 32, 30, 29, 27, 21, 25, 28, 28,
+ 28, 24, 21, 17, 26, 30, 29, 29, 26, 21, 17, 26, 32, 31, 29, 26, 21, 18, 25,
+ 32, 34, 31, 27, 24, 20, 28, 33, 38, 35, 31, 27, 22, 29, 35, 41, 39, 33, 28,
+ 24, 30, 36, 42, 40, 36, 31, 26, 35, 40, 39, 32, 27, 23, 19, 35, 40, 41, 34,
+ 29, 24, 20, 36, 41, 43, 35, 29, 25, 20, 36, 39, 43, 38, 33, 28, 23, 35, 42,
+ 45, 42, 36, 30, 26, 38, 43, 47, 45, 39, 32, 27, 38, 43, 47, 47, 41, 33, 29,
+ 4, 7, 8, 11, 10, 9, 6, 4, 6, 8, 10, 10, 7, 5, 3, 5, 7, 8, 9,
+ 6, 3, 3, 3, 5, 7, 8, 5, 3, 1, 0, 3, 5, 7, 5, 3, 2, 2, 4,
+ 5, 7, 5, 3, 4, 3, 3, 4, 6, 5, 3, 4, 5, 7, 9, 8, 6, 7, 3,
+ 4, 6, 8, 7, 5, 4, 2, 3, 5, 7, 5, 3, 2, 2, 2, 3, 5, 4, 3,
+ 0, 1, 1, 2, 4, 3, 2, 0, 4, 3, 3, 4, 2, 2, 0, 7, 4, 3, 3,
+ 3, 1, 0, 3, 4, 4, 5, 7, 8, 8, 2, 3, 5, 6, 6, 6, 6, 2, 3,
+ 5, 4, 4, 4, 5, 3, 2, 3, 3, 2, 3, 3, 6, 2, 4, 4, 4, 4, 3,
+ 7, 4, 5, 5, 5, 5, 3, 8, 6, 6, 7, 6, 6, 6, 2, 7, 8, 8, 9,
+ 10, 11, 3, 7, 9, 9, 10, 10, 9, 8, 7, 9, 9, 10, 10, 8, 11, 7, 10,
+ 10, 10, 10, 7, 10, 9, 11, 11, 12, 11, 11, 7, 11, 13, 13, 13, 13, 12, 8,
+ 14, 15, 15, 14, 14, 14, 12, 15, 15, 16, 17, 16, 11, 14, 16, 17, 18, 17, 15,
+ 9, 14, 18, 18, 18, 18, 16, 12, 14, 20, 19, 18, 18, 17, 13, 14, 21, 22, 21,
+ 20, 18, 14, 14, 22, 24, 23, 22, 20, 15, 16, 22, 27, 25, 23, 21, 17, 24, 27,
+ 24, 24, 22, 18, 14, 24, 28, 27, 26, 23, 18, 14, 24, 29, 29, 26, 22, 19, 15,
+ 24, 30, 30, 27, 24, 20, 15, 23, 30, 32, 30, 25, 21, 17, 24, 30, 36, 33, 27,
+ 23, 19, 25, 30, 35, 34, 30, 25, 20, 32, 37, 34, 29, 24, 20, 17, 33, 37, 37,
+ 30, 26, 21, 16, 32, 38, 39, 32, 26, 22, 17, 32, 37, 40, 33, 27, 23, 19, 31,
+ 37, 40, 36, 30, 24, 20, 31, 37, 41, 38, 32, 27, 21, 33, 36, 42, 40, 34, 28,
+ 23, 5, 7, 10, 14, 14, 11, 8, 5, 6, 9, 13, 14, 11, 8, 4, 6, 8, 11,
+ 13, 10, 6, 4, 4, 5, 8, 11, 8, 2, 2, 2, 2, 4, 6, 6, 2, 3, 3,
+ 3, 3, 5, 6, 2, 9, 8, 5, 3, 4, 6, 2, 4, 6, 8, 11, 11, 8, 6,
+ 4, 5, 7, 9, 10, 7, 5, 3, 5, 6, 8, 8, 6, 4, 3, 3, 4, 5, 5,
+ 4, 1, 0, 2, 2, 2, 2, 2, 1, 5, 5, 5, 4, 2, 2, 1, 12, 10, 7,
+ 5, 2, 2, 1, 4, 5, 5, 6, 7, 6, 5, 3, 4, 5, 5, 5, 5, 5, 2,
+ 4, 4, 4, 4, 4, 4, 2, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 2, 1,
+ 1, 8, 7, 7, 5, 3, 1, 1, 14, 14, 9, 6, 4, 1, 1, 2, 4, 4, 5,
+ 5, 6, 6, 2, 3, 5, 5, 5, 5, 6, 2, 3, 4, 5, 5, 5, 6, 3, 4,
+ 4, 4, 5, 5, 6, 3, 7, 7, 7, 7, 7, 6, 8, 13, 12, 11, 9, 8, 8,
+ 14, 18, 15, 13, 11, 10, 9, 9, 9, 10, 10, 11, 9, 6, 10, 11, 11, 11, 12,
+ 10, 6, 10, 12, 12, 12, 13, 12, 7, 10, 14, 13, 13, 14, 12, 8, 11, 17, 17,
+ 16, 16, 15, 9, 14, 19, 21, 20, 19, 16, 11, 18, 22, 24, 21, 20, 19, 13, 17,
+ 20, 20, 20, 17, 12, 8, 18, 22, 21, 21, 18, 13, 9, 17, 23, 23, 22, 19, 15,
+ 10, 18, 22, 24, 23, 20, 16, 11, 17, 23, 26, 24, 22, 19, 13, 19, 25, 28, 27,
+ 23, 20, 15, 22, 26, 29, 27, 24, 21, 17, 25, 30, 28, 25, 20, 15, 11, 26, 30,
+ 31, 26, 22, 16, 12, 26, 31, 33, 27, 22, 18, 13, 26, 30, 33, 27, 23, 19, 15,
+ 24, 29, 32, 29, 24, 21, 17, 25, 28, 32, 31, 26, 22, 19, 27, 30, 32, 32, 28,
+ 23, 20, 5, 6, 7, 8, 8, 7, 7, 5, 6, 7, 8, 8, 7, 6, 4, 5, 6,
+ 7, 7, 5, 4, 3, 4, 5, 5, 5, 3, 2, 1, 2, 2, 2, 3, 2, 1, 3,
+ 3, 3, 2, 3, 2, 1, 9, 7, 5, 3, 2, 2, 2, 4, 5, 7, 7, 7, 7,
+ 7, 4, 5, 6, 7, 7, 6, 5, 3, 4, 6, 6, 6, 5, 4, 3, 3, 3, 4,
+ 3, 3, 1, 2, 0, 2, 2, 2, 1, 1, 5, 4, 4, 3, 1, 2, 1, 12, 9,
+ 6, 4, 2, 1, 1, 3, 5, 5, 6, 7, 7, 8, 3, 4, 5, 6, 6, 6, 7,
+ 2, 3, 4, 5, 5, 5, 5, 3, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2,
+ 2, 3, 8, 7, 6, 5, 4, 3, 3, 14, 12, 9, 7, 5, 4, 4, 3, 5, 6,
+ 7, 8, 9, 10, 2, 5, 6, 7, 8, 8, 9, 3, 6, 6, 6, 7, 7, 8, 6,
+ 6, 7, 7, 7, 8, 8, 6, 10, 9, 10, 10, 9, 9, 8, 15, 14, 13, 12, 10,
+ 10, 14, 21, 18, 15, 13, 12, 11, 11, 12, 12, 13, 14, 13, 10, 12, 14, 14, 14,
+ 15, 14, 9, 12, 15, 15, 15, 15, 14, 9, 13, 17, 16, 15, 16, 15, 11, 13, 20,
+ 19, 19, 19, 17, 12, 17, 22, 24, 23, 20, 18, 13, 21, 26, 28, 25, 23, 21, 15,
+ 21, 22, 21, 23, 20, 16, 11, 21, 24, 24, 24, 21, 16, 12, 21, 26, 27, 25, 22,
+ 17, 12, 21, 26, 27, 26, 22, 17, 13, 20, 26, 29, 29, 25, 20, 15, 22, 28, 35,
+ 34, 28, 22, 17, 25, 33, 36, 36, 31, 25, 18, 29, 36, 35, 28, 22, 19, 14, 29,
+ 35, 37, 29, 24, 19, 15, 30, 36, 39, 30, 25, 20, 15, 29, 35, 39, 32, 26, 21,
+ 17, 28, 34, 38, 36, 31, 24, 19, 29, 35, 41, 40, 32, 27, 21, 32, 37, 43, 41,
+ 35, 28, 23, 5, 7, 8, 9, 9, 9, 8, 5, 6, 7, 9, 9, 7, 6, 4, 5,
+ 7, 7, 7, 5, 4, 4, 4, 4, 5, 5, 3, 1, 2, 1, 2, 2, 2, 2, 1,
+ 3, 3, 3, 2, 2, 2, 1, 8, 7, 4, 2, 2, 2, 1, 4, 5, 7, 8, 8,
+ 8, 8, 4, 5, 7, 8, 8, 7, 7, 4, 5, 6, 6, 6, 5, 4, 3, 3, 3,
+ 4, 3, 3, 2, 2, 2, 0, 1, 1, 1, 2, 5, 4, 4, 3, 1, 1, 2, 12,
+ 9, 6, 3, 2, 1, 2, 4, 5, 6, 7, 8, 10, 10, 3, 4, 6, 7, 8, 8,
+ 9, 3, 4, 6, 6, 6, 6, 7, 3, 3, 4, 4, 4, 5, 6, 5, 2, 4, 5,
+ 5, 4, 5, 9, 7, 9, 8, 6, 5, 6, 14, 12, 11, 9, 8, 7, 7, 3, 7,
+ 8, 9, 10, 12, 12, 3, 7, 9, 10, 10, 10, 11, 7, 8, 9, 10, 10, 10, 11,
+ 10, 7, 10, 10, 10, 10, 10, 9, 11, 12, 12, 12, 12, 11, 9, 16, 17, 15, 14,
+ 13, 13, 14, 22, 20, 18, 16, 15, 14, 13, 15, 15, 16, 17, 16, 12, 14, 16, 17,
+ 17, 18, 16, 11, 14, 18, 19, 18, 17, 17, 13, 14, 19, 19, 19, 19, 17, 13, 14,
+ 23, 21, 21, 20, 19, 14, 18, 25, 27, 25, 23, 21, 16, 23, 28, 31, 28, 25, 23,
+ 18, 24, 25, 26, 26, 22, 18, 14, 23, 29, 26, 28, 24, 19, 15, 24, 29, 30, 28,
+ 24, 20, 15, 24, 29, 30, 30, 26, 20, 16, 23, 29, 33, 32, 28, 23, 17, 26, 31,
+ 38, 37, 30, 24, 19, 28, 34, 39, 38, 33, 26, 21, 33, 39, 38, 31, 26, 20, 17,
+ 33, 39, 41, 32, 27, 22, 17, 33, 40, 42, 34, 27, 23, 18, 33, 38, 43, 36, 29,
+ 24, 19, 31, 37, 42, 39, 33, 28, 21, 32, 37, 44, 44, 35, 29, 23, 34, 41, 45,
+ 45, 38, 32, 25, 5, 6, 9, 10, 10, 9, 8, 5, 6, 8, 9, 9, 8, 6, 4,
+ 5, 7, 8, 7, 6, 4, 4, 4, 4, 5, 6, 4, 2, 2, 2, 2, 2, 3, 2,
+ 1, 3, 3, 3, 2, 2, 2, 1, 8, 7, 4, 2, 2, 2, 1, 4, 6, 7, 9,
+ 9, 9, 10, 4, 5, 6, 8, 9, 8, 8, 4, 5, 6, 6, 6, 6, 6, 3, 3,
+ 3, 4, 3, 3, 3, 2, 2, 2, 0, 0, 1, 4, 6, 5, 4, 3, 2, 2, 4,
+ 11, 8, 6, 4, 3, 2, 4, 4, 5, 7, 9, 10, 11, 12, 3, 4, 8, 9, 9,
+ 10, 10, 3, 4, 7, 8, 8, 8, 8, 6, 3, 5, 6, 6, 6, 7, 8, 3, 5,
+ 7, 7, 6, 7, 11, 7, 9, 10, 8, 8, 7, 13, 11, 11, 12, 10, 9, 9, 3,
+ 8, 10, 11, 12, 13, 14, 6, 8, 11, 12, 13, 13, 13, 9, 8, 11, 12, 13, 12,
+ 13, 12, 8, 12, 12, 13, 13, 11, 11, 11, 14, 14, 15, 14, 14, 11, 16, 18, 18,
+ 16, 16, 16, 13, 20, 21, 20, 18, 17, 17, 13, 17, 18, 19, 19, 19, 15, 16, 18,
+ 19, 20, 21, 19, 13, 15, 19, 21, 21, 21, 19, 15, 16, 21, 22, 21, 22, 20, 15,
+ 15, 24, 25, 24, 24, 22, 17, 19, 27, 29, 28, 26, 23, 18, 22, 28, 32, 30, 29,
+ 25, 20, 26, 29, 28, 28, 26, 22, 17, 27, 31, 31, 30, 26, 22, 17, 26, 34, 33,
+ 32, 27, 22, 18, 26, 33, 34, 32, 27, 23, 18, 25, 32, 36, 35, 31, 25, 20, 27,
+ 35, 40, 39, 33, 28, 22, 30, 36, 41, 42, 35, 30, 24, 37, 42, 40, 34, 28, 24,
+ 20, 36, 43, 43, 35, 30, 23, 20, 36, 42, 45, 37, 31, 24, 20, 36, 41, 46, 39,
+ 32, 26, 22, 35, 40, 45, 42, 34, 30, 24, 35, 42, 47, 46, 38, 32, 25, 38, 43,
+ 48, 48, 41, 34, 27, 5, 6, 9, 12, 13, 10, 8, 5, 6, 8, 12, 12, 9, 6,
+ 4, 5, 7, 9, 10, 7, 3, 4, 4, 4, 6, 8, 5, 2, 2, 1, 1, 3, 6,
+ 4, 1, 3, 2, 2, 2, 4, 4, 1, 5, 3, 2, 2, 3, 4, 1, 4, 5, 7,
+ 10, 10, 9, 9, 4, 5, 6, 9, 9, 7, 7, 3, 4, 5, 7, 6, 5, 5, 3,
+ 3, 3, 3, 3, 3, 3, 2, 2, 1, 0, 0, 1, 3, 4, 3, 2, 2, 2, 1,
+ 3, 7, 4, 3, 2, 3, 2, 3, 4, 5, 7, 8, 10, 11, 11, 3, 4, 7, 9,
+ 9, 9, 9, 3, 4, 7, 7, 7, 7, 8, 7, 2, 4, 6, 6, 6, 6, 9, 2,
+ 5, 7, 7, 7, 6, 9, 4, 6, 9, 9, 8, 6, 9, 7, 7, 10, 9, 9, 9,
+ 3, 7, 11, 11, 12, 13, 14, 8, 7, 11, 12, 13, 13, 13, 11, 8, 12, 12, 13,
+ 12, 12, 12, 7, 12, 13, 13, 13, 11, 12, 10, 14, 14, 15, 15, 14, 10, 12, 15,
+ 16, 16, 16, 15, 9, 15, 18, 18, 17, 17, 17, 14, 17, 18, 19, 20, 20, 14, 16,
+ 18, 20, 20, 21, 19, 13, 16, 19, 21, 21, 21, 19, 15, 16, 21, 21, 22, 22, 20,
+ 16, 15, 24, 24, 24, 24, 22, 17, 16, 24, 28, 27, 26, 23, 18, 18, 27, 30, 28,
+ 27, 25, 20, 27, 30, 28, 28, 25, 22, 18, 26, 31, 30, 31, 27, 22, 17, 26, 34,
+ 33, 31, 26, 22, 18, 26, 32, 34, 32, 28, 23, 18, 25, 32, 37, 35, 31, 26, 20,
+ 26, 33, 39, 38, 32, 28, 22, 27, 34, 40, 40, 35, 29, 24, 37, 43, 40, 34, 30,
+ 24, 21, 37, 42, 43, 36, 30, 25, 20, 37, 42, 45, 36, 30, 26, 21, 36, 41, 46,
+ 38, 31, 27, 22, 35, 41, 44, 40, 34, 28, 24, 36, 42, 46, 45, 38, 31, 26, 35,
+ 41, 46, 45, 40, 33, 28, 4, 5, 6, 8, 8, 7, 6, 4, 4, 5, 8, 8, 5,
+ 4, 3, 4, 5, 6, 6, 4, 2, 3, 3, 3, 4, 6, 3, 1, 2, 1, 0, 2,
+ 5, 3, 1, 2, 2, 2, 3, 4, 3, 1, 4, 3, 2, 3, 3, 3, 1, 4, 4,
+ 5, 6, 6, 7, 8, 3, 4, 5, 6, 6, 6, 6, 3, 4, 4, 4, 3, 4, 4,
+ 2, 2, 2, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 2, 3, 2, 2, 2, 1,
+ 1, 2, 7, 4, 2, 2, 3, 2, 2, 3, 4, 6, 7, 8, 9, 10, 2, 3, 6,
+ 7, 7, 8, 9, 3, 3, 6, 6, 6, 6, 7, 7, 2, 4, 6, 6, 5, 5, 9,
+ 2, 4, 6, 7, 7, 5, 10, 4, 5, 7, 8, 8, 5, 9, 7, 6, 8, 9, 9,
+ 9, 3, 7, 10, 11, 12, 12, 13, 8, 7, 11, 12, 13, 12, 12, 12, 8, 12, 12,
+ 12, 12, 11, 14, 7, 11, 13, 13, 13, 10, 14, 10, 12, 14, 14, 14, 13, 11, 12,
+ 14, 15, 16, 16, 15, 9, 15, 16, 17, 17, 17, 17, 14, 17, 19, 19, 20, 19, 13,
+ 17, 18, 19, 20, 20, 19, 12, 17, 19, 20, 21, 20, 19, 15, 17, 21, 21, 21, 22,
+ 20, 15, 16, 22, 24, 24, 23, 22, 17, 15, 23, 26, 26, 24, 22, 18, 16, 24, 28,
+ 27, 26, 24, 20, 26, 29, 28, 28, 25, 21, 17, 26, 31, 30, 30, 26, 22, 17, 26,
+ 33, 32, 31, 26, 22, 17, 26, 32, 33, 32, 27, 23, 18, 25, 32, 35, 33, 30, 25,
+ 20, 26, 33, 38, 36, 30, 26, 21, 26, 33, 38, 39, 33, 27, 23, 36, 41, 38, 32,
+ 27, 24, 20, 35, 41, 42, 34, 29, 25, 20, 35, 41, 44, 36, 30, 25, 20, 35, 40,
+ 45, 37, 31, 26, 22, 35, 40, 44, 40, 33, 27, 23, 34, 40, 44, 43, 36, 30, 25,
+ 35, 40, 45, 45, 37, 31, 25, 4, 6, 9, 11, 10, 9, 6, 4, 6, 8, 10, 10,
+ 7, 4, 3, 5, 7, 8, 9, 6, 4, 3, 3, 5, 7, 8, 5, 3, 1, 0, 3,
+ 5, 7, 5, 3, 2, 2, 4, 5, 7, 5, 3, 4, 3, 3, 4, 6, 5, 3, 4,
+ 5, 7, 9, 8, 7, 6, 3, 4, 6, 8, 7, 5, 4, 3, 4, 5, 6, 5, 3,
+ 2, 2, 2, 4, 5, 4, 3, 0, 1, 1, 2, 3, 3, 2, 0, 3, 2, 3, 4,
+ 2, 2, 0, 6, 4, 3, 3, 2, 1, 0, 3, 4, 4, 5, 7, 8, 8, 2, 3,
+ 5, 6, 6, 6, 6, 2, 3, 5, 4, 4, 4, 5, 3, 2, 3, 3, 3, 3, 3,
+ 6, 2, 4, 4, 4, 4, 3, 7, 4, 5, 5, 5, 5, 3, 9, 7, 6, 6, 6,
+ 6, 6, 2, 7, 8, 9, 9, 10, 11, 3, 7, 9, 9, 10, 10, 9, 8, 7, 9,
+ 9, 10, 9, 8, 11, 7, 10, 10, 9, 10, 7, 10, 9, 11, 11, 12, 11, 10, 8,
+ 11, 13, 13, 13, 13, 12, 8, 14, 15, 15, 14, 14, 13, 13, 15, 16, 16, 16, 16,
+ 11, 14, 17, 17, 17, 18, 16, 10, 14, 18, 18, 18, 18, 16, 12, 14, 20, 19, 18,
+ 18, 17, 13, 14, 21, 21, 20, 20, 18, 14, 15, 22, 24, 23, 21, 19, 15, 16, 23,
+ 26, 24, 24, 20, 17, 24, 25, 24, 24, 21, 18, 14, 24, 28, 27, 25, 22, 18, 14,
+ 24, 30, 29, 27, 22, 19, 15, 23, 29, 30, 27, 23, 20, 15, 23, 29, 32, 30, 26,
+ 22, 17, 24, 30, 36, 33, 28, 23, 18, 25, 30, 35, 35, 29, 25, 20, 32, 37, 34,
+ 29, 23, 20, 17, 32, 37, 36, 30, 25, 21, 17, 33, 37, 39, 31, 26, 22, 17, 32,
+ 37, 40, 33, 27, 23, 18, 31, 38, 39, 36, 29, 25, 20, 31, 36, 40, 39, 32, 26,
+ 20, 32, 36, 41, 40, 34, 28, 23, 10, 12, 14, 17, 18, 16, 13, 10, 11, 13, 17,
+ 17, 14, 12, 9, 11, 12, 14, 15, 12, 10, 9, 9, 10, 11, 14, 9, 4, 6, 7,
+ 7, 8, 8, 7, 4, 2, 2, 3, 3, 6, 7, 4, 5, 4, 2, 3, 5, 8, 4,
+ 9, 11, 12, 15, 15, 14, 12, 9, 10, 12, 14, 14, 12, 11, 8, 10, 11, 12, 12,
+ 11, 9, 8, 8, 9, 9, 9, 8, 3, 5, 5, 5, 5, 4, 4, 3, 0, 2, 2,
+ 2, 3, 3, 3, 8, 6, 3, 2, 2, 3, 3, 8, 10, 11, 11, 13, 13, 12, 8,
+ 9, 11, 11, 12, 12, 11, 7, 8, 10, 10, 10, 10, 9, 7, 7, 7, 7, 8, 7,
+ 4, 3, 3, 4, 4, 3, 3, 3, 3, 2, 2, 1, 2, 2, 3, 9, 9, 5, 2,
+ 1, 3, 3, 7, 9, 10, 11, 12, 13, 13, 7, 8, 10, 11, 12, 12, 12, 6, 8,
+ 9, 10, 10, 10, 10, 4, 7, 7, 7, 7, 7, 5, 2, 3, 4, 4, 4, 4, 5,
+ 3, 8, 7, 7, 6, 6, 6, 9, 13, 11, 9, 7, 7, 8, 7, 8, 9, 11, 12,
+ 13, 13, 8, 9, 9, 10, 11, 11, 12, 8, 10, 11, 11, 11, 9, 9, 9, 12, 12,
+ 11, 12, 11, 6, 8, 13, 13, 14, 14, 13, 8, 9, 15, 16, 15, 15, 15, 10, 13,
+ 17, 18, 16, 16, 16, 12, 16, 18, 19, 19, 15, 12, 13, 16, 20, 20, 20, 16, 12,
+ 11, 16, 22, 22, 21, 18, 12, 9, 16, 21, 21, 21, 19, 14, 10, 15, 21, 22, 21,
+ 18, 16, 12, 16, 20, 22, 21, 19, 16, 13, 17, 21, 23, 22, 20, 17, 15, 24, 28,
+ 27, 23, 18, 14, 12, 24, 28, 28, 24, 20, 15, 10, 24, 28, 30, 23, 21, 16, 11,
+ 24, 27, 29, 25, 20, 18, 13, 22, 26, 28, 25, 20, 18, 15, 22, 25, 27, 26, 20,
+ 18, 16, 22, 24, 27, 28, 22, 19, 16, 9, 11, 12, 13, 13, 13, 12, 9, 10, 12,
+ 13, 13, 11, 10, 8, 10, 11, 11, 11, 10, 8, 8, 8, 9, 9, 9, 6, 3, 5,
+ 6, 6, 6, 4, 3, 2, 2, 2, 2, 2, 3, 3, 3, 4, 3, 2, 2, 3, 3,
+ 3, 8, 10, 11, 12, 13, 13, 12, 8, 9, 10, 12, 12, 11, 10, 7, 9, 10, 10,
+ 10, 9, 7, 7, 7, 7, 8, 7, 6, 2, 5, 4, 4, 5, 3, 2, 2, 2, 0,
+ 1, 2, 2, 2, 2, 7, 5, 2, 2, 2, 2, 2, 8, 9, 10, 11, 12, 13, 13,
+ 7, 8, 10, 11, 11, 11, 11, 7, 8, 9, 9, 9, 9, 8, 7, 7, 6, 6, 7,
+ 6, 4, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 2, 2, 3, 3, 9, 8, 5,
+ 3, 3, 3, 4, 7, 9, 10, 11, 13, 14, 14, 7, 9, 10, 11, 12, 12, 12, 5,
+ 9, 10, 10, 10, 10, 9, 6, 8, 7, 7, 8, 7, 7, 4, 6, 7, 7, 7, 7,
+ 8, 3, 10, 10, 9, 9, 9, 9, 9, 16, 13, 11, 10, 10, 11, 10, 11, 11, 12,
+ 13, 14, 14, 10, 12, 13, 13, 14, 12, 12, 11, 14, 14, 14, 14, 12, 9, 11, 15,
+ 15, 15, 15, 13, 9, 10, 17, 16, 16, 17, 15, 11, 12, 18, 20, 18, 18, 16, 12,
+ 16, 21, 23, 20, 20, 18, 14, 19, 21, 21, 21, 18, 15, 14, 20, 23, 23, 23, 19,
+ 15, 12, 19, 25, 25, 24, 20, 15, 11, 19, 25, 26, 25, 21, 17, 12, 18, 23, 29,
+ 27, 23, 18, 14, 19, 25, 31, 30, 25, 20, 15, 21, 27, 32, 32, 27, 22, 17, 29,
+ 34, 33, 27, 21, 17, 14, 28, 34, 36, 29, 23, 18, 13, 28, 34, 39, 30, 24, 19,
+ 14, 28, 34, 38, 30, 25, 20, 15, 27, 33, 39, 35, 28, 22, 17, 26, 32, 37, 37,
+ 29, 24, 18, 27, 33, 36, 37, 32, 26, 20, 9, 11, 12, 13, 13, 13, 12, 9, 10,
+ 11, 13, 13, 11, 10, 8, 9, 11, 11, 10, 9, 7, 8, 8, 8, 9, 8, 6, 2,
+ 6, 5, 5, 5, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 3, 1, 2, 2,
+ 2, 2, 8, 10, 11, 12, 13, 13, 13, 8, 9, 10, 12, 12, 11, 10, 8, 8, 10,
+ 10, 9, 9, 7, 8, 7, 7, 7, 7, 5, 3, 5, 4, 4, 4, 2, 2, 2, 2,
+ 1, 0, 2, 2, 2, 2, 6, 4, 2, 1, 2, 2, 3, 8, 9, 10, 11, 13, 14,
+ 15, 8, 8, 10, 12, 12, 12, 12, 7, 8, 10, 10, 10, 10, 9, 6, 7, 8, 8,
+ 8, 6, 5, 4, 3, 5, 5, 4, 4, 5, 5, 3, 5, 4, 4, 4, 5, 7, 6,
+ 7, 6, 5, 5, 5, 8, 11, 12, 13, 14, 16, 15, 6, 10, 12, 13, 14, 14, 13,
+ 6, 10, 12, 11, 11, 11, 10, 9, 9, 9, 9, 9, 9, 9, 7, 7, 9, 10, 10,
+ 10, 11, 5, 12, 12, 12, 12, 12, 12, 7, 15, 15, 14, 13, 13, 13, 11, 14, 15,
+ 15, 16, 16, 16, 13, 15, 16, 16, 16, 15, 13, 12, 16, 17, 17, 17, 15, 12, 13,
+ 18, 18, 18, 18, 17, 13, 12, 19, 19, 19, 19, 18, 13, 13, 22, 23, 22, 20, 19,
+ 15, 17, 22, 26, 24, 22, 21, 16, 22, 24, 25, 24, 22, 18, 16, 22, 26, 26, 27,
+ 23, 18, 13, 22, 29, 28, 27, 23, 18, 14, 22, 27, 28, 28, 24, 19, 15, 22, 28,
+ 31, 31, 26, 21, 16, 22, 28, 34, 32, 27, 22, 18, 24, 29, 35, 36, 30, 24, 20,
+ 32, 38, 37, 29, 24, 21, 16, 31, 38, 38, 32, 26, 21, 16, 31, 37, 41, 32, 27,
+ 21, 17, 31, 37, 42, 34, 28, 23, 18, 30, 35, 42, 38, 30, 24, 19, 29, 35, 41,
+ 40, 33, 26, 21, 30, 36, 40, 41, 34, 28, 22, 8, 9, 11, 12, 13, 12, 11, 8,
+ 9, 11, 12, 12, 11, 9, 7, 8, 10, 10, 10, 8, 6, 7, 7, 7, 8, 8, 5,
+ 2, 5, 4, 4, 5, 3, 2, 2, 2, 2, 1, 2, 2, 2, 2, 3, 2, 1, 1,
+ 2, 2, 2, 8, 8, 10, 12, 12, 12, 13, 7, 8, 9, 11, 12, 10, 10, 7, 8,
+ 9, 10, 9, 8, 8, 6, 6, 6, 7, 6, 5, 4, 4, 3, 3, 3, 2, 2, 4,
+ 2, 2, 2, 0, 1, 2, 4, 5, 3, 2, 1, 2, 3, 4, 7, 8, 10, 11, 13,
+ 14, 15, 6, 7, 10, 12, 12, 13, 12, 6, 7, 10, 10, 10, 10, 10, 5, 6, 7,
+ 8, 8, 7, 6, 6, 2, 4, 5, 5, 6, 6, 7, 2, 5, 6, 6, 7, 6, 6,
+ 5, 6, 8, 7, 8, 8, 6, 10, 12, 13, 14, 16, 15, 5, 10, 12, 13, 14, 14,
+ 13, 8, 10, 12, 12, 12, 12, 11, 11, 8, 11, 11, 11, 12, 11, 10, 8, 12, 12,
+ 12, 13, 13, 8, 11, 14, 14, 14, 14, 14, 6, 14, 15, 16, 15, 15, 16, 13, 16,
+ 17, 18, 19, 18, 15, 15, 18, 19, 19, 19, 18, 13, 15, 18, 20, 19, 20, 18, 14,
+ 14, 20, 21, 20, 21, 20, 15, 14, 21, 22, 22, 22, 20, 16, 14, 23, 25, 24, 24,
+ 22, 17, 16, 23, 27, 26, 25, 23, 19, 25, 27, 27, 27, 24, 21, 17, 25, 31, 29,
+ 30, 25, 20, 16, 25, 31, 31, 30, 26, 21, 16, 25, 31, 32, 31, 27, 22, 18, 23,
+ 31, 34, 33, 29, 24, 18, 24, 31, 36, 34, 31, 25, 20, 26, 32, 37, 38, 33, 27,
+ 22, 35, 40, 40, 33, 27, 23, 19, 35, 41, 42, 34, 29, 24, 19, 35, 40, 44, 36,
+ 30, 25, 19, 35, 41, 45, 37, 31, 26, 20, 33, 39, 44, 40, 33, 27, 23, 33, 39,
+ 45, 42, 35, 30, 23, 33, 39, 44, 44, 38, 30, 25, 6, 8, 11, 14, 14, 12, 9,
+ 6, 7, 10, 13, 13, 11, 7, 5, 6, 9, 11, 12, 9, 5, 5, 5, 6, 8, 10,
+ 7, 2, 2, 2, 2, 5, 7, 5, 2, 2, 1, 1, 2, 5, 5, 2, 3, 2, 2,
+ 2, 4, 5, 2, 5, 6, 8, 11, 11, 10, 10, 5, 6, 7, 10, 11, 8, 8, 4,
+ 5, 6, 8, 8, 6, 6, 4, 4, 4, 5, 5, 3, 2, 2, 1, 1, 2, 2, 1,
+ 2, 3, 2, 2, 1, 0, 1, 2, 6, 3, 2, 2, 1, 1, 2, 5, 6, 8, 9,
+ 10, 11, 12, 4, 5, 8, 9, 10, 10, 9, 3, 5, 8, 8, 8, 8, 7, 5, 3,
+ 5, 5, 5, 5, 5, 8, 2, 4, 5, 6, 6, 5, 9, 3, 5, 7, 7, 7, 5,
+ 8, 6, 6, 8, 8, 8, 8, 3, 8, 10, 10, 11, 13, 14, 6, 8, 10, 11, 12,
+ 12, 13, 10, 8, 11, 12, 12, 12, 11, 12, 7, 11, 12, 11, 12, 10, 12, 8, 12,
+ 13, 13, 13, 13, 9, 11, 13, 14, 14, 14, 15, 8, 14, 16, 16, 15, 15, 16, 13,
+ 16, 17, 18, 20, 18, 14, 15, 17, 19, 19, 20, 19, 12, 15, 19, 20, 20, 20, 19,
+ 14, 15, 20, 20, 21, 21, 19, 14, 14, 21, 22, 22, 22, 20, 16, 14, 23, 25, 24,
+ 23, 22, 17, 16, 24, 28, 26, 27, 24, 19, 26, 29, 27, 28, 24, 21, 16, 25, 30,
+ 29, 29, 26, 21, 17, 25, 33, 33, 31, 26, 22, 17, 25, 32, 33, 31, 27, 23, 17,
+ 25, 31, 35, 33, 28, 24, 18, 25, 32, 36, 36, 30, 24, 20, 26, 32, 38, 39, 33,
+ 27, 22, 35, 41, 39, 33, 28, 23, 19, 36, 41, 42, 35, 29, 24, 19, 36, 40, 45,
+ 36, 30, 24, 20, 35, 40, 45, 37, 31, 25, 21, 34, 40, 44, 40, 33, 27, 22, 34,
+ 39, 43, 43, 35, 29, 23, 35, 39, 44, 44, 38, 31, 25, 4, 6, 10, 12, 13, 11,
+ 8, 4, 5, 9, 12, 12, 9, 6, 4, 5, 7, 10, 10, 8, 4, 3, 3, 4, 7,
+ 9, 6, 2, 1, 1, 1, 4, 6, 4, 2, 2, 2, 2, 3, 5, 4, 2, 4, 3,
+ 2, 2, 4, 5, 2, 4, 5, 7, 10, 10, 8, 8, 4, 5, 6, 9, 9, 6, 6,
+ 3, 4, 5, 7, 7, 4, 4, 3, 2, 2, 4, 4, 2, 2, 2, 2, 1, 2, 1,
+ 1, 2, 3, 2, 2, 2, 1, 0, 2, 6, 4, 2, 2, 2, 1, 2, 3, 4, 6,
+ 7, 8, 10, 10, 3, 4, 6, 7, 8, 8, 8, 2, 3, 6, 6, 6, 6, 7, 6,
+ 2, 4, 5, 5, 5, 5, 8, 2, 4, 6, 6, 6, 5, 9, 4, 5, 7, 7, 7,
+ 5, 8, 7, 6, 8, 8, 8, 8, 2, 7, 10, 10, 11, 12, 13, 7, 7, 11, 11,
+ 12, 12, 12, 11, 7, 11, 12, 11, 12, 10, 12, 7, 11, 12, 12, 12, 10, 12, 9,
+ 13, 13, 13, 13, 13, 9, 11, 14, 14, 15, 15, 14, 9, 14, 16, 17, 16, 16, 16,
+ 13, 16, 18, 18, 18, 18, 13, 15, 17, 19, 20, 20, 18, 12, 15, 19, 20, 20, 20,
+ 18, 14, 15, 20, 21, 21, 20, 19, 15, 15, 22, 23, 22, 23, 21, 16, 15, 23, 27,
+ 24, 24, 21, 18, 16, 24, 28, 27, 25, 23, 19, 25, 28, 27, 27, 25, 20, 16, 26,
+ 30, 29, 29, 25, 21, 16, 25, 32, 32, 31, 26, 21, 17, 26, 32, 33, 31, 26, 22,
+ 18, 24, 31, 35, 33, 29, 23, 19, 25, 32, 37, 35, 30, 25, 21, 25, 32, 37, 38,
+ 32, 27, 22, 35, 40, 39, 32, 27, 23, 19, 36, 40, 41, 34, 27, 24, 19, 35, 39,
+ 43, 35, 30, 24, 19, 36, 39, 44, 36, 31, 26, 21, 34, 39, 44, 38, 32, 27, 23,
+ 34, 39, 43, 42, 35, 29, 24, 34, 40, 43, 44, 37, 30, 25, 4, 6, 8, 11, 11,
+ 9, 6, 4, 6, 8, 10, 10, 7, 4, 3, 5, 7, 8, 9, 6, 4, 3, 3, 5,
+ 7, 8, 5, 3, 1, 0, 3, 5, 7, 5, 3, 2, 2, 4, 4, 7, 5, 3, 4,
+ 3, 3, 4, 6, 5, 3, 4, 5, 7, 9, 8, 6, 6, 3, 4, 6, 8, 7, 5,
+ 4, 3, 4, 5, 7, 5, 4, 2, 2, 2, 4, 5, 4, 3, 0, 1, 1, 2, 4,
+ 3, 2, 0, 3, 2, 3, 4, 2, 2, 0, 7, 4, 3, 3, 3, 1, 0, 3, 4,
+ 5, 5, 7, 8, 8, 2, 3, 5, 6, 6, 6, 6, 2, 3, 5, 4, 4, 4, 5,
+ 3, 2, 3, 3, 2, 3, 3, 6, 2, 4, 4, 4, 4, 3, 7, 4, 5, 5, 5,
+ 5, 3, 8, 7, 6, 6, 6, 6, 6, 2, 7, 8, 8, 9, 10, 11, 3, 7, 9,
+ 9, 10, 10, 10, 9, 7, 10, 9, 10, 10, 8, 11, 7, 10, 10, 9, 10, 7, 10,
+ 9, 11, 11, 12, 11, 11, 7, 11, 13, 12, 13, 13, 12, 8, 14, 15, 14, 14, 14,
+ 14, 12, 16, 16, 16, 17, 16, 11, 14, 16, 17, 17, 18, 16, 10, 15, 18, 18, 18,
+ 17, 16, 12, 14, 20, 19, 19, 19, 17, 12, 14, 21, 21, 21, 20, 18, 14, 15, 22,
+ 24, 22, 21, 19, 15, 16, 23, 26, 25, 23, 21, 17, 24, 26, 24, 25, 21, 18, 14,
+ 24, 28, 26, 25, 22, 18, 14, 24, 30, 29, 27, 23, 19, 14, 24, 29, 30, 28, 25,
+ 19, 15, 23, 29, 32, 30, 25, 21, 17, 24, 30, 35, 32, 27, 23, 18, 25, 30, 35,
+ 35, 30, 25, 20, 32, 37, 35, 28, 24, 21, 16, 32, 37, 38, 31, 25, 20, 17, 33,
+ 37, 39, 32, 27, 22, 17, 32, 37, 40, 33, 27, 22, 18, 31, 36, 41, 37, 30, 24,
+ 20, 31, 35, 40, 40, 32, 26, 21, 32, 37, 40, 41, 34, 28, 23, 17, 19, 20, 23,
+ 22, 21, 20, 17, 19, 20, 22, 22, 20, 18, 16, 18, 19, 19, 20, 17, 14, 16, 16,
+ 16, 17, 17, 12, 7, 13, 13, 13, 12, 10, 10, 7, 9, 9, 7, 6, 8, 9, 7,
+ 3, 3, 4, 6, 7, 9, 7, 16, 18, 19, 21, 21, 21, 20, 15, 18, 19, 20, 20,
+ 19, 17, 15, 17, 18, 19, 18, 17, 14, 14, 15, 15, 16, 14, 11, 7, 12, 12, 12,
+ 11, 7, 7, 7, 8, 7, 6, 5, 6, 6, 6, 0, 2, 4, 4, 5, 6, 6, 15,
+ 17, 18, 19, 21, 21, 20, 14, 16, 17, 19, 19, 19, 17, 15, 16, 17, 17, 17, 16,
+ 13, 15, 14, 14, 14, 13, 11, 6, 10, 10, 10, 9, 5, 6, 6, 3, 5, 5, 4,
+ 5, 6, 6, 2, 2, 2, 4, 5, 5, 6, 15, 16, 17, 18, 20, 21, 21, 14, 16,
+ 18, 19, 19, 19, 19, 12, 16, 17, 17, 17, 16, 15, 9, 15, 14, 14, 13, 10, 10,
+ 6, 11, 10, 9, 5, 5, 7, 4, 4, 4, 4, 4, 5, 6, 2, 7, 5, 5, 5,
+ 5, 6, 14, 15, 17, 19, 19, 21, 21, 12, 15, 16, 18, 19, 18, 19, 11, 14, 15,
+ 15, 16, 15, 14, 12, 12, 12, 12, 11, 10, 6, 9, 10, 10, 10, 11, 11, 6, 6,
+ 11, 13, 12, 12, 12, 7, 7, 12, 14, 13, 13, 12, 9, 13, 15, 16, 17, 17, 19,
+ 21, 13, 17, 17, 18, 16, 17, 18, 13, 19, 18, 18, 14, 14, 13, 13, 18, 19, 18,
+ 16, 11, 8, 12, 17, 19, 18, 15, 12, 9, 12, 16, 19, 17, 15, 13, 11, 13, 15,
+ 18, 18, 16, 14, 12, 21, 25, 24, 21, 16, 18, 19, 21, 25, 25, 21, 17, 16, 16,
+ 21, 24, 26, 22, 18, 13, 12, 21, 23, 25, 21, 18, 14, 11, 19, 21, 24, 20, 16,
+ 14, 12, 18, 21, 22, 21, 16, 15, 13, 17, 19, 22, 22, 18, 15, 13, 15, 16, 17,
+ 19, 18, 18, 17, 14, 16, 17, 18, 18, 17, 15, 13, 15, 16, 16, 16, 14, 11, 13,
+ 14, 14, 13, 12, 9, 5, 11, 11, 10, 9, 6, 5, 4, 7, 7, 5, 4, 5, 5,
+ 5, 2, 2, 2, 3, 5, 5, 4, 14, 15, 16, 18, 18, 18, 17, 14, 15, 16, 17,
+ 18, 16, 14, 13, 14, 15, 16, 15, 13, 10, 13, 12, 13, 12, 11, 9, 4, 10, 9,
+ 9, 8, 4, 4, 4, 6, 5, 4, 3, 3, 4, 4, 2, 0, 2, 2, 3, 4, 4,
+ 13, 14, 15, 16, 18, 18, 18, 13, 13, 15, 16, 16, 16, 15, 13, 13, 14, 14, 14,
+ 13, 11, 12, 12, 11, 11, 10, 8, 5, 8, 8, 8, 8, 3, 4, 4, 3, 4, 4,
+ 3, 3, 4, 4, 2, 2, 1, 2, 3, 4, 5, 13, 15, 15, 17, 19, 19, 19, 12,
+ 15, 16, 17, 17, 18, 16, 10, 15, 15, 15, 15, 14, 13, 7, 14, 13, 13, 12, 9,
+ 9, 5, 10, 9, 8, 6, 6, 7, 3, 6, 6, 7, 7, 7, 8, 2, 9, 9, 8,
+ 8, 9, 9, 13, 14, 15, 17, 18, 19, 20, 11, 14, 15, 16, 17, 17, 17, 12, 13,
+ 14, 14, 15, 14, 12, 11, 13, 12, 12, 13, 12, 8, 9, 13, 14, 14, 14, 14, 9,
+ 8, 15, 16, 17, 16, 15, 11, 10, 16, 19, 19, 19, 17, 12, 16, 19, 19, 20, 17,
+ 18, 19, 16, 21, 21, 21, 18, 16, 16, 16, 23, 23, 22, 18, 14, 12, 17, 22, 24,
+ 23, 19, 15, 11, 15, 22, 25, 25, 22, 17, 12, 16, 22, 28, 28, 24, 19, 14, 17,
+ 23, 28, 29, 25, 21, 16, 26, 32, 31, 24, 20, 17, 18, 25, 31, 33, 26, 21, 16,
+ 15, 26, 31, 35, 27, 22, 17, 12, 25, 31, 36, 29, 23, 18, 14, 24, 30, 35, 32,
+ 25, 21, 15, 24, 31, 35, 33, 28, 23, 17, 24, 31, 35, 33, 29, 24, 19, 11, 12,
+ 14, 15, 15, 15, 14, 10, 12, 13, 15, 14, 13, 11, 10, 11, 13, 13, 12, 10, 8,
+ 10, 10, 10, 10, 9, 6, 3, 7, 7, 7, 6, 4, 3, 2, 4, 3, 3, 2, 3,
+ 3, 2, 2, 1, 1, 2, 3, 3, 2, 10, 11, 13, 14, 15, 14, 14, 10, 11, 12,
+ 13, 14, 12, 12, 10, 10, 11, 12, 11, 10, 8, 10, 9, 9, 9, 8, 6, 3, 7,
+ 6, 6, 6, 3, 2, 3, 3, 2, 2, 2, 2, 2, 3, 4, 2, 0, 2, 2, 2,
+ 3, 10, 10, 11, 13, 14, 16, 16, 10, 10, 12, 13, 13, 14, 14, 10, 10, 11, 12,
+ 11, 11, 10, 9, 8, 9, 9, 9, 6, 5, 4, 5, 6, 6, 4, 3, 5, 3, 2,
+ 3, 3, 3, 4, 5, 4, 3, 4, 4, 4, 5, 5, 10, 13, 13, 14, 16, 17, 17,
+ 8, 12, 13, 14, 15, 15, 14, 6, 12, 13, 13, 13, 12, 11, 8, 11, 10, 10, 10,
+ 9, 9, 6, 7, 8, 9, 9, 10, 9, 4, 9, 10, 10, 11, 11, 11, 4, 12, 12,
+ 12, 12, 12, 12, 11, 13, 14, 15, 16, 17, 17, 11, 14, 15, 15, 16, 15, 14, 11,
+ 15, 16, 16, 16, 14, 11, 11, 17, 17, 16, 17, 15, 11, 11, 18, 17, 18, 18, 17,
+ 12, 12, 19, 21, 20, 20, 18, 14, 14, 20, 23, 21, 22, 20, 16, 21, 23, 23, 23,
+ 20, 17, 17, 21, 25, 25, 25, 21, 17, 14, 21, 28, 27, 26, 22, 17, 13, 21, 25,
+ 29, 27, 22, 19, 14, 20, 27, 31, 29, 25, 20, 15, 20, 26, 33, 32, 26, 21, 17,
+ 22, 27, 32, 34, 29, 24, 18, 30, 35, 35, 28, 23, 19, 16, 30, 36, 38, 31, 24,
+ 19, 15, 30, 36, 39, 32, 26, 20, 15, 30, 35, 40, 32, 27, 22, 17, 28, 35, 40,
+ 35, 29, 23, 19, 28, 35, 40, 38, 30, 26, 20, 29, 35, 40, 41, 33, 28, 21, 9,
+ 10, 12, 13, 13, 13, 12, 8, 9, 11, 13, 13, 11, 9, 8, 9, 11, 11, 11, 9,
+ 7, 8, 7, 8, 9, 8, 5, 2, 5, 5, 5, 5, 4, 2, 2, 2, 2, 1, 2,
+ 3, 2, 2, 3, 2, 1, 2, 2, 2, 2, 8, 9, 11, 12, 13, 12, 13, 8, 9,
+ 10, 12, 12, 10, 10, 7, 8, 9, 10, 10, 8, 8, 7, 7, 7, 8, 7, 5, 4,
+ 5, 4, 3, 4, 3, 2, 3, 2, 2, 1, 1, 2, 2, 3, 4, 2, 2, 0, 1,
+ 2, 3, 8, 8, 10, 11, 13, 14, 14, 7, 8, 10, 12, 12, 12, 12, 7, 7, 10,
+ 10, 10, 10, 9, 5, 6, 8, 8, 8, 6, 6, 5, 3, 5, 5, 5, 5, 6, 6,
+ 2, 5, 5, 5, 6, 6, 5, 4, 5, 7, 7, 7, 7, 7, 11, 12, 13, 14, 16,
+ 15, 5, 10, 12, 14, 13, 13, 13, 7, 10, 11, 11, 12, 12, 11, 10, 9, 10, 11,
+ 11, 11, 10, 9, 8, 11, 11, 12, 12, 12, 7, 10, 13, 13, 13, 13, 14, 5, 13,
+ 14, 15, 14, 14, 15, 12, 15, 16, 17, 18, 17, 15, 14, 16, 18, 18, 19, 17, 13,
+ 14, 17, 19, 19, 18, 17, 13, 14, 20, 19, 19, 20, 18, 14, 13, 21, 21, 20, 20,
+ 19, 15, 14, 22, 23, 23, 22, 21, 17, 16, 22, 26, 25, 25, 23, 18, 24, 27, 26,
+ 26, 23, 19, 16, 24, 29, 28, 28, 24, 21, 15, 24, 31, 31, 29, 26, 20, 16, 24,
+ 30, 31, 30, 27, 21, 17, 23, 30, 33, 31, 27, 22, 18, 23, 30, 35, 34, 29, 24,
+ 19, 25, 30, 35, 36, 32, 27, 21, 33, 40, 39, 32, 26, 22, 18, 34, 40, 42, 34,
+ 28, 23, 18, 33, 40, 43, 35, 29, 23, 18, 34, 40, 44, 36, 30, 25, 19, 32, 38,
+ 44, 39, 32, 26, 21, 33, 38, 43, 42, 34, 28, 23, 32, 39, 42, 43, 36, 30, 24,
+ 7, 9, 13, 15, 15, 14, 10, 6, 8, 11, 15, 15, 12, 9, 6, 7, 10, 12, 13,
+ 10, 7, 6, 5, 7, 10, 11, 8, 4, 3, 3, 4, 6, 8, 6, 3, 1, 1, 2,
+ 3, 6, 6, 3, 3, 2, 2, 2, 5, 6, 3, 6, 7, 10, 13, 13, 10, 10, 6,
+ 7, 9, 12, 12, 9, 8, 5, 6, 8, 10, 10, 7, 6, 5, 5, 5, 7, 6, 5,
+ 3, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 1, 2, 3, 5, 3, 2, 1,
+ 0, 1, 2, 5, 6, 8, 9, 11, 12, 12, 5, 6, 8, 9, 10, 10, 10, 4, 5,
+ 8, 8, 8, 8, 8, 4, 4, 5, 6, 6, 5, 5, 6, 2, 3, 4, 4, 4, 4,
+ 8, 3, 4, 5, 5, 5, 5, 7, 5, 5, 7, 7, 7, 7, 5, 8, 10, 10, 12,
+ 13, 13, 4, 8, 10, 11, 11, 11, 11, 7, 8, 10, 10, 10, 10, 10, 10, 7, 10,
+ 11, 11, 11, 9, 10, 8, 11, 11, 12, 11, 12, 8, 10, 13, 13, 13, 13, 13, 7,
+ 13, 15, 15, 15, 14, 15, 12, 16, 16, 17, 18, 17, 14, 14, 16, 18, 18, 19, 17,
+ 12, 14, 18, 19, 19, 18, 17, 13, 14, 19, 19, 19, 19, 18, 13, 13, 21, 21, 21,
+ 21, 19, 15, 13, 22, 23, 23, 22, 20, 16, 15, 23, 26, 25, 24, 22, 18, 24, 27,
+ 26, 26, 23, 19, 15, 24, 30, 28, 28, 25, 20, 15, 24, 31, 30, 29, 24, 20, 16,
+ 25, 31, 31, 30, 25, 21, 16, 24, 30, 34, 32, 27, 23, 17, 24, 30, 36, 35, 28,
+ 23, 20, 24, 31, 35, 36, 31, 26, 20, 34, 39, 39, 32, 26, 21, 18, 34, 40, 39,
+ 33, 28, 23, 18, 34, 41, 43, 35, 28, 23, 18, 33, 39, 44, 35, 30, 24, 19, 32,
+ 39, 42, 39, 31, 26, 20, 32, 39, 43, 41, 34, 28, 23, 32, 38, 42, 43, 36, 30,
+ 24, 5, 8, 11, 15, 15, 13, 9, 5, 7, 10, 14, 14, 11, 7, 4, 5, 9, 11,
+ 13, 9, 6, 4, 4, 6, 8, 11, 7, 3, 1, 1, 2, 5, 8, 6, 3, 2, 2,
+ 2, 3, 6, 6, 3, 4, 3, 2, 3, 5, 6, 3, 4, 5, 9, 12, 12, 9, 9,
+ 4, 5, 8, 11, 11, 8, 7, 3, 5, 6, 8, 8, 6, 4, 3, 3, 3, 6, 5,
+ 4, 1, 2, 1, 1, 2, 2, 2, 1, 3, 2, 2, 3, 1, 1, 1, 7, 4, 2,
+ 2, 1, 0, 1, 4, 5, 6, 7, 9, 10, 10, 3, 4, 7, 7, 8, 8, 8, 3,
+ 4, 6, 6, 6, 6, 6, 4, 2, 4, 4, 4, 4, 4, 7, 2, 4, 5, 5, 4,
+ 4, 8, 3, 5, 6, 6, 6, 4, 8, 6, 6, 7, 7, 7, 7, 3, 7, 9, 9,
+ 10, 11, 12, 5, 7, 10, 10, 11, 11, 11, 8, 7, 10, 10, 10, 11, 10, 11, 7,
+ 10, 11, 11, 11, 8, 10, 9, 11, 12, 12, 12, 12, 8, 11, 13, 13, 14, 13, 13,
+ 8, 14, 15, 15, 14, 14, 15, 13, 16, 16, 17, 18, 17, 12, 15, 16, 17, 18, 19,
+ 17, 11, 15, 18, 19, 19, 19, 18, 13, 15, 20, 20, 20, 20, 18, 13, 14, 21, 21,
+ 21, 21, 19, 15, 14, 22, 25, 23, 22, 21, 16, 16, 24, 27, 25, 25, 23, 18, 24,
+ 28, 27, 26, 23, 19, 15, 24, 29, 28, 27, 24, 19, 15, 24, 32, 31, 29, 25, 20,
+ 16, 24, 31, 31, 29, 26, 21, 16, 23, 29, 33, 31, 27, 22, 18, 24, 30, 37, 34,
+ 28, 24, 19, 25, 30, 37, 36, 31, 26, 21, 34, 39, 37, 31, 26, 22, 18, 35, 40,
+ 39, 33, 27, 22, 18, 34, 39, 42, 34, 29, 24, 18, 34, 37, 42, 34, 29, 24, 19,
+ 33, 38, 42, 37, 31, 26, 21, 34, 38, 41, 41, 34, 27, 23, 33, 38, 42, 42, 35,
+ 28, 24, 4, 6, 8, 11, 11, 9, 6, 4, 6, 8, 10, 10, 7, 4, 3, 5, 7,
+ 8, 9, 6, 4, 3, 3, 5, 6, 8, 5, 3, 1, 0, 3, 5, 8, 5, 3, 2,
+ 2, 4, 5, 7, 5, 3, 4, 3, 3, 4, 6, 5, 3, 4, 5, 7, 9, 8, 7,
+ 6, 3, 4, 6, 8, 7, 5, 4, 3, 4, 5, 7, 5, 4, 2, 2, 2, 3, 5,
+ 4, 3, 0, 1, 1, 2, 4, 3, 2, 0, 3, 2, 3, 4, 2, 2, 0, 7, 4,
+ 3, 3, 3, 1, 0, 3, 4, 5, 5, 7, 8, 8, 2, 3, 5, 6, 6, 6, 6,
+ 2, 3, 5, 4, 4, 4, 5, 3, 2, 3, 3, 3, 3, 3, 5, 2, 4, 4, 4,
+ 4, 3, 7, 4, 5, 5, 5, 5, 3, 8, 7, 6, 6, 6, 6, 6, 2, 7, 8,
+ 9, 9, 10, 11, 3, 7, 9, 9, 10, 10, 9, 9, 7, 10, 9, 9, 10, 8, 11,
+ 7, 10, 10, 10, 10, 7, 9, 9, 11, 11, 11, 11, 11, 8, 11, 13, 13, 13, 13,
+ 12, 8, 14, 15, 14, 14, 14, 13, 12, 15, 16, 16, 16, 16, 11, 14, 17, 17, 17,
+ 18, 16, 10, 14, 17, 18, 18, 18, 16, 12, 14, 19, 18, 19, 18, 17, 12, 14, 21,
+ 21, 20, 19, 19, 14, 15, 22, 24, 23, 21, 19, 16, 16, 22, 26, 24, 23, 21, 17,
+ 24, 26, 24, 24, 21, 18, 14, 24, 28, 27, 26, 23, 18, 14, 24, 30, 28, 27, 23,
+ 19, 15, 24, 29, 31, 27, 24, 20, 16, 23, 28, 32, 30, 25, 21, 17, 24, 30, 34,
+ 33, 28, 23, 19, 24, 30, 35, 34, 30, 24, 20, 32, 37, 35, 28, 24, 20, 17, 33,
+ 37, 36, 30, 25, 21, 16, 33, 37, 39, 31, 26, 22, 17, 32, 37, 40, 33, 27, 23,
+ 19, 31, 36, 39, 36, 30, 23, 20, 31, 36, 40, 38, 32, 26, 21, 33, 36, 41, 40,
+ 34, 28, 22, 2, 3, 4, 8, 8, 6, 2, 2, 2, 3, 7, 8, 4, 2, 2, 2,
+ 2, 4, 5, 2, 2, 1, 2, 2, 2, 3, 2, 3, 2, 3, 3, 3, 4, 3, 3,
+ 6, 6, 6, 6, 4, 3, 3, 12, 11, 8, 6, 5, 3, 3, 2, 2, 3, 5, 5,
+ 2, 2, 1, 2, 3, 3, 4, 2, 1, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2,
+ 2, 1, 1, 3, 4, 3, 4, 4, 4, 3, 3, 8, 8, 8, 7, 5, 3, 3, 15,
+ 13, 10, 7, 5, 4, 3, 0, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2,
+ 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 2, 2, 4, 6, 5, 5, 5,
+ 5, 4, 4, 11, 10, 9, 8, 6, 5, 4, 18, 16, 12, 9, 7, 6, 5, 2, 2,
+ 2, 3, 3, 3, 7, 2, 3, 4, 4, 4, 4, 7, 4, 4, 5, 5, 5, 5, 7,
+ 7, 7, 7, 7, 8, 8, 8, 8, 12, 11, 11, 12, 11, 10, 12, 17, 16, 15, 14,
+ 13, 12, 18, 23, 20, 17, 15, 14, 13, 10, 10, 10, 10, 11, 10, 7, 12, 12, 12,
+ 11, 12, 11, 7, 12, 13, 13, 14, 13, 12, 8, 12, 17, 16, 16, 17, 15, 12, 15,
+ 21, 21, 20, 20, 19, 14, 19, 24, 25, 25, 23, 20, 15, 22, 27, 30, 27, 25, 22,
+ 17, 19, 21, 20, 20, 17, 13, 8, 19, 22, 22, 22, 18, 14, 10, 19, 24, 24, 24,
+ 19, 15, 11, 19, 25, 27, 27, 23, 19, 15, 22, 28, 32, 31, 28, 23, 17, 24, 30,
+ 36, 35, 30, 25, 18, 27, 34, 37, 37, 32, 26, 20, 27, 32, 32, 25, 20, 16, 11,
+ 27, 33, 34, 28, 22, 17, 13, 26, 34, 36, 29, 24, 19, 15, 28, 34, 38, 32, 27,
+ 23, 19, 29, 35, 40, 36, 32, 27, 21, 31, 37, 40, 40, 33, 27, 23, 33, 38, 42,
+ 42, 36, 30, 25, 1, 2, 3, 3, 3, 2, 2, 1, 2, 2, 3, 3, 2, 1, 1,
+ 2, 2, 2, 2, 2, 1, 2, 1, 2, 2, 2, 2, 4, 4, 3, 4, 4, 4, 4,
+ 4, 8, 7, 7, 7, 5, 4, 4, 13, 12, 9, 7, 6, 4, 4, 1, 2, 2, 3,
+ 2, 2, 1, 2, 1, 2, 2, 2, 1, 1, 2, 0, 2, 2, 2, 1, 1, 2, 2,
+ 2, 2, 2, 2, 4, 5, 5, 5, 5, 5, 4, 4, 10, 9, 9, 8, 6, 4, 4,
+ 17, 14, 11, 8, 6, 5, 4, 2, 0, 1, 2, 2, 1, 5, 2, 1, 1, 1, 1,
+ 1, 5, 2, 2, 1, 1, 1, 1, 6, 4, 2, 4, 4, 4, 4, 7, 8, 6, 7,
+ 7, 8, 7, 7, 13, 12, 12, 11, 10, 8, 7, 19, 17, 15, 13, 11, 10, 9, 2,
+ 5, 5, 6, 6, 6, 9, 5, 6, 6, 7, 7, 7, 9, 8, 7, 7, 8, 8, 8,
+ 9, 11, 10, 11, 11, 11, 12, 11, 11, 15, 15, 15, 15, 15, 14, 13, 20, 20, 18,
+ 17, 16, 15, 19, 26, 23, 21, 18, 18, 17, 13, 14, 13, 13, 13, 13, 9, 14, 15,
+ 14, 15, 15, 14, 9, 14, 17, 16, 17, 16, 15, 12, 15, 21, 20, 20, 20, 19, 16,
+ 18, 25, 25, 24, 24, 22, 17, 22, 28, 30, 29, 26, 24, 19, 26, 31, 33, 30, 29,
+ 26, 20, 22, 24, 23, 23, 19, 15, 11, 23, 26, 25, 24, 21, 16, 12, 23, 28, 27,
+ 26, 23, 19, 15, 23, 29, 31, 30, 27, 22, 19, 25, 32, 36, 34, 31, 26, 21, 29,
+ 34, 41, 39, 34, 27, 22, 32, 37, 42, 41, 36, 30, 24, 31, 36, 34, 28, 23, 18,
+ 14, 30, 37, 37, 30, 23, 20, 15, 31, 36, 39, 33, 26, 22, 19, 30, 36, 42, 36,
+ 31, 27, 23, 32, 39, 45, 41, 35, 29, 24, 34, 40, 47, 45, 39, 31, 26, 38, 43,
+ 47, 46, 41, 34, 28, 1, 3, 4, 5, 5, 4, 3, 1, 2, 4, 5, 5, 4, 3,
+ 2, 1, 4, 4, 5, 5, 5, 2, 2, 4, 5, 6, 6, 6, 4, 4, 6, 7, 8,
+ 7, 6, 8, 8, 9, 10, 8, 7, 6, 14, 13, 10, 10, 9, 7, 6, 2, 1, 3,
+ 4, 4, 3, 1, 2, 1, 3, 4, 4, 3, 1, 2, 2, 2, 4, 4, 3, 3, 2,
+ 3, 3, 5, 5, 5, 5, 5, 5, 6, 7, 7, 6, 4, 11, 10, 10, 10, 8, 6,
+ 4, 18, 15, 12, 10, 8, 6, 4, 2, 1, 0, 1, 1, 2, 5, 2, 2, 1, 1,
+ 1, 2, 4, 3, 2, 2, 2, 3, 3, 6, 7, 3, 6, 6, 6, 6, 8, 10, 7,
+ 9, 9, 9, 9, 8, 15, 12, 13, 13, 11, 10, 7, 21, 19, 16, 14, 13, 11, 10,
+ 3, 6, 6, 6, 7, 6, 8, 8, 6, 7, 7, 8, 8, 9, 10, 7, 9, 9, 10,
+ 10, 10, 12, 10, 13, 13, 13, 13, 11, 13, 15, 17, 17, 17, 16, 14, 15, 22, 22,
+ 21, 19, 18, 17, 20, 27, 25, 22, 21, 20, 19, 14, 15, 15, 14, 15, 13, 8, 16,
+ 15, 16, 16, 15, 14, 8, 16, 17, 18, 19, 18, 17, 13, 16, 22, 22, 22, 22, 21,
+ 17, 19, 27, 27, 26, 26, 24, 18, 23, 31, 33, 31, 29, 25, 21, 28, 34, 35, 32,
+ 31, 28, 22, 23, 25, 24, 23, 20, 16, 12, 23, 28, 26, 25, 21, 17, 13, 24, 29,
+ 28, 28, 25, 21, 17, 24, 31, 33, 32, 28, 24, 21, 27, 33, 39, 36, 33, 27, 21,
+ 31, 37, 42, 41, 36, 29, 24, 33, 40, 45, 44, 38, 31, 26, 32, 38, 36, 29, 24,
+ 19, 15, 32, 37, 38, 30, 25, 21, 17, 31, 38, 40, 35, 29, 24, 19, 32, 39, 45,
+ 39, 32, 28, 24, 34, 41, 46, 44, 37, 31, 25, 37, 44, 48, 48, 41, 33, 28, 40,
+ 45, 49, 50, 43, 36, 30, 1, 3, 6, 8, 8, 6, 3, 1, 2, 5, 7, 8, 6,
+ 4, 1, 2, 4, 7, 8, 7, 5, 2, 2, 5, 7, 9, 8, 7, 4, 5, 7, 9,
+ 11, 9, 8, 9, 9, 10, 11, 11, 9, 8, 16, 14, 12, 11, 11, 9, 7, 1, 1,
+ 4, 6, 5, 3, 1, 2, 1, 3, 5, 5, 2, 2, 2, 1, 2, 5, 5, 4, 3,
+ 2, 3, 4, 6, 6, 5, 5, 6, 6, 7, 9, 8, 7, 5, 12, 11, 11, 11, 9,
+ 7, 5, 19, 17, 13, 11, 9, 7, 5, 2, 2, 1, 0, 1, 2, 4, 2, 2, 1,
+ 1, 1, 2, 4, 5, 2, 3, 3, 3, 3, 6, 8, 3, 6, 7, 7, 7, 7, 11,
+ 8, 10, 10, 10, 10, 8, 16, 13, 15, 14, 13, 11, 8, 21, 20, 18, 16, 14, 12,
+ 11, 5, 6, 7, 7, 7, 5, 7, 9, 6, 7, 8, 8, 8, 8, 12, 7, 9, 10,
+ 11, 11, 10, 14, 10, 14, 15, 14, 14, 12, 15, 15, 18, 18, 18, 18, 15, 16, 23,
+ 22, 21, 21, 19, 17, 22, 29, 26, 24, 22, 20, 20, 15, 14, 14, 14, 14, 13, 7,
+ 18, 15, 16, 15, 16, 15, 8, 18, 17, 18, 19, 19, 17, 14, 17, 22, 23, 23, 23,
+ 22, 18, 20, 28, 28, 27, 27, 25, 20, 23, 32, 34, 32, 28, 26, 22, 28, 35, 37,
+ 34, 32, 28, 23, 24, 26, 24, 23, 20, 16, 12, 24, 28, 26, 26, 22, 18, 14, 24,
+ 30, 29, 29, 25, 21, 18, 24, 32, 34, 33, 30, 25, 22, 28, 35, 40, 38, 34, 29,
+ 23, 32, 37, 45, 42, 36, 30, 25, 36, 42, 45, 45, 39, 33, 27, 32, 38, 35, 29,
+ 23, 19, 15, 32, 38, 37, 31, 26, 21, 18, 33, 38, 41, 35, 29, 26, 21, 32, 40,
+ 45, 39, 35, 29, 25, 36, 43, 48, 44, 39, 33, 26, 38, 45, 50, 50, 41, 35, 29,
+ 41, 47, 51, 51, 44, 37, 30, 1, 3, 6, 8, 8, 6, 3, 2, 2, 5, 7, 8,
+ 6, 5, 2, 1, 5, 8, 9, 8, 6, 2, 3, 5, 8, 10, 9, 8, 5, 6, 8,
+ 10, 12, 10, 9, 10, 11, 12, 13, 13, 10, 9, 17, 15, 13, 12, 12, 10, 9, 2,
+ 1, 4, 6, 5, 2, 2, 2, 1, 3, 5, 5, 3, 2, 2, 2, 2, 6, 5, 4,
+ 4, 2, 3, 5, 7, 7, 6, 7, 7, 7, 8, 10, 10, 8, 7, 13, 12, 13, 13,
+ 10, 9, 7, 21, 18, 14, 13, 11, 9, 7, 2, 2, 1, 1, 0, 2, 4, 3, 2,
+ 1, 1, 2, 3, 5, 7, 2, 3, 4, 5, 4, 6, 10, 3, 7, 8, 8, 8, 8,
+ 13, 9, 11, 12, 12, 11, 8, 17, 15, 16, 15, 14, 13, 8, 23, 21, 19, 17, 15,
+ 14, 13, 6, 6, 7, 7, 7, 4, 7, 11, 7, 8, 8, 9, 9, 8, 13, 7, 10,
+ 11, 12, 12, 11, 16, 10, 15, 16, 16, 16, 13, 16, 17, 19, 20, 20, 19, 16, 18,
+ 24, 25, 23, 22, 21, 19, 23, 30, 28, 25, 23, 22, 22, 16, 15, 15, 15, 15, 13,
+ 7, 19, 17, 16, 16, 18, 16, 8, 19, 18, 19, 20, 21, 19, 15, 19, 23, 23, 25,
+ 25, 24, 20, 20, 28, 30, 29, 29, 26, 21, 24, 33, 35, 33, 31, 29, 23, 30, 37,
+ 38, 35, 33, 30, 25, 25, 26, 25, 24, 20, 16, 12, 24, 28, 26, 25, 22, 19, 15,
+ 24, 30, 29, 30, 27, 23, 19, 25, 32, 35, 34, 31, 27, 23, 28, 36, 41, 39, 35,
+ 30, 25, 33, 40, 46, 43, 38, 32, 27, 37, 42, 47, 46, 40, 34, 28, 32, 38, 35,
+ 28, 23, 19, 16, 33, 37, 38, 31, 26, 22, 19, 34, 38, 40, 35, 31, 27, 22, 33,
+ 40, 45, 40, 35, 31, 27, 35, 43, 47, 46, 39, 34, 29, 41, 46, 50, 50, 42, 36,
+ 30, 43, 48, 50, 53, 45, 38, 32, 3, 5, 7, 8, 8, 6, 5, 2, 4, 6, 8,
+ 9, 7, 6, 1, 3, 6, 9, 10, 9, 8, 2, 4, 7, 9, 12, 11, 10, 5, 7,
+ 10, 11, 14, 12, 10, 11, 12, 13, 14, 14, 12, 10, 17, 16, 15, 14, 14, 12, 10,
+ 1, 4, 5, 7, 5, 3, 2, 2, 2, 5, 6, 6, 4, 3, 2, 2, 5, 7, 7,
+ 6, 5, 2, 3, 6, 8, 8, 7, 8, 6, 7, 10, 11, 11, 9, 8, 13, 13, 14,
+ 14, 11, 10, 8, 21, 18, 15, 14, 12, 10, 8, 2, 1, 2, 2, 2, 0, 2, 2,
+ 2, 1, 1, 2, 1, 4, 4, 2, 1, 3, 3, 3, 5, 9, 3, 5, 6, 6, 7,
+ 7, 11, 9, 10, 10, 10, 10, 7, 17, 15, 15, 14, 12, 11, 7, 23, 21, 17, 15,
+ 14, 12, 11, 4, 5, 5, 5, 5, 3, 5, 9, 6, 6, 6, 7, 7, 7, 12, 7,
+ 8, 9, 10, 11, 9, 15, 10, 13, 14, 14, 14, 11, 15, 15, 18, 18, 18, 17, 15,
+ 17, 23, 23, 22, 20, 19, 17, 24, 30, 26, 24, 22, 20, 19, 15, 13, 13, 12, 13,
+ 11, 5, 18, 15, 14, 14, 15, 14, 7, 19, 16, 17, 19, 19, 18, 13, 19, 21, 22,
+ 23, 23, 21, 18, 19, 27, 28, 27, 27, 24, 19, 23, 31, 34, 31, 29, 25, 21, 29,
+ 35, 36, 33, 31, 28, 23, 23, 24, 22, 20, 18, 14, 10, 23, 26, 24, 22, 20, 17,
+ 13, 23, 28, 27, 27, 23, 21, 17, 23, 30, 32, 30, 29, 24, 21, 27, 34, 37, 36,
+ 32, 28, 23, 31, 38, 43, 42, 35, 30, 25, 35, 40, 44, 43, 37, 31, 26, 31, 33,
+ 30, 25, 20, 17, 14, 30, 33, 33, 27, 23, 20, 17, 31, 33, 36, 31, 27, 25, 21,
+ 30, 35, 42, 36, 32, 28, 25, 34, 39, 44, 43, 37, 32, 27, 38, 42, 47, 47, 40,
+ 33, 28, 41, 46, 49, 48, 42, 36, 30, 6, 7, 9, 10, 10, 8, 6, 5, 6, 8,
+ 10, 11, 9, 8, 4, 6, 8, 11, 13, 11, 9, 3, 6, 9, 12, 14, 12, 11, 5,
+ 8, 10, 13, 15, 14, 11, 11, 13, 14, 14, 16, 14, 11, 17, 17, 15, 15, 14, 13,
+ 11, 4, 6, 7, 9, 7, 5, 3, 3, 5, 7, 8, 7, 6, 5, 1, 5, 7, 9,
+ 8, 7, 6, 1, 5, 8, 10, 9, 9, 8, 5, 8, 10, 12, 11, 10, 8, 12, 13,
+ 15, 15, 12, 10, 8, 20, 18, 16, 14, 12, 10, 8, 1, 5, 5, 4, 4, 2, 0,
+ 2, 3, 5, 4, 3, 3, 2, 2, 2, 4, 5, 4, 5, 4, 4, 3, 6, 6, 6,
+ 6, 6, 8, 8, 10, 10, 10, 8, 6, 15, 15, 15, 13, 11, 9, 6, 22, 20, 17,
+ 14, 11, 9, 8, 2, 2, 2, 2, 1, 2, 3, 5, 3, 3, 2, 3, 3, 4, 8,
+ 5, 4, 6, 6, 6, 6, 11, 7, 9, 10, 10, 10, 8, 12, 14, 14, 14, 14, 13,
+ 11, 15, 20, 19, 17, 16, 15, 13, 22, 26, 22, 20, 18, 16, 15, 12, 10, 9, 9,
+ 9, 8, 3, 15, 12, 11, 11, 11, 10, 4, 15, 14, 13, 14, 14, 13, 10, 14, 18,
+ 18, 18, 19, 17, 14, 17, 25, 23, 23, 23, 20, 15, 22, 28, 30, 27, 24, 21, 17,
+ 26, 31, 33, 29, 26, 23, 19, 20, 19, 18, 16, 14, 10, 6, 19, 22, 18, 17, 15,
+ 13, 9, 19, 23, 22, 21, 19, 16, 13, 20, 25, 26, 26, 23, 20, 17, 23, 30, 33,
+ 30, 28, 23, 19, 27, 33, 38, 35, 29, 25, 20, 32, 35, 40, 38, 32, 27, 22, 24,
+ 28, 25, 19, 15, 13, 10, 25, 28, 27, 21, 17, 15, 13, 25, 28, 29, 25, 22, 19,
+ 16, 25, 29, 35, 31, 27, 23, 20, 28, 35, 39, 36, 31, 27, 22, 32, 37, 41, 41,
+ 34, 28, 23, 36, 40, 42, 43, 37, 30, 26, 2, 3, 8, 12, 12, 10, 7, 2, 3,
+ 6, 11, 11, 9, 4, 2, 2, 4, 8, 9, 7, 2, 2, 2, 2, 4, 7, 5, 2,
+ 2, 2, 2, 3, 5, 5, 2, 6, 6, 6, 5, 4, 5, 2, 11, 11, 8, 6, 4,
+ 5, 2, 2, 2, 5, 8, 9, 6, 2, 1, 2, 3, 7, 9, 4, 2, 2, 2, 2,
+ 4, 5, 2, 2, 0, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 7,
+ 7, 8, 6, 4, 3, 2, 15, 13, 10, 7, 5, 3, 2, 1, 2, 2, 2, 3, 2,
+ 2, 0, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 0, 1, 1, 1, 1,
+ 1, 1, 2, 5, 4, 4, 4, 4, 3, 2, 11, 9, 9, 7, 5, 3, 2, 17, 16,
+ 12, 9, 6, 4, 3, 1, 1, 1, 1, 1, 1, 5, 2, 2, 2, 3, 3, 3, 6,
+ 2, 3, 3, 3, 4, 4, 6, 5, 5, 6, 6, 6, 7, 7, 6, 10, 10, 10, 10,
+ 9, 9, 11, 15, 14, 14, 12, 11, 10, 17, 21, 18, 16, 14, 13, 12, 9, 9, 9,
+ 9, 10, 9, 6, 11, 11, 10, 11, 11, 10, 6, 11, 12, 12, 12, 12, 11, 7, 11,
+ 15, 14, 15, 15, 14, 11, 13, 20, 19, 19, 19, 18, 12, 17, 23, 25, 24, 21, 20,
+ 14, 21, 26, 27, 24, 24, 21, 16, 18, 20, 19, 19, 16, 12, 7, 18, 21, 21, 21,
+ 18, 12, 9, 18, 23, 22, 22, 18, 14, 10, 18, 24, 26, 25, 22, 18, 14, 20, 25,
+ 30, 28, 25, 21, 16, 22, 28, 33, 31, 27, 23, 18, 25, 31, 34, 34, 29, 25, 19,
+ 26, 31, 29, 24, 19, 15, 10, 26, 31, 31, 26, 21, 16, 11, 26, 31, 33, 27, 23,
+ 18, 14, 25, 31, 35, 30, 26, 21, 18, 27, 32, 35, 33, 28, 24, 20, 29, 35, 37,
+ 36, 31, 27, 22, 32, 34, 36, 37, 31, 28, 23, 2, 2, 3, 3, 3, 2, 2, 1,
+ 2, 3, 3, 3, 2, 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 4, 3, 3, 7, 6, 7, 6, 5, 3, 3, 13, 11, 8, 7,
+ 5, 3, 3, 1, 2, 2, 3, 2, 2, 2, 1, 2, 2, 3, 2, 2, 1, 2, 2,
+ 2, 2, 2, 2, 1, 2, 1, 2, 2, 1, 2, 3, 4, 4, 4, 4, 4, 3, 3,
+ 9, 8, 8, 7, 5, 4, 3, 16, 14, 10, 8, 6, 4, 3, 2, 1, 2, 2, 2,
+ 2, 3, 2, 0, 1, 1, 1, 2, 3, 2, 1, 1, 0, 1, 0, 4, 3, 2, 2,
+ 2, 3, 3, 6, 7, 6, 6, 6, 6, 5, 6, 12, 11, 10, 9, 8, 7, 6, 18,
+ 17, 13, 11, 9, 8, 7, 2, 4, 4, 4, 5, 4, 8, 3, 5, 5, 5, 6, 6,
+ 8, 6, 6, 6, 6, 7, 7, 8, 9, 9, 9, 10, 10, 10, 10, 10, 13, 13, 13,
+ 13, 13, 12, 12, 19, 18, 16, 16, 15, 14, 18, 25, 22, 19, 18, 16, 15, 12, 12,
+ 12, 12, 12, 12, 8, 14, 14, 13, 13, 14, 12, 8, 13, 15, 15, 15, 15, 14, 11,
+ 13, 19, 19, 18, 19, 18, 14, 17, 24, 23, 23, 23, 20, 16, 20, 27, 29, 27, 25,
+ 22, 17, 25, 30, 31, 29, 28, 25, 19, 21, 23, 22, 21, 18, 14, 10, 21, 25, 23,
+ 23, 20, 15, 11, 20, 26, 27, 26, 22, 17, 13, 21, 27, 28, 28, 25, 21, 17, 24,
+ 30, 34, 33, 30, 24, 19, 27, 33, 40, 37, 32, 26, 21, 30, 36, 40, 39, 34, 29,
+ 22, 29, 36, 33, 27, 20, 17, 13, 29, 35, 35, 28, 23, 18, 14, 29, 35, 38, 31,
+ 25, 20, 16, 29, 34, 42, 35, 30, 25, 21, 31, 37, 43, 40, 34, 29, 22, 33, 40,
+ 45, 43, 37, 30, 25, 35, 42, 46, 46, 39, 32, 27, 1, 3, 4, 5, 5, 4, 3,
+ 1, 3, 4, 5, 5, 4, 3, 1, 2, 4, 4, 5, 4, 4, 1, 2, 4, 5, 5,
+ 5, 6, 4, 4, 6, 7, 7, 6, 6, 8, 8, 10, 10, 8, 7, 6, 14, 12, 11,
+ 10, 9, 7, 6, 1, 2, 4, 5, 4, 3, 1, 1, 1, 3, 4, 4, 3, 0, 2,
+ 1, 3, 4, 3, 4, 3, 2, 2, 3, 5, 5, 5, 5, 5, 5, 6, 8, 7, 6,
+ 5, 10, 10, 10, 11, 8, 6, 5, 18, 15, 11, 10, 8, 7, 5, 2, 1, 1, 1,
+ 1, 1, 4, 2, 1, 0, 1, 1, 1, 4, 2, 1, 2, 2, 2, 2, 5, 5, 3,
+ 5, 5, 5, 5, 7, 9, 7, 8, 8, 8, 8, 7, 14, 12, 13, 11, 10, 9, 7,
+ 20, 18, 15, 14, 12, 10, 9, 2, 5, 6, 6, 6, 5, 8, 6, 6, 6, 7, 7,
+ 7, 8, 9, 7, 8, 8, 9, 9, 9, 11, 9, 12, 12, 12, 12, 11, 12, 15, 16,
+ 16, 16, 16, 14, 14, 21, 20, 19, 18, 17, 16, 20, 27, 24, 21, 20, 18, 17, 13,
+ 13, 13, 14, 14, 13, 8, 15, 15, 14, 15, 15, 14, 8, 15, 17, 17, 17, 17, 16,
+ 12, 15, 22, 21, 20, 21, 20, 16, 18, 26, 25, 24, 25, 23, 18, 23, 30, 32, 29,
+ 28, 24, 20, 26, 33, 35, 31, 30, 26, 21, 23, 25, 23, 23, 19, 15, 11, 23, 27,
+ 25, 24, 20, 16, 12, 23, 28, 27, 28, 24, 19, 16, 22, 30, 32, 31, 28, 23, 19,
+ 26, 33, 36, 35, 31, 26, 21, 29, 35, 41, 40, 34, 29, 22, 33, 38, 43, 43, 36,
+ 30, 24, 30, 37, 35, 28, 22, 18, 14, 31, 36, 37, 30, 24, 20, 16, 31, 36, 40,
+ 33, 28, 22, 19, 30, 38, 44, 38, 31, 27, 23, 33, 39, 45, 42, 36, 31, 25, 35,
+ 41, 48, 46, 39, 32, 27, 40, 44, 48, 51, 41, 35, 29, 1, 4, 6, 9, 9, 7,
+ 4, 1, 3, 5, 8, 8, 7, 5, 1, 2, 5, 7, 9, 8, 6, 1, 3, 5, 8,
+ 10, 8, 8, 5, 5, 7, 9, 11, 10, 8, 9, 9, 11, 12, 11, 9, 8, 15, 14,
+ 12, 12, 11, 10, 8, 1, 2, 4, 7, 6, 3, 1, 1, 1, 4, 6, 6, 3, 1,
+ 2, 1, 3, 6, 5, 4, 3, 2, 3, 4, 6, 6, 6, 6, 5, 6, 7, 9, 9,
+ 7, 6, 11, 11, 11, 12, 9, 8, 6, 19, 16, 13, 12, 9, 8, 6, 2, 1, 1,
+ 1, 2, 1, 3, 2, 1, 1, 0, 1, 1, 4, 3, 2, 2, 2, 2, 3, 5, 7,
+ 3, 5, 6, 5, 6, 7, 10, 8, 9, 9, 10, 8, 7, 15, 13, 14, 13, 11, 10,
+ 7, 21, 20, 17, 15, 13, 11, 10, 3, 5, 6, 6, 6, 4, 7, 8, 6, 7, 7,
+ 7, 7, 7, 11, 7, 8, 9, 10, 10, 9, 13, 9, 13, 13, 13, 13, 11, 13, 15,
+ 17, 16, 17, 17, 14, 16, 22, 22, 21, 19, 18, 16, 21, 28, 26, 23, 21, 20, 18,
+ 14, 14, 14, 14, 14, 13, 7, 16, 15, 15, 15, 15, 14, 7, 16, 17, 17, 18, 18,
+ 17, 13, 16, 21, 22, 22, 22, 21, 17, 19, 27, 27, 27, 26, 24, 19, 23, 30, 32,
+ 31, 28, 26, 20, 28, 33, 35, 32, 31, 28, 22, 23, 24, 23, 23, 19, 16, 11, 22,
+ 27, 25, 24, 21, 17, 13, 23, 30, 28, 28, 24, 21, 17, 23, 31, 33, 32, 28, 24,
+ 21, 28, 34, 36, 36, 33, 27, 22, 31, 36, 44, 42, 36, 29, 24, 34, 39, 43, 45,
+ 37, 32, 26, 31, 36, 35, 28, 22, 18, 14, 31, 37, 37, 31, 25, 20, 17, 31, 37,
+ 41, 33, 29, 25, 19, 31, 38, 45, 39, 33, 28, 24, 35, 39, 46, 44, 36, 32, 26,
+ 38, 45, 51, 50, 40, 33, 28, 40, 46, 50, 51, 43, 35, 30, 1, 4, 7, 10, 10,
+ 8, 4, 1, 3, 6, 9, 9, 7, 5, 1, 2, 5, 8, 10, 8, 6, 2, 3, 6,
+ 8, 11, 9, 8, 5, 6, 8, 10, 12, 10, 8, 10, 10, 12, 12, 12, 10, 8, 16,
+ 14, 12, 12, 12, 10, 8, 1, 2, 5, 7, 7, 4, 1, 1, 1, 4, 6, 6, 4,
+ 2, 2, 1, 3, 6, 6, 5, 3, 2, 3, 5, 7, 7, 6, 6, 5, 6, 8, 9,
+ 9, 7, 6, 12, 11, 12, 12, 9, 8, 6, 19, 17, 13, 12, 10, 8, 6, 2, 1,
+ 1, 1, 2, 2, 3, 2, 2, 1, 1, 0, 1, 4, 4, 2, 2, 3, 3, 2, 5,
+ 8, 3, 6, 6, 6, 6, 7, 11, 8, 9, 10, 10, 9, 7, 16, 14, 14, 13, 12,
+ 10, 7, 21, 20, 17, 15, 13, 12, 11, 4, 5, 6, 6, 6, 4, 7, 8, 6, 7,
+ 7, 7, 7, 7, 12, 6, 8, 10, 10, 10, 9, 13, 9, 13, 13, 14, 14, 11, 14,
+ 15, 17, 17, 18, 17, 14, 16, 22, 22, 21, 20, 18, 17, 21, 28, 25, 23, 22, 20,
+ 19, 15, 14, 14, 14, 14, 13, 7, 17, 15, 15, 15, 15, 14, 7, 16, 17, 17, 19,
+ 19, 17, 13, 17, 21, 23, 23, 22, 21, 17, 19, 27, 27, 27, 27, 24, 19, 24, 32,
+ 34, 31, 30, 26, 20, 28, 36, 36, 34, 31, 29, 23, 23, 25, 23, 23, 20, 16, 11,
+ 23, 27, 25, 24, 22, 17, 14, 23, 29, 28, 28, 24, 20, 17, 23, 31, 34, 33, 29,
+ 25, 21, 27, 34, 38, 37, 33, 28, 22, 30, 38, 44, 42, 36, 30, 24, 35, 40, 44,
+ 44, 37, 32, 26, 32, 35, 34, 27, 22, 18, 14, 31, 37, 36, 29, 24, 21, 17, 31,
+ 36, 38, 33, 29, 24, 21, 32, 39, 43, 38, 33, 28, 24, 35, 42, 46, 44, 37, 32,
+ 27, 37, 43, 49, 48, 42, 34, 28, 41, 46, 49, 51, 44, 36, 30, 3, 5, 6, 8,
+ 8, 6, 4, 2, 5, 6, 8, 8, 6, 5, 1, 4, 6, 8, 9, 8, 6, 2, 4,
+ 7, 9, 11, 9, 8, 4, 6, 8, 10, 12, 10, 8, 10, 10, 12, 12, 12, 10, 8,
+ 15, 15, 13, 12, 12, 10, 8, 1, 4, 5, 7, 5, 4, 2, 1, 3, 5, 6, 5,
+ 3, 2, 2, 2, 5, 6, 6, 5, 4, 2, 3, 5, 7, 7, 6, 6, 5, 6, 8,
+ 10, 9, 7, 6, 12, 11, 12, 12, 10, 8, 6, 19, 16, 14, 12, 10, 8, 6, 2,
+ 1, 2, 2, 3, 1, 3, 2, 1, 1, 1, 1, 0, 3, 3, 1, 1, 2, 2, 2,
+ 4, 7, 3, 5, 5, 5, 5, 5, 11, 8, 9, 9, 9, 8, 5, 15, 14, 13, 12,
+ 11, 9, 5, 21, 18, 16, 14, 12, 11, 9, 2, 5, 5, 5, 5, 4, 6, 7, 6,
+ 6, 6, 6, 6, 6, 11, 6, 7, 8, 9, 9, 7, 14, 9, 12, 12, 12, 12, 10,
+ 14, 14, 17, 16, 17, 16, 13, 16, 21, 21, 20, 19, 17, 16, 21, 27, 25, 22, 20,
+ 18, 18, 14, 13, 13, 13, 13, 13, 6, 16, 15, 14, 14, 14, 13, 6, 17, 16, 16,
+ 17, 17, 16, 12, 17, 21, 21, 21, 22, 19, 15, 19, 27, 27, 26, 25, 23, 18, 23,
+ 30, 32, 30, 27, 25, 20, 27, 33, 34, 31, 29, 27, 21, 23, 23, 22, 21, 18, 15,
+ 10, 23, 26, 23, 22, 19, 16, 12, 23, 28, 26, 25, 23, 19, 16, 23, 29, 32, 29,
+ 27, 23, 19, 26, 33, 37, 34, 31, 26, 21, 31, 37, 40, 40, 33, 28, 23, 33, 38,
+ 42, 42, 35, 29, 24, 30, 34, 30, 25, 20, 16, 13, 29, 34, 33, 27, 22, 20, 16,
+ 30, 34, 36, 30, 26, 23, 19, 29, 36, 40, 34, 30, 27, 23, 33, 39, 43, 41, 35,
+ 30, 25, 37, 41, 46, 46, 37, 31, 27, 40, 44, 47, 47, 40, 34, 28, 6, 7, 9,
+ 12, 11, 9, 6, 5, 6, 8, 11, 11, 8, 6, 4, 6, 8, 10, 11, 9, 8, 3,
+ 6, 9, 10, 13, 11, 9, 5, 7, 9, 11, 14, 12, 9, 9, 10, 11, 12, 14, 12,
+ 9, 15, 14, 12, 12, 13, 12, 9, 4, 6, 7, 9, 8, 6, 4, 3, 6, 7, 8,
+ 7, 5, 3, 1, 5, 7, 8, 7, 6, 5, 1, 4, 8, 9, 8, 8, 6, 5, 7,
+ 9, 10, 9, 9, 6, 11, 11, 12, 12, 10, 8, 6, 18, 16, 13, 12, 10, 8, 6,
+ 1, 5, 5, 4, 5, 4, 2, 1, 3, 4, 4, 4, 3, 0, 1, 2, 4, 4, 3,
+ 3, 2, 2, 3, 5, 5, 4, 4, 4, 7, 8, 8, 7, 8, 6, 4, 14, 13, 12,
+ 10, 8, 7, 4, 20, 17, 14, 11, 9, 7, 5, 1, 2, 2, 2, 2, 3, 5, 2,
+ 3, 2, 2, 3, 3, 3, 7, 4, 4, 5, 5, 5, 4, 10, 7, 8, 8, 8, 8,
+ 6, 10, 13, 12, 12, 12, 11, 9, 14, 19, 17, 15, 14, 13, 11, 19, 23, 20, 17,
+ 15, 14, 13, 11, 10, 9, 9, 10, 9, 5, 13, 12, 11, 10, 11, 9, 3, 13, 13,
+ 13, 14, 13, 11, 8, 13, 18, 17, 16, 17, 15, 12, 16, 23, 22, 21, 21, 18, 13,
+ 20, 26, 27, 25, 22, 20, 15, 23, 28, 31, 26, 24, 22, 16, 19, 19, 17, 16, 15,
+ 11, 8, 19, 22, 18, 17, 15, 11, 8, 19, 23, 21, 20, 17, 14, 12, 20, 25, 25,
+ 24, 22, 18, 15, 22, 28, 31, 28, 25, 22, 17, 27, 31, 36, 33, 27, 23, 19, 29,
+ 34, 38, 35, 29, 25, 20, 25, 27, 25, 19, 16, 13, 10, 25, 27, 27, 20, 17, 14,
+ 11, 25, 28, 29, 24, 21, 18, 15, 25, 29, 34, 29, 25, 21, 18, 28, 33, 36, 34,
+ 30, 24, 20, 31, 35, 40, 38, 32, 26, 21, 35, 38, 41, 41, 34, 28, 23, 3, 7,
+ 12, 15, 16, 14, 11, 2, 5, 10, 14, 15, 14, 8, 2, 4, 8, 11, 13, 11, 6,
+ 2, 2, 5, 8, 10, 10, 5, 2, 2, 3, 6, 8, 9, 5, 5, 6, 6, 5, 7,
+ 9, 5, 12, 11, 8, 4, 7, 9, 5, 2, 4, 8, 12, 13, 11, 5, 2, 3, 7,
+ 10, 12, 9, 3, 2, 2, 5, 7, 8, 6, 2, 1, 2, 2, 4, 5, 3, 2, 2,
+ 3, 3, 3, 3, 3, 2, 7, 7, 7, 6, 3, 2, 2, 15, 13, 9, 7, 4, 3,
+ 2, 2, 2, 3, 5, 6, 4, 2, 1, 2, 2, 3, 4, 3, 1, 0, 2, 2, 2,
+ 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 3, 2, 10, 9,
+ 9, 8, 5, 3, 2, 17, 16, 12, 9, 6, 4, 2, 0, 1, 1, 1, 2, 2, 4,
+ 1, 1, 1, 1, 1, 1, 5, 1, 2, 2, 3, 3, 3, 5, 4, 4, 5, 5, 5,
+ 6, 7, 5, 9, 8, 9, 9, 8, 7, 11, 14, 13, 13, 11, 10, 9, 17, 20, 17,
+ 15, 13, 12, 11, 8, 8, 8, 9, 9, 8, 5, 10, 10, 9, 10, 10, 9, 5, 10,
+ 11, 11, 11, 12, 11, 6, 10, 14, 13, 14, 14, 13, 9, 12, 18, 18, 18, 18, 17,
+ 11, 16, 21, 22, 21, 20, 19, 13, 19, 24, 24, 22, 21, 20, 16, 17, 18, 18, 18,
+ 15, 11, 7, 17, 20, 19, 19, 16, 12, 7, 17, 22, 22, 21, 19, 14, 9, 17, 22,
+ 23, 22, 20, 17, 13, 18, 24, 26, 24, 23, 20, 15, 21, 26, 29, 28, 24, 21, 17,
+ 23, 28, 30, 29, 26, 22, 18, 25, 28, 26, 22, 19, 14, 9, 24, 28, 28, 24, 20,
+ 16, 10, 24, 29, 31, 24, 21, 17, 13, 24, 29, 31, 26, 23, 20, 17, 25, 28, 32,
+ 30, 25, 22, 18, 27, 29, 33, 32, 27, 23, 20, 28, 31, 33, 34, 29, 24, 21, 2,
+ 2, 3, 5, 5, 3, 2, 2, 2, 3, 4, 5, 2, 2, 2, 2, 2, 3, 3, 2,
+ 1, 0, 2, 2, 2, 3, 2, 3, 2, 2, 3, 3, 4, 3, 3, 6, 6, 7, 6,
+ 4, 3, 3, 12, 11, 8, 6, 5, 3, 3, 1, 2, 3, 3, 3, 2, 2, 1, 2,
+ 2, 3, 2, 2, 1, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 2, 2, 1, 3,
+ 4, 3, 4, 4, 4, 3, 3, 9, 8, 8, 7, 5, 3, 3, 16, 13, 10, 8, 5,
+ 4, 3, 1, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 0, 1,
+ 1, 1, 1, 3, 2, 2, 2, 1, 2, 2, 5, 6, 5, 5, 5, 5, 4, 5, 12,
+ 10, 10, 8, 7, 5, 5, 18, 17, 12, 10, 8, 7, 6, 2, 3, 3, 3, 4, 4,
+ 7, 2, 4, 4, 5, 5, 5, 8, 5, 5, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+ 9, 9, 9, 9, 13, 12, 12, 13, 12, 11, 12, 18, 16, 16, 15, 13, 12, 18, 23,
+ 20, 18, 17, 15, 14, 11, 11, 11, 11, 11, 11, 7, 12, 13, 12, 13, 13, 12, 7,
+ 12, 14, 14, 14, 14, 13, 9, 13, 18, 17, 17, 18, 16, 13, 16, 22, 22, 21, 22,
+ 19, 14, 19, 26, 27, 26, 24, 22, 16, 24, 29, 31, 28, 26, 23, 18, 19, 21, 20,
+ 20, 17, 13, 9, 20, 23, 22, 23, 19, 14, 10, 20, 25, 25, 24, 20, 16, 12, 20,
+ 26, 28, 28, 24, 20, 16, 23, 28, 32, 32, 29, 23, 18, 26, 32, 37, 37, 31, 25,
+ 20, 28, 34, 39, 39, 33, 28, 22, 28, 34, 32, 26, 20, 16, 12, 28, 34, 34, 27,
+ 22, 17, 13, 27, 34, 38, 30, 24, 20, 16, 28, 34, 39, 34, 28, 23, 20, 30, 36,
+ 41, 39, 32, 28, 22, 32, 38, 44, 41, 36, 30, 24, 34, 40, 45, 43, 38, 31, 25,
+ 2, 4, 5, 6, 6, 5, 4, 1, 4, 5, 6, 6, 5, 4, 0, 3, 5, 5, 5,
+ 4, 4, 1, 2, 4, 5, 5, 5, 6, 3, 4, 6, 6, 7, 6, 6, 8, 8, 9,
+ 9, 8, 6, 6, 13, 12, 11, 9, 8, 6, 6, 0, 3, 5, 5, 5, 4, 2, 1,
+ 2, 4, 5, 5, 4, 2, 1, 1, 4, 4, 4, 3, 2, 2, 2, 3, 5, 4, 5,
+ 5, 5, 4, 6, 7, 7, 6, 5, 9, 9, 10, 10, 8, 6, 5, 17, 14, 11, 10,
+ 8, 6, 5, 1, 1, 2, 3, 3, 1, 4, 2, 1, 2, 2, 2, 1, 4, 2, 1,
+ 0, 0, 0, 0, 5, 4, 2, 3, 3, 3, 3, 6, 8, 6, 7, 7, 7, 6, 6,
+ 13, 11, 11, 10, 9, 7, 6, 19, 18, 14, 12, 10, 9, 8, 2, 4, 5, 5, 5,
+ 5, 7, 4, 5, 5, 6, 6, 6, 8, 8, 6, 7, 7, 7, 8, 8, 10, 9, 10,
+ 10, 11, 11, 10, 11, 14, 14, 15, 15, 14, 13, 13, 20, 19, 18, 17, 15, 15, 19,
+ 26, 22, 20, 18, 17, 16, 12, 13, 12, 13, 13, 12, 7, 13, 14, 14, 14, 14, 13,
+ 8, 14, 16, 16, 16, 16, 15, 11, 13, 20, 19, 19, 20, 18, 15, 18, 25, 24, 24,
+ 24, 21, 16, 22, 28, 30, 28, 26, 23, 18, 26, 31, 33, 29, 28, 25, 20, 22, 23,
+ 22, 22, 18, 14, 10, 22, 25, 24, 24, 20, 16, 12, 22, 27, 26, 26, 23, 18, 14,
+ 21, 29, 30, 29, 26, 22, 18, 26, 31, 36, 34, 30, 25, 20, 27, 34, 39, 38, 33,
+ 27, 21, 31, 37, 41, 41, 34, 28, 23, 29, 35, 34, 27, 22, 17, 13, 29, 36, 36,
+ 29, 24, 19, 14, 29, 36, 39, 32, 26, 22, 17, 30, 36, 42, 35, 30, 26, 22, 31,
+ 38, 44, 41, 33, 29, 24, 34, 40, 47, 47, 38, 30, 26, 37, 43, 47, 47, 39, 33,
+ 27, 2, 6, 9, 11, 11, 10, 7, 2, 5, 8, 11, 11, 9, 6, 0, 3, 7, 9,
+ 10, 7, 6, 1, 2, 5, 7, 9, 8, 7, 3, 4, 6, 8, 10, 9, 7, 7, 8,
+ 10, 10, 10, 9, 7, 14, 13, 11, 10, 10, 9, 7, 0, 3, 7, 10, 9, 6, 3,
+ 1, 2, 5, 8, 9, 6, 2, 1, 1, 4, 6, 6, 4, 2, 2, 2, 4, 6, 6,
+ 5, 4, 4, 5, 6, 8, 7, 6, 4, 10, 9, 10, 10, 8, 6, 4, 17, 14, 11,
+ 11, 8, 6, 4, 1, 1, 2, 3, 4, 3, 5, 2, 0, 2, 2, 3, 2, 4, 2,
+ 1, 0, 0, 0, 0, 3, 4, 2, 3, 3, 3, 3, 5, 8, 6, 7, 7, 7, 6,
+ 5, 13, 11, 11, 10, 9, 7, 5, 19, 17, 14, 12, 10, 9, 8, 2, 4, 5, 5,
+ 6, 6, 8, 5, 5, 6, 6, 7, 7, 7, 10, 6, 7, 7, 7, 7, 7, 11, 9,
+ 10, 11, 11, 10, 9, 12, 14, 14, 15, 14, 14, 12, 13, 20, 19, 18, 17, 16, 14,
+ 19, 26, 23, 21, 18, 17, 16, 12, 13, 12, 13, 14, 13, 8, 15, 14, 14, 15, 15,
+ 14, 7, 14, 16, 16, 16, 15, 14, 11, 14, 20, 19, 19, 20, 17, 14, 17, 25, 24,
+ 23, 24, 21, 16, 21, 28, 30, 28, 26, 23, 18, 25, 31, 33, 30, 27, 25, 20, 22,
+ 24, 22, 22, 19, 16, 12, 21, 26, 24, 24, 20, 16, 12, 22, 28, 26, 26, 21, 18,
+ 14, 22, 29, 30, 30, 26, 22, 18, 25, 33, 36, 34, 30, 25, 19, 28, 34, 41, 38,
+ 33, 27, 22, 31, 37, 42, 41, 36, 29, 23, 30, 35, 34, 28, 23, 18, 14, 29, 36,
+ 36, 30, 23, 19, 14, 30, 35, 38, 32, 26, 21, 17, 29, 37, 41, 35, 31, 25, 21,
+ 32, 39, 45, 39, 34, 29, 23, 34, 41, 47, 46, 38, 31, 26, 37, 43, 47, 46, 40,
+ 33, 27, 3, 6, 9, 13, 13, 10, 7, 2, 5, 8, 12, 12, 9, 6, 0, 4, 7,
+ 10, 11, 8, 6, 1, 3, 5, 8, 10, 9, 7, 4, 4, 6, 8, 11, 9, 7, 7,
+ 8, 10, 10, 10, 9, 7, 13, 12, 10, 10, 10, 9, 7, 1, 4, 7, 10, 10, 7,
+ 3, 1, 2, 6, 9, 9, 6, 2, 1, 1, 5, 7, 7, 4, 2, 1, 2, 4, 6,
+ 6, 5, 4, 4, 5, 6, 8, 7, 5, 4, 10, 9, 10, 10, 7, 6, 4, 16, 14,
+ 11, 10, 8, 6, 4, 1, 1, 3, 4, 5, 3, 4, 1, 1, 2, 2, 3, 2, 3,
+ 2, 1, 0, 0, 0, 1, 3, 4, 2, 3, 3, 3, 3, 4, 8, 6, 7, 7, 7,
+ 6, 4, 13, 12, 11, 10, 9, 7, 4, 19, 17, 14, 12, 10, 9, 8, 1, 5, 5,
+ 5, 6, 6, 8, 5, 6, 6, 6, 7, 7, 7, 9, 6, 7, 7, 7, 7, 6, 11,
+ 8, 11, 11, 10, 11, 8, 12, 14, 15, 15, 15, 14, 12, 13, 19, 19, 18, 17, 15,
+ 14, 19, 26, 23, 20, 19, 17, 16, 12, 13, 13, 13, 14, 13, 8, 14, 15, 14, 14,
+ 15, 13, 7, 15, 16, 16, 16, 16, 14, 11, 14, 20, 20, 20, 20, 18, 14, 17, 25,
+ 25, 24, 23, 21, 16, 22, 29, 30, 28, 26, 23, 18, 26, 32, 33, 31, 28, 25, 20,
+ 22, 23, 23, 23, 20, 16, 11, 22, 26, 24, 24, 21, 16, 12, 23, 29, 27, 26, 22,
+ 17, 14, 23, 29, 31, 29, 26, 22, 18, 25, 32, 37, 34, 30, 25, 19, 28, 35, 41,
+ 38, 33, 27, 22, 31, 36, 41, 40, 34, 30, 23, 31, 35, 33, 27, 22, 18, 14, 30,
+ 36, 36, 29, 23, 18, 14, 29, 36, 38, 31, 25, 21, 17, 31, 36, 40, 35, 30, 26,
+ 22, 32, 38, 43, 40, 34, 29, 23, 36, 41, 45, 45, 37, 31, 25, 37, 43, 46, 46,
+ 40, 34, 27, 3, 6, 8, 9, 9, 7, 5, 2, 6, 7, 9, 9, 6, 4, 1, 4,
+ 6, 8, 8, 6, 5, 1, 3, 5, 7, 9, 8, 6, 4, 4, 7, 8, 11, 9, 6,
+ 8, 8, 10, 10, 10, 9, 6, 13, 12, 10, 10, 10, 9, 6, 0, 4, 6, 8, 7,
+ 5, 3, 1, 3, 5, 7, 6, 4, 2, 1, 1, 5, 6, 4, 3, 2, 2, 2, 4,
+ 6, 5, 5, 4, 5, 5, 6, 8, 7, 6, 4, 10, 9, 10, 10, 7, 6, 4, 16,
+ 13, 11, 10, 8, 6, 4, 1, 1, 3, 3, 4, 3, 5, 2, 1, 2, 3, 3, 2,
+ 3, 2, 1, 0, 0, 0, 0, 2, 4, 2, 3, 3, 3, 3, 4, 9, 7, 7, 7,
+ 7, 6, 4, 14, 11, 11, 10, 9, 7, 4, 18, 16, 13, 11, 10, 9, 8, 2, 5,
+ 5, 6, 6, 6, 8, 5, 6, 6, 6, 7, 7, 6, 9, 6, 7, 7, 8, 7, 6,
+ 11, 8, 11, 11, 10, 11, 8, 12, 14, 15, 14, 15, 14, 11, 13, 20, 19, 18, 16,
+ 16, 14, 18, 25, 22, 20, 18, 17, 16, 13, 13, 13, 13, 14, 13, 8, 15, 15, 14,
+ 15, 15, 13, 7, 15, 16, 16, 16, 16, 14, 10, 15, 20, 20, 20, 20, 18, 15, 18,
+ 25, 24, 23, 24, 21, 16, 22, 28, 29, 28, 26, 23, 18, 25, 30, 32, 29, 28, 25,
+ 20, 23, 24, 23, 22, 19, 16, 11, 23, 26, 24, 22, 20, 16, 11, 23, 28, 26, 24,
+ 21, 18, 14, 22, 29, 31, 28, 25, 22, 18, 25, 31, 35, 34, 29, 24, 19, 28, 33,
+ 39, 37, 31, 26, 22, 31, 35, 41, 39, 33, 27, 23, 29, 34, 32, 26, 22, 18, 14,
+ 30, 34, 33, 27, 22, 18, 14, 29, 33, 36, 29, 24, 21, 17, 29, 34, 39, 34, 29,
+ 26, 21, 32, 37, 42, 38, 34, 27, 23, 34, 40, 44, 44, 35, 29, 25, 37, 41, 45,
+ 45, 37, 31, 27, 7, 9, 11, 13, 13, 11, 8, 6, 8, 10, 12, 13, 10, 6, 5,
+ 7, 9, 11, 11, 8, 6, 3, 6, 7, 10, 12, 10, 8, 4, 6, 8, 10, 13, 10,
+ 8, 7, 8, 9, 10, 12, 10, 7, 11, 10, 9, 9, 11, 11, 8, 5, 7, 9, 11,
+ 10, 7, 5, 4, 6, 8, 10, 9, 6, 4, 1, 6, 7, 9, 7, 5, 3, 0, 4,
+ 6, 8, 7, 6, 5, 4, 5, 7, 8, 8, 7, 5, 9, 8, 9, 10, 7, 7, 5,
+ 13, 11, 10, 9, 8, 6, 5, 2, 6, 6, 6, 6, 5, 4, 0, 4, 5, 5, 5,
+ 4, 2, 1, 3, 5, 3, 3, 3, 0, 1, 2, 3, 3, 3, 3, 2, 6, 6, 6,
+ 5, 5, 4, 2, 12, 10, 8, 8, 6, 4, 2, 15, 13, 10, 8, 6, 5, 3, 1,
+ 2, 3, 3, 4, 5, 6, 1, 3, 3, 4, 4, 4, 5, 4, 5, 4, 4, 4, 4,
+ 4, 7, 7, 7, 6, 6, 6, 5, 8, 12, 11, 10, 10, 9, 8, 12, 16, 14, 13,
+ 12, 11, 10, 15, 20, 16, 14, 14, 12, 11, 11, 10, 10, 10, 11, 10, 6, 12, 12,
+ 11, 11, 12, 10, 5, 12, 14, 13, 12, 12, 10, 7, 12, 17, 15, 15, 15, 13, 10,
+ 15, 22, 20, 19, 18, 16, 12, 19, 23, 25, 22, 20, 18, 13, 20, 25, 27, 24, 22,
+ 19, 15, 19, 19, 18, 17, 15, 12, 9, 19, 22, 19, 18, 16, 12, 8, 19, 23, 21,
+ 19, 16, 13, 10, 19, 23, 24, 22, 19, 17, 13, 21, 27, 29, 27, 23, 20, 15, 24,
+ 28, 34, 29, 25, 20, 17, 26, 30, 34, 34, 27, 23, 18, 25, 28, 26, 20, 17, 14,
+ 11, 25, 28, 28, 22, 18, 14, 11, 25, 28, 30, 23, 19, 16, 12, 25, 29, 32, 27,
+ 23, 19, 16, 27, 31, 36, 33, 27, 22, 18, 29, 33, 38, 36, 29, 24, 20, 30, 36,
+ 38, 38, 32, 26, 21, 6, 10, 15, 18, 18, 17, 15, 5, 9, 13, 16, 17, 15, 12,
+ 3, 7, 11, 14, 15, 13, 10, 2, 5, 8, 11, 13, 13, 9, 2, 2, 6, 9, 11,
+ 13, 10, 5, 5, 5, 8, 11, 13, 10, 11, 10, 6, 6, 10, 13, 9, 3, 7, 12,
+ 15, 15, 13, 10, 2, 6, 10, 13, 15, 13, 7, 2, 4, 8, 11, 12, 10, 4, 2,
+ 2, 5, 8, 8, 8, 3, 2, 3, 3, 6, 7, 7, 3, 7, 7, 6, 5, 5, 6,
+ 3, 14, 12, 9, 5, 4, 4, 3, 2, 4, 7, 9, 10, 9, 4, 2, 3, 5, 7,
+ 8, 7, 2, 1, 2, 4, 5, 4, 4, 1, 0, 2, 1, 1, 2, 1, 2, 4, 4,
+ 4, 4, 4, 3, 2, 10, 10, 9, 8, 6, 3, 2, 16, 15, 11, 9, 7, 4, 2,
+ 1, 1, 1, 2, 2, 3, 3, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2,
+ 2, 4, 3, 3, 3, 4, 4, 4, 5, 5, 8, 7, 8, 8, 7, 6, 10, 13, 12,
+ 12, 10, 9, 8, 16, 18, 15, 13, 12, 11, 10, 7, 7, 8, 8, 9, 7, 3, 9,
+ 9, 9, 10, 9, 8, 4, 9, 10, 11, 11, 11, 10, 5, 9, 13, 13, 13, 13, 12,
+ 8, 11, 16, 16, 16, 15, 15, 10, 14, 19, 19, 18, 17, 16, 12, 18, 21, 21, 19,
+ 18, 17, 14, 16, 16, 16, 16, 14, 11, 6, 16, 19, 17, 17, 15, 12, 7, 16, 20,
+ 19, 18, 16, 13, 8, 15, 20, 20, 19, 17, 15, 12, 17, 21, 23, 21, 19, 17, 14,
+ 19, 22, 25, 24, 21, 18, 15, 21, 23, 27, 25, 22, 18, 16, 21, 25, 23, 19, 17,
+ 14, 9, 21, 25, 26, 20, 17, 15, 10, 22, 25, 27, 21, 18, 15, 12, 21, 24, 28,
+ 22, 18, 17, 15, 20, 25, 29, 26, 21, 19, 16, 22, 26, 29, 28, 23, 20, 17, 24,
+ 27, 30, 30, 24, 21, 18, 3, 3, 5, 8, 8, 6, 3, 2, 3, 4, 7, 8, 5,
+ 3, 2, 3, 4, 5, 6, 4, 2, 2, 2, 3, 3, 4, 2, 2, 1, 2, 2, 2,
+ 3, 2, 2, 5, 5, 5, 4, 3, 2, 2, 11, 9, 7, 5, 3, 2, 2, 2, 3,
+ 4, 6, 5, 4, 3, 2, 3, 4, 5, 5, 3, 2, 1, 2, 3, 3, 3, 3, 2,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 7, 6, 7, 6, 3,
+ 2, 2, 14, 12, 8, 6, 4, 2, 2, 1, 2, 3, 3, 3, 3, 3, 1, 2, 3,
+ 3, 3, 3, 3, 1, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 1, 2, 4,
+ 4, 3, 3, 4, 2, 3, 10, 9, 8, 7, 5, 3, 3, 17, 15, 11, 8, 6, 5,
+ 4, 2, 3, 3, 4, 4, 4, 7, 2, 4, 4, 5, 5, 5, 8, 3, 5, 5, 6,
+ 6, 6, 8, 5, 6, 6, 6, 7, 7, 8, 6, 10, 10, 10, 10, 10, 9, 10, 16,
+ 15, 14, 13, 11, 11, 16, 21, 18, 16, 14, 13, 12, 10, 11, 11, 11, 12, 11, 7,
+ 11, 12, 12, 12, 13, 12, 8, 12, 13, 14, 14, 14, 13, 9, 12, 16, 16, 15, 15,
+ 15, 11, 14, 20, 19, 19, 19, 17, 12, 17, 23, 25, 24, 22, 20, 14, 20, 27, 29,
+ 26, 24, 22, 16, 19, 21, 21, 20, 18, 14, 9, 20, 23, 23, 23, 19, 14, 10, 19,
+ 25, 25, 24, 21, 16, 11, 19, 24, 26, 26, 22, 18, 14, 21, 26, 31, 29, 27, 21,
+ 16, 23, 29, 34, 34, 28, 24, 17, 26, 31, 35, 34, 31, 25, 20, 28, 34, 32, 26,
+ 22, 17, 12, 27, 34, 34, 28, 23, 18, 13, 27, 33, 37, 30, 24, 19, 14, 28, 33,
+ 38, 31, 26, 22, 18, 27, 34, 39, 36, 30, 25, 19, 29, 36, 39, 39, 33, 27, 22,
+ 33, 37, 39, 41, 34, 30, 24, 5, 8, 9, 10, 10, 9, 8, 4, 7, 9, 10, 9,
+ 8, 7, 3, 6, 8, 8, 8, 7, 5, 2, 4, 6, 6, 7, 5, 4, 1, 2, 4,
+ 5, 5, 5, 4, 5, 5, 7, 6, 5, 5, 4, 10, 10, 8, 7, 6, 5, 4, 3,
+ 6, 8, 9, 8, 8, 6, 2, 5, 7, 9, 8, 7, 5, 1, 4, 7, 7, 7, 6,
+ 4, 1, 2, 4, 5, 5, 4, 3, 2, 2, 4, 5, 4, 4, 3, 7, 6, 8, 7,
+ 5, 4, 3, 14, 11, 9, 8, 5, 4, 3, 2, 4, 6, 6, 7, 5, 6, 1, 2,
+ 5, 5, 6, 5, 5, 1, 2, 3, 3, 3, 3, 3, 1, 1, 0, 0, 0, 0, 3,
+ 5, 4, 3, 3, 3, 3, 3, 10, 9, 8, 7, 5, 4, 3, 16, 15, 11, 8, 7,
+ 6, 5, 1, 4, 5, 5, 6, 6, 9, 2, 5, 5, 6, 7, 7, 8, 4, 6, 6,
+ 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 11, 11, 11, 11, 10, 10, 10,
+ 17, 16, 14, 13, 12, 11, 16, 22, 19, 17, 15, 14, 13, 11, 12, 12, 12, 13, 12,
+ 8, 12, 13, 14, 14, 14, 13, 8, 12, 15, 15, 15, 15, 13, 9, 12, 17, 16, 16,
+ 16, 15, 11, 15, 21, 21, 20, 20, 18, 13, 18, 25, 26, 25, 22, 19, 14, 23, 27,
+ 29, 26, 25, 22, 16, 21, 22, 22, 22, 19, 15, 11, 21, 25, 24, 24, 20, 15, 11,
+ 21, 26, 25, 25, 20, 16, 12, 21, 26, 27, 26, 23, 18, 14, 21, 28, 31, 31, 27,
+ 21, 16, 24, 30, 37, 35, 29, 23, 18, 28, 33, 37, 38, 31, 25, 20, 29, 33, 34,
+ 27, 22, 18, 13, 28, 36, 36, 29, 23, 18, 14, 28, 35, 38, 30, 24, 19, 14, 28,
+ 34, 39, 32, 26, 22, 18, 29, 35, 42, 38, 32, 26, 20, 31, 37, 43, 41, 34, 27,
+ 22, 33, 39, 44, 44, 37, 30, 23, 5, 9, 12, 15, 15, 13, 10, 4, 8, 11, 14,
+ 15, 12, 9, 3, 6, 10, 13, 13, 10, 7, 2, 4, 7, 10, 11, 8, 7, 1, 2,
+ 5, 8, 10, 9, 7, 5, 5, 7, 8, 9, 8, 7, 10, 10, 8, 8, 8, 8, 7,
+ 3, 7, 10, 13, 12, 10, 7, 2, 5, 9, 12, 12, 9, 5, 1, 4, 8, 10, 10,
+ 7, 4, 1, 2, 5, 7, 7, 5, 3, 3, 2, 4, 6, 6, 6, 3, 7, 6, 8,
+ 8, 6, 5, 3, 13, 12, 9, 8, 6, 4, 3, 1, 4, 6, 7, 8, 6, 6, 1,
+ 2, 5, 6, 6, 5, 4, 1, 1, 3, 3, 3, 3, 3, 1, 1, 0, 0, 0, 0,
+ 3, 5, 4, 3, 3, 3, 3, 3, 10, 8, 8, 7, 5, 4, 3, 16, 14, 11, 9,
+ 7, 5, 5, 1, 4, 5, 5, 6, 7, 8, 1, 5, 6, 6, 7, 7, 7, 5, 6,
+ 6, 6, 6, 7, 7, 8, 7, 7, 7, 7, 7, 6, 9, 11, 11, 11, 11, 11, 9,
+ 11, 17, 16, 15, 13, 12, 11, 16, 22, 19, 17, 15, 14, 12, 11, 12, 12, 13, 14,
+ 13, 9, 12, 14, 13, 14, 15, 13, 8, 12, 15, 15, 14, 15, 13, 9, 12, 17, 16,
+ 16, 16, 15, 11, 15, 22, 20, 20, 20, 18, 12, 18, 25, 27, 25, 22, 19, 14, 23,
+ 28, 29, 27, 25, 22, 16, 21, 22, 22, 22, 19, 15, 11, 21, 24, 24, 24, 20, 15,
+ 11, 21, 26, 26, 25, 20, 16, 11, 21, 26, 28, 26, 22, 18, 15, 22, 27, 31, 30,
+ 26, 21, 16, 25, 31, 37, 35, 29, 23, 18, 28, 33, 37, 37, 32, 25, 20, 29, 35,
+ 33, 27, 22, 18, 14, 29, 36, 36, 30, 23, 19, 14, 30, 35, 38, 30, 24, 19, 14,
+ 29, 35, 40, 33, 27, 22, 18, 28, 35, 40, 38, 31, 25, 20, 31, 37, 43, 40, 34,
+ 27, 22, 33, 40, 44, 43, 37, 29, 23, 6, 9, 13, 16, 16, 14, 10, 5, 8, 11,
+ 15, 15, 12, 9, 3, 6, 10, 13, 14, 11, 7, 2, 4, 7, 10, 12, 9, 6, 1,
+ 3, 5, 8, 11, 9, 6, 5, 6, 7, 8, 10, 9, 6, 10, 9, 8, 8, 9, 9,
+ 6, 3, 7, 10, 13, 13, 10, 7, 2, 6, 9, 12, 12, 9, 5, 1, 4, 8, 10,
+ 10, 7, 3, 1, 2, 5, 7, 7, 5, 2, 3, 2, 4, 6, 6, 5, 2, 7, 6,
+ 8, 8, 5, 5, 2, 13, 10, 9, 8, 6, 4, 3, 1, 4, 6, 7, 8, 6, 6,
+ 1, 3, 5, 6, 6, 5, 4, 1, 2, 3, 3, 3, 3, 3, 2, 1, 0, 0, 0,
+ 0, 3, 5, 4, 4, 4, 4, 3, 3, 11, 9, 8, 7, 6, 4, 3, 15, 13, 10,
+ 9, 7, 6, 5, 1, 5, 5, 6, 6, 7, 9, 2, 6, 6, 6, 7, 7, 7, 5,
+ 6, 7, 7, 7, 7, 6, 8, 7, 8, 7, 7, 7, 6, 9, 11, 12, 11, 12, 10,
+ 9, 11, 17, 16, 15, 13, 12, 11, 15, 21, 19, 17, 15, 14, 13, 11, 13, 13, 13,
+ 14, 13, 9, 12, 14, 14, 14, 15, 13, 8, 12, 16, 15, 15, 15, 13, 10, 13, 18,
+ 17, 17, 16, 15, 11, 14, 22, 21, 21, 21, 18, 13, 19, 26, 27, 25, 22, 20, 15,
+ 22, 27, 29, 27, 25, 22, 16, 21, 24, 22, 22, 19, 16, 11, 22, 26, 25, 24, 21,
+ 16, 11, 21, 28, 26, 25, 20, 16, 12, 21, 27, 27, 27, 23, 19, 15, 23, 28, 34,
+ 31, 27, 22, 16, 25, 31, 37, 35, 28, 24, 18, 28, 33, 37, 37, 32, 26, 19, 31,
+ 35, 33, 29, 22, 18, 14, 30, 36, 36, 30, 23, 18, 15, 29, 35, 37, 31, 24, 20,
+ 14, 31, 36, 39, 33, 28, 22, 18, 29, 35, 40, 37, 32, 26, 20, 31, 38, 42, 41,
+ 34, 27, 22, 34, 39, 44, 43, 37, 31, 24, 6, 9, 12, 16, 16, 13, 11, 5, 8,
+ 12, 15, 16, 12, 9, 3, 7, 10, 13, 14, 11, 7, 2, 5, 8, 10, 13, 9, 6,
+ 1, 3, 6, 8, 12, 10, 7, 5, 5, 7, 8, 11, 10, 6, 8, 7, 7, 7, 9,
+ 9, 7, 3, 7, 10, 13, 13, 10, 7, 2, 6, 9, 12, 12, 9, 5, 1, 4, 8,
+ 10, 10, 7, 3, 1, 2, 5, 7, 7, 5, 3, 3, 2, 4, 6, 6, 5, 3, 7,
+ 6, 7, 7, 5, 5, 3, 11, 8, 7, 7, 5, 4, 3, 2, 5, 6, 7, 8, 6,
+ 6, 1, 3, 5, 6, 6, 5, 4, 1, 2, 4, 3, 3, 3, 3, 2, 1, 0, 0,
+ 0, 0, 2, 6, 4, 4, 4, 4, 3, 2, 9, 8, 6, 6, 5, 4, 2, 13, 10,
+ 8, 7, 7, 6, 5, 1, 5, 5, 6, 6, 7, 9, 2, 6, 6, 7, 7, 7, 7,
+ 6, 6, 7, 7, 7, 7, 6, 8, 7, 8, 8, 7, 8, 6, 9, 12, 12, 11, 11,
+ 10, 9, 10, 15, 15, 14, 13, 12, 11, 12, 18, 17, 15, 14, 14, 13, 12, 13, 13,
+ 13, 14, 13, 9, 13, 14, 14, 15, 15, 13, 7, 13, 16, 16, 15, 15, 13, 10, 13,
+ 18, 17, 16, 17, 15, 11, 15, 23, 21, 21, 20, 18, 13, 18, 24, 26, 24, 22, 20,
+ 14, 20, 25, 28, 25, 23, 22, 16, 22, 24, 22, 21, 19, 15, 11, 22, 25, 24, 23,
+ 20, 16, 12, 22, 28, 26, 24, 20, 16, 12, 22, 26, 27, 25, 22, 19, 15, 23, 29,
+ 33, 30, 25, 22, 16, 25, 30, 35, 33, 28, 23, 18, 26, 31, 36, 36, 30, 25, 20,
+ 30, 34, 31, 26, 22, 18, 14, 30, 33, 33, 28, 22, 19, 14, 29, 33, 35, 28, 23,
+ 19, 15, 29, 34, 37, 30, 26, 22, 18, 29, 34, 39, 36, 29, 24, 21, 31, 36, 40,
+ 39, 32, 27, 21, 32, 37, 40, 40, 33, 27, 23, 8, 11, 13, 15, 15, 13, 10, 7,
+ 10, 12, 15, 15, 12, 8, 6, 8, 11, 13, 14, 10, 7, 5, 7, 9, 10, 12, 9,
+ 6, 2, 5, 7, 9, 12, 9, 6, 3, 5, 6, 8, 11, 9, 6, 4, 5, 6, 8,
+ 10, 9, 6, 6, 8, 11, 13, 12, 10, 7, 6, 8, 10, 12, 12, 8, 5, 4, 7,
+ 9, 10, 9, 7, 4, 2, 5, 7, 8, 7, 5, 3, 1, 3, 6, 7, 6, 5, 3,
+ 3, 4, 5, 6, 6, 5, 3, 6, 5, 5, 6, 5, 4, 3, 4, 7, 7, 7, 8,
+ 7, 6, 2, 6, 7, 7, 6, 5, 4, 1, 5, 5, 5, 4, 4, 2, 2, 2, 3,
+ 3, 3, 2, 0, 3, 3, 2, 2, 2, 1, 0, 5, 4, 3, 2, 3, 1, 0, 9,
+ 5, 4, 3, 2, 2, 2, 2, 4, 4, 5, 6, 8, 8, 1, 5, 4, 5, 6, 5,
+ 6, 1, 6, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 4, 5, 8, 7, 7,
+ 7, 7, 6, 6, 10, 9, 8, 8, 8, 7, 9, 14, 12, 10, 10, 9, 9, 11, 11,
+ 11, 11, 12, 11, 8, 12, 13, 12, 12, 12, 11, 6, 12, 14, 14, 13, 12, 11, 7,
+ 12, 16, 14, 13, 13, 12, 8, 13, 18, 16, 16, 15, 14, 9, 14, 19, 20, 18, 16,
+ 14, 10, 15, 20, 22, 21, 18, 16, 12, 19, 21, 18, 18, 16, 13, 9, 19, 22, 20,
+ 19, 17, 13, 9, 20, 24, 22, 20, 16, 13, 10, 20, 24, 23, 21, 17, 15, 11, 19,
+ 24, 26, 24, 21, 16, 12, 20, 24, 30, 27, 23, 17, 14, 21, 26, 29, 30, 25, 19,
+ 14, 26, 30, 27, 22, 18, 14, 12, 26, 30, 30, 23, 18, 15, 12, 26, 30, 32, 24,
+ 19, 16, 12, 26, 30, 33, 26, 21, 16, 14, 25, 29, 34, 30, 24, 19, 15, 25, 30,
+ 35, 34, 27, 21, 16, 27, 32, 36, 35, 29, 23, 17, 9, 13, 17, 22, 21, 18, 16,
+ 8, 12, 16, 20, 21, 18, 15, 7, 10, 14, 18, 20, 17, 13, 6, 9, 12, 15, 17,
+ 15, 11, 4, 5, 8, 11, 13, 14, 12, 2, 3, 6, 10, 12, 14, 12, 7, 5, 5,
+ 8, 12, 13, 11, 7, 10, 15, 18, 18, 15, 13, 6, 9, 13, 16, 17, 15, 11, 5,
+ 8, 11, 14, 15, 13, 9, 5, 6, 9, 11, 12, 11, 6, 2, 3, 5, 8, 9, 10,
+ 6, 3, 3, 4, 6, 8, 8, 6, 10, 8, 4, 5, 6, 6, 5, 5, 8, 10, 11,
+ 13, 11, 8, 5, 7, 9, 10, 11, 10, 7, 4, 6, 7, 8, 9, 8, 6, 4, 4,
+ 5, 5, 5, 6, 3, 0, 2, 1, 1, 1, 2, 3, 6, 6, 5, 4, 1, 2, 3,
+ 11, 10, 7, 5, 2, 1, 2, 5, 6, 6, 7, 7, 8, 7, 4, 5, 6, 6, 7,
+ 7, 7, 3, 5, 6, 6, 6, 6, 6, 2, 3, 3, 4, 4, 4, 3, 2, 3, 4,
+ 4, 4, 3, 3, 6, 8, 7, 7, 6, 5, 5, 11, 13, 9, 8, 8, 7, 6, 6,
+ 7, 7, 7, 8, 7, 7, 7, 8, 8, 9, 9, 7, 7, 7, 10, 10, 10, 11, 9,
+ 6, 7, 11, 10, 10, 10, 10, 5, 7, 11, 10, 10, 10, 10, 6, 9, 13, 13, 12,
+ 11, 10, 8, 12, 15, 15, 13, 12, 11, 9, 14, 15, 14, 14, 13, 9, 7, 14, 16,
+ 15, 15, 14, 11, 6, 14, 17, 16, 16, 14, 11, 7, 14, 16, 16, 15, 14, 11, 8,
+ 13, 15, 17, 16, 14, 11, 9, 13, 16, 20, 18, 14, 12, 10, 15, 18, 20, 19, 16,
+ 13, 10, 18, 23, 21, 17, 15, 12, 8, 18, 22, 23, 18, 16, 13, 9, 18, 23, 24,
+ 18, 16, 13, 10, 19, 22, 24, 19, 15, 13, 10, 17, 20, 23, 20, 16, 13, 11, 17,
+ 20, 23, 22, 18, 14, 11, 18, 22, 24, 24, 19, 15, 12, 7, 8, 10, 12, 12, 11,
+ 9, 6, 8, 9, 12, 12, 9, 8, 6, 7, 8, 10, 11, 8, 6, 6, 6, 6, 7,
+ 9, 6, 3, 3, 3, 4, 4, 4, 3, 2, 1, 2, 2, 2, 3, 3, 2, 7, 6,
+ 3, 2, 3, 3, 2, 6, 7, 8, 10, 10, 9, 8, 6, 7, 8, 10, 9, 8, 7,
+ 5, 6, 7, 8, 8, 7, 5, 4, 5, 5, 6, 5, 5, 2, 2, 2, 2, 3, 2,
+ 2, 2, 3, 3, 3, 2, 2, 2, 2, 10, 8, 5, 3, 1, 2, 2, 5, 6, 7,
+ 8, 9, 9, 8, 4, 6, 7, 8, 8, 8, 8, 4, 5, 6, 6, 6, 6, 6, 4,
+ 4, 4, 4, 4, 4, 3, 2, 0, 1, 1, 1, 2, 3, 6, 5, 5, 3, 1, 1,
+ 2, 12, 11, 7, 4, 2, 1, 2, 4, 6, 7, 8, 9, 9, 9, 4, 5, 7, 8,
+ 8, 8, 9, 3, 5, 6, 7, 7, 7, 7, 4, 4, 5, 5, 5, 5, 5, 3, 6,
+ 6, 6, 7, 7, 7, 6, 12, 11, 10, 9, 7, 8, 12, 17, 14, 12, 10, 9, 9,
+ 9, 10, 10, 10, 11, 10, 9, 10, 11, 11, 12, 12, 11, 9, 11, 12, 13, 13, 13,
+ 12, 8, 10, 14, 13, 14, 14, 13, 8, 11, 16, 16, 16, 16, 14, 9, 13, 19, 21,
+ 20, 19, 16, 11, 17, 22, 24, 22, 20, 18, 12, 18, 20, 20, 20, 17, 13, 9, 18,
+ 22, 22, 22, 19, 14, 9, 18, 24, 24, 24, 19, 15, 10, 19, 23, 25, 24, 21, 16,
+ 11, 17, 23, 27, 25, 23, 18, 13, 19, 25, 31, 28, 25, 20, 14, 23, 28, 31, 30,
+ 26, 22, 16, 27, 33, 32, 26, 21, 16, 11, 26, 32, 33, 28, 23, 17, 12, 27, 32,
+ 35, 28, 23, 18, 13, 26, 32, 36, 29, 25, 19, 14, 26, 31, 33, 31, 26, 22, 16,
+ 25, 30, 35, 33, 28, 23, 18, 27, 31, 34, 35, 30, 25, 20, 9, 11, 12, 14, 13,
+ 13, 12, 8, 11, 12, 13, 13, 11, 10, 6, 10, 11, 12, 12, 10, 8, 5, 8, 9,
+ 10, 10, 8, 5, 2, 5, 6, 6, 6, 5, 4, 1, 3, 4, 5, 5, 5, 4, 7,
+ 7, 5, 4, 5, 5, 4, 6, 10, 11, 13, 12, 12, 10, 5, 9, 11, 12, 12, 11,
+ 9, 4, 7, 10, 11, 10, 9, 7, 4, 5, 7, 8, 8, 7, 4, 2, 2, 4, 5,
+ 5, 4, 4, 4, 3, 5, 4, 4, 4, 4, 10, 8, 6, 5, 3, 4, 4, 5, 7,
+ 9, 10, 11, 10, 10, 4, 6, 8, 9, 10, 8, 8, 4, 5, 7, 7, 7, 7, 6,
+ 4, 3, 3, 3, 4, 4, 2, 2, 1, 0, 0, 0, 1, 2, 6, 5, 4, 3, 2,
+ 1, 2, 12, 10, 7, 5, 4, 3, 2, 4, 6, 7, 8, 10, 11, 10, 3, 6, 7,
+ 8, 9, 9, 9, 3, 6, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 7, 6, 4,
+ 8, 7, 8, 8, 8, 8, 7, 13, 12, 11, 10, 9, 9, 12, 18, 15, 13, 11, 10,
+ 10, 11, 12, 12, 12, 13, 12, 10, 11, 13, 13, 13, 14, 12, 9, 12, 14, 14, 14,
+ 14, 13, 9, 11, 16, 15, 15, 15, 14, 9, 12, 18, 17, 17, 17, 15, 11, 14, 20,
+ 21, 21, 19, 16, 12, 18, 23, 26, 23, 21, 18, 13, 20, 22, 22, 22, 19, 15, 11,
+ 19, 24, 23, 24, 19, 15, 11, 20, 26, 24, 24, 20, 15, 11, 20, 25, 26, 26, 21,
+ 17, 12, 19, 25, 29, 27, 23, 18, 13, 20, 26, 32, 32, 26, 19, 14, 23, 29, 33,
+ 35, 28, 22, 16, 27, 35, 33, 28, 21, 17, 13, 29, 34, 36, 29, 23, 18, 13, 28,
+ 35, 38, 30, 24, 19, 14, 29, 34, 38, 30, 25, 20, 15, 27, 34, 38, 34, 28, 22,
+ 17, 27, 34, 39, 38, 31, 24, 19, 30, 36, 40, 41, 34, 26, 21, 9, 13, 16, 19,
+ 18, 17, 14, 8, 11, 15, 18, 18, 16, 13, 6, 10, 14, 16, 16, 13, 10, 5, 8,
+ 11, 13, 14, 11, 8, 2, 5, 7, 10, 11, 9, 7, 2, 3, 5, 7, 10, 9, 7,
+ 7, 6, 6, 7, 9, 9, 7, 7, 10, 14, 16, 16, 14, 11, 5, 9, 12, 15, 16,
+ 13, 9, 4, 7, 11, 13, 13, 11, 7, 4, 5, 8, 10, 10, 9, 4, 2, 2, 4,
+ 7, 7, 7, 4, 4, 3, 5, 5, 5, 6, 4, 9, 7, 6, 5, 4, 5, 4, 5,
+ 7, 9, 10, 12, 10, 9, 4, 6, 8, 9, 10, 9, 8, 4, 5, 7, 7, 7, 7,
+ 5, 4, 3, 3, 3, 4, 4, 2, 1, 1, 0, 0, 0, 1, 2, 6, 5, 4, 3,
+ 2, 1, 2, 11, 9, 7, 5, 3, 2, 2, 4, 6, 7, 8, 9, 11, 10, 4, 6,
+ 7, 8, 8, 9, 8, 3, 6, 7, 6, 6, 7, 7, 6, 6, 6, 6, 6, 6, 5,
+ 5, 8, 7, 7, 8, 8, 7, 7, 13, 12, 11, 10, 9, 9, 11, 17, 15, 13, 11,
+ 10, 10, 11, 12, 12, 12, 13, 13, 10, 11, 13, 13, 14, 14, 13, 8, 11, 14, 15,
+ 14, 14, 12, 9, 11, 17, 15, 15, 15, 13, 9, 11, 18, 17, 17, 17, 15, 10, 15,
+ 21, 22, 21, 19, 16, 12, 19, 23, 25, 23, 21, 18, 13, 21, 22, 22, 21, 19, 15,
+ 11, 20, 24, 24, 23, 20, 15, 10, 21, 26, 26, 24, 20, 15, 11, 20, 26, 26, 25,
+ 21, 16, 12, 19, 25, 29, 27, 23, 18, 13, 21, 26, 33, 32, 25, 20, 15, 24, 30,
+ 33, 33, 28, 22, 16, 28, 35, 33, 27, 21, 17, 13, 29, 35, 36, 29, 22, 18, 13,
+ 29, 35, 39, 30, 23, 18, 14, 29, 34, 38, 32, 25, 19, 14, 28, 33, 39, 34, 27,
+ 22, 17, 28, 33, 39, 38, 30, 24, 18, 29, 34, 39, 40, 33, 26, 20, 9, 13, 16,
+ 20, 20, 18, 15, 8, 12, 15, 19, 20, 17, 13, 7, 10, 14, 17, 18, 14, 11, 5,
+ 8, 11, 14, 16, 13, 8, 3, 5, 7, 10, 13, 10, 8, 2, 3, 5, 8, 11, 10,
+ 8, 3, 4, 4, 7, 10, 11, 8, 7, 10, 14, 17, 16, 14, 11, 5, 9, 13, 17,
+ 16, 12, 9, 5, 8, 11, 14, 14, 11, 7, 4, 6, 8, 11, 10, 9, 4, 2, 2,
+ 5, 7, 7, 7, 4, 3, 2, 4, 5, 6, 6, 4, 6, 3, 3, 5, 4, 5, 4,
+ 5, 8, 10, 11, 12, 10, 10, 4, 6, 8, 9, 10, 9, 8, 4, 5, 7, 7, 7,
+ 7, 5, 4, 4, 3, 4, 4, 4, 2, 1, 1, 0, 0, 0, 1, 2, 5, 3, 3,
+ 2, 2, 1, 2, 7, 5, 4, 4, 3, 2, 2, 4, 6, 7, 8, 9, 11, 10, 4,
+ 6, 7, 8, 9, 9, 8, 3, 6, 7, 6, 7, 7, 6, 6, 6, 6, 6, 6, 6,
+ 5, 5, 8, 8, 8, 8, 7, 7, 5, 10, 10, 10, 10, 8, 8, 7, 13, 12, 11,
+ 11, 10, 10, 11, 12, 12, 13, 13, 12, 10, 12, 13, 13, 14, 14, 13, 8, 12, 15,
+ 15, 14, 14, 13, 9, 11, 17, 15, 15, 15, 13, 9, 12, 18, 17, 17, 16, 15, 10,
+ 14, 19, 21, 19, 19, 16, 12, 15, 20, 23, 21, 21, 18, 13, 22, 22, 22, 21, 19,
+ 15, 11, 21, 24, 23, 23, 19, 15, 11, 20, 26, 25, 24, 20, 15, 11, 21, 25, 26,
+ 26, 21, 16, 12, 20, 25, 30, 27, 23, 18, 13, 21, 26, 32, 31, 25, 19, 15, 21,
+ 26, 32, 31, 27, 21, 16, 29, 35, 32, 26, 22, 17, 13, 29, 34, 35, 29, 23, 18,
+ 14, 29, 35, 37, 30, 23, 18, 14, 29, 34, 38, 31, 25, 20, 15, 28, 32, 37, 33,
+ 27, 22, 16, 28, 34, 38, 36, 30, 24, 18, 29, 34, 39, 39, 32, 26, 20, 8, 12,
+ 16, 19, 20, 17, 14, 7, 11, 15, 19, 19, 16, 12, 6, 9, 13, 16, 17, 14, 10,
+ 4, 7, 10, 13, 15, 12, 8, 1, 4, 7, 9, 13, 11, 8, 2, 3, 5, 8, 12,
+ 11, 8, 4, 4, 4, 7, 10, 11, 8, 6, 10, 13, 16, 16, 14, 10, 5, 8, 12,
+ 15, 16, 12, 8, 3, 7, 11, 13, 13, 10, 6, 3, 5, 8, 10, 9, 8, 4, 1,
+ 2, 4, 7, 7, 7, 4, 3, 3, 4, 6, 5, 6, 4, 6, 4, 3, 5, 4, 4,
+ 4, 4, 7, 9, 10, 11, 9, 8, 3, 5, 8, 9, 9, 8, 6, 3, 4, 6, 6,
+ 6, 6, 4, 3, 2, 3, 3, 3, 3, 1, 2, 2, 1, 1, 1, 0, 1, 4, 3,
+ 3, 2, 2, 2, 1, 8, 6, 4, 3, 3, 2, 2, 3, 5, 6, 7, 8, 10, 9,
+ 2, 6, 6, 7, 7, 8, 7, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 5, 6, 8, 8, 8, 8, 8, 7, 5, 10, 10, 9, 9, 8, 8, 8, 14, 12,
+ 11, 10, 10, 10, 11, 12, 12, 12, 13, 13, 9, 12, 14, 13, 14, 14, 12, 7, 12,
+ 15, 15, 14, 14, 13, 8, 12, 17, 15, 15, 15, 13, 9, 13, 19, 17, 17, 16, 15,
+ 10, 14, 19, 20, 18, 18, 16, 11, 14, 20, 23, 21, 19, 18, 13, 21, 23, 22, 21,
+ 18, 14, 10, 22, 25, 23, 23, 19, 15, 11, 21, 26, 25, 23, 19, 15, 11, 21, 25,
+ 26, 24, 21, 16, 11, 20, 25, 28, 26, 22, 17, 13, 21, 26, 30, 28, 23, 19, 15,
+ 21, 27, 30, 30, 25, 20, 16, 28, 33, 30, 25, 21, 17, 13, 28, 33, 33, 26, 22,
+ 18, 13, 29, 33, 35, 27, 22, 18, 14, 29, 32, 35, 29, 23, 19, 15, 27, 32, 36,
+ 31, 25, 20, 16, 28, 32, 35, 34, 27, 23, 18, 27, 31, 36, 35, 29, 23, 19, 8,
+ 10, 13, 16, 15, 13, 10, 7, 10, 12, 15, 15, 12, 8, 6, 8, 11, 13, 14, 10,
+ 7, 5, 6, 9, 10, 13, 9, 6, 2, 5, 7, 9, 12, 9, 6, 3, 5, 6, 8,
+ 11, 10, 6, 4, 4, 6, 8, 10, 9, 6, 6, 9, 11, 13, 12, 10, 7, 5, 8,
+ 10, 13, 12, 8, 5, 4, 7, 9, 10, 9, 7, 4, 2, 5, 7, 8, 7, 6, 3,
+ 1, 3, 5, 7, 6, 5, 3, 3, 3, 5, 6, 5, 5, 3, 6, 4, 5, 6, 5,
+ 4, 3, 4, 7, 8, 8, 9, 7, 6, 2, 6, 7, 7, 7, 5, 4, 2, 5, 6,
+ 5, 4, 4, 2, 2, 3, 3, 3, 3, 2, 0, 3, 3, 2, 2, 2, 1, 0, 5,
+ 4, 3, 2, 2, 1, 0, 8, 5, 3, 3, 2, 2, 1, 2, 4, 4, 5, 7, 8,
+ 8, 1, 5, 4, 5, 6, 6, 6, 1, 6, 5, 5, 5, 5, 5, 5, 6, 5, 5,
+ 5, 5, 4, 5, 8, 7, 7, 7, 6, 6, 5, 10, 9, 8, 8, 8, 7, 8, 13,
+ 12, 10, 9, 9, 9, 11, 11, 11, 11, 12, 11, 8, 12, 12, 12, 13, 12, 11, 6,
+ 12, 14, 14, 13, 12, 11, 7, 12, 16, 14, 13, 13, 12, 8, 13, 18, 16, 16, 15,
+ 13, 9, 14, 19, 20, 17, 16, 14, 10, 15, 20, 23, 20, 18, 16, 12, 20, 21, 19,
+ 18, 15, 13, 9, 20, 22, 20, 18, 16, 13, 9, 20, 24, 22, 20, 16, 13, 10, 20,
+ 24, 23, 21, 17, 14, 11, 19, 23, 26, 24, 20, 16, 12, 20, 24, 30, 28, 23, 17,
+ 14, 21, 26, 29, 30, 25, 19, 14, 26, 30, 28, 22, 17, 15, 12, 26, 30, 29, 24,
+ 18, 15, 11, 26, 30, 32, 25, 19, 16, 12, 25, 29, 33, 26, 21, 17, 13, 25, 29,
+ 33, 30, 24, 20, 15, 26, 31, 35, 34, 27, 20, 16, 27, 32, 35, 35, 29, 23, 17,
+ 14, 17, 21, 24, 24, 22, 19, 13, 16, 20, 24, 25, 21, 18, 12, 15, 18, 21, 23,
+ 19, 15, 12, 13, 15, 17, 21, 16, 12, 9, 9, 11, 14, 14, 15, 12, 5, 5, 8,
+ 11, 13, 14, 12, 2, 3, 6, 9, 13, 15, 12, 13, 15, 19, 21, 22, 19, 17, 12,
+ 14, 17, 20, 21, 18, 14, 11, 13, 15, 18, 18, 16, 12, 11, 11, 13, 14, 15, 12,
+ 7, 8, 8, 9, 10, 9, 10, 7, 3, 3, 5, 7, 9, 9, 7, 3, 3, 3, 6,
+ 8, 8, 7, 11, 13, 14, 16, 18, 17, 15, 11, 12, 14, 15, 16, 15, 14, 10, 11,
+ 13, 13, 13, 13, 12, 10, 10, 10, 10, 11, 9, 5, 6, 6, 6, 6, 5, 5, 5,
+ 0, 2, 1, 2, 4, 4, 5, 4, 4, 2, 1, 3, 4, 5, 10, 11, 13, 14, 15,
+ 16, 15, 10, 11, 12, 14, 14, 15, 14, 9, 10, 12, 12, 12, 13, 12, 6, 9, 9,
+ 9, 10, 9, 7, 4, 5, 4, 5, 5, 4, 4, 0, 2, 2, 2, 3, 3, 5, 4,
+ 6, 3, 3, 3, 4, 5, 9, 10, 12, 13, 14, 15, 15, 8, 9, 11, 12, 13, 13,
+ 14, 8, 9, 10, 10, 11, 11, 12, 8, 8, 8, 8, 9, 8, 5, 5, 8, 7, 7,
+ 7, 6, 5, 4, 7, 7, 7, 7, 7, 5, 5, 8, 9, 8, 8, 8, 5, 11, 12,
+ 12, 13, 12, 14, 15, 11, 13, 13, 13, 12, 12, 13, 11, 14, 14, 13, 11, 10, 11,
+ 11, 13, 13, 13, 11, 9, 5, 9, 11, 13, 12, 10, 8, 5, 8, 11, 14, 13, 11,
+ 8, 6, 8, 12, 15, 15, 11, 9, 7, 15, 19, 18, 15, 13, 13, 14, 15, 19, 20,
+ 16, 13, 11, 12, 15, 18, 21, 16, 14, 11, 9, 15, 18, 20, 15, 13, 9, 6, 14,
+ 16, 19, 16, 11, 9, 7, 13, 15, 18, 17, 13, 10, 8, 12, 16, 19, 19, 14, 11,
+ 8, 12, 14, 15, 17, 17, 16, 14, 12, 13, 15, 16, 17, 14, 13, 11, 12, 14, 15,
+ 15, 12, 11, 10, 11, 11, 12, 12, 8, 4, 8, 8, 8, 8, 6, 5, 4, 4, 4,
+ 4, 3, 4, 5, 4, 2, 2, 2, 3, 4, 5, 4, 11, 13, 14, 15, 16, 15, 14,
+ 10, 12, 13, 15, 15, 14, 12, 10, 11, 13, 13, 13, 12, 10, 9, 10, 10, 10, 11,
+ 9, 4, 7, 7, 7, 7, 4, 4, 4, 2, 3, 3, 3, 3, 4, 4, 5, 4, 2,
+ 2, 3, 3, 4, 10, 11, 13, 14, 15, 15, 15, 9, 10, 12, 13, 14, 14, 13, 9,
+ 10, 12, 12, 12, 11, 10, 9, 9, 9, 9, 9, 8, 4, 5, 5, 5, 5, 3, 3,
+ 4, 2, 0, 1, 2, 2, 3, 4, 7, 6, 2, 0, 3, 3, 4, 10, 11, 12, 13,
+ 15, 16, 16, 9, 11, 12, 13, 14, 14, 14, 8, 11, 12, 12, 12, 12, 11, 5, 9,
+ 9, 9, 9, 8, 7, 3, 5, 5, 5, 5, 5, 5, 2, 7, 7, 6, 6, 6, 7,
+ 7, 13, 10, 8, 8, 8, 8, 10, 11, 12, 13, 14, 16, 16, 9, 10, 11, 13, 14,
+ 14, 14, 9, 11, 11, 11, 12, 12, 11, 9, 12, 11, 12, 12, 11, 7, 8, 13, 14,
+ 13, 14, 13, 9, 9, 15, 17, 17, 16, 15, 10, 12, 17, 19, 18, 18, 16, 11, 16,
+ 19, 18, 19, 16, 15, 16, 16, 21, 21, 21, 17, 13, 13, 16, 22, 23, 21, 17, 12,
+ 10, 16, 22, 23, 23, 19, 14, 10, 15, 21, 25, 23, 21, 17, 11, 16, 22, 27, 25,
+ 21, 18, 13, 18, 22, 26, 25, 23, 20, 15, 25, 30, 29, 25, 19, 15, 14, 25, 30,
+ 31, 26, 20, 15, 12, 25, 30, 33, 26, 21, 16, 11, 25, 30, 34, 28, 22, 18, 14,
+ 24, 29, 32, 27, 23, 20, 16, 24, 27, 30, 29, 24, 21, 17, 23, 27, 30, 30, 25,
+ 22, 19, 13, 16, 17, 18, 18, 17, 16, 12, 15, 17, 18, 18, 16, 14, 11, 14, 16,
+ 16, 15, 13, 11, 10, 12, 13, 13, 13, 10, 5, 7, 9, 10, 10, 8, 6, 5, 3,
+ 5, 7, 5, 6, 6, 5, 2, 3, 5, 5, 6, 6, 5, 11, 14, 16, 17, 17, 17,
+ 15, 10, 13, 15, 16, 17, 15, 13, 9, 12, 15, 15, 14, 13, 10, 9, 9, 12, 13,
+ 11, 9, 5, 7, 6, 8, 9, 6, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 3,
+ 3, 5, 4, 5, 5, 9, 12, 14, 15, 16, 15, 14, 9, 10, 12, 14, 15, 13, 12,
+ 9, 10, 11, 11, 11, 11, 9, 9, 8, 8, 8, 8, 7, 3, 5, 5, 5, 4, 3,
+ 3, 3, 1, 1, 0, 1, 2, 3, 3, 6, 5, 3, 1, 2, 3, 3, 9, 11, 11,
+ 13, 15, 15, 15, 9, 11, 12, 13, 14, 14, 13, 7, 11, 11, 11, 11, 11, 10, 5,
+ 9, 8, 8, 8, 7, 7, 3, 5, 5, 6, 6, 6, 6, 2, 8, 8, 7, 7, 8,
+ 8, 6, 12, 11, 9, 9, 9, 9, 10, 10, 11, 13, 14, 15, 15, 9, 11, 12, 13,
+ 13, 13, 13, 10, 12, 13, 13, 13, 11, 9, 10, 13, 13, 13, 13, 12, 8, 9, 15,
+ 14, 15, 15, 14, 9, 10, 16, 18, 17, 17, 15, 11, 13, 18, 21, 18, 18, 17, 13,
+ 18, 20, 20, 20, 17, 15, 15, 18, 22, 21, 21, 18, 13, 12, 18, 24, 24, 23, 18,
+ 14, 10, 18, 23, 25, 24, 20, 15, 11, 18, 23, 27, 26, 21, 17, 13, 17, 23, 29,
+ 28, 24, 18, 14, 19, 25, 29, 31, 26, 20, 16, 27, 33, 32, 25, 20, 16, 14, 26,
+ 32, 34, 27, 22, 17, 12, 27, 33, 36, 28, 22, 17, 12, 27, 32, 37, 29, 24, 19,
+ 14, 25, 31, 37, 32, 26, 20, 16, 25, 31, 36, 35, 28, 22, 17, 26, 32, 37, 37,
+ 30, 24, 19, 12, 16, 19, 22, 22, 20, 18, 12, 15, 18, 21, 21, 19, 15, 10, 13,
+ 17, 19, 19, 17, 13, 8, 11, 14, 16, 17, 13, 8, 6, 8, 10, 12, 12, 10, 8,
+ 2, 4, 7, 9, 11, 10, 8, 2, 3, 5, 8, 10, 10, 8, 10, 13, 17, 20, 20,
+ 17, 14, 9, 12, 16, 19, 19, 16, 12, 8, 11, 14, 17, 16, 14, 9, 8, 9, 11,
+ 13, 13, 11, 5, 5, 5, 8, 10, 8, 8, 5, 1, 2, 4, 6, 6, 7, 5, 4,
+ 3, 3, 5, 5, 6, 5, 8, 11, 13, 14, 16, 13, 13, 8, 9, 12, 13, 14, 12,
+ 11, 7, 8, 10, 10, 10, 10, 7, 8, 7, 7, 7, 7, 6, 2, 4, 3, 3, 3,
+ 2, 2, 2, 2, 2, 1, 0, 1, 2, 2, 4, 3, 2, 2, 1, 2, 2, 8, 9,
+ 10, 11, 13, 14, 14, 8, 9, 10, 11, 12, 12, 11, 5, 9, 10, 9, 9, 9, 8,
+ 5, 8, 7, 6, 7, 6, 6, 3, 6, 6, 6, 6, 6, 6, 2, 8, 8, 7, 7,
+ 7, 7, 4, 11, 10, 9, 9, 9, 9, 9, 11, 11, 12, 13, 13, 13, 9, 11, 12,
+ 12, 13, 12, 11, 10, 13, 14, 13, 13, 11, 8, 10, 15, 13, 14, 14, 12, 8, 10,
+ 16, 15, 15, 15, 13, 9, 11, 16, 18, 17, 16, 14, 11, 12, 17, 20, 19, 18, 17,
+ 12, 19, 21, 20, 20, 18, 14, 13, 18, 23, 22, 22, 18, 14, 10, 19, 24, 24, 23,
+ 19, 14, 10, 19, 24, 25, 23, 20, 15, 10, 18, 24, 28, 26, 21, 17, 12, 18, 24,
+ 30, 29, 23, 18, 13, 18, 24, 30, 30, 25, 19, 15, 27, 34, 32, 26, 20, 16, 12,
+ 27, 34, 34, 28, 22, 17, 12, 27, 34, 37, 28, 22, 17, 13, 28, 33, 37, 30, 23,
+ 18, 14, 26, 32, 36, 33, 25, 19, 15, 26, 31, 36, 34, 28, 21, 17, 26, 31, 36,
+ 37, 29, 24, 18, 11, 15, 18, 21, 22, 20, 16, 10, 14, 17, 21, 22, 18, 14, 8,
+ 12, 16, 19, 20, 16, 12, 7, 10, 13, 16, 18, 14, 9, 4, 7, 9, 12, 14, 12,
+ 9, 0, 3, 6, 9, 13, 12, 9, 3, 3, 5, 8, 11, 12, 9, 8, 12, 16, 19,
+ 18, 16, 12, 7, 11, 14, 18, 18, 14, 10, 6, 10, 13, 16, 15, 13, 9, 6, 7,
+ 10, 12, 12, 10, 5, 3, 4, 6, 9, 9, 8, 5, 2, 2, 4, 6, 7, 7, 5,
+ 5, 4, 3, 6, 5, 6, 5, 6, 10, 11, 13, 14, 12, 11, 5, 8, 10, 11, 12,
+ 11, 8, 5, 7, 9, 9, 9, 9, 6, 6, 5, 5, 5, 6, 5, 3, 1, 1, 2,
+ 2, 2, 2, 2, 4, 3, 2, 1, 0, 1, 2, 7, 4, 3, 2, 1, 1, 2, 6,
+ 7, 8, 9, 10, 12, 11, 5, 7, 8, 9, 9, 9, 9, 4, 7, 7, 7, 7, 7,
+ 6, 5, 6, 6, 5, 5, 5, 5, 3, 7, 6, 6, 6, 6, 6, 4, 9, 8, 8,
+ 8, 7, 7, 7, 12, 10, 9, 9, 9, 9, 10, 11, 11, 11, 12, 12, 11, 11, 12,
+ 12, 13, 13, 11, 9, 11, 14, 14, 13, 13, 11, 8, 11, 15, 14, 13, 14, 12, 8,
+ 11, 17, 16, 15, 15, 13, 9, 12, 17, 18, 17, 16, 15, 10, 13, 17, 20, 19, 18,
+ 16, 12, 20, 21, 20, 21, 18, 14, 11, 20, 23, 23, 23, 19, 14, 9, 19, 25, 24,
+ 23, 19, 14, 10, 20, 24, 26, 24, 20, 15, 11, 18, 24, 27, 26, 21, 16, 12, 19,
+ 23, 30, 29, 23, 18, 14, 19, 24, 29, 31, 25, 20, 15, 28, 34, 32, 26, 21, 16,
+ 12, 27, 34, 34, 27, 21, 17, 12, 27, 33, 36, 29, 23, 18, 13, 28, 32, 37, 29,
+ 24, 19, 13, 26, 32, 36, 33, 25, 20, 15, 26, 32, 37, 33, 28, 22, 17, 27, 32,
+ 35, 36, 30, 23, 18, 10, 14, 18, 21, 21, 18, 15, 9, 12, 16, 20, 21, 17, 13,
+ 7, 11, 15, 18, 19, 16, 11, 6, 9, 12, 14, 16, 14, 9, 3, 6, 8, 11, 14,
+ 12, 9, 2, 4, 6, 9, 13, 12, 9, 4, 4, 5, 8, 11, 12, 9, 7, 11, 14,
+ 18, 18, 15, 11, 6, 10, 13, 17, 17, 13, 9, 5, 8, 12, 14, 14, 12, 8, 4,
+ 6, 9, 11, 11, 10, 5, 1, 3, 5, 8, 8, 8, 5, 2, 3, 4, 7, 7, 7,
+ 5, 6, 4, 4, 6, 5, 6, 5, 5, 8, 10, 11, 13, 11, 9, 3, 7, 9, 10,
+ 11, 9, 7, 3, 6, 7, 8, 7, 7, 4, 3, 3, 4, 4, 4, 4, 1, 2, 1,
+ 1, 1, 1, 2, 1, 4, 3, 2, 2, 1, 0, 1, 8, 6, 3, 3, 2, 1, 1,
+ 3, 5, 6, 7, 8, 10, 9, 3, 5, 6, 7, 8, 8, 7, 2, 5, 6, 6, 5,
+ 6, 5, 5, 6, 6, 5, 5, 5, 4, 4, 8, 7, 6, 7, 6, 6, 4, 10, 8,
+ 8, 8, 8, 7, 8, 12, 11, 10, 9, 9, 9, 11, 11, 11, 11, 12, 12, 9, 12,
+ 13, 12, 13, 13, 12, 7, 12, 14, 14, 13, 12, 11, 8, 11, 16, 14, 14, 14, 12,
+ 8, 12, 18, 17, 15, 16, 13, 9, 13, 18, 19, 18, 16, 15, 11, 14, 18, 21, 19,
+ 19, 16, 12, 20, 21, 20, 20, 17, 14, 10, 20, 23, 22, 22, 18, 14, 9, 20, 25,
+ 24, 23, 19, 14, 10, 20, 25, 24, 23, 20, 15, 10, 19, 25, 26, 25, 21, 16, 12,
+ 20, 25, 29, 28, 22, 18, 14, 20, 25, 29, 30, 24, 19, 15, 28, 33, 29, 24, 20,
+ 16, 12, 28, 32, 31, 26, 21, 16, 12, 28, 32, 34, 27, 23, 17, 13, 27, 31, 35,
+ 28, 23, 18, 14, 26, 31, 34, 31, 24, 20, 15, 26, 31, 35, 33, 26, 21, 17, 27,
+ 30, 35, 35, 29, 22, 18, 8, 11, 12, 16, 15, 13, 10, 7, 10, 12, 15, 15, 12,
+ 8, 6, 8, 11, 13, 14, 10, 7, 5, 6, 9, 10, 12, 9, 6, 2, 5, 7, 9,
+ 12, 9, 6, 3, 5, 6, 8, 11, 9, 6, 4, 5, 6, 8, 10, 9, 6, 6, 9,
+ 11, 13, 12, 10, 7, 5, 8, 10, 12, 12, 8, 5, 4, 7, 9, 10, 9, 7, 4,
+ 2, 5, 7, 8, 7, 6, 3, 1, 3, 5, 7, 6, 5, 3, 3, 3, 5, 6, 5,
+ 5, 3, 6, 4, 5, 6, 5, 4, 3, 4, 7, 8, 8, 9, 7, 6, 2, 6, 7,
+ 7, 7, 5, 4, 2, 5, 6, 5, 4, 4, 2, 2, 3, 3, 3, 3, 2, 0, 3,
+ 2, 2, 2, 2, 1, 0, 5, 4, 3, 2, 2, 1, 0, 8, 6, 3, 2, 2, 2,
+ 1, 2, 4, 4, 5, 6, 7, 8, 1, 4, 4, 5, 6, 5, 6, 1, 6, 5, 5,
+ 5, 5, 5, 5, 6, 5, 5, 5, 5, 4, 5, 8, 7, 7, 7, 7, 6, 5, 10,
+ 9, 8, 8, 8, 7, 8, 13, 12, 10, 9, 9, 9, 11, 11, 11, 11, 12, 11, 8,
+ 12, 12, 13, 12, 12, 11, 6, 12, 14, 14, 13, 12, 11, 7, 12, 16, 14, 13, 13,
+ 12, 8, 12, 18, 17, 15, 15, 13, 9, 13, 19, 20, 18, 16, 14, 10, 14, 20, 23,
+ 20, 18, 16, 11, 20, 20, 19, 17, 15, 13, 9, 20, 22, 20, 19, 16, 13, 9, 20,
+ 23, 22, 20, 17, 13, 10, 19, 24, 24, 21, 17, 14, 11, 19, 24, 26, 24, 20, 16,
+ 12, 20, 24, 30, 27, 22, 17, 13, 21, 25, 30, 29, 24, 19, 14, 26, 30, 27, 21,
+ 17, 14, 12, 26, 30, 29, 23, 18, 15, 12, 26, 30, 32, 25, 19, 16, 12, 26, 29,
+ 33, 26, 21, 17, 13, 25, 29, 33, 30, 24, 19, 15, 25, 31, 34, 34, 27, 21, 16,
+ 27, 32, 35, 35, 29, 23, 17, 20, 21, 23, 25, 26, 24, 23, 20, 21, 23, 24, 25,
+ 23, 20, 19, 21, 21, 23, 24, 20, 16, 18, 19, 19, 19, 20, 15, 11, 15, 15, 15,
+ 14, 14, 15, 10, 11, 11, 8, 10, 12, 14, 10, 5, 3, 5, 8, 11, 14, 10, 19,
+ 20, 22, 24, 24, 23, 22, 18, 20, 21, 23, 24, 22, 20, 17, 19, 20, 20, 21, 18,
+ 16, 17, 17, 18, 18, 16, 13, 8, 14, 14, 14, 12, 9, 9, 8, 9, 9, 7, 6,
+ 8, 8, 8, 2, 2, 4, 5, 7, 8, 8, 17, 19, 20, 21, 23, 24, 22, 17, 18,
+ 20, 21, 22, 21, 19, 17, 18, 19, 19, 19, 18, 15, 16, 16, 16, 16, 15, 12, 9,
+ 11, 12, 12, 11, 7, 8, 8, 4, 7, 6, 4, 6, 8, 8, 0, 2, 2, 4, 6,
+ 6, 8, 17, 18, 20, 21, 22, 23, 23, 16, 18, 20, 20, 21, 21, 21, 14, 19, 19,
+ 19, 18, 18, 17, 10, 17, 16, 16, 15, 11, 11, 8, 12, 12, 10, 6, 7, 8, 5,
+ 5, 5, 3, 4, 6, 7, 0, 3, 3, 3, 4, 5, 6, 15, 17, 19, 20, 21, 23,
+ 23, 12, 17, 18, 20, 20, 20, 21, 12, 16, 16, 17, 17, 16, 15, 12, 13, 13, 13,
+ 12, 10, 7, 9, 9, 8, 8, 7, 6, 6, 6, 7, 7, 6, 7, 7, 6, 4, 6,
+ 8, 7, 7, 8, 6, 13, 14, 15, 16, 18, 21, 23, 13, 14, 15, 16, 17, 19, 19,
+ 13, 14, 15, 15, 14, 15, 14, 13, 13, 13, 13, 12, 9, 6, 10, 12, 13, 12, 9,
+ 8, 6, 9, 10, 13, 13, 10, 8, 6, 7, 10, 14, 14, 11, 8, 7, 16, 19, 18,
+ 16, 17, 20, 21, 16, 19, 20, 17, 15, 17, 18, 16, 18, 21, 16, 14, 13, 13, 16,
+ 17, 20, 15, 12, 10, 7, 14, 16, 18, 15, 11, 9, 7, 12, 15, 17, 16, 12, 10,
+ 8, 11, 15, 18, 17, 13, 10, 8, 18, 19, 21, 22, 22, 21, 21, 18, 19, 20, 22,
+ 22, 19, 18, 17, 18, 19, 20, 19, 17, 14, 17, 17, 17, 17, 15, 12, 8, 14, 13,
+ 13, 11, 9, 8, 8, 10, 10, 7, 7, 8, 9, 8, 4, 2, 4, 6, 8, 8, 8,
+ 17, 19, 20, 21, 22, 21, 20, 16, 18, 19, 20, 21, 19, 17, 16, 17, 19, 19, 18,
+ 16, 13, 16, 16, 16, 16, 14, 11, 7, 14, 12, 12, 11, 7, 7, 7, 9, 8, 6,
+ 5, 6, 7, 7, 2, 2, 3, 4, 6, 6, 7, 16, 17, 18, 20, 21, 21, 21, 16,
+ 17, 18, 19, 20, 19, 18, 15, 17, 17, 17, 16, 16, 13, 15, 15, 14, 14, 13, 10,
+ 6, 10, 11, 11, 9, 5, 6, 6, 4, 6, 5, 3, 4, 5, 6, 2, 0, 2, 3,
+ 4, 5, 6, 16, 17, 18, 19, 21, 22, 22, 15, 17, 19, 19, 20, 19, 19, 13, 17,
+ 18, 18, 17, 16, 15, 9, 16, 15, 15, 14, 10, 11, 7, 12, 11, 10, 5, 5, 7,
+ 4, 6, 5, 5, 5, 5, 6, 2, 7, 6, 6, 6, 6, 7, 15, 17, 18, 20, 20,
+ 22, 22, 12, 16, 18, 18, 19, 19, 19, 12, 15, 16, 16, 16, 15, 13, 12, 13, 13,
+ 13, 12, 10, 6, 10, 10, 11, 11, 13, 11, 7, 7, 11, 14, 14, 15, 13, 8, 8,
+ 13, 16, 16, 16, 15, 10, 14, 16, 17, 17, 19, 21, 21, 14, 18, 18, 19, 17, 18,
+ 18, 14, 19, 20, 19, 16, 14, 13, 14, 19, 21, 21, 17, 12, 8, 12, 18, 22, 21,
+ 18, 16, 10, 12, 19, 22, 20, 19, 17, 12, 14, 19, 22, 21, 20, 17, 13, 22, 27,
+ 27, 22, 18, 20, 20, 22, 28, 29, 24, 18, 17, 17, 22, 27, 30, 24, 19, 14, 12,
+ 22, 26, 30, 24, 20, 16, 12, 20, 25, 29, 24, 19, 17, 14, 20, 25, 26, 25, 20,
+ 18, 15, 21, 23, 25, 25, 22, 19, 17, 16, 19, 19, 21, 20, 20, 19, 15, 18, 19,
+ 20, 20, 18, 16, 14, 16, 18, 19, 18, 15, 13, 13, 15, 16, 15, 15, 11, 7, 10,
+ 11, 12, 11, 9, 7, 6, 6, 8, 8, 6, 7, 7, 6, 1, 3, 5, 6, 7, 7,
+ 6, 14, 17, 19, 19, 20, 19, 18, 13, 16, 18, 19, 19, 17, 15, 12, 14, 18, 17,
+ 16, 15, 12, 12, 12, 14, 15, 13, 10, 6, 9, 9, 11, 10, 7, 6, 6, 5, 5,
+ 7, 6, 6, 6, 6, 2, 1, 4, 5, 5, 6, 6, 12, 15, 16, 18, 19, 18, 17,
+ 12, 13, 16, 17, 17, 16, 14, 12, 12, 14, 14, 14, 13, 10, 11, 11, 10, 11, 10,
+ 8, 4, 7, 7, 7, 7, 4, 4, 3, 2, 2, 3, 2, 3, 3, 3, 2, 2, 0,
+ 2, 3, 4, 4, 12, 13, 14, 16, 17, 18, 17, 11, 13, 14, 15, 16, 16, 14, 9,
+ 13, 13, 14, 13, 13, 11, 6, 12, 11, 11, 10, 8, 8, 4, 8, 7, 7, 5, 5,
+ 6, 2, 6, 6, 6, 6, 6, 6, 2, 8, 8, 7, 7, 7, 8, 12, 12, 14, 15,
+ 16, 17, 17, 10, 12, 13, 14, 15, 15, 15, 10, 12, 12, 12, 12, 12, 10, 10, 12,
+ 11, 12, 12, 10, 7, 8, 13, 13, 13, 14, 12, 8, 8, 14, 16, 16, 16, 14, 9,
+ 10, 15, 18, 17, 18, 16, 11, 16, 19, 18, 19, 16, 17, 17, 15, 20, 20, 20, 16,
+ 14, 14, 15, 22, 22, 20, 17, 12, 10, 16, 21, 22, 23, 18, 13, 10, 15, 21, 25,
+ 24, 20, 16, 11, 15, 21, 27, 27, 22, 17, 12, 17, 22, 29, 30, 24, 19, 14, 24,
+ 31, 30, 24, 19, 16, 16, 25, 31, 33, 26, 19, 15, 13, 24, 30, 35, 27, 21, 16,
+ 11, 24, 30, 35, 27, 22, 17, 13, 24, 29, 35, 31, 24, 19, 14, 24, 29, 34, 34,
+ 27, 21, 16, 24, 30, 34, 35, 29, 23, 18, 14, 18, 21, 23, 23, 22, 19, 13, 17,
+ 20, 22, 23, 20, 17, 11, 15, 19, 20, 20, 18, 15, 10, 13, 16, 18, 18, 14, 9,
+ 7, 10, 12, 14, 14, 11, 9, 4, 6, 8, 10, 12, 11, 9, 2, 3, 6, 9, 11,
+ 11, 9, 12, 15, 19, 21, 21, 19, 16, 11, 14, 18, 21, 20, 17, 13, 9, 12, 16,
+ 18, 18, 15, 11, 9, 10, 13, 16, 14, 12, 7, 6, 7, 9, 11, 10, 9, 6, 2,
+ 3, 6, 8, 8, 8, 6, 4, 2, 4, 7, 7, 7, 6, 9, 13, 14, 16, 17, 16,
+ 14, 9, 11, 13, 15, 15, 13, 11, 9, 10, 12, 12, 12, 11, 8, 9, 8, 8, 9,
+ 9, 7, 3, 5, 4, 5, 5, 4, 3, 2, 1, 0, 1, 1, 2, 3, 2, 4, 3,
+ 2, 0, 2, 2, 3, 9, 10, 11, 12, 13, 14, 14, 9, 10, 11, 12, 13, 12, 12,
+ 7, 10, 10, 10, 10, 10, 9, 4, 9, 7, 7, 7, 6, 6, 2, 5, 4, 5, 5,
+ 5, 5, 1, 7, 6, 6, 6, 6, 6, 4, 9, 9, 8, 7, 7, 8, 9, 10, 10,
+ 12, 13, 14, 14, 9, 10, 11, 12, 12, 12, 12, 9, 12, 12, 12, 12, 10, 8, 9,
+ 13, 12, 12, 13, 11, 7, 8, 14, 13, 13, 13, 12, 8, 10, 15, 17, 16, 15, 13,
+ 9, 11, 16, 19, 17, 17, 15, 10, 17, 19, 18, 18, 16, 14, 14, 17, 21, 21, 20,
+ 17, 12, 11, 17, 23, 22, 21, 18, 13, 9, 17, 22, 24, 22, 19, 14, 9, 17, 21,
+ 26, 24, 20, 16, 10, 16, 22, 28, 27, 22, 16, 12, 16, 23, 28, 29, 24, 18, 13,
+ 26, 32, 31, 24, 19, 15, 13, 26, 31, 33, 26, 20, 15, 11, 25, 31, 34, 27, 21,
+ 16, 11, 26, 31, 36, 29, 22, 17, 12, 24, 30, 35, 32, 23, 18, 14, 25, 30, 35,
+ 33, 26, 21, 15, 25, 30, 35, 35, 29, 23, 17, 12, 16, 20, 23, 23, 20, 18, 12,
+ 15, 19, 22, 23, 19, 16, 10, 14, 17, 20, 21, 17, 14, 9, 11, 14, 17, 19, 15,
+ 10, 5, 8, 10, 13, 15, 13, 10, 2, 5, 7, 10, 13, 13, 10, 3, 3, 6, 9,
+ 12, 12, 10, 10, 14, 17, 21, 20, 17, 14, 9, 12, 16, 19, 20, 16, 12, 7, 11,
+ 15, 17, 17, 14, 10, 6, 8, 11, 13, 13, 12, 6, 4, 5, 8, 10, 9, 9, 6,
+ 1, 3, 5, 7, 8, 8, 6, 5, 3, 4, 6, 6, 7, 6, 7, 11, 13, 14, 15,
+ 13, 11, 6, 9, 12, 13, 13, 12, 9, 6, 8, 10, 10, 10, 10, 6, 6, 6, 7,
+ 7, 7, 7, 2, 2, 2, 3, 3, 3, 3, 2, 3, 3, 2, 1, 1, 2, 2, 5,
+ 4, 3, 2, 0, 1, 2, 6, 7, 8, 9, 11, 12, 11, 6, 7, 8, 9, 10, 10,
+ 9, 5, 7, 7, 7, 7, 7, 6, 3, 6, 5, 5, 5, 4, 5, 2, 6, 5, 5,
+ 5, 5, 5, 3, 8, 7, 7, 7, 6, 6, 5, 11, 9, 8, 8, 8, 8, 9, 10,
+ 10, 11, 11, 12, 11, 10, 11, 11, 12, 12, 11, 9, 10, 12, 13, 12, 12, 11, 7,
+ 10, 14, 13, 12, 13, 11, 7, 10, 16, 14, 14, 14, 12, 8, 11, 16, 17, 16, 15,
+ 13, 9, 12, 16, 20, 18, 17, 15, 11, 18, 20, 20, 19, 16, 13, 11, 18, 22, 21,
+ 21, 18, 13, 9, 19, 24, 24, 22, 18, 13, 9, 19, 23, 25, 23, 19, 14, 10, 17,
+ 23, 26, 25, 20, 15, 11, 18, 23, 28, 27, 22, 17, 13, 18, 24, 29, 29, 24, 19,
+ 14, 27, 32, 31, 25, 19, 15, 11, 27, 32, 33, 26, 21, 16, 11, 27, 33, 35, 27,
+ 22, 17, 12, 26, 32, 36, 29, 22, 18, 12, 26, 31, 36, 32, 24, 19, 14, 25, 30,
+ 36, 34, 26, 20, 16, 25, 30, 35, 36, 29, 23, 17, 11, 15, 19, 22, 22, 20, 17,
+ 10, 14, 17, 21, 22, 18, 15, 9, 12, 16, 19, 20, 17, 12, 7, 10, 13, 16, 18,
+ 15, 10, 4, 7, 9, 12, 15, 13, 10, 2, 5, 7, 10, 14, 13, 10, 4, 4, 6,
+ 9, 12, 13, 10, 9, 12, 16, 20, 19, 16, 12, 7, 11, 15, 18, 18, 15, 11, 6,
+ 9, 13, 16, 16, 13, 9, 5, 7, 10, 13, 12, 11, 6, 1, 4, 7, 9, 9, 9,
+ 6, 3, 3, 5, 8, 8, 8, 6, 5, 4, 5, 7, 7, 7, 6, 6, 10, 12, 12,
+ 14, 12, 9, 4, 8, 10, 12, 12, 11, 7, 4, 7, 9, 9, 9, 9, 5, 4, 5,
+ 6, 5, 6, 6, 2, 1, 1, 3, 2, 2, 2, 2, 4, 3, 3, 2, 1, 1, 2,
+ 7, 5, 4, 2, 1, 0, 1, 4, 5, 6, 7, 9, 10, 9, 4, 5, 6, 7, 8,
+ 8, 7, 4, 5, 6, 5, 5, 5, 5, 4, 5, 4, 4, 4, 4, 3, 2, 6, 5,
+ 5, 5, 5, 5, 4, 8, 7, 7, 7, 6, 6, 7, 12, 9, 9, 8, 8, 8, 10,
+ 10, 10, 11, 11, 11, 9, 11, 12, 11, 12, 12, 10, 7, 11, 13, 13, 12, 12, 10,
+ 7, 11, 15, 13, 13, 13, 11, 7, 11, 16, 15, 14, 14, 12, 8, 12, 17, 17, 16,
+ 16, 13, 9, 13, 17, 21, 18, 17, 16, 11, 20, 20, 19, 19, 16, 13, 9, 19, 22,
+ 21, 20, 18, 13, 9, 19, 24, 23, 22, 17, 13, 9, 19, 23, 25, 22, 18, 14, 9,
+ 18, 23, 26, 24, 20, 16, 11, 18, 23, 28, 27, 21, 17, 13, 19, 23, 29, 29, 24,
+ 19, 14, 27, 31, 29, 24, 20, 16, 11, 26, 30, 31, 25, 20, 16, 12, 27, 32, 33,
+ 27, 21, 16, 11, 27, 31, 34, 28, 22, 17, 12, 26, 29, 34, 30, 23, 19, 14, 25,
+ 30, 34, 32, 25, 21, 16, 25, 30, 35, 34, 28, 22, 17, 10, 14, 17, 19, 19, 17,
+ 14, 9, 13, 15, 18, 19, 16, 13, 8, 11, 14, 16, 18, 15, 11, 7, 9, 12, 15,
+ 17, 13, 10, 3, 6, 9, 12, 15, 13, 10, 3, 5, 8, 10, 14, 13, 10, 5, 4,
+ 7, 10, 12, 13, 10, 8, 11, 15, 17, 16, 14, 11, 7, 10, 14, 16, 15, 13, 10,
+ 5, 9, 12, 15, 13, 12, 8, 4, 6, 9, 12, 11, 10, 6, 1, 4, 7, 9, 9,
+ 9, 6, 3, 4, 5, 8, 8, 8, 6, 7, 5, 5, 7, 7, 7, 6, 5, 9, 11,
+ 11, 12, 11, 8, 3, 7, 10, 10, 11, 9, 5, 2, 6, 8, 8, 8, 8, 3, 2,
+ 4, 5, 5, 5, 5, 2, 2, 2, 3, 2, 2, 2, 1, 5, 4, 3, 2, 2, 1,
+ 1, 8, 6, 4, 3, 2, 1, 0, 2, 4, 4, 6, 7, 9, 8, 2, 3, 4, 6,
+ 6, 6, 6, 1, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 3, 3, 8,
+ 6, 5, 5, 5, 5, 5, 10, 8, 7, 7, 6, 6, 8, 13, 10, 9, 8, 8, 7,
+ 10, 10, 10, 10, 11, 10, 8, 11, 12, 11, 11, 11, 10, 6, 12, 13, 12, 12, 11,
+ 10, 6, 12, 15, 13, 12, 12, 11, 6, 12, 17, 15, 14, 13, 12, 8, 13, 17, 18,
+ 16, 15, 13, 9, 13, 18, 20, 17, 16, 14, 10, 19, 19, 18, 17, 15, 12, 8, 19,
+ 20, 19, 18, 16, 12, 8, 19, 23, 21, 19, 15, 12, 8, 19, 23, 22, 19, 16, 13,
+ 9, 18, 22, 24, 22, 18, 14, 10, 19, 23, 27, 25, 20, 15, 12, 18, 23, 28, 27,
+ 22, 17, 13, 25, 28, 25, 20, 17, 14, 11, 24, 28, 27, 22, 17, 14, 11, 24, 28,
+ 29, 22, 17, 14, 11, 25, 27, 30, 24, 19, 16, 12, 23, 27, 31, 28, 22, 17, 13,
+ 24, 28, 32, 31, 24, 19, 14, 25, 28, 32, 32, 26, 20, 15, 3, 7, 11, 15, 16,
+ 13, 11, 2, 5, 10, 14, 15, 13, 8, 2, 4, 8, 11, 13, 10, 5, 2, 2, 5,
+ 8, 10, 9, 5, 2, 2, 2, 5, 8, 10, 5, 5, 6, 6, 5, 8, 9, 5, 12,
+ 10, 8, 5, 6, 9, 5, 2, 4, 8, 11, 13, 10, 5, 2, 3, 7, 10, 13, 8,
+ 3, 2, 2, 5, 7, 9, 5, 2, 1, 2, 2, 4, 5, 3, 2, 2, 3, 3, 3,
+ 3, 3, 2, 7, 7, 8, 6, 3, 2, 2, 15, 13, 10, 7, 5, 3, 2, 2, 2,
+ 3, 5, 6, 4, 2, 1, 2, 2, 3, 4, 3, 1, 0, 2, 2, 2, 2, 2, 1,
+ 1, 2, 1, 1, 1, 1, 2, 4, 4, 4, 4, 4, 3, 2, 10, 10, 9, 8, 6,
+ 4, 2, 17, 16, 12, 9, 6, 4, 2, 0, 1, 1, 1, 2, 2, 5, 1, 2, 2,
+ 2, 2, 2, 5, 2, 2, 3, 3, 3, 3, 5, 4, 4, 5, 5, 5, 6, 7, 6,
+ 9, 9, 9, 9, 8, 7, 10, 15, 14, 13, 11, 10, 9, 17, 20, 17, 15, 13, 12,
+ 11, 8, 8, 8, 9, 9, 8, 5, 10, 10, 10, 10, 10, 9, 5, 10, 11, 11, 12,
+ 12, 10, 6, 10, 14, 14, 14, 14, 13, 9, 13, 18, 18, 18, 18, 17, 11, 15, 21,
+ 23, 21, 20, 18, 13, 20, 24, 25, 23, 21, 20, 15, 17, 18, 18, 18, 16, 11, 7,
+ 17, 20, 20, 19, 17, 12, 7, 17, 22, 21, 20, 19, 14, 9, 17, 23, 22, 22, 20,
+ 17, 14, 18, 24, 26, 25, 23, 20, 15, 21, 26, 29, 28, 24, 20, 17, 24, 28, 29,
+ 30, 25, 23, 18, 24, 29, 27, 22, 19, 15, 9, 24, 28, 29, 23, 20, 16, 11, 24,
+ 29, 30, 25, 21, 17, 13, 24, 29, 30, 26, 22, 20, 17, 25, 28, 32, 29, 25, 23,
+ 19, 26, 30, 33, 32, 26, 24, 20, 28, 32, 34, 33, 28, 25, 22, 6, 7, 8, 8,
+ 9, 7, 7, 5, 7, 8, 9, 9, 7, 6, 4, 6, 7, 8, 8, 7, 6, 3, 6,
+ 7, 7, 8, 7, 7, 3, 6, 7, 8, 9, 8, 7, 7, 9, 10, 9, 9, 8, 7,
+ 13, 13, 11, 10, 9, 8, 7, 4, 6, 7, 7, 7, 6, 5, 3, 6, 7, 7, 7,
+ 6, 5, 2, 5, 7, 7, 6, 6, 6, 1, 4, 7, 7, 6, 6, 7, 4, 5, 7,
+ 8, 7, 7, 6, 9, 9, 11, 10, 8, 7, 7, 16, 14, 12, 11, 8, 7, 7, 2,
+ 5, 6, 6, 6, 5, 2, 1, 4, 5, 5, 5, 5, 2, 1, 3, 4, 4, 5, 5,
+ 2, 1, 3, 4, 4, 5, 5, 4, 6, 6, 6, 6, 6, 5, 4, 11, 11, 11, 9,
+ 7, 5, 4, 18, 17, 13, 10, 7, 5, 4, 1, 0, 1, 1, 1, 2, 4, 1, 2,
+ 2, 2, 2, 2, 4, 2, 3, 2, 2, 3, 3, 4, 5, 5, 5, 6, 6, 6, 6,
+ 6, 10, 9, 9, 10, 9, 8, 12, 15, 14, 13, 11, 11, 9, 19, 21, 18, 15, 13,
+ 12, 11, 9, 8, 8, 8, 9, 7, 4, 10, 10, 9, 9, 10, 8, 4, 10, 11, 11,
+ 11, 11, 10, 6, 10, 15, 14, 14, 14, 13, 10, 13, 19, 18, 18, 19, 17, 11, 17,
+ 22, 24, 23, 21, 19, 13, 20, 26, 28, 25, 24, 21, 15, 17, 18, 17, 17, 14, 10,
+ 6, 17, 21, 19, 19, 16, 11, 7, 17, 21, 22, 21, 17, 13, 9, 17, 24, 25, 25,
+ 21, 17, 13, 20, 25, 31, 30, 25, 20, 15, 22, 28, 35, 33, 28, 22, 16, 25, 31,
+ 35, 36, 30, 24, 18, 25, 31, 29, 22, 17, 13, 9, 25, 30, 32, 25, 19, 15, 10,
+ 25, 31, 34, 27, 21, 17, 13, 24, 31, 37, 30, 26, 21, 17, 27, 33, 38, 36, 31,
+ 25, 18, 28, 36, 40, 40, 33, 27, 20, 32, 38, 41, 41, 35, 29, 23, 6, 9, 12,
+ 13, 13, 11, 9, 5, 8, 11, 13, 13, 11, 10, 4, 7, 10, 12, 13, 12, 10, 3,
+ 7, 10, 12, 14, 12, 11, 5, 7, 9, 12, 14, 13, 11, 8, 9, 11, 12, 14, 13,
+ 11, 14, 14, 12, 12, 13, 13, 11, 5, 7, 10, 12, 11, 9, 6, 4, 6, 9, 11,
+ 11, 9, 6, 2, 5, 8, 10, 10, 9, 7, 1, 5, 8, 11, 10, 10, 8, 4, 6,
+ 8, 10, 10, 11, 8, 10, 11, 11, 12, 10, 10, 8, 17, 16, 13, 12, 10, 9, 8,
+ 2, 5, 6, 7, 7, 5, 2, 1, 4, 5, 6, 6, 5, 2, 1, 3, 5, 5, 5,
+ 5, 3, 1, 3, 5, 5, 5, 6, 4, 6, 7, 7, 7, 7, 6, 4, 13, 12, 12,
+ 10, 8, 6, 4, 20, 18, 14, 11, 8, 6, 4, 1, 1, 0, 1, 1, 1, 3, 1,
+ 2, 1, 1, 1, 1, 3, 4, 3, 3, 3, 3, 3, 4, 7, 6, 6, 6, 6, 7,
+ 6, 8, 11, 10, 10, 10, 10, 8, 13, 16, 15, 14, 12, 11, 10, 19, 22, 19, 16,
+ 14, 13, 12, 9, 8, 8, 8, 8, 7, 3, 11, 10, 9, 9, 9, 8, 3, 10, 12,
+ 12, 12, 11, 10, 7, 10, 16, 15, 14, 15, 14, 10, 14, 21, 20, 20, 19, 17, 12,
+ 18, 24, 25, 23, 21, 18, 14, 22, 27, 28, 26, 24, 21, 15, 17, 19, 17, 17, 13,
+ 9, 5, 17, 21, 19, 18, 15, 11, 6, 17, 22, 22, 21, 17, 13, 10, 18, 24, 26,
+ 25, 21, 17, 13, 20, 27, 30, 30, 26, 20, 15, 23, 29, 36, 35, 29, 23, 17, 27,
+ 32, 37, 37, 31, 25, 19, 25, 30, 29, 22, 16, 12, 8, 25, 31, 31, 24, 18, 14,
+ 10, 25, 30, 35, 27, 22, 17, 13, 26, 31, 37, 31, 26, 21, 17, 27, 34, 39, 37,
+ 30, 25, 19, 30, 37, 43, 41, 33, 27, 20, 33, 38, 43, 43, 36, 29, 23, 7, 9,
+ 12, 15, 15, 13, 10, 6, 8, 11, 14, 15, 13, 10, 5, 7, 10, 13, 15, 14, 11,
+ 4, 7, 11, 14, 16, 14, 12, 5, 8, 11, 13, 17, 15, 12, 9, 11, 12, 13, 16,
+ 14, 12, 16, 15, 14, 13, 15, 15, 12, 5, 8, 10, 13, 12, 9, 6, 4, 7, 9,
+ 12, 12, 9, 6, 3, 5, 9, 11, 11, 10, 8, 1, 5, 9, 11, 11, 11, 9, 5,
+ 7, 9, 11, 11, 11, 9, 11, 12, 13, 13, 11, 10, 8, 19, 17, 14, 13, 10, 10,
+ 8, 3, 6, 6, 6, 7, 5, 2, 1, 4, 6, 6, 6, 5, 2, 1, 3, 5, 5,
+ 5, 6, 3, 2, 4, 5, 5, 6, 6, 5, 7, 8, 8, 8, 8, 7, 5, 14, 13,
+ 13, 11, 9, 7, 5, 21, 19, 15, 12, 9, 7, 6, 1, 1, 1, 0, 1, 1, 3,
+ 2, 2, 2, 1, 2, 1, 3, 5, 3, 3, 4, 4, 4, 5, 8, 6, 7, 7, 7,
+ 7, 7, 9, 12, 11, 11, 12, 10, 9, 14, 18, 16, 15, 14, 12, 11, 21, 24, 20,
+ 17, 15, 13, 12, 10, 9, 8, 8, 8, 7, 3, 11, 10, 9, 9, 9, 8, 3, 11,
+ 12, 12, 13, 12, 11, 7, 11, 16, 16, 16, 16, 15, 11, 15, 22, 21, 20, 21, 18,
+ 13, 19, 25, 26, 25, 22, 20, 14, 23, 28, 30, 27, 25, 21, 16, 18, 19, 17, 17,
+ 14, 9, 5, 18, 21, 19, 19, 15, 11, 7, 18, 23, 22, 22, 18, 14, 11, 18, 25,
+ 27, 25, 23, 19, 15, 21, 28, 32, 32, 26, 21, 16, 24, 32, 37, 36, 29, 23, 18,
+ 28, 34, 38, 38, 32, 26, 20, 26, 31, 29, 22, 17, 12, 8, 25, 31, 31, 24, 19,
+ 15, 11, 25, 30, 33, 28, 24, 18, 14, 26, 31, 37, 32, 27, 22, 18, 29, 35, 40,
+ 38, 31, 26, 20, 31, 38, 42, 42, 34, 28, 22, 34, 39, 44, 44, 37, 30, 24, 7,
+ 10, 12, 15, 15, 12, 10, 6, 9, 11, 14, 15, 13, 11, 5, 7, 11, 15, 16, 15,
+ 12, 4, 8, 11, 14, 17, 16, 13, 6, 9, 11, 14, 18, 16, 13, 11, 12, 14, 14,
+ 17, 16, 13, 17, 17, 15, 14, 16, 15, 13, 5, 8, 10, 13, 11, 9, 6, 4, 7,
+ 10, 12, 12, 9, 7, 3, 6, 9, 12, 12, 10, 8, 2, 6, 10, 12, 12, 11, 9,
+ 5, 8, 10, 12, 12, 12, 9, 12, 13, 14, 15, 12, 11, 9, 20, 19, 16, 14, 12,
+ 10, 9, 3, 6, 7, 6, 7, 5, 1, 1, 5, 6, 6, 6, 5, 2, 2, 4, 5,
+ 6, 6, 6, 4, 2, 4, 6, 6, 6, 6, 6, 7, 9, 9, 9, 10, 8, 6, 15,
+ 15, 14, 12, 10, 9, 6, 22, 21, 17, 14, 11, 9, 7, 2, 1, 1, 1, 0, 1,
+ 3, 3, 2, 2, 2, 2, 2, 3, 7, 3, 3, 4, 5, 5, 5, 9, 6, 8, 9,
+ 9, 8, 7, 10, 12, 12, 12, 13, 12, 10, 16, 19, 17, 16, 15, 14, 12, 22, 25,
+ 21, 18, 16, 15, 14, 10, 9, 8, 8, 8, 7, 2, 13, 11, 10, 9, 10, 9, 3,
+ 13, 12, 12, 13, 13, 12, 9, 13, 17, 17, 18, 17, 16, 12, 16, 23, 22, 22, 22,
+ 19, 14, 21, 27, 28, 26, 24, 21, 16, 24, 29, 31, 28, 27, 23, 17, 18, 19, 18,
+ 17, 13, 9, 5, 19, 21, 19, 18, 15, 12, 8, 18, 23, 22, 23, 19, 16, 12, 19,
+ 24, 28, 27, 23, 19, 16, 22, 29, 32, 31, 28, 23, 17, 27, 32, 39, 36, 29, 24,
+ 19, 30, 36, 38, 39, 33, 27, 21, 25, 29, 26, 21, 16, 12, 8, 25, 30, 29, 23,
+ 19, 16, 12, 26, 30, 31, 26, 23, 19, 15, 25, 32, 35, 31, 27, 24, 20, 29, 35,
+ 39, 38, 32, 26, 21, 32, 38, 43, 42, 35, 29, 23, 35, 40, 43, 45, 37, 31, 25,
+ 6, 8, 10, 12, 12, 9, 8, 6, 8, 9, 11, 12, 11, 9, 5, 7, 9, 12, 14,
+ 12, 11, 4, 7, 10, 13, 16, 14, 13, 6, 9, 12, 14, 18, 15, 13, 11, 13, 15,
+ 16, 17, 15, 13, 18, 18, 16, 15, 16, 15, 13, 5, 7, 8, 10, 8, 6, 5, 4,
+ 6, 8, 9, 9, 7, 6, 3, 6, 8, 10, 10, 9, 8, 2, 6, 9, 11, 11, 11,
+ 10, 6, 9, 11, 13, 13, 12, 9, 13, 14, 16, 15, 13, 12, 10, 22, 19, 17, 16,
+ 13, 11, 10, 3, 6, 5, 5, 5, 3, 2, 2, 5, 5, 4, 4, 4, 3, 2, 4,
+ 5, 5, 6, 6, 5, 3, 4, 6, 7, 7, 7, 8, 8, 9, 10, 11, 11, 10, 8,
+ 16, 17, 15, 14, 12, 10, 8, 23, 22, 18, 14, 12, 10, 8, 2, 2, 1, 1, 1,
+ 0, 2, 4, 2, 2, 2, 2, 3, 4, 8, 4, 3, 5, 6, 6, 6, 11, 6, 8,
+ 9, 9, 10, 8, 11, 13, 13, 14, 14, 12, 11, 16, 20, 18, 17, 16, 14, 13, 23,
+ 26, 22, 19, 17, 16, 15, 11, 9, 8, 8, 8, 7, 2, 14, 11, 10, 9, 10, 9,
+ 4, 14, 13, 12, 13, 14, 12, 9, 14, 17, 17, 17, 17, 17, 13, 16, 23, 23, 23,
+ 22, 19, 15, 21, 27, 28, 26, 23, 21, 17, 26, 31, 31, 28, 26, 22, 18, 18, 17,
+ 15, 13, 12, 9, 6, 18, 19, 16, 14, 12, 12, 9, 18, 21, 18, 19, 17, 15, 13,
+ 18, 23, 25, 24, 22, 19, 17, 22, 29, 31, 29, 26, 23, 18, 26, 33, 37, 34, 28,
+ 23, 20, 30, 36, 38, 37, 30, 25, 21, 22, 25, 22, 17, 13, 10, 9, 22, 25, 24,
+ 18, 15, 14, 12, 22, 25, 27, 22, 20, 18, 15, 22, 27, 33, 29, 25, 22, 20, 27,
+ 31, 37, 34, 30, 25, 21, 31, 36, 40, 40, 32, 27, 22, 35, 39, 42, 41, 35, 29,
+ 24, 9, 11, 13, 16, 16, 13, 10, 9, 11, 12, 15, 16, 14, 12, 8, 9, 12, 16,
+ 17, 15, 13, 8, 10, 13, 16, 19, 16, 14, 8, 11, 13, 17, 20, 18, 14, 11, 13,
+ 15, 16, 19, 18, 15, 18, 17, 16, 16, 18, 17, 14, 8, 10, 12, 13, 11, 8, 7,
+ 8, 9, 11, 13, 12, 10, 8, 7, 8, 11, 14, 13, 11, 9, 6, 8, 11, 14, 13,
+ 12, 11, 6, 10, 12, 14, 14, 13, 11, 13, 14, 16, 15, 14, 13, 11, 22, 19, 17,
+ 15, 13, 12, 11, 7, 8, 8, 7, 7, 5, 3, 6, 8, 8, 7, 7, 6, 4, 5,
+ 7, 7, 8, 8, 8, 6, 3, 7, 8, 8, 9, 9, 8, 7, 9, 10, 10, 10, 9,
+ 8, 16, 16, 15, 13, 11, 9, 8, 23, 22, 18, 14, 11, 9, 8, 5, 4, 3, 3,
+ 3, 3, 0, 2, 2, 3, 2, 2, 2, 2, 6, 2, 2, 3, 3, 3, 4, 10, 3,
+ 5, 6, 6, 6, 6, 9, 10, 10, 10, 10, 9, 7, 15, 18, 16, 14, 12, 10, 8,
+ 24, 25, 19, 15, 13, 11, 9, 4, 3, 2, 2, 2, 2, 0, 6, 3, 3, 2, 3,
+ 3, 2, 6, 4, 4, 5, 6, 6, 5, 6, 8, 9, 10, 10, 9, 8, 9, 14, 15,
+ 15, 15, 13, 9, 16, 20, 22, 20, 17, 14, 10, 22, 25, 25, 21, 19, 15, 11, 8,
+ 7, 6, 5, 3, 2, 1, 7, 10, 7, 6, 4, 4, 3, 8, 11, 10, 11, 9, 8,
+ 7, 8, 14, 17, 17, 14, 12, 10, 12, 19, 23, 23, 20, 16, 11, 18, 24, 30, 28,
+ 22, 17, 13, 24, 28, 31, 30, 24, 19, 14, 13, 16, 14, 8, 5, 3, 2, 13, 16,
+ 16, 10, 7, 6, 5, 13, 16, 19, 15, 12, 10, 8, 13, 18, 25, 21, 18, 15, 13,
+ 17, 24, 30, 28, 23, 19, 14, 22, 28, 34, 34, 27, 21, 15, 27, 32, 35, 35, 29,
+ 23, 17, 6, 10, 15, 18, 18, 16, 15, 5, 9, 14, 17, 18, 15, 13, 3, 8, 11,
+ 15, 16, 14, 11, 2, 5, 9, 11, 13, 13, 10, 1, 2, 6, 10, 12, 14, 10, 5,
+ 5, 5, 8, 11, 14, 10, 11, 9, 5, 7, 10, 14, 10, 3, 7, 12, 15, 16, 14,
+ 10, 2, 6, 11, 14, 15, 13, 8, 2, 5, 9, 11, 12, 11, 5, 2, 2, 6, 8,
+ 9, 8, 3, 2, 2, 3, 6, 7, 8, 3, 7, 7, 6, 5, 6, 6, 4, 15, 12,
+ 8, 5, 4, 5, 4, 2, 5, 7, 9, 11, 9, 5, 2, 3, 6, 8, 8, 8, 2,
+ 1, 2, 4, 5, 5, 5, 1, 0, 2, 2, 1, 2, 1, 1, 4, 4, 4, 4, 4,
+ 2, 1, 10, 9, 9, 8, 5, 3, 1, 16, 15, 11, 9, 6, 4, 2, 1, 1, 1,
+ 2, 3, 4, 2, 0, 1, 1, 2, 2, 1, 3, 1, 1, 1, 1, 1, 1, 3, 3,
+ 3, 3, 3, 4, 4, 4, 5, 7, 7, 7, 8, 7, 6, 9, 13, 11, 11, 10, 9,
+ 8, 16, 18, 14, 13, 12, 10, 9, 7, 7, 7, 8, 8, 7, 3, 8, 8, 9, 9,
+ 9, 8, 3, 8, 10, 10, 10, 11, 9, 5, 8, 13, 12, 13, 13, 12, 8, 11, 16,
+ 15, 15, 15, 14, 10, 14, 18, 19, 17, 16, 16, 12, 17, 20, 21, 18, 18, 16, 13,
+ 16, 16, 16, 16, 14, 10, 5, 15, 18, 17, 16, 14, 11, 7, 15, 19, 18, 17, 16,
+ 13, 8, 15, 19, 19, 18, 16, 15, 11, 16, 20, 22, 21, 18, 16, 13, 18, 22, 25,
+ 23, 20, 17, 14, 20, 24, 25, 25, 21, 18, 15, 21, 25, 22, 19, 16, 14, 8, 21,
+ 25, 25, 20, 16, 14, 10, 20, 25, 27, 21, 17, 15, 11, 21, 24, 27, 22, 18, 17,
+ 15, 21, 25, 28, 26, 21, 18, 16, 21, 25, 29, 28, 23, 19, 17, 24, 26, 29, 29,
+ 24, 20, 17, 7, 8, 9, 11, 11, 9, 7, 6, 7, 8, 10, 10, 8, 7, 5, 7,
+ 8, 9, 9, 8, 7, 4, 6, 7, 8, 8, 8, 8, 4, 7, 8, 8, 9, 8, 8,
+ 7, 8, 9, 9, 9, 8, 8, 13, 13, 11, 9, 9, 8, 7, 5, 7, 8, 9, 9,
+ 7, 6, 4, 7, 8, 8, 8, 7, 6, 3, 6, 7, 7, 7, 7, 6, 2, 5, 7,
+ 8, 7, 7, 7, 3, 5, 7, 8, 7, 7, 7, 8, 9, 10, 10, 8, 7, 7, 16,
+ 15, 12, 10, 8, 7, 7, 3, 6, 6, 6, 7, 6, 3, 2, 5, 6, 6, 6, 6,
+ 3, 1, 4, 5, 5, 6, 6, 3, 1, 4, 5, 5, 6, 6, 5, 5, 6, 6, 6,
+ 6, 6, 5, 11, 11, 11, 9, 7, 5, 5, 18, 17, 13, 10, 7, 5, 3, 2, 2,
+ 2, 2, 2, 2, 2, 1, 0, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3,
+ 4, 4, 4, 4, 4, 4, 5, 6, 8, 8, 8, 8, 7, 6, 11, 14, 13, 12, 10,
+ 9, 8, 18, 20, 16, 14, 12, 10, 9, 7, 7, 7, 7, 7, 6, 2, 9, 9, 8,
+ 8, 8, 7, 3, 9, 10, 10, 10, 10, 8, 4, 9, 13, 13, 12, 13, 12, 8, 12,
+ 18, 17, 17, 18, 15, 10, 15, 21, 23, 21, 20, 17, 11, 19, 24, 26, 23, 22, 18,
+ 13, 16, 17, 17, 16, 13, 9, 5, 15, 20, 18, 18, 14, 10, 6, 15, 21, 20, 20,
+ 16, 12, 8, 16, 22, 24, 23, 19, 15, 11, 18, 24, 29, 28, 24, 19, 13, 20, 27,
+ 32, 31, 27, 21, 15, 25, 30, 34, 32, 28, 23, 17, 23, 29, 28, 22, 16, 12, 7,
+ 23, 29, 30, 24, 18, 13, 9, 23, 30, 33, 26, 20, 15, 11, 24, 29, 34, 29, 24,
+ 20, 15, 24, 31, 35, 34, 28, 23, 17, 27, 33, 37, 36, 30, 25, 19, 30, 35, 39,
+ 38, 33, 27, 22, 7, 10, 12, 14, 14, 12, 10, 7, 9, 12, 13, 14, 12, 10, 5,
+ 8, 10, 13, 13, 12, 11, 4, 8, 10, 12, 14, 13, 12, 5, 8, 10, 13, 15, 13,
+ 12, 8, 10, 11, 13, 14, 13, 11, 14, 14, 13, 12, 13, 13, 12, 5, 8, 11, 12,
+ 11, 10, 7, 5, 7, 10, 12, 11, 10, 7, 3, 6, 9, 11, 11, 10, 8, 3, 6,
+ 9, 11, 11, 11, 9, 5, 6, 8, 11, 11, 11, 9, 10, 11, 12, 12, 10, 10, 9,
+ 17, 16, 13, 12, 10, 9, 9, 4, 6, 7, 7, 8, 6, 3, 2, 5, 6, 7, 7,
+ 6, 3, 1, 4, 5, 5, 6, 6, 3, 1, 4, 5, 6, 6, 6, 4, 6, 7, 7,
+ 7, 7, 6, 5, 12, 12, 11, 10, 8, 6, 4, 19, 19, 14, 11, 8, 6, 5, 2,
+ 2, 1, 1, 2, 2, 3, 1, 1, 0, 1, 1, 1, 3, 2, 2, 2, 2, 2, 2,
+ 4, 6, 5, 5, 5, 5, 5, 5, 7, 9, 9, 9, 9, 9, 7, 12, 15, 14, 13,
+ 11, 10, 9, 19, 21, 17, 15, 13, 11, 11, 8, 7, 7, 7, 7, 6, 3, 9, 9,
+ 8, 8, 8, 7, 3, 9, 10, 10, 10, 10, 9, 5, 9, 14, 14, 14, 14, 13, 9,
+ 13, 19, 19, 18, 18, 16, 11, 17, 22, 24, 23, 20, 18, 12, 21, 26, 28, 24, 23,
+ 20, 14, 16, 18, 17, 16, 13, 9, 4, 16, 20, 18, 18, 14, 9, 6, 17, 21, 21,
+ 20, 16, 12, 8, 17, 24, 25, 24, 21, 16, 12, 20, 25, 30, 29, 25, 19, 14, 22,
+ 28, 35, 32, 28, 21, 16, 25, 31, 35, 36, 28, 23, 18, 24, 30, 27, 21, 16, 12,
+ 7, 24, 30, 30, 23, 18, 13, 9, 24, 30, 33, 26, 21, 16, 12, 24, 31, 36, 30,
+ 24, 20, 16, 25, 32, 38, 34, 29, 23, 18, 28, 34, 39, 40, 33, 26, 20, 31, 37,
+ 41, 41, 35, 28, 21, 8, 10, 13, 16, 16, 13, 11, 7, 9, 12, 15, 16, 14, 11,
+ 6, 8, 11, 14, 16, 14, 12, 5, 9, 11, 14, 17, 15, 13, 6, 8, 11, 14, 17,
+ 16, 13, 10, 11, 12, 14, 17, 16, 13, 16, 15, 13, 13, 15, 15, 13, 6, 8, 11,
+ 14, 12, 10, 7, 5, 8, 10, 13, 12, 10, 7, 4, 7, 10, 12, 12, 10, 9, 3,
+ 7, 10, 12, 12, 12, 9, 5, 7, 9, 12, 12, 12, 9, 11, 11, 13, 13, 11, 12,
+ 9, 19, 17, 15, 13, 10, 10, 9, 4, 7, 7, 7, 8, 6, 2, 3, 5, 7, 7,
+ 7, 6, 2, 1, 5, 6, 6, 6, 6, 4, 1, 5, 6, 6, 7, 7, 5, 7, 8,
+ 8, 8, 8, 7, 5, 14, 14, 13, 11, 9, 7, 5, 21, 20, 15, 12, 9, 7, 5,
+ 2, 1, 1, 1, 2, 2, 2, 2, 1, 1, 0, 1, 1, 3, 3, 2, 2, 2, 3,
+ 3, 4, 6, 5, 6, 6, 6, 6, 6, 8, 11, 10, 10, 10, 9, 8, 14, 17, 15,
+ 14, 12, 11, 9, 21, 23, 18, 16, 14, 12, 11, 8, 8, 7, 7, 7, 6, 3, 10,
+ 9, 8, 8, 8, 7, 3, 10, 11, 11, 11, 11, 10, 6, 10, 15, 15, 15, 15, 14,
+ 10, 13, 20, 20, 19, 19, 16, 11, 18, 25, 25, 24, 21, 18, 13, 22, 27, 28, 26,
+ 23, 20, 15, 17, 18, 17, 16, 12, 8, 4, 17, 20, 18, 17, 14, 10, 6, 17, 22,
+ 22, 21, 17, 13, 9, 17, 24, 26, 25, 21, 17, 13, 20, 26, 30, 30, 25, 20, 15,
+ 23, 29, 36, 33, 28, 22, 17, 27, 32, 36, 37, 30, 24, 19, 24, 30, 28, 21, 16,
+ 11, 7, 24, 29, 29, 23, 18, 13, 10, 24, 30, 33, 27, 21, 17, 13, 24, 31, 37,
+ 30, 25, 21, 17, 28, 33, 39, 36, 31, 24, 19, 30, 36, 41, 41, 33, 27, 20, 33,
+ 38, 43, 43, 36, 28, 22, 8, 11, 14, 17, 17, 15, 12, 7, 10, 13, 17, 17, 14,
+ 12, 6, 9, 12, 15, 18, 15, 13, 5, 9, 12, 15, 19, 16, 14, 6, 9, 12, 15,
+ 19, 17, 14, 10, 12, 13, 14, 18, 17, 14, 16, 15, 14, 14, 16, 16, 14, 6, 9,
+ 11, 15, 14, 11, 8, 5, 8, 11, 13, 13, 10, 8, 4, 7, 10, 13, 13, 11, 9,
+ 3, 7, 11, 13, 13, 12, 10, 5, 8, 10, 12, 13, 12, 10, 12, 12, 14, 14, 12,
+ 12, 10, 19, 17, 15, 14, 11, 11, 10, 4, 7, 8, 8, 9, 7, 3, 3, 6, 7,
+ 7, 7, 6, 3, 1, 5, 6, 7, 7, 7, 4, 1, 5, 7, 7, 7, 7, 6, 7,
+ 8, 9, 8, 8, 7, 6, 14, 14, 14, 12, 9, 7, 6, 21, 20, 16, 12, 10, 8,
+ 6, 2, 2, 1, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 2, 4, 2, 2, 3,
+ 3, 3, 3, 7, 5, 6, 6, 6, 6, 5, 8, 11, 10, 10, 11, 9, 8, 14, 17,
+ 15, 14, 12, 11, 10, 21, 23, 19, 16, 14, 12, 11, 8, 7, 7, 7, 7, 6, 2,
+ 10, 9, 8, 8, 8, 7, 2, 10, 11, 10, 11, 11, 10, 6, 10, 15, 15, 15, 15,
+ 14, 10, 14, 21, 20, 20, 19, 17, 12, 18, 24, 26, 24, 21, 18, 13, 23, 28, 29,
+ 26, 24, 21, 15, 17, 18, 17, 15, 12, 9, 4, 16, 19, 18, 17, 13, 10, 6, 17,
+ 22, 21, 21, 18, 13, 9, 16, 24, 26, 25, 22, 18, 13, 20, 27, 30, 30, 26, 21,
+ 15, 24, 30, 37, 35, 28, 23, 17, 28, 32, 37, 37, 30, 24, 19, 24, 28, 25, 20,
+ 15, 11, 7, 24, 28, 28, 22, 17, 13, 10, 24, 29, 31, 25, 21, 17, 13, 24, 30,
+ 35, 31, 25, 21, 17, 28, 33, 38, 35, 30, 24, 19, 30, 35, 41, 40, 32, 27, 21,
+ 34, 39, 41, 42, 35, 29, 23, 8, 11, 14, 17, 17, 15, 11, 7, 10, 13, 16, 16,
+ 14, 12, 6, 9, 12, 15, 17, 16, 13, 5, 9, 12, 16, 18, 16, 14, 6, 9, 12,
+ 15, 18, 17, 14, 10, 12, 13, 14, 18, 17, 14, 17, 16, 14, 14, 16, 17, 14, 6,
+ 9, 12, 14, 13, 11, 8, 5, 8, 11, 13, 13, 10, 8, 4, 7, 10, 14, 13, 11,
+ 9, 3, 7, 11, 13, 13, 12, 10, 5, 8, 10, 13, 13, 12, 10, 11, 12, 14, 13,
+ 12, 12, 10, 19, 17, 15, 14, 11, 11, 9, 4, 7, 8, 8, 9, 7, 3, 3, 6,
+ 7, 7, 7, 6, 3, 2, 5, 6, 7, 7, 7, 4, 1, 5, 7, 7, 7, 7, 6,
+ 6, 8, 9, 9, 8, 7, 6, 14, 14, 13, 11, 9, 8, 6, 21, 19, 15, 12, 10,
+ 8, 6, 2, 2, 1, 1, 2, 3, 2, 1, 1, 1, 1, 0, 0, 2, 5, 2, 2,
+ 3, 3, 3, 3, 7, 5, 6, 6, 6, 6, 5, 9, 11, 10, 11, 10, 10, 8, 14,
+ 17, 16, 14, 13, 11, 10, 21, 23, 19, 16, 14, 13, 12, 9, 8, 7, 6, 7, 6,
+ 2, 11, 10, 8, 8, 8, 7, 2, 11, 11, 10, 11, 11, 10, 6, 10, 15, 15, 15,
+ 15, 14, 10, 14, 21, 20, 20, 19, 16, 12, 18, 24, 25, 23, 21, 18, 13, 23, 27,
+ 29, 25, 22, 20, 15, 16, 16, 14, 13, 11, 8, 5, 16, 18, 15, 13, 12, 10, 6,
+ 16, 20, 17, 17, 15, 12, 10, 17, 21, 23, 21, 19, 17, 13, 20, 26, 28, 26, 23,
+ 20, 15, 24, 29, 33, 30, 25, 21, 17, 27, 33, 34, 33, 26, 23, 19, 21, 23, 20,
+ 15, 13, 10, 7, 21, 24, 23, 17, 14, 13, 9, 21, 24, 25, 21, 18, 16, 13, 21,
+ 25, 30, 25, 22, 19, 16, 25, 29, 34, 32, 27, 23, 19, 29, 33, 37, 36, 29, 24,
+ 20, 32, 35, 37, 38, 32, 26, 21, 9, 11, 14, 17, 17, 14, 11, 9, 11, 13, 16,
+ 17, 13, 11, 8, 10, 12, 15, 17, 15, 12, 8, 10, 13, 16, 18, 15, 13, 8, 10,
+ 13, 15, 19, 17, 13, 11, 11, 13, 15, 18, 16, 13, 16, 15, 14, 14, 16, 17, 13,
+ 8, 10, 12, 14, 13, 10, 7, 8, 9, 11, 13, 13, 9, 7, 7, 8, 11, 13, 12,
+ 10, 8, 6, 9, 12, 14, 13, 12, 9, 6, 9, 11, 13, 13, 12, 10, 11, 12, 13,
+ 13, 12, 11, 10, 19, 17, 14, 13, 11, 11, 10, 7, 9, 9, 8, 9, 7, 4, 6,
+ 8, 8, 7, 7, 6, 3, 5, 8, 8, 7, 7, 6, 5, 4, 7, 8, 8, 7, 7,
+ 6, 7, 9, 9, 8, 8, 7, 6, 14, 14, 13, 11, 9, 7, 6, 21, 19, 14, 11,
+ 9, 7, 6, 5, 4, 3, 3, 3, 4, 2, 3, 3, 3, 2, 2, 2, 0, 2, 2,
+ 2, 3, 2, 2, 2, 7, 3, 4, 5, 4, 4, 4, 8, 9, 9, 8, 8, 7, 5,
+ 14, 15, 14, 11, 10, 8, 6, 20, 21, 16, 13, 11, 9, 7, 5, 3, 2, 2, 2,
+ 2, 2, 6, 4, 3, 2, 3, 2, 0, 5, 5, 4, 4, 4, 4, 3, 5, 8, 8,
+ 8, 8, 8, 6, 9, 14, 13, 13, 13, 10, 7, 14, 19, 20, 18, 15, 12, 8, 20,
+ 23, 23, 19, 17, 14, 9, 9, 9, 7, 6, 4, 3, 2, 9, 11, 9, 8, 5, 3,
+ 2, 8, 12, 11, 11, 9, 6, 5, 9, 14, 17, 16, 13, 10, 8, 12, 18, 22, 21,
+ 18, 14, 9, 17, 23, 27, 25, 20, 15, 11, 22, 26, 29, 28, 23, 17, 12, 14, 18,
+ 15, 10, 6, 3, 2, 13, 18, 18, 12, 8, 5, 3, 14, 18, 20, 15, 12, 9, 6,
+ 14, 19, 25, 21, 17, 13, 11, 17, 23, 28, 26, 22, 17, 12, 22, 26, 32, 32, 25,
+ 19, 14, 26, 30, 33, 33, 27, 20, 15, 10, 14, 18, 22, 22, 20, 17, 8, 12, 17,
+ 20, 22, 19, 16, 7, 11, 14, 17, 20, 17, 13, 5, 9, 12, 15, 16, 15, 13, 3,
+ 6, 10, 13, 15, 16, 14, 4, 5, 8, 11, 14, 16, 13, 10, 7, 7, 11, 13, 15,
+ 14, 7, 11, 15, 18, 20, 17, 13, 5, 9, 14, 17, 19, 15, 12, 4, 8, 12, 13,
+ 15, 14, 9, 3, 6, 9, 11, 12, 13, 8, 2, 3, 7, 10, 12, 12, 8, 6, 5,
+ 6, 8, 10, 11, 8, 12, 10, 6, 8, 8, 8, 8, 4, 8, 10, 12, 13, 12, 9,
+ 2, 7, 9, 11, 11, 11, 7, 1, 5, 8, 9, 9, 10, 4, 1, 3, 4, 5, 5,
+ 5, 1, 3, 3, 3, 3, 3, 3, 1, 9, 8, 7, 5, 4, 2, 1, 14, 13, 9,
+ 7, 5, 4, 1, 2, 2, 4, 5, 7, 8, 6, 1, 1, 2, 3, 4, 5, 2, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 4, 6, 6, 6, 6, 5,
+ 4, 9, 10, 9, 8, 8, 7, 6, 14, 15, 11, 10, 9, 8, 8, 6, 6, 6, 6,
+ 6, 4, 6, 7, 7, 8, 8, 8, 6, 3, 7, 9, 8, 8, 9, 8, 3, 7, 10,
+ 10, 10, 10, 9, 6, 9, 13, 12, 12, 12, 11, 8, 11, 15, 15, 14, 13, 12, 9,
+ 14, 17, 17, 15, 14, 13, 10, 13, 13, 13, 13, 11, 8, 5, 12, 15, 14, 13, 12,
+ 9, 6, 12, 16, 15, 14, 12, 10, 7, 13, 15, 16, 15, 13, 11, 9, 13, 16, 18,
+ 17, 16, 12, 10, 14, 18, 22, 20, 17, 14, 11, 16, 20, 22, 21, 18, 15, 12, 17,
+ 20, 19, 16, 13, 11, 6, 18, 21, 21, 16, 13, 11, 8, 17, 21, 23, 17, 14, 11,
+ 9, 17, 21, 23, 19, 15, 13, 11, 17, 21, 24, 21, 18, 15, 12, 18, 22, 25, 25,
+ 19, 16, 13, 20, 23, 26, 25, 21, 16, 14, 7, 8, 10, 13, 13, 11, 9, 7, 8,
+ 10, 12, 13, 10, 8, 6, 8, 9, 10, 11, 9, 7, 5, 7, 8, 9, 10, 8, 8,
+ 5, 7, 8, 9, 10, 9, 8, 7, 8, 9, 9, 9, 8, 8, 13, 13, 11, 9, 9,
+ 8, 8, 7, 8, 9, 10, 11, 9, 7, 6, 7, 8, 10, 11, 8, 7, 4, 7, 8,
+ 8, 8, 7, 6, 3, 6, 7, 8, 7, 7, 7, 3, 6, 8, 8, 8, 8, 7, 8,
+ 9, 10, 9, 7, 7, 7, 16, 15, 12, 10, 8, 7, 7, 4, 7, 7, 7, 7, 7,
+ 5, 3, 6, 7, 7, 7, 6, 4, 2, 5, 6, 6, 6, 6, 5, 1, 5, 6, 6,
+ 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 11, 11, 10, 9, 7, 5, 6, 18, 17,
+ 13, 10, 7, 5, 4, 2, 3, 3, 3, 3, 4, 2, 1, 1, 2, 2, 2, 2, 2,
+ 1, 0, 1, 1, 1, 1, 2, 3, 2, 2, 3, 3, 3, 3, 5, 7, 6, 6, 7,
+ 6, 5, 11, 12, 12, 11, 9, 8, 7, 18, 20, 15, 13, 11, 9, 8, 6, 6, 6,
+ 6, 6, 5, 2, 7, 7, 7, 7, 7, 6, 2, 8, 9, 9, 8, 9, 7, 3, 8,
+ 12, 11, 11, 12, 11, 7, 10, 16, 16, 15, 17, 14, 9, 13, 19, 22, 20, 19, 16,
+ 11, 18, 23, 24, 22, 20, 18, 12, 14, 16, 15, 16, 12, 8, 4, 14, 18, 17, 18,
+ 14, 9, 5, 14, 20, 19, 20, 16, 11, 7, 14, 20, 22, 22, 19, 14, 10, 16, 22,
+ 27, 24, 22, 18, 12, 19, 25, 30, 28, 24, 20, 14, 23, 28, 31, 30, 26, 22, 16,
+ 22, 28, 26, 20, 16, 11, 6, 22, 28, 28, 23, 18, 12, 8, 22, 27, 31, 24, 19,
+ 14, 10, 22, 27, 32, 25, 22, 18, 14, 23, 28, 33, 30, 25, 22, 16, 26, 30, 34,
+ 32, 28, 24, 18, 28, 31, 34, 35, 29, 25, 21, 9, 12, 14, 14, 15, 13, 12, 8,
+ 10, 13, 14, 15, 13, 11, 6, 10, 12, 14, 14, 12, 11, 5, 8, 11, 13, 14, 13,
+ 12, 6, 8, 11, 14, 15, 14, 12, 8, 9, 11, 13, 15, 14, 12, 14, 14, 12, 12,
+ 13, 14, 12, 6, 10, 12, 13, 13, 11, 9, 6, 9, 11, 13, 13, 11, 8, 4, 7,
+ 10, 12, 12, 10, 9, 3, 7, 10, 12, 11, 11, 9, 4, 6, 9, 11, 11, 12, 10,
+ 9, 10, 12, 12, 11, 11, 9, 17, 15, 13, 12, 10, 10, 9, 4, 7, 9, 9, 9,
+ 8, 4, 3, 6, 8, 8, 8, 7, 4, 2, 5, 7, 7, 7, 7, 4, 2, 5, 6,
+ 6, 7, 7, 5, 6, 6, 7, 6, 7, 6, 5, 12, 12, 11, 10, 7, 6, 5, 19,
+ 18, 14, 10, 8, 6, 4, 2, 2, 3, 3, 3, 3, 2, 1, 1, 2, 2, 2, 2,
+ 2, 1, 1, 0, 0, 0, 0, 2, 4, 3, 3, 4, 4, 4, 5, 6, 8, 7, 7,
+ 8, 7, 6, 12, 14, 12, 11, 10, 8, 7, 19, 20, 16, 13, 11, 10, 9, 7, 6,
+ 6, 6, 6, 5, 2, 8, 8, 7, 7, 8, 6, 2, 8, 9, 9, 9, 9, 7, 4,
+ 8, 13, 12, 12, 12, 11, 7, 11, 17, 17, 16, 16, 14, 9, 15, 21, 23, 20, 19,
+ 16, 11, 19, 24, 26, 23, 21, 18, 13, 15, 17, 15, 16, 12, 8, 4, 15, 18, 18,
+ 17, 13, 9, 5, 15, 20, 19, 19, 15, 11, 7, 15, 21, 23, 23, 19, 15, 11, 18,
+ 25, 28, 26, 23, 18, 12, 20, 26, 33, 31, 26, 19, 14, 23, 29, 33, 33, 28, 22,
+ 16, 22, 28, 27, 21, 15, 11, 6, 22, 28, 30, 22, 17, 12, 8, 23, 28, 32, 25,
+ 19, 15, 11, 23, 29, 34, 28, 22, 18, 15, 24, 31, 37, 34, 28, 21, 16, 27, 34,
+ 39, 37, 31, 25, 18, 30, 35, 40, 40, 33, 26, 20, 9, 13, 16, 19, 19, 17, 14,
+ 8, 12, 15, 18, 19, 16, 13, 7, 10, 14, 17, 18, 15, 13, 6, 9, 12, 15, 17,
+ 15, 13, 6, 9, 11, 15, 17, 15, 13, 8, 9, 11, 14, 16, 16, 13, 14, 14, 12,
+ 13, 16, 15, 13, 7, 11, 14, 17, 16, 13, 10, 6, 9, 13, 16, 16, 13, 9, 5,
+ 8, 11, 14, 14, 11, 8, 4, 7, 10, 12, 12, 12, 9, 5, 7, 10, 12, 12, 12,
+ 9, 9, 10, 11, 12, 11, 11, 9, 17, 15, 13, 11, 10, 10, 9, 5, 8, 9, 10,
+ 11, 9, 6, 4, 6, 8, 9, 10, 9, 5, 3, 5, 7, 7, 7, 7, 4, 2, 6,
+ 6, 6, 6, 7, 5, 6, 7, 7, 6, 6, 6, 5, 12, 12, 11, 9, 7, 5, 5,
+ 19, 17, 13, 10, 7, 5, 4, 3, 2, 3, 4, 4, 5, 3, 1, 2, 2, 3, 3,
+ 3, 3, 1, 1, 0, 0, 0, 0, 2, 4, 3, 3, 3, 3, 3, 4, 6, 8, 7,
+ 7, 7, 7, 5, 12, 14, 12, 11, 10, 8, 7, 19, 20, 16, 13, 11, 10, 8, 7,
+ 6, 6, 7, 7, 6, 3, 8, 8, 7, 8, 8, 7, 3, 8, 9, 9, 9, 9, 7,
+ 4, 8, 13, 12, 12, 12, 11, 7, 11, 17, 17, 16, 16, 14, 9, 15, 21, 22, 21,
+ 18, 16, 11, 19, 24, 25, 23, 21, 18, 12, 15, 17, 16, 16, 13, 9, 5, 15, 19,
+ 18, 17, 14, 9, 5, 15, 20, 19, 18, 14, 11, 7, 15, 22, 24, 22, 19, 15, 10,
+ 18, 24, 28, 26, 23, 18, 12, 21, 26, 34, 32, 25, 20, 14, 24, 29, 34, 33, 27,
+ 22, 16, 23, 28, 27, 21, 16, 12, 7, 23, 29, 29, 22, 17, 12, 8, 23, 28, 32,
+ 24, 19, 15, 10, 22, 29, 35, 29, 22, 18, 14, 23, 30, 36, 34, 27, 22, 16, 27,
+ 32, 39, 38, 31, 24, 18, 30, 36, 40, 40, 33, 26, 20, 10, 14, 18, 20, 21, 19,
+ 15, 8, 12, 16, 20, 21, 17, 13, 7, 10, 15, 18, 19, 16, 13, 6, 9, 12, 15,
+ 19, 16, 13, 6, 9, 12, 15, 18, 16, 14, 8, 9, 11, 14, 18, 17, 13, 14, 13,
+ 12, 13, 16, 17, 14, 7, 11, 15, 18, 17, 14, 11, 6, 10, 13, 16, 17, 13, 9,
+ 5, 8, 12, 14, 14, 11, 9, 4, 7, 10, 13, 13, 12, 10, 5, 7, 10, 12, 12,
+ 12, 9, 10, 10, 11, 12, 12, 11, 10, 17, 15, 12, 12, 10, 11, 9, 5, 8, 10,
+ 11, 12, 10, 6, 4, 7, 9, 10, 10, 9, 5, 3, 6, 7, 7, 7, 7, 4, 1,
+ 6, 6, 6, 7, 7, 5, 6, 7, 7, 6, 6, 6, 5, 13, 12, 11, 9, 7, 6,
+ 5, 19, 17, 13, 10, 7, 5, 4, 3, 3, 3, 4, 5, 6, 3, 1, 1, 2, 3,
+ 3, 3, 2, 1, 1, 0, 0, 0, 0, 1, 4, 3, 4, 3, 3, 3, 3, 6, 8,
+ 7, 7, 7, 6, 5, 13, 14, 12, 11, 9, 8, 6, 19, 21, 15, 13, 11, 9, 8,
+ 7, 6, 6, 6, 7, 6, 3, 9, 8, 7, 7, 8, 6, 2, 9, 10, 9, 8, 8,
+ 7, 3, 9, 13, 12, 12, 12, 11, 7, 12, 18, 17, 16, 16, 13, 8, 15, 21, 22,
+ 20, 18, 15, 10, 19, 24, 26, 23, 21, 17, 12, 15, 16, 15, 15, 12, 9, 4, 15,
+ 19, 17, 17, 13, 9, 4, 15, 20, 19, 18, 14, 10, 7, 15, 21, 23, 21, 18, 14,
+ 10, 18, 24, 29, 26, 23, 17, 12, 20, 27, 33, 31, 25, 19, 14, 24, 29, 33, 33,
+ 28, 21, 15, 22, 27, 26, 20, 15, 11, 7, 23, 27, 27, 21, 16, 12, 7, 22, 27,
+ 29, 23, 18, 14, 10, 23, 28, 33, 27, 23, 18, 13, 25, 30, 35, 33, 26, 22, 16,
+ 27, 32, 38, 38, 30, 23, 18, 29, 34, 38, 37, 32, 25, 20, 10, 14, 17, 21, 21,
+ 18, 15, 9, 12, 15, 20, 20, 17, 13, 7, 11, 14, 17, 19, 15, 13, 6, 9, 12,
+ 16, 18, 16, 14, 6, 9, 12, 15, 19, 17, 14, 9, 10, 11, 14, 18, 17, 14, 14,
+ 13, 12, 14, 17, 17, 13, 7, 11, 15, 18, 17, 15, 11, 6, 10, 13, 16, 17, 13,
+ 9, 5, 8, 12, 15, 14, 11, 9, 4, 8, 11, 13, 13, 12, 9, 5, 7, 10, 12,
+ 13, 12, 10, 10, 10, 11, 12, 12, 12, 10, 16, 14, 12, 12, 11, 11, 10, 5, 8,
+ 10, 11, 12, 10, 6, 4, 7, 9, 10, 10, 9, 5, 3, 6, 7, 8, 7, 7, 4,
+ 2, 6, 7, 7, 7, 7, 5, 6, 7, 7, 7, 7, 6, 5, 13, 12, 11, 9, 7,
+ 5, 5, 17, 16, 12, 10, 7, 5, 4, 3, 3, 3, 4, 5, 6, 4, 1, 1, 2,
+ 3, 3, 3, 2, 1, 1, 0, 0, 0, 0, 1, 4, 4, 4, 3, 3, 3, 3, 7,
+ 9, 8, 7, 8, 6, 5, 13, 14, 12, 10, 9, 8, 6, 18, 19, 15, 13, 11, 9,
+ 8, 7, 6, 6, 6, 7, 6, 4, 9, 8, 7, 7, 8, 6, 2, 9, 10, 9, 8,
+ 8, 7, 3, 9, 14, 13, 12, 12, 10, 7, 12, 18, 17, 16, 16, 14, 9, 15, 21,
+ 22, 20, 18, 15, 10, 18, 23, 24, 22, 19, 17, 12, 15, 15, 14, 13, 11, 9, 4,
+ 16, 17, 15, 14, 12, 9, 4, 15, 19, 17, 15, 12, 10, 7, 15, 20, 21, 18, 16,
+ 14, 10, 18, 23, 26, 23, 20, 17, 12, 21, 25, 31, 27, 22, 18, 14, 23, 28, 31,
+ 29, 24, 20, 15, 20, 23, 20, 15, 13, 11, 7, 21, 23, 22, 17, 13, 11, 7, 20,
+ 23, 23, 19, 15, 13, 10, 20, 24, 28, 22, 18, 17, 14, 23, 26, 30, 28, 23, 19,
+ 16, 26, 29, 33, 32, 25, 21, 17, 28, 31, 34, 34, 27, 22, 18, 11, 13, 16, 19,
+ 19, 16, 13, 10, 12, 15, 18, 18, 15, 11, 8, 11, 14, 16, 17, 14, 11, 7, 9,
+ 12, 15, 17, 15, 12, 8, 10, 12, 15, 18, 15, 12, 8, 9, 12, 14, 17, 16, 12,
+ 12, 12, 11, 13, 16, 16, 12, 9, 11, 14, 16, 15, 12, 9, 7, 10, 12, 15, 14,
+ 11, 8, 7, 9, 12, 13, 12, 10, 7, 6, 9, 11, 13, 12, 10, 8, 6, 8, 10,
+ 12, 12, 11, 9, 10, 9, 10, 12, 11, 11, 8, 15, 13, 11, 11, 10, 9, 8, 7,
+ 9, 10, 10, 11, 9, 6, 6, 8, 9, 9, 9, 7, 4, 4, 7, 8, 7, 6, 6,
+ 4, 4, 8, 7, 7, 6, 6, 5, 6, 8, 7, 7, 6, 6, 5, 12, 11, 10, 8,
+ 6, 5, 5, 16, 15, 11, 9, 6, 5, 4, 5, 4, 4, 4, 5, 6, 4, 3, 3,
+ 4, 4, 3, 3, 2, 2, 2, 2, 2, 1, 1, 0, 4, 2, 3, 3, 2, 2, 2,
+ 7, 7, 6, 6, 6, 5, 3, 12, 13, 11, 9, 7, 6, 4, 17, 18, 13, 10, 8,
+ 7, 5, 4, 4, 3, 3, 3, 4, 4, 5, 5, 3, 3, 3, 3, 2, 6, 5, 4,
+ 4, 4, 3, 2, 5, 9, 8, 7, 7, 6, 4, 8, 14, 13, 12, 12, 9, 5, 13,
+ 17, 18, 16, 14, 11, 7, 16, 20, 21, 18, 15, 13, 8, 10, 10, 9, 9, 7, 4,
+ 3, 9, 12, 11, 10, 8, 4, 2, 9, 13, 12, 12, 8, 5, 3, 9, 15, 16, 15,
+ 12, 9, 6, 13, 18, 22, 20, 17, 12, 8, 16, 21, 27, 24, 19, 14, 10, 20, 24,
+ 27, 27, 21, 16, 11, 15, 19, 18, 13, 9, 6, 3, 15, 20, 20, 15, 10, 6, 3,
+ 15, 20, 22, 16, 12, 8, 5, 15, 20, 25, 20, 16, 13, 9, 18, 23, 28, 26, 21,
+ 16, 11, 21, 26, 31, 30, 23, 17, 13, 24, 29, 31, 32, 25, 20, 14, 13, 16, 20,
+ 25, 25, 24, 19, 11, 14, 18, 23, 25, 21, 17, 10, 14, 17, 20, 23, 19, 16, 8,
+ 12, 14, 17, 20, 18, 15, 6, 9, 12, 15, 17, 18, 15, 5, 8, 11, 13, 16, 18,
+ 15, 7, 8, 9, 13, 15, 18, 15, 9, 14, 17, 20, 21, 19, 15, 8, 13, 15, 19,
+ 21, 18, 14, 7, 11, 14, 17, 17, 15, 12, 6, 9, 12, 14, 14, 14, 11, 3, 7,
+ 10, 12, 13, 14, 11, 4, 6, 9, 11, 12, 12, 11, 9, 7, 8, 10, 10, 11, 11,
+ 7, 11, 13, 14, 16, 15, 11, 5, 10, 11, 13, 13, 14, 9, 4, 8, 10, 11, 11,
+ 11, 7, 3, 5, 7, 8, 8, 8, 5, 2, 4, 6, 6, 6, 6, 5, 6, 6, 5,
+ 5, 5, 5, 5, 10, 9, 7, 4, 3, 4, 4, 4, 6, 7, 7, 9, 11, 11, 3,
+ 4, 6, 6, 7, 8, 7, 1, 3, 4, 4, 4, 4, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 2, 6, 7, 6, 5, 4, 4, 3, 10, 12, 8, 6,
+ 5, 4, 4, 4, 4, 4, 4, 6, 8, 10, 4, 5, 5, 5, 5, 5, 7, 4, 5,
+ 6, 5, 5, 5, 2, 4, 7, 6, 6, 6, 5, 3, 5, 9, 8, 8, 8, 7, 4,
+ 7, 11, 11, 11, 9, 8, 5, 10, 13, 13, 12, 11, 9, 6, 9, 10, 9, 9, 8,
+ 6, 8, 9, 11, 10, 10, 8, 6, 4, 9, 12, 11, 11, 9, 6, 4, 9, 12, 12,
+ 12, 10, 7, 5, 9, 13, 15, 14, 12, 9, 6, 11, 14, 18, 16, 13, 10, 7, 13,
+ 16, 18, 18, 14, 11, 8, 14, 17, 16, 12, 10, 7, 6, 14, 18, 17, 14, 10, 7,
+ 5, 14, 17, 19, 14, 11, 8, 5, 13, 16, 20, 15, 12, 9, 7, 13, 17, 20, 17,
+ 15, 11, 8, 14, 18, 21, 20, 16, 12, 9, 16, 19, 22, 22, 17, 14, 10, 10, 11,
+ 14, 17, 17, 14, 12, 9, 11, 13, 16, 17, 14, 11, 9, 11, 12, 13, 14, 13, 10,
+ 8, 9, 10, 12, 13, 10, 8, 6, 7, 8, 9, 11, 9, 8, 6, 7, 8, 8, 10,
+ 9, 8, 12, 12, 9, 8, 9, 9, 8, 9, 10, 12, 14, 14, 12, 10, 8, 10, 11,
+ 13, 14, 11, 9, 7, 9, 10, 11, 11, 10, 8, 6, 8, 9, 9, 9, 8, 7, 4,
+ 6, 7, 8, 7, 7, 7, 7, 8, 9, 8, 7, 7, 7, 15, 13, 11, 8, 6, 7,
+ 7, 7, 9, 10, 9, 10, 9, 8, 5, 8, 9, 9, 9, 9, 7, 4, 8, 9, 9,
+ 8, 8, 7, 3, 6, 6, 7, 7, 7, 6, 3, 4, 5, 6, 6, 6, 6, 9, 9,
+ 10, 8, 6, 6, 6, 17, 16, 12, 9, 6, 5, 5, 4, 5, 6, 6, 6, 7, 3,
+ 3, 4, 5, 5, 5, 5, 3, 2, 2, 3, 3, 4, 4, 2, 0, 0, 0, 0, 0,
+ 0, 0, 4, 5, 4, 4, 4, 4, 3, 9, 11, 10, 9, 7, 5, 4, 17, 19, 14,
+ 10, 9, 7, 6, 5, 5, 6, 6, 6, 5, 3, 6, 7, 7, 7, 8, 6, 3, 6,
+ 8, 9, 9, 9, 7, 3, 6, 10, 9, 9, 10, 9, 4, 8, 14, 14, 14, 14, 12,
+ 6, 11, 17, 19, 17, 16, 14, 8, 16, 21, 21, 19, 17, 16, 10, 13, 16, 16, 16,
+ 13, 8, 3, 14, 18, 17, 17, 14, 9, 5, 14, 19, 20, 18, 15, 10, 6, 14, 19,
+ 20, 19, 17, 13, 8, 14, 20, 22, 21, 18, 16, 10, 17, 21, 26, 23, 21, 17, 12,
+ 20, 23, 26, 25, 21, 19, 14, 22, 26, 25, 20, 16, 11, 6, 21, 26, 26, 21, 17,
+ 13, 8, 22, 26, 28, 23, 19, 14, 9, 21, 26, 28, 23, 19, 16, 12, 21, 25, 28,
+ 25, 20, 18, 14, 22, 26, 29, 28, 23, 19, 16, 23, 26, 30, 28, 24, 21, 17, 13,
+ 16, 18, 20, 19, 17, 16, 11, 14, 17, 19, 20, 17, 15, 10, 14, 17, 18, 18, 15,
+ 13, 8, 11, 14, 16, 17, 14, 12, 6, 9, 12, 13, 15, 13, 12, 5, 8, 11, 13,
+ 15, 14, 12, 11, 11, 10, 13, 14, 14, 12, 10, 13, 16, 18, 17, 16, 13, 9, 12,
+ 16, 18, 17, 16, 13, 7, 11, 14, 16, 16, 14, 11, 6, 9, 12, 14, 13, 12, 10,
+ 4, 7, 10, 12, 12, 12, 10, 7, 8, 9, 11, 11, 11, 10, 14, 12, 10, 10, 10,
+ 10, 10, 7, 11, 13, 14, 15, 13, 9, 6, 10, 12, 13, 13, 12, 8, 5, 8, 11,
+ 11, 11, 11, 7, 3, 6, 7, 7, 8, 8, 5, 3, 5, 6, 6, 6, 7, 5, 9,
+ 9, 9, 7, 5, 5, 5, 16, 15, 11, 8, 5, 5, 4, 5, 5, 6, 7, 8, 8,
+ 5, 3, 4, 5, 6, 6, 6, 4, 2, 2, 3, 4, 4, 4, 3, 0, 0, 0, 0,
+ 0, 0, 0, 4, 4, 4, 4, 4, 3, 2, 9, 11, 9, 8, 6, 5, 4, 16, 18,
+ 12, 9, 8, 6, 5, 5, 5, 6, 6, 7, 6, 5, 7, 7, 7, 7, 8, 6, 4,
+ 7, 8, 8, 8, 8, 6, 3, 7, 10, 9, 9, 9, 7, 4, 8, 14, 13, 13, 13,
+ 11, 5, 11, 17, 18, 17, 15, 12, 7, 15, 20, 21, 19, 17, 15, 9, 14, 15, 15,
+ 16, 13, 8, 5, 14, 17, 17, 16, 14, 9, 4, 14, 20, 19, 18, 14, 9, 5, 14,
+ 19, 21, 19, 15, 11, 7, 14, 20, 24, 23, 19, 14, 9, 17, 23, 29, 28, 22, 16,
+ 11, 20, 25, 30, 30, 24, 19, 13, 22, 28, 27, 21, 16, 11, 7, 22, 28, 29, 23,
+ 16, 12, 7, 22, 28, 32, 24, 17, 12, 8, 22, 28, 33, 25, 20, 15, 11, 21, 27,
+ 33, 30, 24, 19, 13, 23, 30, 35, 34, 27, 21, 15, 25, 31, 35, 36, 29, 23, 17,
+ 13, 16, 20, 23, 24, 21, 18, 11, 15, 19, 23, 23, 19, 16, 10, 14, 17, 21, 21,
+ 19, 15, 9, 11, 15, 18, 20, 16, 14, 6, 9, 12, 15, 18, 16, 13, 6, 8, 11,
+ 14, 17, 16, 14, 11, 11, 11, 13, 16, 16, 13, 10, 14, 17, 20, 20, 17, 14, 9,
+ 13, 16, 20, 20, 16, 13, 7, 11, 15, 18, 17, 15, 11, 6, 9, 12, 14, 14, 13,
+ 10, 4, 7, 10, 13, 13, 13, 10, 7, 7, 9, 11, 12, 12, 10, 14, 12, 10, 11,
+ 11, 11, 10, 8, 11, 13, 14, 15, 14, 9, 6, 10, 12, 13, 14, 13, 8, 5, 8,
+ 11, 10, 11, 11, 7, 4, 6, 7, 7, 8, 8, 5, 4, 5, 6, 6, 6, 6, 5,
+ 9, 9, 8, 7, 5, 5, 5, 16, 14, 10, 7, 5, 4, 4, 5, 6, 6, 7, 8,
+ 9, 6, 3, 4, 5, 6, 6, 6, 5, 2, 3, 3, 4, 3, 4, 3, 0, 0, 0,
+ 0, 0, 0, 0, 4, 5, 4, 4, 4, 3, 2, 9, 11, 9, 7, 6, 5, 3, 16,
+ 17, 12, 9, 7, 6, 5, 6, 6, 6, 6, 7, 6, 6, 7, 7, 7, 7, 8, 6,
+ 5, 7, 8, 8, 8, 8, 6, 3, 7, 10, 9, 9, 9, 7, 4, 9, 14, 13, 13,
+ 13, 10, 5, 11, 17, 19, 17, 15, 12, 7, 15, 19, 22, 19, 17, 14, 9, 14, 15,
+ 15, 15, 12, 9, 6, 14, 18, 17, 16, 13, 9, 4, 14, 19, 19, 18, 14, 9, 5,
+ 14, 19, 20, 19, 15, 11, 7, 14, 20, 25, 23, 19, 14, 9, 17, 23, 29, 28, 21,
+ 16, 10, 20, 26, 30, 30, 24, 18, 12, 22, 27, 26, 20, 15, 11, 7, 22, 28, 29,
+ 22, 16, 12, 7, 21, 28, 31, 23, 17, 12, 7, 22, 27, 31, 24, 19, 15, 11, 21,
+ 28, 33, 29, 24, 18, 12, 24, 29, 35, 34, 27, 20, 14, 26, 31, 36, 36, 29, 22,
+ 16, 13, 17, 21, 24, 24, 21, 18, 12, 15, 19, 23, 24, 20, 16, 10, 14, 17, 21,
+ 22, 18, 15, 9, 12, 15, 18, 21, 17, 14, 6, 9, 12, 15, 19, 17, 14, 6, 8,
+ 11, 14, 18, 17, 14, 11, 10, 10, 13, 16, 16, 14, 10, 14, 18, 22, 20, 18, 14,
+ 9, 13, 17, 20, 20, 16, 12, 8, 11, 15, 18, 17, 14, 11, 7, 9, 12, 14, 14,
+ 13, 9, 5, 7, 10, 12, 13, 12, 10, 7, 8, 9, 11, 12, 12, 10, 13, 11, 10,
+ 11, 11, 11, 10, 8, 11, 13, 14, 15, 14, 10, 6, 10, 12, 13, 14, 12, 8, 5,
+ 9, 10, 11, 10, 11, 6, 4, 7, 7, 7, 7, 7, 5, 4, 5, 6, 6, 6, 6,
+ 5, 10, 9, 8, 7, 5, 5, 5, 15, 13, 10, 7, 5, 4, 4, 5, 6, 6, 7,
+ 8, 9, 6, 4, 4, 5, 6, 6, 6, 4, 2, 3, 4, 3, 3, 3, 2, 0, 0,
+ 0, 0, 0, 0, 0, 4, 5, 4, 4, 4, 3, 2, 9, 11, 9, 7, 6, 5, 3,
+ 15, 16, 12, 9, 7, 6, 5, 6, 6, 6, 6, 7, 6, 6, 7, 7, 7, 7, 8,
+ 6, 4, 7, 8, 8, 8, 8, 6, 2, 7, 10, 9, 9, 9, 7, 4, 9, 14, 14,
+ 12, 13, 10, 5, 12, 17, 19, 17, 15, 12, 7, 15, 20, 22, 19, 17, 14, 9, 14,
+ 16, 15, 15, 12, 8, 6, 15, 18, 17, 17, 14, 9, 4, 15, 19, 19, 18, 14, 9,
+ 5, 14, 19, 20, 19, 15, 10, 7, 15, 21, 24, 23, 19, 14, 9, 17, 23, 30, 27,
+ 21, 16, 11, 20, 25, 30, 29, 24, 18, 12, 22, 27, 26, 20, 15, 11, 7, 22, 27,
+ 27, 21, 16, 11, 7, 22, 27, 30, 23, 17, 12, 7, 22, 27, 30, 24, 19, 15, 11,
+ 22, 26, 32, 29, 23, 18, 13, 24, 29, 34, 34, 26, 20, 14, 26, 31, 35, 35, 28,
+ 22, 16, 13, 17, 20, 24, 25, 22, 19, 12, 16, 19, 24, 24, 21, 16, 10, 14, 18,
+ 21, 23, 19, 15, 9, 12, 15, 19, 21, 17, 14, 7, 10, 12, 16, 19, 16, 14, 6,
+ 9, 11, 15, 18, 18, 14, 9, 9, 10, 13, 17, 17, 14, 11, 14, 18, 21, 21, 18,
+ 14, 9, 13, 17, 20, 20, 17, 13, 8, 11, 15, 17, 18, 15, 11, 7, 9, 12, 15,
+ 14, 13, 9, 5, 8, 10, 13, 13, 13, 10, 7, 7, 9, 12, 12, 12, 10, 10, 9,
+ 9, 11, 11, 11, 10, 8, 12, 13, 15, 16, 14, 10, 7, 10, 12, 13, 14, 12, 8,
+ 5, 9, 11, 11, 10, 10, 6, 4, 7, 8, 7, 7, 8, 5, 4, 5, 7, 6, 6,
+ 6, 5, 9, 8, 7, 6, 5, 5, 5, 11, 10, 8, 6, 4, 4, 4, 6, 6, 6,
+ 7, 9, 9, 6, 4, 4, 5, 6, 6, 7, 4, 2, 3, 4, 4, 3, 3, 2, 0,
+ 0, 0, 0, 0, 0, 0, 4, 5, 4, 4, 4, 3, 2, 9, 9, 8, 6, 6, 5,
+ 3, 12, 13, 10, 8, 7, 6, 5, 6, 6, 6, 6, 7, 6, 6, 7, 7, 7, 8,
+ 8, 6, 4, 7, 9, 8, 8, 8, 6, 2, 7, 11, 9, 9, 9, 7, 3, 9, 15,
+ 13, 13, 13, 10, 5, 12, 16, 17, 16, 14, 12, 7, 13, 18, 19, 18, 16, 13, 8,
+ 14, 15, 14, 13, 12, 8, 6, 15, 17, 15, 14, 12, 9, 4, 15, 18, 17, 15, 12,
+ 9, 5, 14, 18, 18, 15, 14, 11, 7, 15, 20, 22, 20, 17, 13, 8, 17, 22, 25,
+ 22, 18, 15, 10, 18, 23, 26, 24, 21, 16, 12, 20, 22, 20, 15, 13, 10, 7, 20,
+ 23, 22, 17, 14, 11, 7, 20, 23, 23, 17, 14, 11, 7, 20, 23, 24, 19, 16, 14,
+ 10, 20, 23, 27, 24, 20, 16, 12, 22, 26, 29, 29, 22, 18, 14, 24, 27, 29, 29,
+ 24, 19, 15, 12, 15, 18, 21, 20, 18, 15, 11, 13, 17, 20, 20, 17, 13, 10, 13,
+ 15, 18, 18, 15, 12, 9, 11, 13, 15, 18, 14, 11, 7, 9, 11, 14, 17, 14, 11,
+ 6, 8, 10, 12, 16, 14, 11, 8, 9, 10, 12, 14, 14, 11, 11, 13, 16, 18, 17,
+ 15, 11, 10, 12, 14, 17, 16, 13, 10, 8, 11, 14, 15, 14, 11, 8, 8, 9, 11,
+ 13, 12, 10, 7, 6, 8, 10, 11, 11, 10, 7, 5, 7, 9, 11, 10, 10, 7, 10,
+ 9, 9, 10, 9, 9, 7, 8, 11, 12, 12, 13, 11, 8, 8, 10, 11, 11, 11, 10,
+ 6, 7, 9, 10, 9, 8, 8, 5, 5, 8, 7, 6, 6, 6, 4, 3, 5, 6, 5,
+ 5, 5, 4, 7, 7, 7, 6, 5, 4, 4, 11, 11, 8, 6, 5, 3, 3, 7, 6,
+ 6, 6, 7, 8, 6, 4, 5, 5, 6, 6, 5, 4, 2, 3, 4, 4, 3, 3, 2,
+ 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 3, 3, 3, 2, 7, 9, 7, 6, 5,
+ 4, 3, 12, 13, 10, 8, 6, 5, 4, 4, 4, 5, 5, 6, 6, 6, 5, 6, 5,
+ 6, 6, 5, 4, 6, 7, 7, 6, 6, 5, 2, 6, 8, 7, 7, 7, 6, 3, 7,
+ 12, 11, 11, 11, 8, 4, 10, 15, 16, 14, 12, 10, 6, 13, 17, 19, 16, 14, 12,
+ 7, 11, 13, 12, 12, 10, 7, 6, 11, 14, 13, 13, 10, 7, 4, 12, 16, 16, 14,
+ 11, 7, 4, 11, 16, 16, 15, 12, 9, 6, 12, 17, 21, 19, 15, 11, 7, 15, 20,
+ 24, 23, 18, 13, 8, 17, 21, 25, 25, 20, 15, 10, 18, 22, 21, 16, 12, 9, 5,
+ 18, 22, 23, 17, 13, 9, 5, 18, 22, 25, 18, 13, 10, 6, 18, 22, 26, 21, 16,
+ 12, 9, 18, 23, 27, 24, 19, 15, 10, 20, 25, 30, 28, 21, 16, 11, 22, 26, 30,
+ 30, 24, 18, 13, 13, 17, 21, 26, 25, 22, 19, 11, 15, 19, 23, 25, 22, 19, 10,
+ 14, 18, 21, 24, 21, 17, 9, 12, 15, 18, 21, 19, 15, 6, 9, 12, 15, 16, 18,
+ 15, 3, 6, 10, 13, 16, 17, 15, 4, 6, 8, 12, 15, 18, 15, 10, 14, 18, 21,
+ 21, 18, 16, 9, 13, 16, 20, 21, 18, 14, 7, 11, 15, 17, 18, 18, 12, 6, 9,
+ 12, 15, 15, 15, 10, 3, 6, 9, 12, 12, 14, 10, 2, 4, 7, 10, 12, 11, 10,
+ 6, 5, 6, 9, 10, 10, 10, 7, 11, 14, 15, 16, 15, 12, 6, 10, 12, 13, 15,
+ 14, 10, 5, 8, 11, 11, 12, 12, 8, 5, 6, 8, 8, 9, 9, 5, 1, 3, 4,
+ 5, 5, 6, 5, 4, 3, 3, 3, 3, 4, 5, 8, 7, 4, 2, 2, 2, 3, 6,
+ 6, 8, 9, 10, 11, 9, 5, 6, 7, 8, 8, 9, 8, 4, 5, 6, 6, 6, 7,
+ 7, 3, 4, 4, 4, 4, 4, 3, 0, 2, 1, 1, 2, 1, 3, 4, 5, 4, 3,
+ 2, 2, 3, 8, 9, 6, 4, 3, 2, 3, 4, 6, 6, 6, 7, 8, 9, 5, 6,
+ 6, 6, 7, 7, 8, 5, 6, 7, 7, 7, 6, 6, 5, 7, 6, 6, 6, 5, 3,
+ 4, 7, 6, 6, 6, 5, 3, 5, 9, 9, 8, 7, 6, 4, 8, 11, 11, 9, 9,
+ 7, 4, 9, 11, 11, 11, 9, 7, 8, 9, 12, 11, 12, 10, 7, 7, 10, 13, 12,
+ 12, 10, 8, 6, 9, 12, 12, 12, 9, 7, 4, 8, 11, 13, 12, 10, 7, 5, 9,
+ 12, 16, 15, 11, 8, 5, 11, 13, 16, 16, 12, 9, 6, 14, 18, 17, 14, 11, 8,
+ 7, 14, 18, 19, 14, 12, 9, 7, 14, 19, 21, 14, 12, 9, 7, 14, 17, 20, 15,
+ 11, 8, 5, 13, 16, 19, 16, 12, 9, 6, 13, 17, 19, 18, 14, 10, 7, 14, 17,
+ 20, 19, 15, 11, 8, 15, 17, 19, 22, 22, 19, 17, 15, 16, 18, 21, 22, 19, 16,
+ 14, 16, 18, 19, 20, 18, 14, 13, 14, 15, 17, 18, 15, 10, 10, 11, 12, 13, 14,
+ 12, 10, 6, 8, 9, 9, 12, 12, 10, 8, 8, 8, 9, 11, 12, 10, 13, 16, 17,
+ 19, 19, 18, 16, 13, 14, 17, 18, 18, 16, 15, 11, 14, 16, 17, 16, 15, 13, 10,
+ 12, 14, 14, 14, 13, 9, 7, 9, 10, 11, 10, 9, 9, 3, 6, 7, 8, 9, 9,
+ 9, 10, 9, 7, 8, 8, 8, 9, 12, 14, 16, 15, 16, 16, 14, 10, 13, 15, 15,
+ 15, 15, 13, 9, 13, 14, 14, 14, 13, 12, 7, 10, 11, 11, 12, 11, 9, 3, 6,
+ 8, 8, 8, 8, 8, 5, 5, 5, 6, 7, 7, 8, 12, 12, 8, 5, 6, 6, 8,
+ 9, 10, 11, 12, 12, 13, 10, 7, 8, 9, 10, 11, 11, 9, 6, 7, 8, 8, 8,
+ 9, 8, 3, 4, 4, 5, 5, 5, 4, 2, 0, 0, 0, 0, 2, 3, 5, 7, 6,
+ 4, 2, 1, 3, 12, 14, 9, 6, 4, 3, 3, 4, 5, 7, 8, 9, 9, 10, 5,
+ 6, 6, 7, 8, 8, 9, 5, 7, 7, 7, 7, 6, 7, 5, 8, 8, 8, 8, 6,
+ 3, 4, 10, 10, 10, 10, 9, 3, 7, 12, 13, 12, 12, 10, 5, 12, 14, 15, 13,
+ 12, 11, 7, 12, 14, 14, 15, 11, 8, 9, 12, 16, 15, 16, 13, 8, 8, 12, 18,
+ 18, 17, 14, 9, 6, 12, 16, 18, 16, 14, 10, 5, 12, 16, 17, 16, 14, 11, 7,
+ 12, 16, 19, 17, 15, 12, 9, 14, 17, 19, 18, 15, 13, 10, 20, 23, 22, 19, 14,
+ 10, 8, 19, 23, 23, 19, 15, 12, 7, 19, 23, 25, 20, 16, 12, 7, 19, 22, 25,
+ 19, 16, 13, 9, 18, 21, 23, 20, 16, 14, 10, 17, 19, 23, 22, 17, 13, 12, 18,
+ 21, 23, 23, 18, 14, 12, 17, 20, 22, 25, 24, 23, 20, 15, 19, 21, 24, 24, 21,
+ 19, 14, 17, 21, 22, 22, 20, 17, 13, 15, 18, 20, 21, 18, 13, 9, 12, 15, 17,
+ 17, 15, 13, 6, 9, 11, 14, 16, 15, 13, 7, 8, 10, 13, 15, 15, 13, 14, 17,
+ 20, 22, 21, 20, 18, 13, 16, 21, 22, 21, 19, 16, 11, 15, 18, 19, 20, 17, 15,
+ 10, 12, 16, 17, 17, 16, 11, 7, 9, 12, 14, 13, 12, 11, 4, 7, 9, 12, 12,
+ 12, 11, 10, 9, 8, 11, 11, 12, 11, 11, 15, 17, 18, 19, 18, 14, 10, 13, 16,
+ 17, 18, 16, 12, 8, 12, 14, 14, 15, 15, 11, 7, 10, 11, 11, 11, 11, 7, 4,
+ 6, 7, 8, 7, 8, 7, 4, 5, 5, 6, 6, 7, 7, 11, 11, 7, 4, 5, 5,
+ 6, 9, 9, 10, 11, 13, 13, 10, 7, 8, 9, 10, 10, 11, 9, 6, 7, 7, 7,
+ 7, 8, 6, 3, 4, 4, 4, 4, 4, 3, 1, 0, 0, 0, 0, 1, 2, 5, 6,
+ 5, 4, 2, 2, 2, 11, 13, 8, 6, 4, 3, 3, 4, 5, 6, 8, 9, 10, 10,
+ 6, 6, 6, 7, 8, 8, 9, 6, 7, 7, 7, 7, 6, 6, 6, 9, 8, 8, 8,
+ 6, 2, 5, 10, 10, 9, 9, 8, 3, 7, 13, 14, 13, 12, 9, 5, 11, 16, 18,
+ 15, 14, 11, 6, 13, 15, 15, 15, 11, 9, 10, 12, 16, 16, 16, 12, 8, 8, 13,
+ 18, 18, 17, 13, 9, 5, 13, 18, 19, 18, 15, 10, 5, 12, 18, 21, 20, 16, 11,
+ 6, 12, 19, 25, 24, 19, 13, 8, 16, 21, 26, 26, 21, 15, 10, 20, 27, 26, 20,
+ 15, 10, 9, 21, 27, 28, 22, 16, 11, 7, 20, 28, 31, 23, 17, 12, 7, 21, 26,
+ 31, 24, 18, 13, 8, 20, 25, 31, 27, 21, 15, 10, 20, 26, 31, 31, 23, 17, 11,
+ 22, 28, 32, 32, 25, 19, 13, 17, 21, 24, 28, 27, 25, 22, 15, 20, 23, 26, 26,
+ 24, 20, 14, 18, 21, 24, 25, 22, 19, 13, 16, 19, 21, 23, 20, 15, 9, 12, 15,
+ 18, 20, 18, 15, 6, 9, 12, 15, 18, 17, 15, 7, 7, 11, 14, 17, 18, 14, 14,
+ 18, 21, 25, 24, 22, 18, 13, 16, 21, 24, 24, 21, 17, 11, 15, 18, 21, 21, 18,
+ 14, 10, 13, 15, 17, 18, 16, 11, 7, 10, 12, 14, 15, 14, 11, 4, 7, 10, 12,
+ 13, 13, 11, 9, 8, 9, 11, 11, 12, 11, 11, 15, 17, 18, 20, 18, 14, 10, 14,
+ 16, 17, 18, 16, 12, 8, 12, 14, 14, 15, 15, 10, 8, 10, 11, 11, 11, 11, 7,
+ 4, 6, 7, 7, 8, 7, 7, 5, 5, 6, 6, 6, 6, 7, 11, 10, 7, 4, 5,
+ 5, 6, 9, 9, 10, 11, 13, 13, 10, 7, 8, 9, 10, 10, 10, 8, 6, 6, 7,
+ 7, 7, 7, 6, 3, 4, 4, 4, 4, 4, 3, 1, 0, 0, 0, 0, 1, 2, 5,
+ 6, 5, 4, 2, 2, 2, 10, 11, 8, 5, 4, 3, 3, 5, 5, 6, 8, 8, 10,
+ 10, 6, 6, 6, 7, 8, 8, 8, 6, 7, 8, 7, 7, 6, 5, 6, 9, 8, 8,
+ 8, 6, 2, 5, 10, 10, 9, 9, 7, 3, 7, 13, 14, 13, 11, 9, 4, 10, 15,
+ 18, 15, 13, 11, 6, 13, 14, 14, 15, 11, 9, 10, 13, 17, 16, 16, 12, 8, 7,
+ 13, 19, 18, 17, 13, 8, 5, 13, 18, 19, 18, 14, 9, 4, 12, 17, 21, 19, 15,
+ 10, 6, 13, 19, 24, 23, 18, 12, 8, 16, 22, 26, 26, 20, 14, 9, 21, 27, 26,
+ 20, 15, 10, 8, 21, 27, 28, 21, 16, 11, 6, 20, 26, 31, 22, 17, 12, 6, 20,
+ 26, 31, 23, 18, 13, 7, 19, 26, 30, 27, 20, 14, 9, 20, 25, 31, 30, 23, 16,
+ 11, 22, 27, 32, 32, 25, 19, 12, 17, 20, 24, 28, 29, 26, 23, 16, 20, 24, 28,
+ 28, 24, 20, 14, 18, 22, 25, 26, 23, 19, 13, 16, 19, 22, 25, 21, 15, 10, 13,
+ 15, 19, 21, 19, 15, 7, 9, 13, 15, 20, 18, 16, 5, 8, 12, 15, 17, 19, 15,
+ 14, 18, 22, 26, 25, 22, 19, 13, 17, 20, 24, 24, 21, 17, 11, 15, 19, 22, 21,
+ 19, 15, 10, 13, 16, 18, 18, 17, 12, 7, 10, 12, 14, 15, 14, 11, 4, 7, 10,
+ 12, 13, 14, 12, 5, 6, 9, 12, 12, 12, 11, 11, 16, 17, 18, 20, 18, 14, 10,
+ 14, 16, 17, 18, 16, 12, 9, 13, 15, 14, 15, 14, 10, 8, 11, 11, 11, 12, 11,
+ 7, 4, 7, 8, 8, 8, 8, 7, 4, 5, 6, 6, 6, 6, 7, 6, 5, 5, 5,
+ 5, 5, 6, 9, 9, 10, 12, 13, 14, 10, 8, 8, 9, 10, 10, 10, 8, 7, 7,
+ 8, 7, 7, 7, 6, 3, 4, 4, 4, 4, 4, 3, 2, 0, 0, 0, 0, 1, 2,
+ 5, 5, 3, 2, 2, 1, 1, 6, 7, 4, 4, 3, 2, 2, 5, 5, 6, 8, 9,
+ 10, 10, 6, 6, 6, 7, 8, 8, 8, 6, 7, 7, 7, 7, 5, 5, 6, 9, 8,
+ 7, 8, 6, 2, 6, 11, 9, 9, 9, 7, 3, 7, 12, 13, 12, 10, 8, 4, 7,
+ 12, 15, 14, 12, 10, 5, 13, 15, 14, 14, 11, 9, 10, 13, 17, 16, 15, 12, 8,
+ 7, 13, 19, 18, 16, 13, 8, 4, 13, 18, 19, 18, 13, 9, 4, 13, 17, 20, 19,
+ 15, 10, 5, 13, 18, 24, 22, 18, 11, 7, 13, 18, 24, 25, 19, 14, 8, 20, 26,
+ 24, 19, 14, 10, 9, 20, 26, 27, 21, 15, 10, 6, 21, 26, 28, 21, 16, 11, 6,
+ 21, 25, 29, 23, 17, 12, 7, 20, 25, 28, 25, 19, 14, 9, 20, 26, 30, 27, 21,
+ 15, 10, 20, 26, 30, 30, 24, 17, 12, 16, 20, 24, 28, 28, 26, 21, 15, 19, 23,
+ 27, 28, 24, 20, 13, 17, 21, 24, 26, 22, 18, 12, 15, 18, 21, 24, 20, 15, 9,
+ 12, 15, 17, 21, 18, 15, 7, 10, 12, 16, 19, 18, 16, 5, 8, 11, 15, 18, 18,
+ 15, 14, 17, 21, 25, 24, 21, 18, 12, 16, 20, 23, 23, 20, 16, 11, 14, 18, 20,
+ 21, 18, 14, 9, 12, 15, 18, 18, 16, 11, 7, 9, 11, 14, 14, 14, 11, 4, 7,
+ 10, 13, 13, 14, 11, 5, 6, 9, 12, 12, 12, 11, 11, 15, 17, 17, 19, 17, 13,
+ 9, 13, 15, 16, 17, 16, 11, 8, 12, 14, 14, 14, 14, 9, 7, 10, 10, 10, 11,
+ 11, 7, 3, 6, 7, 7, 7, 8, 6, 4, 5, 6, 6, 6, 6, 7, 7, 5, 5,
+ 5, 5, 5, 5, 8, 9, 10, 11, 12, 13, 9, 7, 7, 8, 9, 10, 9, 7, 5,
+ 6, 7, 7, 6, 6, 5, 3, 4, 3, 3, 3, 3, 3, 1, 2, 1, 1, 1, 0,
+ 1, 4, 5, 3, 3, 2, 2, 1, 7, 8, 5, 4, 3, 2, 2, 5, 5, 5, 7,
+ 8, 9, 9, 7, 6, 6, 7, 7, 7, 7, 7, 8, 7, 7, 7, 5, 4, 7, 10,
+ 8, 7, 7, 6, 2, 6, 11, 10, 9, 9, 7, 2, 8, 12, 12, 11, 10, 8, 4,
+ 8, 12, 15, 12, 11, 10, 5, 14, 14, 13, 12, 11, 8, 9, 14, 16, 15, 14, 11,
+ 8, 6, 14, 18, 16, 14, 11, 8, 4, 14, 18, 17, 14, 12, 8, 4, 12, 17, 19,
+ 16, 13, 10, 5, 13, 17, 21, 18, 14, 11, 7, 14, 18, 21, 20, 15, 12, 8, 19,
+ 22, 19, 15, 12, 10, 7, 19, 22, 21, 17, 14, 10, 6, 20, 22, 23, 17, 13, 10,
+ 6, 19, 22, 23, 18, 14, 11, 7, 18, 21, 23, 20, 16, 12, 8, 18, 21, 23, 23,
+ 17, 13, 10, 19, 22, 24, 24, 19, 15, 11, 14, 18, 21, 23, 24, 21, 18, 14, 17,
+ 20, 23, 23, 20, 16, 12, 15, 19, 21, 22, 18, 15, 11, 14, 16, 19, 20, 17, 14,
+ 8, 11, 13, 16, 20, 18, 14, 7, 10, 13, 15, 19, 18, 14, 6, 8, 12, 15, 18,
+ 17, 14, 13, 16, 18, 21, 20, 18, 15, 11, 15, 17, 20, 19, 16, 13, 10, 13, 16,
+ 18, 17, 15, 12, 9, 11, 14, 16, 15, 14, 11, 6, 9, 12, 14, 14, 13, 10, 5,
+ 8, 11, 13, 13, 12, 11, 7, 7, 9, 12, 12, 12, 11, 10, 14, 15, 15, 16, 14,
+ 11, 8, 12, 14, 15, 14, 13, 10, 7, 11, 13, 12, 12, 12, 8, 6, 9, 9, 9,
+ 9, 9, 6, 3, 7, 8, 7, 7, 7, 6, 4, 5, 6, 6, 6, 6, 6, 8, 7,
+ 6, 5, 5, 5, 5, 8, 8, 9, 9, 10, 11, 7, 6, 6, 7, 8, 8, 8, 5,
+ 4, 5, 6, 5, 5, 5, 3, 2, 3, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2,
+ 1, 0, 5, 5, 4, 3, 2, 2, 1, 7, 10, 6, 4, 3, 2, 2, 4, 4, 4,
+ 5, 6, 7, 7, 5, 5, 4, 4, 5, 5, 5, 5, 4, 4, 3, 3, 2, 2, 4,
+ 6, 4, 4, 4, 3, 1, 5, 8, 8, 7, 7, 5, 2, 6, 11, 12, 11, 9, 7,
+ 3, 9, 13, 15, 12, 11, 8, 4, 8, 9, 9, 9, 7, 6, 7, 8, 11, 10, 10,
+ 7, 4, 4, 8, 12, 12, 11, 7, 4, 2, 8, 12, 13, 12, 9, 6, 3, 9, 13,
+ 17, 15, 12, 8, 4, 11, 16, 21, 19, 14, 10, 5, 13, 18, 21, 21, 16, 11, 7,
+ 15, 19, 17, 13, 9, 6, 6, 14, 18, 19, 14, 9, 6, 3, 14, 19, 21, 15, 10,
+ 6, 3, 14, 18, 22, 16, 12, 9, 6, 14, 18, 23, 21, 16, 11, 7, 16, 21, 25,
+ 25, 19, 13, 8, 19, 23, 25, 26, 20, 15, 10, 14, 17, 22, 25, 25, 23, 20, 13,
+ 16, 20, 24, 25, 22, 18, 13, 15, 18, 21, 24, 19, 16, 12, 13, 16, 19, 21, 17,
+ 14, 9, 10, 12, 14, 15, 16, 13, 5, 6, 8, 11, 13, 15, 13, 3, 3, 6, 9,
+ 13, 15, 13, 13, 15, 18, 22, 22, 19, 17, 12, 14, 17, 21, 21, 18, 16, 11, 13,
+ 16, 18, 18, 16, 12, 11, 11, 13, 15, 15, 13, 7, 8, 8, 9, 11, 10, 11, 8,
+ 3, 3, 5, 8, 9, 9, 8, 4, 3, 4, 7, 8, 8, 7, 12, 13, 15, 16, 17,
+ 17, 16, 11, 13, 14, 15, 16, 16, 14, 10, 12, 13, 13, 13, 14, 12, 10, 10, 10,
+ 10, 11, 10, 6, 6, 6, 6, 6, 5, 5, 5, 0, 2, 1, 2, 4, 4, 5, 5,
+ 4, 2, 1, 3, 4, 5, 11, 12, 13, 13, 15, 16, 16, 10, 11, 12, 14, 14, 15,
+ 14, 9, 10, 12, 12, 13, 13, 12, 6, 9, 9, 9, 9, 9, 7, 4, 5, 5, 5,
+ 5, 4, 4, 0, 2, 2, 1, 2, 3, 5, 5, 6, 3, 2, 3, 3, 4, 9, 11,
+ 12, 13, 14, 15, 16, 8, 9, 11, 12, 13, 14, 14, 8, 9, 10, 11, 11, 12, 12,
+ 8, 8, 8, 8, 8, 8, 5, 5, 7, 7, 6, 6, 5, 4, 3, 6, 7, 7, 7,
+ 6, 4, 5, 8, 9, 8, 8, 7, 5, 10, 11, 12, 12, 12, 14, 15, 10, 12, 12,
+ 13, 12, 12, 14, 10, 13, 13, 13, 11, 10, 11, 10, 12, 13, 12, 11, 8, 5, 8,
+ 11, 12, 12, 9, 7, 5, 7, 10, 14, 13, 10, 8, 5, 8, 11, 14, 14, 11, 9,
+ 6, 14, 19, 17, 15, 12, 12, 14, 15, 19, 19, 16, 13, 11, 12, 15, 19, 21, 15,
+ 13, 10, 9, 15, 18, 21, 15, 12, 9, 6, 13, 16, 18, 15, 11, 9, 7, 12, 15,
+ 18, 17, 13, 9, 7, 12, 16, 18, 19, 14, 11, 8, 21, 23, 24, 27, 27, 25, 23,
+ 20, 23, 25, 26, 26, 23, 21, 20, 22, 23, 24, 24, 22, 18, 18, 20, 21, 21, 21,
+ 18, 12, 15, 17, 18, 17, 16, 13, 12, 11, 13, 13, 12, 13, 13, 12, 6, 8, 10,
+ 12, 13, 13, 12, 19, 22, 23, 25, 25, 24, 23, 18, 21, 23, 25, 24, 22, 21, 17,
+ 21, 22, 22, 21, 20, 18, 16, 19, 20, 20, 19, 16, 11, 12, 15, 16, 16, 12, 12,
+ 11, 8, 11, 12, 10, 11, 11, 11, 4, 6, 9, 10, 10, 11, 11, 17, 20, 21, 23,
+ 23, 23, 20, 15, 19, 21, 22, 22, 21, 18, 14, 18, 20, 20, 19, 20, 16, 13, 15,
+ 16, 16, 16, 15, 11, 8, 12, 13, 13, 10, 10, 10, 2, 7, 8, 8, 9, 9, 10,
+ 5, 6, 6, 7, 8, 9, 10, 14, 15, 17, 18, 19, 20, 18, 12, 13, 16, 17, 17,
+ 17, 16, 10, 12, 13, 14, 14, 14, 13, 7, 11, 11, 11, 11, 9, 9, 4, 7, 6,
+ 6, 4, 5, 5, 2, 0, 0, 2, 3, 4, 6, 5, 7, 3, 2, 2, 3, 4, 11,
+ 12, 13, 15, 16, 17, 18, 8, 11, 13, 14, 15, 15, 16, 8, 10, 11, 11, 12, 13,
+ 12, 8, 8, 8, 8, 8, 7, 5, 6, 7, 7, 7, 8, 6, 4, 3, 8, 9, 8,
+ 8, 8, 3, 5, 8, 9, 9, 9, 8, 5, 10, 12, 12, 13, 13, 16, 18, 10, 14,
+ 14, 15, 12, 14, 14, 10, 15, 15, 14, 11, 10, 11, 9, 14, 15, 13, 12, 7, 3,
+ 9, 13, 14, 13, 10, 9, 5, 9, 12, 14, 13, 11, 9, 7, 9, 11, 14, 14, 12,
+ 10, 8, 17, 20, 19, 17, 13, 14, 16, 17, 20, 20, 18, 14, 11, 13, 17, 20, 22,
+ 17, 14, 9, 9, 17, 19, 21, 16, 13, 11, 7, 15, 17, 19, 16, 12, 10, 8, 13,
+ 16, 18, 17, 13, 10, 8, 12, 15, 18, 18, 14, 11, 9, 21, 24, 28, 30, 29, 29,
+ 26, 20, 24, 27, 28, 30, 27, 23, 18, 23, 26, 27, 27, 24, 20, 17, 20, 23, 24,
+ 25, 21, 15, 14, 17, 20, 21, 19, 17, 15, 10, 13, 15, 16, 18, 17, 15, 6, 9,
+ 12, 15, 17, 17, 15, 19, 23, 25, 28, 28, 26, 23, 17, 21, 25, 27, 27, 24, 21,
+ 16, 19, 24, 25, 24, 22, 18, 15, 17, 21, 22, 21, 18, 13, 12, 14, 17, 18, 15,
+ 14, 13, 7, 10, 12, 14, 14, 14, 13, 4, 6, 10, 13, 13, 13, 13, 16, 20, 22,
+ 23, 25, 22, 18, 15, 18, 20, 22, 22, 21, 17, 14, 17, 19, 19, 19, 19, 14, 12,
+ 15, 16, 16, 15, 14, 9, 7, 11, 12, 12, 10, 10, 9, 2, 7, 7, 8, 8, 8,
+ 9, 5, 5, 6, 6, 7, 7, 8, 14, 14, 15, 16, 18, 18, 16, 12, 13, 14, 15,
+ 16, 16, 13, 9, 12, 12, 12, 12, 12, 10, 6, 10, 9, 9, 9, 7, 8, 4, 6,
+ 5, 5, 3, 3, 4, 1, 0, 0, 1, 2, 3, 3, 5, 6, 3, 2, 2, 2, 2,
+ 10, 11, 12, 13, 14, 15, 16, 7, 10, 11, 12, 13, 13, 14, 7, 9, 10, 10, 10,
+ 10, 10, 7, 7, 7, 7, 7, 6, 3, 5, 7, 7, 7, 8, 6, 2, 3, 8, 10,
+ 9, 9, 8, 3, 5, 11, 13, 11, 11, 10, 5, 10, 13, 13, 13, 12, 14, 15, 10,
+ 14, 14, 15, 11, 12, 13, 10, 16, 16, 16, 11, 9, 8, 10, 15, 17, 17, 12, 7,
+ 3, 9, 15, 19, 18, 15, 10, 5, 9, 15, 22, 21, 17, 11, 7, 11, 17, 22, 23,
+ 20, 14, 8, 18, 25, 24, 18, 13, 13, 14, 19, 25, 26, 19, 14, 11, 11, 18, 24,
+ 29, 21, 15, 10, 7, 18, 25, 29, 22, 17, 11, 6, 18, 23, 28, 25, 19, 13, 8,
+ 17, 23, 28, 27, 21, 15, 10, 18, 23, 28, 29, 22, 18, 12, 20, 24, 27, 31, 31,
+ 28, 26, 19, 23, 27, 30, 30, 27, 23, 18, 21, 25, 27, 29, 26, 21, 16, 19, 22,
+ 25, 26, 23, 17, 13, 16, 18, 21, 22, 19, 16, 10, 12, 15, 17, 20, 19, 16, 6,
+ 9, 12, 16, 18, 19, 16, 18, 21, 25, 28, 28, 26, 22, 16, 20, 24, 27, 27, 23,
+ 19, 15, 18, 22, 24, 24, 22, 17, 14, 16, 19, 22, 21, 19, 13, 11, 13, 16, 18,
+ 16, 15, 13, 7, 9, 12, 14, 14, 14, 12, 4, 7, 10, 13, 13, 14, 12, 15, 18,
+ 21, 22, 24, 21, 18, 14, 17, 20, 21, 21, 19, 16, 13, 16, 18, 18, 18, 17, 13,
+ 11, 13, 15, 14, 15, 14, 8, 7, 10, 11, 11, 10, 9, 8, 2, 6, 7, 7, 8,
+ 8, 8, 3, 5, 6, 6, 7, 7, 7, 13, 13, 14, 15, 16, 17, 14, 11, 11, 13,
+ 14, 14, 14, 11, 8, 10, 11, 11, 11, 10, 9, 5, 8, 7, 7, 7, 6, 6, 3,
+ 4, 4, 4, 2, 2, 3, 1, 2, 1, 0, 1, 2, 3, 3, 4, 2, 2, 2, 2,
+ 2, 9, 9, 10, 11, 12, 14, 13, 6, 8, 9, 10, 11, 11, 11, 6, 8, 8, 8,
+ 9, 9, 8, 6, 7, 6, 6, 6, 5, 3, 4, 8, 8, 8, 8, 6, 2, 3, 9,
+ 10, 10, 9, 7, 3, 5, 9, 12, 11, 11, 9, 5, 11, 13, 13, 13, 11, 12, 13,
+ 11, 15, 15, 15, 11, 10, 10, 11, 16, 16, 16, 11, 8, 7, 11, 16, 17, 16, 12,
+ 8, 3, 10, 15, 19, 17, 14, 9, 5, 10, 15, 22, 21, 16, 10, 6, 11, 16, 21,
+ 23, 18, 13, 8, 19, 25, 24, 18, 13, 11, 12, 19, 25, 27, 20, 14, 9, 9, 18,
+ 25, 29, 21, 15, 10, 6, 19, 24, 29, 22, 16, 11, 6, 18, 24, 29, 24, 18, 13,
+ 8, 18, 23, 29, 27, 20, 14, 9, 18, 24, 30, 29, 22, 16, 11, 19, 23, 26, 31,
+ 30, 27, 24, 17, 22, 26, 29, 30, 27, 22, 16, 20, 23, 27, 28, 25, 21, 15, 17,
+ 21, 24, 27, 23, 17, 12, 15, 17, 20, 22, 20, 17, 8, 11, 13, 17, 21, 20, 16,
+ 6, 9, 13, 16, 19, 20, 17, 16, 20, 24, 28, 27, 25, 21, 15, 19, 22, 26, 27,
+ 23, 19, 14, 18, 21, 24, 23, 21, 17, 12, 15, 18, 20, 20, 18, 13, 9, 12, 14,
+ 17, 16, 16, 13, 6, 9, 12, 14, 14, 15, 12, 4, 7, 10, 13, 13, 14, 13, 14,
+ 18, 19, 20, 22, 20, 17, 12, 15, 19, 19, 20, 19, 14, 11, 15, 17, 16, 16, 17,
+ 12, 10, 13, 13, 13, 13, 13, 8, 6, 9, 10, 10, 9, 9, 8, 3, 6, 7, 7,
+ 8, 8, 8, 4, 5, 6, 6, 7, 7, 7, 11, 12, 12, 14, 15, 16, 12, 10, 10,
+ 11, 12, 13, 13, 10, 8, 9, 10, 9, 9, 9, 7, 4, 7, 6, 6, 6, 6, 5,
+ 2, 2, 2, 2, 2, 2, 3, 2, 4, 2, 1, 0, 1, 2, 4, 6, 3, 2, 2,
+ 1, 2, 6, 7, 8, 9, 10, 11, 12, 5, 6, 7, 9, 9, 9, 10, 5, 6, 7,
+ 6, 6, 7, 6, 5, 7, 6, 6, 6, 5, 3, 4, 9, 8, 7, 7, 5, 2, 5,
+ 9, 10, 9, 9, 7, 3, 6, 10, 12, 11, 10, 9, 4, 11, 13, 13, 13, 10, 10,
+ 11, 12, 15, 14, 14, 11, 8, 9, 11, 17, 16, 15, 11, 7, 5, 11, 16, 17, 16,
+ 12, 8, 3, 11, 16, 19, 18, 14, 9, 4, 11, 16, 22, 20, 15, 10, 6, 11, 16,
+ 22, 22, 17, 12, 7, 19, 25, 23, 17, 13, 9, 10, 19, 25, 25, 19, 14, 9, 7,
+ 20, 25, 27, 20, 15, 10, 5, 19, 24, 28, 22, 16, 11, 6, 18, 24, 28, 23, 17,
+ 12, 7, 18, 23, 27, 25, 20, 14, 9, 18, 23, 27, 28, 22, 16, 10, 18, 22, 26,
+ 29, 29, 26, 23, 16, 20, 24, 29, 29, 25, 21, 15, 19, 23, 25, 28, 23, 19, 14,
+ 16, 19, 23, 25, 22, 16, 10, 13, 16, 19, 23, 20, 16, 8, 11, 14, 17, 21, 20,
+ 17, 7, 10, 13, 16, 19, 20, 16, 15, 19, 23, 26, 26, 23, 19, 14, 17, 22, 25,
+ 25, 21, 18, 12, 16, 20, 22, 23, 20, 16, 11, 14, 16, 19, 19, 17, 13, 8, 11,
+ 13, 16, 16, 15, 13, 6, 9, 12, 14, 14, 14, 13, 5, 7, 11, 13, 13, 13, 13,
+ 13, 16, 18, 19, 21, 19, 15, 11, 15, 17, 18, 19, 17, 13, 10, 13, 16, 15, 16,
+ 15, 11, 9, 11, 12, 12, 12, 12, 8, 5, 8, 9, 9, 9, 9, 8, 3, 6, 7,
+ 8, 7, 7, 8, 5, 5, 6, 6, 7, 7, 7, 10, 10, 11, 12, 14, 14, 10, 9,
+ 9, 10, 11, 11, 11, 8, 7, 8, 8, 8, 8, 8, 6, 3, 5, 5, 5, 5, 5,
+ 4, 2, 1, 2, 2, 1, 2, 2, 3, 4, 3, 2, 1, 0, 1, 5, 7, 4, 3,
+ 2, 2, 2, 4, 4, 6, 7, 8, 9, 10, 5, 5, 5, 7, 7, 7, 8, 5, 6,
+ 6, 6, 5, 5, 5, 5, 8, 7, 6, 6, 5, 1, 5, 9, 8, 7, 8, 5, 2,
+ 6, 10, 11, 10, 9, 7, 3, 7, 10, 13, 11, 11, 9, 4, 12, 13, 12, 12, 10,
+ 8, 9, 12, 15, 14, 13, 11, 7, 7, 12, 17, 16, 13, 11, 6, 4, 13, 16, 16,
+ 14, 12, 7, 3, 11, 16, 18, 15, 13, 9, 4, 11, 16, 20, 18, 14, 10, 6, 12,
+ 16, 21, 20, 15, 12, 7, 18, 21, 19, 15, 12, 9, 8, 18, 21, 20, 16, 13, 9,
+ 5, 18, 21, 23, 16, 13, 9, 5, 18, 21, 23, 17, 14, 10, 6, 18, 20, 23, 20,
+ 15, 12, 8, 18, 20, 23, 21, 17, 13, 9, 18, 21, 23, 23, 18, 13, 10, 17, 20,
+ 24, 27, 27, 24, 21, 16, 19, 23, 26, 27, 23, 20, 14, 18, 21, 24, 26, 22, 18,
+ 13, 16, 18, 21, 24, 21, 17, 10, 12, 15, 18, 22, 20, 16, 8, 11, 14, 17, 21,
+ 20, 16, 7, 10, 13, 16, 20, 19, 17, 14, 17, 21, 24, 23, 21, 18, 13, 17, 20,
+ 23, 23, 20, 16, 11, 15, 19, 21, 20, 18, 14, 10, 13, 16, 19, 17, 16, 12, 7,
+ 11, 13, 15, 16, 15, 12, 6, 9, 12, 14, 15, 14, 12, 6, 8, 11, 13, 13, 13,
+ 12, 12, 15, 17, 17, 19, 17, 13, 10, 13, 16, 17, 17, 16, 11, 9, 12, 14, 14,
+ 14, 14, 10, 8, 10, 11, 11, 11, 11, 7, 5, 8, 9, 9, 8, 8, 7, 5, 7,
+ 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 9, 9, 10, 11, 12, 13, 8,
+ 8, 8, 9, 9, 9, 10, 6, 6, 7, 7, 7, 6, 6, 4, 3, 4, 4, 3, 3,
+ 3, 3, 3, 3, 2, 2, 1, 1, 1, 5, 5, 4, 2, 2, 1, 0, 7, 7, 4,
+ 3, 2, 2, 2, 3, 3, 4, 5, 6, 8, 8, 4, 4, 4, 5, 5, 5, 6, 4,
+ 5, 4, 3, 3, 3, 3, 4, 6, 4, 3, 3, 3, 0, 5, 6, 5, 4, 4, 3,
+ 2, 6, 8, 9, 7, 6, 4, 2, 7, 10, 11, 8, 7, 5, 2, 6, 6, 6, 6,
+ 5, 7, 7, 6, 8, 7, 7, 5, 4, 5, 6, 9, 8, 7, 4, 2, 2, 6, 8,
+ 9, 8, 5, 3, 2, 7, 10, 13, 12, 9, 5, 2, 8, 12, 17, 15, 11, 7, 3,
+ 10, 14, 18, 17, 12, 8, 4, 11, 15, 14, 9, 6, 6, 6, 10, 15, 16, 10, 6,
+ 3, 3, 10, 15, 18, 11, 7, 3, 2, 10, 14, 18, 12, 8, 5, 3, 10, 15, 20,
+ 17, 12, 8, 4, 12, 17, 22, 20, 15, 10, 5, 14, 20, 23, 22, 17, 11, 7, 20,
+ 21, 23, 26, 26, 25, 23, 19, 21, 23, 25, 26, 23, 21, 18, 20, 21, 23, 23, 19,
+ 16, 18, 18, 19, 19, 20, 15, 11, 15, 15, 15, 14, 14, 14, 10, 11, 11, 8, 10,
+ 12, 14, 10, 5, 3, 5, 8, 11, 14, 10, 19, 20, 22, 23, 24, 23, 22, 18, 20,
+ 21, 24, 24, 21, 19, 18, 19, 21, 21, 20, 19, 16, 17, 17, 18, 18, 16, 13, 8,
+ 14, 14, 14, 13, 9, 9, 8, 9, 9, 7, 6, 8, 9, 9, 2, 2, 4, 5, 7,
+ 8, 8, 18, 20, 20, 21, 23, 23, 22, 17, 18, 20, 21, 22, 21, 19, 17, 18, 19,
+ 20, 19, 18, 16, 16, 17, 16, 16, 15, 12, 9, 11, 12, 12, 11, 7, 8, 8, 4,
+ 7, 6, 4, 6, 8, 8, 0, 2, 2, 4, 6, 6, 8, 17, 18, 19, 21, 23, 24,
+ 23, 16, 18, 20, 20, 21, 21, 20, 14, 18, 19, 19, 19, 18, 17, 10, 17, 16, 16,
+ 15, 12, 12, 7, 12, 11, 11, 6, 7, 8, 5, 5, 5, 3, 4, 5, 6, 0, 3,
+ 3, 3, 4, 5, 6, 15, 17, 19, 20, 22, 23, 23, 12, 16, 18, 19, 20, 20, 21,
+ 12, 16, 17, 17, 17, 17, 15, 12, 13, 13, 13, 12, 10, 7, 10, 9, 8, 8, 7,
+ 6, 6, 6, 7, 7, 6, 7, 7, 5, 4, 6, 8, 7, 7, 8, 6, 13, 14, 15,
+ 16, 19, 22, 23, 13, 14, 14, 16, 17, 18, 19, 13, 15, 15, 14, 14, 15, 14, 13,
+ 13, 13, 13, 12, 9, 6, 11, 12, 13, 12, 9, 8, 6, 8, 10, 13, 13, 10, 8,
+ 7, 7, 10, 13, 14, 11, 8, 7, 16, 19, 18, 16, 17, 20, 21, 16, 18, 19, 17,
+ 16, 17, 18, 16, 18, 20, 16, 14, 13, 13, 16, 17, 20, 15, 12, 10, 7, 14, 16,
+ 18, 16, 11, 8, 7, 12, 15, 17, 17, 12, 10, 8, 11, 15, 18, 18, 14, 10, 8,
+ 27, 30, 31, 32, 32, 31, 30, 26, 29, 31, 32, 32, 29, 27, 25, 28, 29, 30, 29,
+ 26, 23, 24, 26, 27, 27, 25, 21, 15, 21, 24, 23, 21, 18, 16, 15, 17, 19, 16,
+ 15, 17, 17, 15, 11, 11, 13, 15, 16, 16, 15, 25, 28, 30, 31, 30, 30, 29, 24,
+ 27, 30, 31, 30, 29, 26, 22, 26, 29, 28, 27, 25, 22, 22, 24, 25, 25, 23, 20,
+ 14, 18, 20, 22, 20, 15, 15, 14, 13, 16, 15, 14, 14, 14, 14, 7, 9, 12, 13,
+ 13, 14, 14, 23, 26, 27, 29, 30, 29, 26, 21, 25, 27, 29, 29, 27, 23, 20, 24,
+ 26, 26, 25, 24, 20, 18, 21, 22, 22, 21, 18, 14, 13, 17, 18, 17, 14, 14, 13,
+ 6, 13, 12, 10, 12, 12, 13, 3, 7, 8, 10, 11, 11, 13, 20, 21, 22, 24, 25,
+ 26, 25, 18, 20, 22, 23, 23, 22, 21, 15, 20, 20, 21, 20, 19, 18, 11, 19, 17,
+ 17, 16, 12, 13, 9, 14, 13, 11, 7, 8, 9, 6, 7, 6, 4, 6, 7, 7, 3,
+ 0, 3, 4, 5, 6, 8, 16, 19, 20, 21, 22, 24, 24, 12, 18, 20, 21, 21, 21,
+ 21, 12, 18, 18, 18, 18, 17, 16, 12, 14, 15, 14, 13, 10, 8, 10, 8, 9, 8,
+ 5, 4, 7, 6, 5, 5, 5, 5, 4, 6, 3, 3, 5, 5, 6, 5, 5, 13, 14,
+ 16, 17, 20, 22, 24, 13, 13, 14, 16, 18, 19, 20, 13, 13, 13, 14, 15, 16, 14,
+ 13, 12, 12, 12, 10, 9, 6, 10, 10, 11, 9, 7, 5, 5, 7, 8, 10, 9, 7,
+ 6, 4, 5, 7, 10, 10, 8, 7, 4, 14, 17, 16, 15, 18, 21, 22, 14, 16, 17,
+ 15, 16, 18, 18, 14, 17, 18, 14, 13, 13, 13, 14, 15, 17, 13, 11, 8, 4, 12,
+ 13, 15, 12, 8, 6, 5, 10, 12, 14, 13, 9, 7, 5, 8, 11, 14, 14, 10, 8,
+ 6, 25, 28, 31, 33, 33, 32, 29, 23, 27, 30, 32, 33, 30, 26, 21, 26, 29, 30,
+ 30, 27, 23, 21, 23, 26, 27, 28, 22, 17, 17, 20, 22, 23, 21, 19, 16, 13, 16,
+ 17, 17, 20, 19, 17, 8, 11, 14, 17, 19, 19, 16, 22, 25, 29, 31, 31, 29, 27,
+ 20, 25, 28, 30, 29, 28, 24, 19, 23, 26, 28, 27, 25, 21, 18, 21, 24, 25, 23,
+ 20, 15, 15, 17, 19, 20, 17, 16, 15, 11, 13, 15, 15, 15, 16, 15, 5, 9, 12,
+ 14, 15, 15, 15, 20, 23, 26, 27, 28, 26, 22, 18, 22, 24, 25, 26, 24, 20, 17,
+ 21, 23, 22, 22, 22, 17, 15, 18, 19, 19, 19, 17, 11, 9, 14, 15, 15, 12, 12,
+ 12, 3, 10, 11, 10, 10, 11, 11, 3, 6, 8, 8, 9, 9, 10, 17, 18, 18, 19,
+ 21, 22, 19, 14, 16, 17, 18, 19, 19, 16, 11, 15, 16, 16, 15, 15, 13, 8, 13,
+ 12, 12, 11, 10, 10, 6, 9, 8, 8, 5, 5, 6, 3, 3, 3, 2, 3, 4, 4,
+ 3, 3, 0, 2, 3, 4, 5, 12, 14, 15, 16, 17, 18, 19, 8, 13, 14, 15, 16,
+ 16, 16, 8, 12, 13, 13, 13, 13, 11, 8, 10, 10, 10, 9, 7, 4, 6, 6, 5,
+ 5, 6, 4, 4, 4, 6, 8, 8, 8, 6, 3, 2, 7, 10, 10, 10, 8, 3, 9,
+ 11, 11, 13, 15, 17, 18, 9, 12, 12, 13, 13, 15, 16, 9, 14, 14, 14, 11, 11,
+ 10, 9, 13, 15, 15, 11, 7, 3, 7, 12, 17, 17, 13, 8, 3, 7, 13, 19, 19,
+ 15, 10, 5, 8, 14, 20, 20, 17, 12, 6, 16, 22, 22, 16, 13, 16, 17, 16, 23,
+ 24, 18, 12, 13, 14, 17, 23, 26, 19, 13, 9, 10, 17, 22, 26, 20, 14, 9, 5,
+ 15, 21, 25, 23, 17, 12, 6, 15, 21, 25, 23, 18, 14, 8, 16, 21, 24, 24, 20,
+ 15, 10, 23, 26, 30, 33, 32, 30, 27, 21, 25, 29, 32, 32, 29, 26, 19, 24, 27,
+ 31, 31, 27, 22, 18, 21, 24, 27, 28, 24, 18, 15, 18, 20, 23, 23, 21, 18, 11,
+ 14, 17, 18, 22, 21, 18, 8, 11, 14, 17, 20, 21, 18, 19, 23, 27, 31, 29, 27,
+ 25, 18, 22, 26, 30, 29, 25, 22, 17, 20, 25, 26, 26, 24, 19, 16, 19, 22, 24,
+ 23, 20, 15, 13, 15, 18, 19, 18, 17, 14, 9, 11, 14, 16, 16, 16, 14, 5, 8,
+ 12, 15, 15, 15, 14, 17, 21, 23, 24, 26, 23, 20, 16, 19, 22, 23, 23, 21, 18,
+ 15, 18, 20, 20, 20, 20, 15, 13, 16, 16, 17, 17, 15, 10, 8, 12, 13, 13, 12,
+ 11, 10, 3, 8, 9, 9, 9, 10, 10, 3, 6, 7, 8, 8, 9, 8, 15, 15, 16,
+ 17, 19, 19, 15, 13, 14, 15, 16, 17, 17, 13, 10, 12, 13, 13, 13, 13, 10, 6,
+ 10, 9, 9, 9, 8, 8, 4, 6, 6, 6, 4, 3, 4, 2, 2, 2, 2, 2, 3,
+ 3, 3, 4, 2, 0, 2, 2, 3, 9, 10, 11, 12, 13, 15, 15, 6, 10, 11, 12,
+ 13, 13, 13, 6, 9, 9, 9, 10, 9, 9, 6, 7, 6, 6, 6, 5, 3, 4, 6,
+ 6, 6, 6, 4, 3, 2, 7, 9, 8, 8, 6, 2, 4, 8, 11, 10, 9, 8, 3,
+ 9, 11, 11, 12, 12, 14, 15, 9, 13, 13, 13, 11, 11, 11, 9, 15, 15, 14, 10,
+ 8, 8, 9, 14, 15, 15, 11, 6, 2, 8, 13, 18, 16, 12, 8, 3, 8, 14, 20,
+ 19, 14, 9, 5, 9, 14, 20, 21, 16, 11, 6, 17, 23, 22, 17, 11, 12, 13, 17,
+ 23, 24, 18, 12, 10, 10, 17, 23, 27, 19, 13, 8, 7, 17, 22, 27, 20, 15, 9,
+ 5, 16, 21, 27, 22, 16, 11, 6, 16, 22, 27, 25, 18, 13, 8, 16, 22, 27, 27,
+ 21, 15, 9, 20, 25, 28, 32, 32, 30, 27, 20, 23, 27, 31, 32, 27, 24, 17, 22,
+ 25, 29, 30, 26, 22, 17, 19, 22, 25, 28, 24, 18, 14, 16, 19, 21, 24, 22, 18,
+ 10, 12, 15, 18, 22, 21, 18, 8, 10, 14, 17, 20, 21, 18, 18, 22, 26, 29, 29,
+ 25, 22, 17, 20, 23, 27, 28, 24, 20, 15, 19, 23, 25, 25, 22, 19, 14, 17, 20,
+ 22, 22, 20, 14, 11, 14, 15, 18, 18, 17, 14, 7, 10, 13, 15, 16, 17, 14, 5,
+ 8, 12, 14, 15, 15, 14, 16, 19, 20, 22, 24, 22, 18, 14, 17, 20, 21, 22, 20,
+ 15, 13, 17, 18, 18, 19, 17, 13, 12, 15, 15, 14, 15, 14, 10, 8, 11, 11, 11,
+ 11, 10, 9, 3, 8, 9, 9, 9, 9, 9, 4, 6, 7, 7, 8, 8, 8, 13, 14,
+ 14, 15, 17, 18, 13, 12, 12, 13, 14, 14, 14, 11, 9, 11, 11, 11, 11, 11, 8,
+ 5, 9, 8, 7, 7, 7, 6, 3, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2,
+ 2, 2, 4, 5, 3, 2, 0, 1, 2, 7, 7, 8, 9, 11, 12, 12, 5, 7, 8,
+ 9, 9, 10, 10, 5, 6, 7, 7, 7, 7, 7, 5, 6, 5, 5, 5, 4, 2, 3,
+ 7, 6, 6, 6, 4, 2, 3, 8, 9, 8, 8, 6, 2, 4, 8, 11, 10, 10, 7,
+ 3, 10, 12, 12, 11, 9, 11, 12, 10, 14, 13, 13, 10, 8, 9, 10, 16, 15, 14,
+ 10, 6, 6, 10, 15, 16, 15, 11, 6, 2, 9, 14, 18, 17, 12, 8, 3, 9, 15,
+ 20, 19, 14, 9, 5, 10, 15, 20, 21, 16, 11, 6, 18, 24, 23, 16, 12, 10, 10,
+ 18, 23, 24, 18, 13, 8, 8, 18, 24, 26, 19, 13, 8, 5, 18, 23, 28, 21, 15,
+ 10, 4, 17, 22, 27, 23, 16, 11, 6, 17, 22, 27, 26, 18, 13, 8, 17, 22, 27,
+ 27, 21, 15, 9, 20, 24, 27, 31, 30, 28, 25, 18, 22, 25, 29, 30, 27, 23, 16,
+ 20, 24, 27, 28, 25, 21, 15, 18, 21, 24, 27, 23, 18, 12, 15, 17, 21, 24, 21,
+ 18, 9, 12, 15, 18, 22, 21, 18, 8, 11, 14, 17, 21, 21, 18, 17, 20, 24, 28,
+ 28, 25, 21, 15, 20, 23, 26, 27, 23, 19, 14, 17, 21, 24, 24, 21, 17, 13, 15,
+ 19, 20, 20, 19, 14, 10, 12, 15, 17, 17, 17, 14, 7, 10, 13, 15, 16, 16, 14,
+ 6, 9, 12, 15, 14, 14, 14, 14, 17, 20, 21, 22, 20, 16, 13, 16, 18, 19, 20,
+ 19, 14, 12, 15, 17, 17, 17, 16, 12, 11, 13, 13, 13, 14, 13, 9, 7, 9, 10,
+ 10, 10, 10, 9, 4, 8, 9, 9, 9, 9, 9, 5, 7, 7, 7, 8, 8, 8, 12,
+ 12, 13, 14, 15, 16, 11, 10, 11, 11, 12, 13, 13, 9, 9, 9, 10, 10, 9, 9,
+ 7, 5, 7, 6, 6, 6, 6, 5, 2, 3, 3, 3, 2, 3, 3, 3, 3, 2, 2,
+ 1, 1, 2, 5, 6, 4, 3, 1, 0, 1, 5, 5, 6, 8, 8, 10, 11, 4, 5,
+ 6, 7, 8, 8, 9, 4, 5, 5, 5, 5, 5, 5, 4, 6, 5, 5, 5, 3, 2,
+ 3, 8, 7, 6, 6, 4, 1, 4, 9, 10, 8, 8, 6, 2, 5, 9, 12, 10, 10,
+ 8, 3, 10, 12, 11, 11, 9, 9, 10, 10, 14, 13, 12, 10, 7, 7, 10, 16, 14,
+ 13, 10, 5, 4, 10, 15, 15, 14, 10, 6, 2, 10, 14, 17, 15, 12, 8, 3, 10,
+ 15, 19, 17, 13, 9, 5, 10, 15, 20, 19, 15, 11, 6, 17, 21, 19, 15, 11, 8,
+ 8, 17, 21, 20, 16, 12, 8, 6, 18, 21, 22, 16, 12, 8, 4, 17, 20, 22, 17,
+ 13, 9, 5, 17, 20, 23, 20, 15, 11, 6, 16, 20, 23, 21, 16, 13, 8, 16, 20,
+ 23, 23, 18, 13, 9, 19, 23, 26, 29, 30, 28, 24, 17, 21, 24, 29, 29, 26, 22,
+ 16, 20, 23, 27, 27, 24, 20, 14, 17, 20, 24, 26, 22, 18, 11, 14, 16, 20, 23,
+ 21, 18, 10, 13, 15, 19, 22, 21, 18, 8, 11, 15, 18, 21, 21, 18, 16, 20, 23,
+ 27, 26, 23, 20, 14, 19, 22, 26, 25, 22, 18, 13, 17, 20, 23, 23, 20, 16, 12,
+ 15, 17, 21, 20, 18, 14, 9, 12, 14, 17, 17, 17, 14, 8, 11, 13, 16, 16, 16,
+ 14, 6, 9, 12, 15, 15, 15, 14, 13, 17, 19, 20, 21, 19, 15, 12, 16, 18, 19,
+ 19, 18, 13, 11, 14, 16, 16, 16, 16, 11, 10, 12, 13, 12, 13, 13, 9, 6, 9,
+ 10, 10, 10, 10, 9, 5, 8, 9, 9, 9, 9, 9, 6, 7, 8, 8, 8, 8, 8,
+ 11, 11, 12, 13, 14, 15, 9, 9, 9, 10, 11, 12, 11, 7, 8, 8, 9, 9, 8,
+ 8, 6, 4, 6, 5, 5, 5, 5, 4, 3, 3, 3, 3, 3, 3, 2, 4, 4, 3,
+ 2, 2, 2, 2, 6, 8, 5, 3, 2, 1, 0, 2, 3, 5, 6, 7, 8, 9, 2,
+ 3, 4, 5, 6, 6, 7, 2, 3, 3, 3, 3, 4, 4, 2, 5, 3, 2, 2, 2,
+ 1, 3, 7, 5, 4, 4, 3, 0, 5, 8, 7, 5, 3, 3, 2, 7, 8, 8, 6,
+ 5, 3, 2, 6, 6, 5, 5, 6, 7, 9, 6, 8, 5, 5, 5, 5, 6, 6, 8,
+ 6, 5, 3, 3, 3, 6, 7, 7, 5, 4, 3, 1, 7, 9, 10, 8, 6, 3, 2,
+ 8, 10, 13, 11, 7, 4, 3, 9, 11, 14, 14, 9, 5, 3, 8, 11, 10, 6, 5,
+ 7, 7, 8, 11, 11, 7, 4, 4, 4, 8, 11, 13, 7, 4, 3, 2, 8, 11, 14,
+ 9, 6, 4, 3, 9, 11, 15, 13, 8, 5, 3, 9, 13, 18, 16, 11, 6, 3, 11,
+ 15, 18, 17, 12, 7, 4, 14, 17, 20, 24, 23, 22, 19, 13, 16, 19, 23, 23, 21,
+ 17, 12, 14, 17, 20, 21, 18, 16, 11, 13, 15, 17, 20, 18, 15, 11, 13, 14, 15,
+ 19, 17, 15, 9, 11, 13, 15, 17, 17, 15, 12, 12, 12, 14, 16, 17, 15, 12, 15,
+ 17, 20, 21, 19, 16, 11, 13, 16, 19, 21, 18, 14, 11, 13, 15, 17, 18, 16, 13,
+ 9, 12, 14, 15, 15, 14, 13, 9, 11, 13, 14, 14, 14, 13, 7, 10, 11, 13, 13,
+ 13, 13, 14, 14, 11, 12, 12, 12, 12, 10, 13, 14, 15, 16, 15, 12, 9, 12, 13,
+ 13, 15, 14, 11, 8, 11, 12, 12, 12, 13, 11, 7, 10, 11, 11, 12, 11, 11, 6,
+ 9, 11, 11, 11, 11, 11, 9, 10, 10, 9, 10, 11, 11, 15, 16, 12, 9, 9, 10,
+ 11, 8, 8, 9, 10, 10, 11, 4, 7, 7, 8, 8, 8, 9, 5, 6, 6, 6, 7,
+ 7, 7, 4, 4, 5, 5, 6, 6, 6, 4, 4, 4, 4, 5, 5, 5, 4, 9, 11,
+ 10, 8, 6, 3, 3, 15, 16, 12, 9, 7, 4, 2, 0, 1, 1, 2, 2, 2, 4,
+ 2, 2, 2, 2, 2, 1, 5, 2, 3, 3, 3, 4, 2, 4, 2, 6, 5, 6, 6,
+ 5, 2, 4, 9, 8, 7, 7, 7, 3, 9, 11, 11, 9, 9, 8, 4, 14, 14, 13,
+ 10, 9, 8, 6, 8, 8, 8, 8, 7, 2, 3, 8, 10, 9, 9, 7, 3, 2, 7,
+ 11, 10, 9, 8, 5, 0, 8, 10, 11, 10, 8, 7, 4, 8, 11, 14, 13, 11, 8,
+ 6, 10, 13, 17, 15, 12, 9, 7, 12, 15, 17, 16, 13, 10, 8, 12, 16, 14, 11,
+ 8, 6, 2, 12, 16, 16, 12, 9, 6, 2, 12, 16, 18, 12, 9, 7, 4, 12, 15,
+ 19, 14, 11, 8, 7, 12, 16, 19, 17, 13, 10, 7, 13, 17, 20, 19, 15, 11, 8,
+ 15, 18, 20, 21, 16, 12, 9, 15, 17, 19, 20, 21, 18, 17, 14, 16, 18, 20, 20,
+ 19, 17, 12, 15, 18, 19, 20, 18, 17, 11, 14, 17, 18, 20, 19, 17, 11, 14, 17,
+ 19, 21, 19, 17, 10, 14, 16, 18, 20, 19, 17, 13, 13, 15, 18, 20, 19, 17, 13,
+ 15, 18, 19, 18, 16, 14, 12, 15, 17, 19, 18, 16, 14, 11, 14, 16, 17, 17, 16,
+ 14, 9, 13, 16, 17, 17, 17, 15, 9, 12, 15, 17, 17, 17, 15, 8, 11, 14, 16,
+ 16, 16, 15, 16, 14, 13, 15, 15, 15, 15, 10, 14, 14, 15, 15, 13, 10, 9, 12,
+ 14, 14, 14, 13, 10, 8, 11, 12, 13, 13, 13, 10, 7, 11, 12, 12, 13, 13, 11,
+ 7, 10, 11, 12, 12, 12, 11, 10, 11, 10, 11, 11, 11, 11, 17, 17, 12, 10, 10,
+ 10, 10, 8, 8, 8, 9, 9, 9, 3, 7, 7, 7, 7, 8, 8, 3, 6, 6, 6,
+ 6, 6, 6, 4, 4, 5, 5, 6, 6, 6, 4, 6, 6, 5, 5, 5, 5, 4, 10,
+ 12, 10, 9, 6, 5, 3, 17, 19, 14, 10, 7, 5, 3, 1, 0, 1, 1, 2, 2,
+ 3, 2, 2, 1, 2, 2, 1, 3, 2, 3, 3, 3, 3, 1, 3, 2, 7, 6, 6,
+ 6, 5, 3, 5, 11, 10, 10, 11, 8, 3, 10, 14, 16, 15, 13, 10, 5, 16, 19,
+ 20, 16, 15, 12, 7, 9, 10, 10, 10, 7, 2, 2, 8, 12, 12, 11, 8, 3, 2,
+ 8, 14, 14, 14, 9, 5, 1, 8, 15, 17, 17, 13, 9, 5, 11, 17, 22, 21, 17,
+ 12, 7, 14, 20, 26, 25, 20, 15, 9, 18, 23, 27, 25, 21, 17, 11, 17, 22, 21,
+ 15, 10, 5, 1, 16, 22, 24, 17, 12, 6, 2, 16, 22, 25, 18, 13, 9, 4, 16,
+ 22, 27, 22, 17, 13, 9, 18, 24, 29, 26, 21, 17, 11, 20, 26, 30, 29, 23, 18,
+ 13, 23, 28, 30, 30, 25, 21, 15, 14, 17, 20, 22, 23, 20, 18, 13, 16, 19, 22,
+ 23, 20, 18, 12, 15, 18, 21, 23, 20, 18, 11, 14, 17, 21, 23, 22, 19, 12, 14,
+ 18, 20, 24, 23, 19, 11, 14, 17, 20, 23, 22, 19, 14, 14, 16, 19, 22, 21, 19,
+ 12, 15, 18, 21, 19, 17, 14, 11, 14, 18, 19, 19, 16, 14, 10, 13, 16, 19, 18,
+ 17, 15, 9, 13, 16, 18, 18, 18, 16, 10, 13, 15, 18, 18, 18, 15, 10, 11, 15,
+ 17, 18, 18, 16, 17, 16, 14, 16, 17, 16, 16, 10, 13, 15, 15, 15, 13, 9, 9,
+ 12, 13, 13, 14, 12, 9, 8, 11, 12, 12, 12, 13, 10, 8, 11, 12, 12, 12, 13,
+ 11, 7, 10, 12, 12, 12, 12, 11, 12, 12, 11, 11, 11, 11, 11, 19, 18, 14, 10,
+ 10, 10, 10, 8, 8, 8, 8, 8, 8, 2, 7, 7, 7, 7, 7, 7, 2, 6, 6,
+ 6, 6, 6, 6, 3, 4, 6, 6, 6, 6, 6, 4, 6, 7, 6, 6, 6, 5, 4,
+ 12, 13, 12, 10, 8, 6, 4, 19, 20, 15, 11, 8, 6, 5, 1, 1, 0, 1, 1,
+ 1, 2, 2, 2, 2, 1, 2, 1, 2, 2, 4, 3, 4, 4, 2, 3, 2, 7, 7,
+ 7, 7, 6, 4, 6, 12, 12, 11, 11, 9, 5, 11, 15, 17, 16, 14, 11, 6, 18,
+ 21, 20, 17, 16, 13, 7, 9, 11, 9, 9, 6, 2, 2, 9, 13, 11, 11, 7, 3,
+ 1, 9, 14, 14, 14, 10, 6, 2, 9, 16, 18, 17, 14, 10, 6, 12, 18, 23, 21,
+ 18, 13, 7, 15, 21, 28, 26, 20, 15, 9, 19, 25, 29, 27, 23, 17, 11, 16, 22,
+ 21, 15, 9, 5, 1, 17, 22, 23, 16, 11, 6, 2, 16, 22, 26, 19, 13, 9, 5,
+ 16, 23, 29, 23, 18, 14, 10, 18, 25, 31, 28, 22, 17, 11, 22, 28, 33, 33, 26,
+ 19, 13, 25, 30, 34, 34, 28, 21, 15, 14, 17, 21, 23, 24, 20, 18, 14, 16, 19,
+ 23, 24, 21, 18, 12, 15, 18, 22, 23, 22, 19, 11, 15, 18, 22, 25, 23, 20, 12,
+ 15, 18, 22, 26, 23, 20, 11, 14, 17, 21, 25, 23, 20, 16, 15, 17, 20, 23, 22,
+ 19, 12, 15, 18, 20, 20, 17, 14, 12, 14, 17, 20, 19, 17, 14, 10, 13, 17, 19,
+ 19, 18, 15, 9, 13, 17, 20, 19, 18, 16, 10, 13, 16, 18, 19, 19, 16, 11, 12,
+ 15, 18, 18, 18, 16, 18, 17, 15, 17, 17, 17, 16, 10, 13, 14, 15, 15, 12, 9,
+ 9, 12, 13, 13, 14, 12, 9, 9, 11, 13, 13, 13, 13, 10, 8, 11, 13, 13, 13,
+ 14, 11, 7, 10, 12, 12, 12, 12, 11, 13, 13, 13, 12, 12, 11, 11, 20, 19, 15,
+ 12, 10, 11, 10, 9, 8, 8, 8, 8, 8, 2, 8, 7, 7, 7, 7, 6, 2, 6,
+ 6, 6, 7, 6, 6, 3, 4, 6, 6, 6, 6, 6, 5, 6, 8, 8, 8, 8, 6,
+ 5, 13, 15, 13, 11, 9, 7, 5, 21, 21, 16, 12, 9, 7, 6, 2, 1, 1, 0,
+ 1, 1, 2, 3, 2, 2, 2, 2, 0, 2, 2, 4, 4, 4, 4, 3, 3, 2, 8,
+ 8, 8, 8, 7, 5, 7, 13, 12, 12, 13, 10, 6, 13, 17, 18, 17, 14, 12, 7,
+ 19, 22, 22, 19, 17, 14, 8, 9, 11, 9, 9, 6, 2, 2, 10, 12, 11, 11, 7,
+ 3, 2, 10, 15, 14, 14, 11, 6, 3, 9, 17, 18, 18, 15, 10, 7, 13, 19, 24,
+ 22, 18, 13, 8, 16, 22, 29, 27, 21, 16, 10, 21, 26, 30, 30, 25, 17, 12, 17,
+ 22, 20, 14, 9, 5, 1, 17, 22, 23, 16, 11, 6, 3, 17, 22, 25, 19, 14, 10,
+ 6, 17, 24, 29, 24, 18, 14, 10, 20, 26, 31, 29, 23, 18, 12, 23, 30, 35, 33,
+ 26, 19, 14, 25, 32, 35, 36, 28, 22, 16, 15, 18, 21, 24, 24, 21, 18, 14, 17,
+ 19, 23, 24, 22, 19, 12, 15, 19, 23, 25, 23, 20, 11, 16, 19, 22, 26, 24, 20,
+ 12, 16, 19, 23, 26, 24, 21, 12, 15, 18, 22, 26, 24, 21, 17, 17, 18, 20, 24,
+ 24, 21, 12, 16, 19, 22, 20, 17, 14, 12, 15, 18, 20, 20, 17, 15, 10, 14, 17,
+ 20, 20, 19, 16, 10, 14, 17, 20, 20, 19, 16, 10, 14, 17, 19, 20, 20, 17, 12,
+ 14, 16, 19, 19, 19, 17, 20, 18, 16, 18, 18, 18, 17, 10, 14, 15, 14, 15, 13,
+ 9, 10, 12, 13, 14, 14, 13, 10, 9, 12, 13, 14, 14, 14, 11, 9, 12, 14, 14,
+ 14, 14, 12, 8, 11, 13, 13, 13, 13, 12, 14, 14, 14, 13, 13, 12, 12, 22, 20,
+ 16, 12, 11, 11, 10, 9, 8, 8, 8, 8, 8, 2, 9, 7, 7, 7, 7, 7, 3,
+ 6, 6, 6, 7, 7, 7, 4, 6, 6, 7, 7, 7, 7, 6, 7, 8, 9, 9, 9,
+ 8, 6, 14, 16, 14, 12, 10, 8, 6, 22, 22, 17, 13, 10, 8, 7, 2, 2, 1,
+ 1, 0, 1, 2, 3, 2, 2, 2, 2, 1, 3, 3, 4, 4, 5, 5, 4, 3, 3,
+ 8, 8, 9, 9, 7, 6, 7, 14, 13, 13, 13, 11, 7, 14, 18, 19, 18, 15, 12,
+ 8, 20, 23, 23, 19, 18, 14, 9, 10, 10, 9, 8, 5, 2, 2, 10, 12, 10, 9,
+ 7, 4, 2, 10, 13, 12, 13, 10, 7, 4, 10, 15, 17, 16, 15, 11, 7, 13, 19,
+ 23, 21, 18, 14, 9, 17, 23, 28, 26, 20, 16, 11, 21, 27, 29, 28, 23, 18, 13,
+ 15, 17, 14, 10, 8, 4, 1, 15, 17, 15, 11, 9, 7, 3, 15, 17, 18, 15, 13,
+ 11, 7, 15, 19, 23, 20, 17, 15, 11, 18, 23, 27, 25, 22, 18, 13, 23, 27, 31,
+ 30, 24, 19, 15, 27, 30, 32, 32, 26, 21, 17, 13, 16, 20, 23, 23, 20, 17, 13,
+ 16, 18, 22, 23, 21, 19, 12, 14, 18, 22, 25, 22, 19, 11, 15, 19, 22, 26, 23,
+ 20, 12, 15, 18, 21, 25, 23, 20, 12, 15, 18, 21, 25, 23, 20, 18, 18, 17, 20,
+ 23, 23, 20, 12, 15, 17, 20, 18, 15, 13, 11, 14, 17, 19, 19, 17, 14, 10, 13,
+ 16, 19, 20, 17, 15, 9, 12, 16, 20, 19, 18, 16, 9, 13, 16, 19, 20, 19, 16,
+ 13, 14, 16, 18, 18, 18, 15, 21, 19, 17, 17, 17, 17, 16, 10, 13, 13, 13, 14,
+ 11, 8, 9, 12, 12, 12, 13, 12, 9, 8, 11, 12, 13, 13, 13, 10, 7, 11, 12,
+ 13, 13, 13, 11, 7, 11, 12, 13, 13, 13, 11, 15, 16, 15, 13, 12, 12, 11, 23,
+ 22, 17, 14, 11, 10, 10, 8, 7, 7, 7, 7, 7, 2, 7, 6, 6, 6, 6, 6,
+ 2, 4, 5, 5, 6, 6, 6, 4, 8, 5, 6, 6, 6, 7, 6, 8, 9, 10, 10,
+ 10, 9, 7, 15, 17, 16, 14, 11, 9, 8, 23, 24, 18, 15, 12, 10, 8, 2, 2,
+ 2, 1, 1, 0, 2, 4, 2, 2, 2, 2, 2, 2, 4, 3, 3, 4, 5, 5, 4,
+ 4, 7, 7, 8, 8, 8, 8, 8, 13, 13, 13, 13, 11, 8, 15, 19, 19, 17, 14,
+ 12, 9, 22, 24, 22, 18, 15, 13, 10, 6, 6, 4, 3, 3, 2, 1, 6, 7, 5,
+ 4, 3, 3, 2, 6, 9, 6, 7, 6, 6, 6, 6, 11, 12, 12, 11, 10, 9, 11,
+ 15, 19, 17, 15, 13, 10, 17, 21, 25, 22, 17, 13, 11, 22, 25, 26, 24, 19, 15,
+ 12, 10, 12, 8, 5, 3, 2, 2, 10, 12, 10, 6, 4, 4, 4, 10, 12, 13, 9,
+ 9, 8, 7, 10, 14, 18, 16, 13, 12, 10, 14, 18, 23, 22, 18, 15, 12, 19, 23,
+ 27, 27, 21, 16, 13, 24, 27, 28, 29, 23, 18, 13, 9, 11, 13, 15, 16, 13, 11,
+ 9, 11, 12, 15, 16, 14, 12, 8, 10, 12, 16, 18, 15, 13, 7, 10, 13, 16, 19,
+ 17, 15, 8, 11, 14, 17, 20, 17, 14, 11, 13, 15, 16, 19, 17, 14, 18, 18, 16,
+ 16, 18, 17, 15, 8, 10, 11, 14, 11, 9, 7, 7, 9, 11, 13, 12, 10, 8, 7,
+ 8, 11, 14, 13, 11, 10, 6, 9, 12, 14, 13, 12, 11, 6, 10, 12, 14, 14, 13,
+ 11, 13, 14, 15, 16, 14, 13, 11, 21, 19, 17, 15, 13, 12, 11, 7, 9, 8, 7,
+ 7, 5, 3, 6, 8, 8, 7, 7, 7, 5, 5, 7, 7, 8, 8, 8, 6, 3, 7,
+ 8, 9, 9, 9, 8, 7, 10, 10, 10, 10, 9, 8, 16, 16, 15, 13, 11, 9, 8,
+ 23, 22, 18, 14, 12, 10, 8, 5, 4, 3, 3, 3, 2, 0, 3, 2, 3, 2, 2,
+ 3, 2, 6, 2, 2, 3, 3, 3, 4, 10, 3, 5, 6, 6, 6, 6, 9, 10, 10,
+ 10, 10, 9, 7, 16, 18, 16, 14, 12, 10, 8, 23, 24, 19, 15, 12, 11, 9, 4,
+ 3, 2, 2, 2, 2, 0, 6, 4, 3, 2, 2, 3, 2, 6, 4, 4, 5, 5, 5,
+ 5, 6, 8, 8, 9, 10, 9, 8, 9, 15, 15, 15, 15, 12, 9, 15, 21, 21, 19,
+ 16, 14, 10, 22, 25, 25, 21, 18, 15, 11, 8, 7, 5, 5, 3, 2, 1, 8, 10,
+ 7, 6, 4, 4, 3, 8, 11, 10, 11, 9, 7, 6, 8, 13, 16, 16, 15, 12, 10,
+ 12, 19, 23, 22, 19, 16, 11, 18, 24, 30, 27, 22, 16, 13, 23, 28, 31, 30, 24,
+ 18, 14, 12, 16, 14, 8, 5, 3, 2, 13, 16, 16, 10, 6, 5, 4, 12, 16, 19,
+ 15, 12, 10, 8, 13, 18, 25, 21, 18, 15, 12, 17, 23, 29, 28, 23, 19, 14, 22,
+ 27, 33, 33, 26, 20, 16, 27, 32, 34, 34, 28, 23, 17, 16, 19, 23, 26, 26, 25,
+ 22, 15, 19, 22, 25, 26, 23, 21, 14, 17, 20, 23, 24, 22, 19, 13, 15, 17, 20,
+ 22, 20, 18, 12, 14, 16, 18, 21, 19, 18, 10, 12, 15, 17, 20, 20, 18, 10, 11,
+ 13, 16, 19, 20, 18, 14, 17, 20, 24, 24, 23, 18, 13, 16, 19, 22, 24, 22, 17,
+ 12, 14, 18, 20, 20, 18, 15, 11, 13, 15, 17, 18, 16, 14, 10, 12, 14, 15, 16,
+ 17, 14, 8, 11, 13, 14, 15, 15, 15, 11, 11, 12, 13, 14, 15, 14, 12, 14, 16,
+ 17, 19, 19, 14, 11, 13, 15, 16, 16, 17, 13, 10, 12, 14, 14, 15, 15, 12, 9,
+ 12, 12, 12, 12, 13, 12, 7, 10, 12, 12, 11, 12, 12, 8, 9, 10, 10, 11, 11,
+ 12, 12, 12, 10, 9, 10, 11, 12, 10, 10, 11, 11, 12, 14, 6, 8, 9, 9, 10,
+ 10, 11, 6, 7, 7, 8, 8, 9, 9, 5, 4, 6, 7, 7, 7, 7, 6, 5, 5,
+ 5, 6, 6, 7, 4, 8, 8, 7, 6, 5, 5, 4, 12, 12, 8, 6, 5, 4, 2,
+ 2, 2, 2, 2, 3, 4, 6, 0, 1, 1, 2, 1, 2, 5, 0, 2, 2, 1, 1,
+ 1, 4, 0, 2, 2, 1, 1, 1, 4, 2, 4, 3, 3, 4, 2, 2, 5, 7, 7,
+ 6, 5, 3, 2, 9, 10, 9, 7, 6, 4, 2, 4, 5, 5, 5, 3, 2, 4, 4,
+ 6, 6, 6, 3, 1, 3, 4, 7, 7, 6, 4, 1, 2, 4, 7, 8, 7, 5, 3,
+ 2, 4, 8, 10, 9, 7, 4, 2, 6, 10, 14, 11, 9, 6, 2, 9, 12, 14, 13,
+ 10, 6, 3, 9, 12, 11, 8, 5, 2, 2, 9, 12, 12, 8, 5, 2, 1, 9, 12,
+ 14, 9, 6, 3, 1, 9, 12, 15, 10, 7, 5, 2, 9, 12, 15, 13, 10, 7, 4,
+ 10, 13, 17, 16, 11, 8, 5, 12, 15, 16, 17, 13, 9, 6, 16, 19, 20, 22, 22,
+ 20, 18, 15, 18, 20, 21, 22, 20, 17, 14, 16, 18, 21, 22, 19, 18, 13, 15, 19,
+ 20, 22, 20, 18, 13, 16, 18, 20, 22, 21, 19, 11, 15, 18, 19, 21, 20, 18, 12,
+ 13, 17, 19, 20, 20, 18, 14, 17, 19, 21, 19, 18, 16, 13, 16, 18, 20, 19, 18,
+ 16, 12, 15, 17, 19, 18, 17, 16, 11, 14, 17, 18, 17, 18, 16, 11, 14, 17, 19,
+ 18, 18, 17, 9, 12, 15, 17, 18, 18, 16, 15, 14, 14, 16, 16, 17, 16, 12, 15,
+ 16, 16, 16, 15, 12, 11, 14, 15, 15, 15, 15, 12, 10, 13, 14, 14, 15, 15, 12,
+ 9, 12, 14, 13, 14, 14, 12, 8, 11, 13, 13, 13, 13, 12, 10, 10, 11, 12, 13,
+ 13, 12, 17, 16, 12, 10, 11, 11, 12, 10, 9, 10, 10, 10, 11, 4, 9, 8, 9,
+ 9, 9, 9, 4, 7, 7, 7, 8, 8, 8, 5, 5, 7, 7, 7, 7, 7, 6, 6,
+ 6, 6, 6, 6, 6, 5, 10, 11, 10, 9, 6, 5, 4, 17, 18, 13, 10, 7, 5,
+ 3, 2, 2, 2, 2, 2, 2, 4, 1, 0, 1, 1, 1, 2, 4, 1, 2, 2, 2,
+ 1, 0, 4, 1, 5, 5, 5, 5, 4, 3, 4, 9, 9, 9, 9, 7, 3, 9, 13,
+ 15, 14, 12, 9, 4, 16, 18, 18, 15, 14, 11, 5, 7, 9, 8, 9, 5, 1, 3,
+ 7, 11, 10, 10, 7, 2, 3, 7, 13, 13, 12, 8, 3, 2, 7, 13, 15, 15, 12,
+ 7, 4, 9, 16, 19, 18, 16, 11, 5, 12, 18, 22, 20, 17, 13, 7, 17, 21, 23,
+ 22, 18, 15, 9, 15, 20, 18, 14, 9, 4, 2, 15, 19, 20, 15, 11, 5, 1, 15,
+ 19, 21, 16, 12, 7, 3, 15, 20, 23, 19, 16, 12, 7, 16, 21, 24, 22, 18, 15,
+ 9, 18, 22, 25, 24, 20, 16, 12, 21, 23, 26, 25, 21, 18, 13, 16, 19, 21, 24,
+ 24, 22, 19, 15, 18, 20, 23, 25, 21, 19, 13, 17, 20, 23, 24, 21, 20, 13, 16,
+ 19, 22, 24, 22, 20, 13, 16, 19, 22, 25, 23, 20, 12, 15, 18, 21, 24, 23, 21,
+ 13, 14, 17, 20, 23, 24, 21, 13, 17, 20, 22, 21, 18, 16, 12, 16, 18, 21, 20,
+ 18, 15, 12, 14, 17, 20, 20, 18, 16, 10, 14, 17, 21, 19, 19, 17, 11, 14, 17,
+ 19, 20, 20, 17, 9, 13, 16, 18, 19, 19, 17, 16, 15, 15, 18, 17, 18, 17, 11,
+ 14, 16, 16, 16, 14, 11, 10, 13, 15, 15, 15, 14, 11, 10, 12, 14, 13, 14, 14,
+ 11, 9, 12, 14, 14, 14, 14, 12, 8, 11, 13, 13, 13, 13, 12, 11, 11, 12, 12,
+ 12, 12, 12, 18, 17, 13, 11, 11, 11, 11, 10, 9, 9, 9, 10, 9, 3, 9, 8,
+ 8, 8, 8, 8, 3, 8, 7, 7, 7, 7, 7, 3, 5, 7, 7, 7, 7, 7, 5,
+ 6, 6, 6, 6, 6, 6, 4, 11, 13, 11, 9, 7, 5, 4, 18, 20, 14, 10, 8,
+ 6, 4, 2, 1, 2, 2, 2, 2, 3, 1, 1, 0, 1, 1, 1, 3, 1, 2, 2,
+ 2, 2, 1, 3, 1, 6, 6, 6, 6, 5, 4, 5, 10, 10, 10, 10, 8, 4, 11,
+ 14, 16, 14, 12, 9, 5, 17, 19, 19, 16, 14, 12, 6, 7, 9, 8, 8, 5, 1,
+ 2, 7, 11, 10, 10, 6, 2, 2, 7, 13, 12, 12, 8, 4, 2, 8, 14, 16, 16,
+ 12, 8, 5, 10, 17, 21, 20, 16, 11, 6, 13, 20, 26, 25, 19, 13, 8, 17, 23,
+ 26, 27, 22, 16, 9, 15, 20, 19, 13, 8, 4, 1, 16, 21, 22, 15, 10, 5, 0,
+ 15, 20, 24, 17, 12, 8, 4, 15, 22, 27, 21, 16, 12, 8, 17, 24, 29, 27, 21,
+ 15, 10, 20, 26, 31, 32, 24, 18, 12, 23, 29, 33, 33, 26, 19, 14, 16, 19, 22,
+ 25, 25, 22, 20, 15, 18, 21, 25, 25, 22, 19, 13, 16, 20, 23, 26, 22, 21, 13,
+ 16, 19, 23, 26, 24, 21, 13, 16, 19, 22, 26, 25, 21, 12, 16, 19, 22, 25, 24,
+ 22, 15, 15, 17, 21, 25, 25, 21, 14, 16, 20, 22, 21, 19, 15, 13, 15, 19, 21,
+ 21, 19, 15, 12, 14, 17, 20, 21, 19, 17, 11, 15, 18, 21, 21, 20, 17, 12, 14,
+ 17, 20, 20, 21, 17, 11, 13, 16, 19, 20, 19, 17, 18, 16, 15, 18, 18, 18, 17,
+ 12, 14, 16, 16, 17, 14, 11, 11, 13, 15, 15, 15, 14, 10, 10, 12, 14, 15, 14,
+ 15, 11, 9, 13, 14, 14, 15, 15, 12, 9, 12, 13, 14, 14, 14, 12, 12, 13, 12,
+ 12, 12, 13, 12, 19, 18, 14, 11, 12, 12, 11, 10, 9, 9, 9, 9, 9, 2, 9,
+ 8, 8, 8, 8, 8, 2, 8, 7, 7, 8, 7, 8, 3, 5, 7, 7, 7, 7, 7,
+ 6, 7, 7, 7, 7, 7, 7, 4, 13, 14, 13, 11, 8, 6, 5, 19, 21, 16, 12,
+ 9, 7, 5, 2, 2, 1, 2, 2, 2, 2, 2, 1, 1, 0, 1, 1, 3, 2, 2,
+ 2, 3, 3, 2, 3, 2, 6, 6, 6, 7, 5, 5, 6, 12, 11, 11, 11, 8, 5,
+ 12, 16, 17, 15, 13, 10, 6, 18, 21, 20, 17, 15, 12, 7, 8, 9, 8, 8, 5,
+ 1, 2, 8, 11, 10, 9, 6, 2, 2, 8, 13, 13, 13, 9, 5, 3, 8, 15, 17,
+ 17, 13, 9, 6, 11, 18, 22, 21, 17, 12, 7, 14, 20, 26, 26, 19, 14, 9, 19,
+ 24, 28, 28, 22, 16, 10, 15, 21, 19, 13, 8, 3, 1, 15, 20, 21, 15, 9, 5,
+ 1, 15, 21, 24, 18, 13, 9, 5, 15, 22, 28, 23, 17, 13, 9, 18, 24, 30, 27,
+ 21, 16, 11, 20, 27, 32, 32, 24, 18, 12, 24, 30, 34, 34, 27, 20, 14, 16, 19,
+ 23, 26, 26, 23, 20, 15, 18, 21, 25, 26, 23, 20, 14, 17, 20, 23, 25, 24, 22,
+ 13, 17, 20, 24, 26, 25, 22, 14, 17, 20, 24, 27, 25, 21, 12, 16, 18, 23, 27,
+ 25, 22, 16, 16, 18, 22, 25, 25, 22, 14, 17, 20, 23, 22, 19, 16, 13, 16, 19,
+ 22, 22, 18, 16, 12, 15, 18, 21, 20, 20, 17, 11, 15, 18, 22, 21, 20, 17, 12,
+ 15, 18, 20, 21, 21, 17, 11, 14, 17, 20, 20, 20, 18, 18, 17, 16, 19, 18, 19,
+ 17, 12, 15, 15, 16, 17, 15, 11, 11, 14, 15, 15, 15, 14, 11, 10, 13, 14, 15,
+ 15, 15, 12, 10, 13, 15, 15, 15, 15, 12, 9, 13, 14, 14, 14, 14, 12, 13, 14,
+ 14, 13, 13, 13, 12, 20, 19, 15, 12, 12, 12, 12, 10, 9, 9, 9, 10, 10, 3,
+ 9, 8, 8, 8, 8, 8, 3, 8, 8, 8, 8, 8, 8, 3, 5, 7, 8, 8, 8,
+ 8, 6, 7, 8, 8, 8, 8, 7, 4, 13, 15, 13, 11, 9, 7, 5, 20, 21, 16,
+ 12, 10, 8, 6, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, 2, 3, 1,
+ 2, 3, 3, 3, 2, 3, 1, 6, 7, 7, 7, 6, 5, 6, 11, 11, 11, 11, 8,
+ 6, 12, 17, 18, 16, 13, 10, 7, 19, 21, 21, 17, 16, 12, 7, 8, 9, 8, 7,
+ 4, 1, 2, 8, 11, 9, 8, 6, 2, 2, 8, 12, 11, 12, 9, 5, 3, 8, 14,
+ 16, 15, 13, 9, 6, 11, 17, 21, 20, 18, 13, 7, 15, 21, 27, 25, 19, 14, 9,
+ 20, 24, 28, 26, 21, 16, 11, 14, 16, 14, 10, 7, 3, 1, 13, 16, 15, 11, 9,
+ 5, 2, 14, 16, 18, 14, 12, 9, 5, 14, 18, 23, 19, 16, 12, 9, 17, 22, 25,
+ 24, 20, 16, 11, 21, 25, 29, 29, 23, 18, 13, 24, 29, 31, 30, 25, 19, 14, 15,
+ 18, 21, 26, 25, 22, 18, 14, 17, 20, 24, 25, 22, 19, 13, 16, 19, 23, 24, 23,
+ 19, 12, 15, 19, 23, 25, 23, 20, 13, 15, 19, 22, 26, 24, 21, 11, 14, 17, 21,
+ 25, 24, 20, 16, 15, 16, 20, 23, 23, 21, 13, 16, 18, 22, 21, 19, 15, 12, 15,
+ 17, 21, 21, 17, 14, 11, 14, 17, 20, 19, 18, 15, 10, 13, 17, 20, 20, 19, 16,
+ 10, 14, 16, 19, 19, 19, 16, 11, 13, 16, 18, 18, 18, 16, 18, 17, 16, 17, 16,
+ 17, 16, 11, 14, 15, 15, 16, 14, 10, 10, 12, 13, 13, 14, 13, 9, 9, 11, 13,
+ 14, 13, 13, 10, 8, 12, 13, 13, 13, 13, 11, 7, 11, 12, 12, 12, 13, 11, 14,
+ 14, 13, 12, 11, 11, 11, 20, 19, 15, 12, 10, 10, 10, 9, 8, 8, 8, 9, 9,
+ 3, 8, 7, 7, 7, 7, 7, 2, 6, 6, 6, 7, 6, 6, 2, 5, 6, 6, 6,
+ 6, 6, 5, 7, 8, 8, 8, 8, 7, 5, 14, 16, 13, 11, 9, 7, 5, 21, 22,
+ 16, 12, 10, 8, 6, 1, 1, 1, 0, 1, 2, 3, 2, 2, 1, 1, 2, 0, 2,
+ 2, 3, 3, 4, 3, 3, 3, 2, 6, 6, 6, 6, 6, 5, 7, 12, 11, 10, 10,
+ 8, 6, 13, 16, 17, 14, 11, 9, 7, 19, 21, 20, 15, 13, 10, 7, 6, 6, 4,
+ 3, 3, 2, 2, 6, 7, 5, 4, 3, 2, 2, 6, 9, 6, 6, 4, 4, 3, 6,
+ 10, 10, 9, 8, 7, 7, 9, 13, 16, 14, 13, 10, 8, 14, 19, 22, 19, 14, 11,
+ 9, 19, 22, 23, 21, 16, 12, 9, 10, 11, 8, 5, 3, 2, 1, 10, 12, 10, 5,
+ 4, 3, 2, 10, 12, 12, 8, 7, 5, 5, 10, 13, 16, 13, 10, 9, 8, 12, 15,
+ 20, 19, 15, 12, 9, 16, 20, 24, 24, 17, 13, 10, 20, 24, 25, 25, 19, 14, 11,
+ 9, 11, 14, 17, 17, 14, 11, 9, 11, 13, 16, 17, 13, 11, 8, 10, 12, 15, 17,
+ 15, 12, 8, 10, 13, 16, 18, 16, 14, 8, 11, 13, 16, 19, 16, 13, 10, 11, 13,
+ 15, 18, 17, 13, 16, 15, 14, 15, 17, 17, 13, 8, 10, 12, 14, 13, 11, 7, 8,
+ 9, 11, 13, 13, 9, 7, 7, 8, 11, 13, 12, 10, 8, 6, 9, 12, 14, 13, 12,
+ 9, 6, 9, 11, 13, 13, 12, 10, 12, 12, 13, 13, 12, 12, 10, 19, 17, 14, 13,
+ 11, 11, 10, 7, 9, 8, 8, 9, 7, 4, 6, 8, 8, 7, 7, 6, 4, 5, 8,
+ 7, 8, 7, 7, 5, 4, 7, 8, 8, 7, 7, 6, 7, 9, 9, 8, 8, 8, 6,
+ 14, 14, 13, 11, 9, 7, 6, 21, 19, 15, 12, 9, 7, 6, 5, 4, 3, 3, 3,
+ 4, 2, 3, 3, 3, 3, 2, 2, 0, 3, 2, 2, 3, 2, 2, 2, 7, 3, 4,
+ 5, 4, 4, 4, 8, 9, 9, 8, 8, 7, 5, 14, 16, 14, 12, 10, 8, 6, 21,
+ 22, 16, 13, 10, 9, 7, 5, 3, 2, 2, 3, 2, 2, 5, 4, 3, 2, 3, 2,
+ 0, 5, 5, 4, 4, 4, 4, 3, 5, 8, 8, 8, 8, 8, 6, 9, 14, 14, 13,
+ 13, 10, 7, 14, 18, 19, 18, 14, 11, 8, 20, 22, 23, 19, 16, 13, 9, 9, 9,
+ 7, 6, 4, 3, 2, 8, 10, 8, 7, 5, 3, 2, 9, 12, 11, 11, 9, 6, 4,
+ 9, 14, 16, 16, 13, 10, 8, 12, 18, 22, 20, 18, 14, 9, 16, 22, 28, 26, 20,
+ 15, 10, 21, 26, 29, 28, 22, 17, 12, 14, 18, 15, 10, 6, 3, 2, 13, 17, 18,
+ 12, 8, 5, 3, 13, 17, 20, 15, 12, 9, 6, 14, 19, 25, 21, 16, 14, 10, 18,
+ 23, 28, 26, 21, 17, 12, 21, 27, 32, 32, 25, 19, 14, 26, 30, 32, 33, 26, 21,
+ 15, 16, 19, 23, 26, 26, 25, 23, 15, 18, 21, 25, 26, 23, 21, 14, 17, 20, 23,
+ 24, 22, 19, 13, 15, 17, 20, 22, 20, 18, 12, 14, 15, 18, 21, 20, 18, 11, 12,
+ 14, 17, 20, 20, 17, 10, 11, 13, 16, 19, 20, 18, 13, 17, 21, 24, 24, 23, 18,
+ 13, 15, 19, 22, 24, 21, 17, 12, 14, 17, 20, 21, 18, 15, 10, 13, 15, 17, 17,
+ 17, 15, 10, 12, 14, 15, 15, 17, 14, 8, 11, 13, 15, 15, 15, 14, 11, 11, 12,
+ 14, 14, 15, 14, 12, 14, 16, 18, 19, 19, 15, 11, 13, 15, 16, 17, 17, 13, 10,
+ 13, 13, 15, 14, 15, 12, 9, 12, 12, 12, 13, 13, 12, 7, 10, 11, 11, 11, 12,
+ 12, 8, 9, 9, 10, 11, 11, 12, 12, 12, 10, 9, 10, 11, 12, 10, 10, 10, 11,
+ 13, 13, 6, 8, 9, 9, 10, 10, 10, 5, 7, 7, 8, 8, 9, 9, 5, 4, 6,
+ 6, 7, 7, 7, 6, 5, 5, 6, 6, 6, 7, 5, 8, 8, 7, 6, 5, 5, 4,
+ 12, 12, 8, 6, 5, 4, 2, 2, 2, 2, 3, 3, 4, 6, 0, 1, 1, 2, 1,
+ 2, 5, 0, 2, 2, 1, 1, 1, 5, 0, 2, 2, 1, 2, 1, 3, 2, 4, 4,
+ 3, 4, 2, 2, 5, 7, 7, 6, 5, 3, 2, 9, 10, 10, 7, 6, 4, 2, 4,
+ 5, 4, 5, 3, 2, 4, 4, 6, 6, 6, 3, 1, 3, 4, 7, 7, 6, 4, 1,
+ 2, 4, 7, 8, 7, 5, 3, 2, 4, 8, 10, 9, 7, 4, 2, 6, 10, 13, 12,
+ 9, 6, 2, 9, 12, 14, 13, 10, 7, 3, 9, 12, 11, 8, 5, 2, 2, 9, 12,
+ 13, 8, 5, 2, 1, 9, 13, 14, 9, 6, 3, 1, 9, 12, 15, 10, 7, 5, 2,
+ 9, 12, 15, 13, 10, 7, 3, 10, 14, 17, 16, 11, 8, 5, 12, 15, 17, 17, 13,
+ 9, 6, 18, 19, 22, 24, 24, 22, 19, 17, 20, 21, 24, 24, 22, 19, 15, 18, 20,
+ 22, 24, 22, 20, 15, 18, 20, 21, 23, 22, 19, 14, 17, 19, 21, 24, 22, 21, 13,
+ 16, 19, 21, 23, 22, 20, 12, 14, 17, 20, 22, 22, 20, 15, 18, 20, 23, 22, 20,
+ 17, 14, 17, 20, 22, 20, 19, 17, 13, 17, 19, 21, 21, 18, 18, 12, 16, 18, 20,
+ 19, 18, 17, 12, 15, 18, 19, 19, 20, 18, 11, 13, 17, 19, 18, 19, 17, 14, 13,
+ 15, 17, 18, 18, 18, 13, 17, 17, 18, 18, 17, 14, 12, 15, 16, 16, 17, 16, 13,
+ 11, 14, 16, 16, 16, 17, 14, 10, 13, 15, 15, 16, 16, 14, 9, 12, 14, 14, 15,
+ 15, 14, 9, 11, 12, 13, 13, 14, 14, 16, 16, 12, 12, 13, 13, 13, 11, 11, 12,
+ 12, 12, 13, 4, 10, 10, 11, 11, 11, 11, 5, 9, 9, 9, 9, 10, 10, 5, 5,
+ 8, 8, 8, 8, 9, 7, 6, 7, 7, 7, 8, 8, 5, 9, 10, 9, 8, 6, 6,
+ 5, 16, 18, 13, 9, 6, 5, 3, 3, 3, 4, 4, 4, 3, 4, 1, 2, 2, 2,
+ 2, 2, 5, 1, 0, 1, 1, 1, 2, 5, 1, 3, 3, 3, 4, 2, 5, 3, 8,
+ 8, 8, 8, 6, 3, 8, 13, 14, 12, 10, 8, 3, 15, 17, 16, 13, 11, 9, 4,
+ 6, 8, 8, 8, 4, 2, 4, 6, 10, 9, 9, 6, 1, 4, 6, 11, 11, 10, 7,
+ 2, 3, 6, 12, 12, 12, 10, 6, 4, 8, 13, 15, 14, 12, 9, 4, 11, 15, 18,
+ 16, 13, 11, 6, 15, 17, 19, 17, 14, 12, 8, 13, 16, 15, 12, 8, 2, 2, 13,
+ 16, 16, 12, 9, 4, 2, 13, 16, 18, 13, 10, 6, 1, 13, 16, 19, 14, 12, 10,
+ 6, 13, 16, 20, 17, 14, 12, 8, 15, 17, 21, 20, 15, 13, 10, 17, 19, 22, 21,
+ 17, 14, 11, 18, 21, 24, 26, 26, 24, 21, 17, 20, 23, 26, 26, 24, 21, 15, 19,
+ 22, 26, 25, 23, 22, 14, 17, 21, 24, 26, 24, 22, 14, 17, 21, 23, 26, 25, 22,
+ 13, 16, 20, 23, 26, 25, 21, 13, 15, 18, 21, 24, 24, 22, 15, 19, 21, 24, 23,
+ 21, 17, 15, 18, 21, 24, 23, 20, 17, 13, 17, 20, 22, 22, 20, 17, 12, 16, 19,
+ 22, 22, 20, 18, 12, 15, 18, 21, 21, 22, 18, 11, 14, 17, 20, 21, 20, 18, 15,
+ 14, 16, 19, 19, 19, 18, 13, 17, 18, 18, 19, 17, 13, 12, 15, 17, 17, 17, 17,
+ 13, 11, 14, 16, 16, 16, 16, 13, 10, 13, 15, 15, 16, 16, 13, 10, 13, 15, 15,
+ 15, 15, 14, 10, 11, 13, 13, 14, 14, 13, 16, 16, 12, 12, 13, 13, 13, 11, 11,
+ 11, 11, 12, 12, 4, 10, 10, 10, 11, 10, 11, 4, 8, 9, 9, 9, 9, 9, 5,
+ 6, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 4, 10, 11, 10, 8, 7,
+ 6, 4, 17, 18, 13, 9, 7, 5, 3, 3, 3, 3, 4, 4, 3, 4, 2, 2, 2,
+ 2, 2, 3, 4, 2, 1, 0, 0, 0, 2, 4, 2, 4, 3, 3, 4, 3, 4, 4,
+ 8, 8, 8, 8, 6, 3, 9, 13, 14, 12, 10, 7, 4, 15, 18, 18, 14, 12, 9,
+ 5, 5, 7, 7, 7, 3, 2, 3, 5, 9, 9, 8, 5, 1, 3, 5, 11, 11, 10,
+ 6, 2, 3, 6, 12, 14, 14, 10, 6, 4, 8, 14, 19, 18, 15, 9, 5, 11, 17,
+ 24, 23, 18, 11, 6, 16, 21, 25, 26, 20, 13, 7, 13, 19, 18, 12, 7, 2, 2,
+ 13, 19, 20, 14, 9, 3, 2, 13, 19, 23, 16, 11, 6, 2, 13, 20, 25, 19, 15,
+ 10, 6, 15, 21, 26, 25, 19, 14, 8, 17, 24, 29, 28, 21, 16, 10, 20, 26, 30,
+ 31, 24, 17, 11, 18, 22, 26, 29, 29, 26, 23, 17, 20, 25, 28, 29, 26, 22, 15,
+ 19, 23, 26, 28, 24, 22, 14, 17, 20, 24, 27, 25, 22, 14, 17, 20, 23, 27, 25,
+ 22, 13, 16, 20, 23, 26, 25, 22, 13, 14, 18, 22, 25, 25, 22, 15, 19, 23, 26,
+ 25, 22, 19, 14, 18, 22, 25, 24, 21, 18, 13, 16, 20, 23, 22, 20, 17, 13, 16,
+ 19, 21, 22, 21, 18, 12, 15, 18, 21, 21, 21, 18, 11, 14, 17, 20, 20, 21, 18,
+ 15, 14, 17, 19, 19, 19, 18, 13, 17, 19, 19, 21, 18, 14, 12, 15, 17, 19, 19,
+ 17, 13, 11, 14, 16, 16, 16, 16, 12, 10, 14, 15, 15, 15, 15, 12, 10, 13, 14,
+ 14, 14, 15, 13, 10, 11, 13, 13, 13, 13, 13, 17, 16, 12, 12, 12, 12, 12, 11,
+ 11, 12, 12, 13, 13, 5, 11, 10, 10, 11, 11, 11, 4, 9, 9, 9, 9, 8, 8,
+ 4, 5, 9, 8, 8, 8, 8, 6, 7, 7, 7, 7, 7, 7, 3, 10, 11, 10, 9,
+ 6, 6, 3, 17, 18, 13, 9, 7, 5, 3, 3, 3, 4, 4, 5, 4, 5, 1, 2,
+ 2, 3, 3, 4, 4, 1, 1, 0, 0, 0, 2, 3, 1, 4, 4, 3, 4, 3, 3,
+ 4, 8, 8, 8, 8, 5, 3, 10, 13, 14, 12, 10, 7, 4, 15, 18, 17, 14, 12,
+ 9, 4, 6, 7, 7, 7, 4, 3, 4, 6, 10, 8, 9, 6, 2, 3, 6, 11, 10,
+ 10, 6, 2, 2, 6, 12, 14, 13, 10, 6, 4, 9, 15, 18, 18, 14, 9, 4, 12,
+ 17, 24, 22, 17, 11, 6, 16, 21, 24, 25, 19, 13, 7, 14, 19, 18, 12, 7, 3,
+ 4, 13, 19, 20, 14, 8, 3, 2, 13, 19, 22, 15, 10, 6, 2, 14, 20, 25, 19,
+ 14, 10, 5, 15, 21, 27, 24, 19, 13, 8, 17, 24, 29, 29, 22, 15, 9, 21, 27,
+ 30, 30, 23, 17, 11, 18, 22, 26, 29, 30, 27, 23, 17, 21, 24, 28, 30, 26, 21,
+ 15, 19, 23, 26, 27, 24, 21, 14, 17, 21, 24, 27, 25, 22, 14, 17, 20, 23, 27,
+ 25, 22, 14, 16, 19, 23, 26, 25, 21, 13, 15, 18, 21, 25, 25, 22, 15, 19, 23,
+ 26, 26, 23, 20, 14, 18, 22, 25, 25, 22, 18, 13, 16, 20, 23, 23, 20, 17, 12,
+ 15, 19, 21, 22, 21, 17, 13, 15, 18, 21, 21, 20, 17, 11, 13, 17, 19, 20, 20,
+ 17, 15, 15, 15, 18, 18, 18, 18, 13, 16, 18, 19, 21, 19, 15, 12, 15, 18, 18,
+ 19, 17, 13, 12, 14, 16, 16, 15, 16, 11, 11, 14, 14, 15, 15, 15, 12, 10, 13,
+ 14, 14, 14, 14, 12, 10, 12, 13, 13, 13, 13, 12, 17, 16, 13, 12, 12, 12, 11,
+ 12, 11, 11, 12, 13, 13, 6, 11, 9, 10, 11, 11, 11, 4, 9, 9, 9, 8, 8,
+ 8, 4, 5, 9, 8, 8, 8, 8, 6, 7, 7, 7, 7, 7, 7, 3, 11, 12, 10,
+ 8, 6, 5, 3, 17, 18, 13, 10, 7, 5, 3, 4, 3, 4, 4, 5, 5, 5, 1,
+ 1, 2, 3, 3, 3, 4, 1, 1, 0, 0, 0, 1, 2, 1, 4, 4, 3, 4, 3,
+ 3, 5, 9, 8, 8, 8, 6, 3, 10, 14, 14, 12, 10, 7, 4, 15, 18, 17, 13,
+ 12, 9, 5, 6, 7, 7, 7, 4, 4, 5, 6, 9, 8, 8, 5, 2, 3, 6, 11,
+ 10, 9, 6, 2, 2, 6, 13, 13, 12, 10, 6, 4, 9, 15, 18, 17, 14, 9, 5,
+ 12, 18, 23, 21, 16, 11, 6, 16, 20, 24, 23, 18, 13, 7, 13, 15, 13, 10, 7,
+ 3, 4, 13, 16, 15, 11, 8, 3, 1, 12, 15, 17, 11, 9, 6, 2, 12, 17, 20,
+ 16, 13, 9, 6, 15, 19, 23, 21, 18, 13, 7, 18, 23, 26, 26, 20, 15, 9, 21,
+ 25, 28, 28, 22, 17, 11, 17, 20, 25, 28, 28, 25, 21, 16, 19, 23, 28, 28, 24,
+ 21, 14, 18, 21, 25, 27, 23, 20, 13, 16, 19, 22, 26, 23, 20, 13, 16, 19, 22,
+ 26, 24, 21, 12, 15, 18, 22, 25, 24, 21, 13, 14, 17, 21, 23, 24, 21, 14, 18,
+ 21, 25, 24, 22, 18, 13, 17, 20, 24, 23, 20, 16, 12, 16, 19, 22, 21, 19, 15,
+ 11, 14, 17, 20, 20, 19, 16, 12, 14, 17, 20, 19, 19, 16, 9, 12, 15, 18, 18,
+ 19, 16, 16, 14, 14, 17, 17, 17, 16, 12, 15, 17, 18, 19, 17, 13, 11, 14, 16,
+ 17, 17, 16, 11, 11, 13, 15, 14, 15, 14, 10, 10, 13, 13, 13, 13, 13, 11, 9,
+ 12, 12, 12, 12, 12, 11, 11, 12, 11, 11, 12, 11, 11, 17, 16, 12, 10, 10, 10,
+ 10, 10, 10, 10, 11, 12, 12, 6, 9, 8, 9, 9, 10, 10, 4, 8, 7, 7, 7,
+ 7, 7, 3, 5, 7, 6, 6, 6, 6, 5, 6, 6, 6, 5, 6, 5, 2, 11, 12,
+ 10, 9, 7, 5, 3, 17, 17, 13, 10, 7, 5, 3, 2, 1, 2, 3, 4, 5, 5,
+ 1, 0, 1, 2, 2, 3, 3, 1, 2, 2, 2, 1, 0, 2, 1, 5, 4, 4, 4,
+ 3, 3, 5, 9, 8, 8, 8, 6, 3, 11, 14, 14, 11, 9, 7, 4, 16, 17, 17,
+ 13, 10, 8, 5, 5, 5, 4, 3, 3, 3, 5, 5, 7, 5, 4, 3, 2, 2, 5,
+ 7, 5, 4, 2, 2, 1, 5, 8, 8, 7, 6, 5, 4, 8, 11, 13, 11, 10, 8,
+ 5, 12, 15, 18, 16, 11, 8, 6, 16, 18, 19, 18, 13, 9, 7, 8, 9, 7, 5,
+ 3, 3, 3, 8, 9, 8, 6, 3, 2, 1, 8, 9, 10, 6, 4, 3, 2, 7, 10,
+ 13, 10, 8, 6, 6, 10, 13, 17, 16, 12, 9, 7, 13, 17, 20, 19, 14, 10, 8,
+ 17, 20, 21, 21, 16, 11, 8, 13, 16, 19, 22, 22, 20, 16, 12, 15, 18, 22, 22,
+ 19, 15, 11, 15, 18, 20, 21, 18, 15, 10, 12, 15, 18, 21, 19, 16, 9, 12, 15,
+ 18, 21, 20, 16, 8, 11, 14, 17, 21, 19, 16, 11, 11, 13, 16, 20, 19, 16, 11,
+ 14, 17, 20, 18, 17, 14, 10, 13, 16, 19, 18, 15, 12, 8, 12, 15, 17, 16, 14,
+ 12, 7, 10, 13, 16, 16, 14, 12, 7, 10, 12, 15, 15, 15, 12, 9, 10, 11, 14,
+ 14, 14, 12, 14, 12, 11, 13, 13, 13, 12, 8, 12, 13, 14, 15, 13, 10, 7, 10,
+ 13, 13, 13, 13, 8, 6, 9, 11, 11, 11, 10, 7, 5, 9, 9, 9, 9, 9, 7,
+ 6, 8, 9, 9, 9, 8, 7, 12, 11, 9, 8, 7, 7, 7, 15, 13, 10, 8, 7,
+ 6, 6, 6, 6, 7, 8, 9, 9, 5, 5, 4, 5, 6, 6, 7, 3, 3, 3, 4,
+ 4, 3, 3, 2, 2, 3, 3, 3, 2, 2, 2, 6, 7, 6, 5, 5, 4, 2, 12,
+ 12, 9, 8, 6, 5, 3, 15, 16, 11, 9, 7, 5, 4, 4, 3, 3, 2, 3, 4,
+ 5, 4, 4, 3, 3, 3, 3, 3, 4, 5, 4, 3, 2, 2, 0, 4, 8, 5, 5,
+ 4, 4, 3, 8, 12, 9, 9, 8, 6, 4, 11, 14, 14, 12, 10, 7, 5, 15, 17,
+ 17, 14, 12, 9, 5, 8, 7, 6, 5, 4, 4, 4, 7, 9, 6, 6, 4, 3, 2,
+ 8, 11, 8, 7, 5, 3, 2, 8, 11, 12, 11, 9, 6, 5, 10, 14, 17, 16, 13,
+ 9, 5, 13, 18, 22, 20, 15, 10, 7, 16, 20, 23, 22, 17, 12, 8, 12, 15, 13,
+ 9, 5, 4, 3, 11, 15, 16, 11, 6, 3, 2, 12, 15, 18, 12, 8, 5, 3, 12,
+ 16, 21, 16, 12, 9, 6, 14, 19, 24, 21, 16, 12, 8, 16, 21, 26, 26, 19, 13,
+ 9, 20, 24, 27, 27, 22, 15, 10, 15, 19, 23, 26, 26, 25, 22, 14, 18, 22, 25,
+ 25, 23, 21, 13, 17, 20, 23, 24, 21, 19, 13, 15, 18, 20, 22, 20, 18, 12, 14,
+ 16, 18, 21, 20, 18, 10, 12, 15, 17, 20, 20, 18, 10, 11, 13, 15, 19, 19, 18,
+ 14, 17, 20, 23, 24, 23, 19, 13, 16, 19, 22, 24, 21, 17, 12, 14, 17, 20, 20,
+ 19, 15, 11, 13, 16, 17, 17, 17, 15, 10, 12, 14, 16, 16, 17, 14, 8, 11, 12,
+ 14, 14, 15, 15, 11, 11, 12, 14, 14, 14, 14, 12, 14, 16, 17, 19, 19, 15, 11,
+ 13, 15, 16, 17, 17, 12, 10, 13, 14, 14, 14, 15, 12, 9, 12, 12, 12, 13, 13,
+ 12, 7, 10, 12, 12, 12, 12, 12, 8, 9, 9, 10, 11, 12, 12, 12, 13, 10, 9,
+ 10, 11, 11, 10, 10, 10, 11, 13, 14, 6, 8, 9, 9, 10, 10, 11, 5, 7, 8,
+ 8, 8, 9, 9, 5, 4, 6, 6, 7, 7, 7, 5, 5, 5, 6, 6, 6, 7, 5,
+ 8, 8, 7, 6, 5, 5, 4, 12, 12, 8, 6, 5, 4, 2, 2, 2, 2, 2, 3,
+ 4, 6, 0, 1, 1, 2, 1, 2, 5, 0, 1, 1, 1, 1, 1, 5, 0, 2, 2,
+ 1, 2, 1, 4, 2, 4, 4, 3, 4, 2, 2, 5, 7, 7, 6, 5, 3, 2, 9,
+ 10, 10, 7, 6, 4, 2, 4, 5, 4, 5, 3, 2, 5, 4, 6, 6, 5, 3, 1,
+ 3, 4, 7, 7, 6, 4, 1, 2, 4, 7, 8, 7, 5, 3, 2, 4, 8, 11, 9,
+ 7, 4, 2, 6, 10, 14, 12, 9, 6, 2, 9, 12, 14, 13, 10, 7, 3, 9, 12,
+ 11, 7, 5, 2, 2, 9, 12, 13, 8, 5, 2, 1, 9, 12, 14, 9, 6, 3, 1,
+ 9, 12, 15, 10, 7, 5, 2, 8, 12, 15, 13, 9, 7, 4, 10, 14, 17, 16, 11,
+ 8, 5, 12, 15, 17, 17, 13, 9, 6, 21, 24, 26, 29, 28, 27, 25, 21, 23, 26,
+ 28, 29, 26, 23, 19, 22, 25, 26, 27, 25, 23, 18, 21, 23, 26, 27, 24, 21, 16,
+ 19, 21, 23, 25, 23, 22, 15, 19, 21, 22, 24, 23, 21, 12, 15, 19, 22, 24, 23,
+ 22, 20, 23, 25, 27, 26, 24, 22, 18, 21, 24, 26, 26, 23, 21, 17, 20, 23, 25,
+ 23, 23, 20, 16, 18, 21, 23, 22, 21, 19, 14, 17, 19, 22, 21, 21, 19, 12, 15,
+ 18, 20, 21, 20, 19, 12, 12, 17, 19, 20, 19, 20, 17, 21, 22, 22, 22, 21, 18,
+ 15, 20, 21, 22, 21, 20, 17, 14, 18, 20, 20, 20, 20, 17, 12, 16, 17, 17, 18,
+ 18, 16, 11, 14, 16, 16, 17, 17, 16, 8, 12, 14, 14, 16, 16, 16, 13, 13, 11,
+ 13, 15, 15, 15, 14, 15, 16, 16, 17, 17, 8, 13, 13, 14, 15, 15, 15, 8, 10,
+ 12, 13, 13, 13, 14, 8, 7, 10, 10, 10, 10, 11, 9, 7, 8, 9, 9, 9, 10,
+ 6, 8, 8, 7, 7, 7, 8, 6, 13, 14, 10, 7, 6, 6, 5, 6, 6, 7, 8,
+ 8, 6, 8, 2, 5, 6, 6, 6, 6, 8, 2, 3, 4, 4, 4, 5, 9, 2, 0,
+ 0, 0, 0, 2, 6, 3, 5, 4, 5, 5, 3, 5, 5, 9, 9, 8, 6, 4, 3,
+ 11, 12, 11, 8, 6, 5, 2, 4, 6, 6, 6, 3, 5, 7, 4, 7, 7, 7, 4,
+ 5, 6, 4, 8, 8, 8, 6, 3, 6, 4, 8, 8, 7, 5, 3, 3, 4, 8, 9,
+ 8, 7, 5, 2, 7, 9, 12, 11, 8, 6, 3, 11, 12, 13, 12, 9, 6, 4, 9,
+ 12, 11, 9, 6, 3, 6, 9, 12, 13, 9, 7, 3, 5, 9, 13, 15, 10, 7, 4,
+ 3, 10, 12, 15, 9, 7, 6, 3, 8, 11, 14, 12, 8, 6, 5, 9, 13, 15, 14,
+ 10, 7, 6, 11, 14, 16, 16, 12, 8, 6, 22, 26, 28, 31, 31, 29, 26, 21, 24,
+ 28, 31, 31, 28, 25, 19, 23, 26, 29, 29, 27, 23, 18, 20, 24, 27, 29, 26, 22,
+ 15, 18, 21, 24, 27, 25, 22, 14, 16, 20, 23, 27, 25, 22, 12, 15, 18, 22, 25,
+ 25, 22, 19, 23, 26, 30, 27, 26, 22, 18, 21, 25, 28, 28, 24, 22, 16, 20, 24,
+ 27, 25, 24, 20, 15, 18, 21, 23, 23, 21, 19, 13, 16, 19, 22, 22, 21, 19, 11,
+ 15, 17, 21, 21, 21, 19, 12, 12, 16, 19, 20, 20, 19, 16, 20, 22, 23, 24, 22,
+ 18, 15, 18, 21, 22, 22, 21, 17, 14, 17, 20, 20, 20, 20, 15, 13, 15, 16, 16,
+ 17, 17, 14, 10, 13, 15, 15, 15, 16, 14, 8, 12, 13, 14, 14, 14, 14, 13, 13,
+ 12, 12, 13, 13, 13, 13, 14, 15, 16, 17, 17, 9, 12, 13, 14, 15, 15, 15, 8,
+ 9, 11, 12, 13, 12, 13, 8, 6, 9, 9, 9, 9, 9, 7, 6, 8, 8, 8, 8,
+ 8, 4, 8, 8, 7, 6, 6, 7, 4, 13, 15, 10, 7, 5, 5, 3, 5, 6, 7,
+ 8, 8, 7, 8, 2, 5, 5, 6, 7, 6, 8, 2, 3, 3, 4, 4, 4, 5, 2,
+ 0, 0, 0, 0, 2, 4, 3, 5, 5, 4, 5, 3, 3, 6, 10, 11, 9, 7, 4,
+ 2, 12, 15, 14, 11, 9, 6, 2, 4, 6, 7, 7, 4, 6, 7, 4, 8, 8, 9,
+ 5, 5, 6, 4, 10, 10, 10, 6, 2, 4, 4, 10, 11, 11, 7, 3, 2, 5, 11,
+ 15, 15, 12, 6, 2, 8, 14, 20, 19, 14, 8, 3, 13, 17, 21, 21, 16, 10, 4,
+ 12, 19, 18, 12, 7, 5, 6, 13, 19, 21, 14, 8, 3, 5, 13, 19, 23, 15, 10,
+ 4, 2, 13, 18, 23, 17, 11, 7, 3, 12, 18, 23, 21, 16, 10, 4, 14, 20, 25,
+ 24, 19, 12, 6, 17, 23, 25, 25, 20, 14, 8, 22, 26, 29, 32, 33, 30, 26, 20,
+ 24, 28, 33, 33, 29, 26, 19, 23, 27, 30, 31, 28, 23, 18, 20, 24, 27, 30, 26,
+ 23, 15, 18, 21, 24, 28, 26, 23, 14, 17, 19, 23, 27, 26, 22, 12, 15, 18, 22,
+ 26, 25, 22, 19, 23, 27, 29, 29, 27, 23, 18, 21, 26, 29, 29, 25, 22, 16, 20,
+ 24, 27, 26, 24, 20, 15, 18, 21, 24, 23, 22, 19, 13, 16, 19, 21, 22, 21, 19,
+ 12, 15, 18, 20, 21, 21, 18, 12, 12, 17, 19, 20, 19, 19, 16, 20, 22, 23, 24,
+ 22, 19, 15, 18, 20, 22, 23, 22, 17, 13, 17, 19, 19, 20, 19, 15, 13, 15, 15,
+ 16, 16, 17, 13, 10, 13, 15, 15, 14, 15, 13, 8, 12, 13, 14, 14, 13, 13, 13,
+ 13, 12, 12, 13, 13, 12, 14, 14, 15, 16, 17, 18, 10, 13, 13, 14, 15, 15, 15,
+ 8, 10, 11, 12, 12, 12, 12, 7, 6, 10, 9, 9, 9, 9, 7, 6, 8, 8, 8,
+ 7, 7, 4, 8, 8, 7, 6, 6, 6, 3, 13, 14, 9, 6, 5, 5, 2, 6, 6,
+ 7, 8, 9, 8, 9, 1, 5, 6, 6, 7, 6, 8, 1, 3, 3, 3, 4, 4, 5,
+ 1, 0, 0, 0, 0, 2, 3, 3, 5, 4, 4, 4, 2, 2, 6, 10, 11, 9, 6,
+ 3, 2, 12, 15, 14, 10, 9, 5, 2, 4, 6, 7, 7, 5, 7, 8, 4, 8, 8,
+ 8, 5, 5, 6, 4, 10, 10, 9, 5, 2, 3, 4, 9, 11, 10, 6, 2, 2, 5,
+ 11, 15, 14, 11, 5, 2, 8, 14, 20, 18, 13, 7, 2, 12, 17, 21, 21, 15, 10,
+ 4, 13, 19, 17, 12, 7, 5, 7, 13, 18, 20, 13, 8, 3, 4, 13, 19, 21, 14,
+ 9, 4, 1, 13, 17, 22, 16, 10, 6, 2, 12, 17, 24, 21, 15, 9, 4, 14, 20,
+ 25, 25, 18, 11, 6, 17, 22, 26, 26, 20, 14, 8, 22, 26, 30, 34, 33, 30, 28,
+ 21, 24, 29, 33, 33, 30, 26, 19, 23, 27, 31, 32, 28, 24, 18, 20, 24, 28, 30,
+ 27, 23, 15, 18, 21, 25, 29, 26, 23, 14, 17, 20, 23, 27, 27, 23, 12, 15, 19,
+ 23, 26, 26, 22, 19, 23, 28, 30, 29, 26, 24, 18, 21, 25, 29, 30, 26, 22, 17,
+ 20, 24, 27, 27, 23, 20, 15, 18, 21, 23, 24, 22, 18, 14, 16, 19, 22, 22, 22,
+ 18, 12, 15, 17, 20, 20, 21, 19, 12, 13, 17, 20, 19, 20, 18, 17, 20, 22, 23,
+ 24, 23, 18, 15, 19, 21, 22, 23, 21, 17, 15, 17, 20, 19, 20, 19, 15, 13, 16,
+ 16, 17, 17, 16, 13, 10, 14, 15, 15, 15, 15, 13, 9, 12, 14, 14, 14, 14, 13,
+ 12, 12, 12, 12, 13, 13, 12, 15, 15, 15, 16, 17, 18, 10, 13, 13, 14, 15, 15,
+ 15, 8, 10, 12, 12, 12, 12, 12, 7, 6, 10, 9, 9, 9, 9, 7, 6, 8, 8,
+ 8, 7, 8, 4, 8, 8, 7, 7, 6, 6, 3, 12, 12, 9, 6, 5, 5, 2, 6,
+ 6, 7, 8, 9, 8, 10, 2, 5, 6, 7, 7, 6, 8, 1, 4, 4, 4, 4, 4,
+ 4, 1, 0, 0, 0, 0, 1, 3, 3, 5, 4, 4, 4, 2, 2, 6, 10, 10, 8,
+ 6, 3, 2, 11, 13, 13, 10, 8, 5, 1, 5, 7, 7, 7, 5, 7, 8, 5, 9,
+ 8, 8, 5, 5, 6, 5, 10, 9, 8, 5, 2, 3, 5, 10, 10, 9, 6, 2, 2,
+ 5, 11, 14, 13, 10, 5, 1, 8, 14, 19, 17, 12, 7, 2, 12, 16, 20, 20, 14,
+ 9, 3, 12, 15, 13, 10, 6, 6, 7, 12, 15, 15, 11, 7, 4, 4, 12, 15, 16,
+ 11, 8, 3, 2, 12, 15, 17, 13, 10, 6, 2, 11, 16, 19, 17, 14, 9, 4, 14,
+ 19, 23, 23, 16, 11, 5, 16, 21, 24, 23, 19, 13, 7, 21, 24, 28, 32, 33, 29,
+ 25, 19, 23, 27, 31, 32, 28, 25, 18, 21, 25, 29, 30, 27, 23, 17, 20, 22, 25,
+ 28, 25, 21, 14, 17, 20, 23, 27, 24, 21, 13, 16, 18, 22, 26, 25, 21, 11, 14,
+ 17, 21, 24, 24, 21, 17, 22, 25, 29, 28, 26, 23, 17, 20, 24, 28, 28, 24, 20,
+ 15, 19, 22, 26, 26, 23, 18, 14, 16, 20, 22, 22, 21, 17, 12, 15, 18, 20, 20,
+ 20, 17, 10, 13, 16, 19, 19, 19, 17, 10, 12, 15, 18, 18, 18, 17, 16, 19, 21,
+ 21, 24, 21, 17, 14, 17, 20, 21, 21, 20, 15, 13, 16, 18, 18, 18, 18, 14, 12,
+ 14, 15, 15, 15, 15, 11, 10, 13, 14, 13, 13, 13, 12, 8, 11, 12, 12, 12, 12,
+ 11, 10, 10, 10, 11, 11, 11, 11, 13, 13, 14, 15, 16, 17, 9, 11, 12, 13, 14,
+ 14, 14, 7, 9, 11, 10, 11, 10, 10, 6, 5, 9, 8, 7, 7, 7, 6, 5, 6,
+ 6, 6, 6, 6, 3, 8, 7, 6, 5, 4, 5, 3, 10, 10, 7, 5, 4, 3, 2,
+ 5, 5, 6, 7, 8, 8, 9, 1, 4, 5, 5, 6, 6, 7, 1, 2, 3, 3, 3,
+ 3, 4, 1, 2, 2, 2, 1, 0, 2, 2, 6, 5, 4, 4, 2, 2, 6, 9, 9,
+ 7, 5, 3, 1, 9, 11, 10, 7, 6, 4, 2, 5, 4, 4, 4, 5, 7, 8, 5,
+ 6, 4, 4, 4, 4, 6, 5, 8, 5, 4, 2, 2, 3, 5, 7, 7, 4, 3, 2,
+ 1, 5, 8, 10, 8, 6, 4, 2, 8, 11, 13, 10, 7, 5, 3, 10, 12, 13, 12,
+ 8, 6, 3, 9, 11, 8, 5, 5, 6, 6, 9, 11, 9, 5, 4, 4, 4, 9, 11,
+ 11, 6, 4, 2, 1, 8, 11, 11, 7, 5, 3, 2, 8, 10, 13, 11, 8, 5, 4,
+ 10, 13, 15, 14, 9, 7, 4, 11, 14, 15, 15, 11, 7, 5, 17, 21, 24, 28, 27,
+ 25, 22, 16, 20, 24, 27, 27, 23, 20, 14, 18, 21, 24, 26, 23, 18, 13, 16, 19,
+ 22, 25, 20, 17, 10, 13, 15, 19, 22, 20, 17, 9, 11, 14, 18, 22, 21, 17, 7,
+ 10, 14, 16, 20, 20, 17, 14, 18, 21, 25, 24, 22, 18, 13, 17, 21, 24, 23, 20,
+ 16, 12, 15, 20, 21, 21, 19, 14, 11, 13, 16, 19, 18, 17, 13, 8, 11, 13, 16,
+ 16, 16, 13, 7, 9, 12, 15, 15, 15, 12, 6, 8, 12, 14, 13, 14, 13, 12, 16,
+ 17, 18, 19, 18, 14, 10, 14, 16, 17, 17, 16, 12, 9, 13, 15, 14, 14, 14, 10,
+ 9, 11, 11, 11, 11, 11, 8, 5, 8, 9, 9, 9, 9, 8, 5, 7, 8, 8, 8,
+ 8, 8, 6, 6, 7, 6, 7, 7, 6, 10, 10, 10, 11, 12, 13, 9, 8, 8, 9,
+ 10, 10, 10, 6, 6, 7, 7, 7, 7, 7, 4, 3, 4, 4, 3, 4, 4, 3, 3,
+ 3, 2, 2, 2, 2, 2, 5, 5, 3, 3, 2, 1, 0, 6, 7, 4, 3, 2, 2,
+ 1, 2, 3, 4, 5, 6, 8, 8, 4, 3, 4, 5, 5, 5, 6, 4, 5, 4, 3,
+ 3, 3, 3, 4, 6, 4, 3, 3, 2, 0, 4, 6, 5, 4, 4, 2, 1, 6, 7,
+ 8, 7, 5, 3, 2, 7, 9, 11, 8, 7, 5, 2, 7, 6, 5, 5, 5, 7, 7,
+ 7, 8, 6, 6, 4, 4, 5, 7, 8, 7, 7, 4, 2, 2, 7, 8, 8, 8, 5,
+ 3, 2, 6, 9, 12, 11, 8, 5, 2, 7, 11, 16, 15, 10, 6, 3, 9, 13, 17,
+ 17, 12, 7, 3, 10, 14, 13, 8, 5, 6, 6, 10, 14, 15, 10, 6, 3, 3, 10,
+ 14, 17, 10, 6, 3, 2, 10, 13, 17, 12, 8, 5, 2, 10, 14, 19, 16, 11, 8,
+ 4, 12, 16, 21, 20, 14, 9, 5, 14, 18, 22, 22, 16, 11, 6, 19, 22, 24, 28,
+ 28, 25, 22, 18, 21, 23, 26, 28, 25, 22, 17, 20, 22, 24, 26, 23, 20, 16, 18,
+ 20, 22, 24, 21, 17, 13, 15, 17, 18, 21, 19, 16, 11, 12, 14, 16, 19, 18, 16,
+ 8, 10, 12, 15, 17, 18, 16, 17, 20, 23, 25, 25, 23, 20, 17, 19, 22, 24, 24,
+ 22, 19, 15, 18, 21, 22, 22, 20, 18, 14, 16, 18, 19, 19, 18, 14, 10, 13, 14,
+ 15, 15, 15, 14, 8, 10, 12, 14, 14, 15, 14, 9, 9, 11, 12, 13, 13, 14, 15,
+ 18, 19, 20, 20, 20, 16, 13, 17, 18, 19, 19, 18, 16, 12, 16, 17, 17, 17, 17,
+ 15, 11, 14, 15, 15, 15, 15, 13, 7, 10, 12, 11, 12, 12, 13, 5, 8, 9, 10,
+ 11, 12, 12, 10, 10, 8, 8, 10, 11, 11, 12, 14, 14, 15, 15, 16, 9, 11, 12,
+ 13, 13, 13, 14, 9, 9, 10, 11, 11, 12, 12, 9, 6, 8, 8, 9, 9, 9, 7,
+ 4, 5, 5, 5, 6, 6, 6, 5, 6, 5, 4, 4, 5, 5, 10, 10, 6, 4, 3,
+ 3, 3, 4, 5, 6, 7, 7, 8, 9, 2, 4, 5, 6, 6, 7, 9, 2, 3, 4,
+ 4, 4, 5, 8, 2, 3, 3, 3, 3, 2, 4, 0, 3, 2, 2, 2, 1, 3, 3,
+ 4, 5, 4, 3, 1, 1, 7, 7, 7, 5, 4, 3, 1, 5, 6, 6, 7, 5, 6,
+ 8, 5, 7, 7, 7, 5, 5, 7, 5, 8, 8, 8, 6, 4, 6, 5, 7, 8, 7,
+ 5, 2, 1, 3, 6, 9, 8, 5, 2, 1, 4, 8, 11, 10, 7, 4, 1, 6, 10,
+ 12, 11, 8, 5, 2, 10, 13, 13, 9, 6, 5, 6, 10, 14, 14, 10, 7, 5, 6,
+ 10, 14, 15, 10, 7, 5, 3, 10, 13, 15, 10, 7, 4, 1, 9, 11, 14, 12, 8,
+ 5, 2, 8, 12, 15, 14, 9, 6, 3, 10, 13, 15, 15, 11, 7, 4, 27, 29, 32,
+ 35, 35, 32, 30, 25, 29, 31, 34, 34, 31, 28, 25, 27, 31, 32, 33, 30, 26, 22,
+ 26, 28, 30, 32, 28, 24, 20, 23, 25, 26, 28, 26, 23, 16, 19, 21, 24, 26, 25,
+ 23, 13, 17, 21, 23, 26, 26, 23, 24, 28, 31, 33, 32, 29, 28, 23, 26, 29, 32,
+ 32, 29, 26, 21, 25, 28, 30, 30, 28, 25, 20, 23, 26, 28, 26, 25, 21, 16, 20,
+ 22, 24, 24, 22, 21, 14, 17, 20, 21, 22, 22, 21, 10, 13, 18, 21, 21, 21, 21,
+ 21, 25, 28, 28, 28, 27, 24, 20, 24, 26, 27, 27, 27, 23, 18, 22, 24, 25, 26,
+ 25, 22, 16, 20, 21, 22, 22, 23, 18, 11, 16, 17, 18, 18, 18, 18, 7, 13, 16,
+ 16, 16, 17, 18, 9, 10, 13, 15, 15, 16, 17, 18, 19, 21, 22, 22, 22, 15, 16,
+ 18, 20, 20, 20, 21, 14, 13, 16, 17, 18, 18, 18, 14, 9, 14, 14, 14, 15, 15,
+ 12, 7, 10, 10, 10, 10, 11, 8, 7, 7, 7, 8, 9, 9, 6, 9, 9, 6, 6,
+ 7, 8, 7, 9, 11, 12, 14, 14, 13, 14, 4, 9, 11, 12, 12, 11, 14, 4, 7,
+ 8, 8, 9, 9, 12, 4, 5, 5, 5, 5, 6, 6, 3, 0, 0, 0, 0, 3, 6,
+ 3, 3, 3, 2, 1, 1, 6, 6, 6, 5, 2, 2, 2, 5, 4, 5, 6, 6, 9,
+ 12, 13, 4, 5, 6, 6, 8, 10, 11, 4, 6, 6, 6, 5, 7, 9, 4, 5, 5,
+ 5, 3, 3, 6, 2, 3, 5, 4, 2, 2, 4, 2, 4, 7, 6, 3, 2, 2, 5,
+ 5, 8, 7, 4, 2, 1, 7, 10, 9, 7, 7, 10, 12, 7, 10, 11, 8, 6, 8,
+ 10, 7, 10, 12, 7, 5, 5, 6, 7, 9, 12, 7, 4, 2, 2, 5, 8, 10, 7,
+ 4, 2, 1, 4, 7, 10, 9, 5, 2, 1, 5, 9, 11, 11, 7, 3, 2, 27, 30,
+ 34, 37, 37, 34, 32, 25, 29, 32, 36, 36, 34, 29, 23, 27, 32, 34, 35, 32, 28,
+ 22, 25, 28, 31, 33, 29, 24, 19, 22, 25, 27, 30, 28, 24, 16, 19, 21, 24, 29,
+ 27, 24, 13, 17, 20, 24, 27, 27, 24, 24, 27, 31, 34, 33, 31, 28, 22, 26, 30,
+ 33, 33, 31, 27, 20, 24, 29, 31, 30, 29, 25, 19, 22, 25, 28, 28, 27, 22, 16,
+ 18, 22, 25, 24, 23, 21, 13, 16, 19, 23, 22, 23, 21, 10, 14, 18, 21, 21, 21,
+ 21, 21, 24, 27, 29, 29, 28, 23, 19, 23, 26, 27, 28, 26, 22, 18, 21, 24, 24,
+ 25, 24, 20, 16, 20, 20, 20, 21, 22, 17, 10, 16, 17, 17, 17, 18, 16, 7, 13,
+ 15, 16, 16, 16, 16, 8, 11, 13, 13, 15, 15, 15, 18, 19, 20, 21, 22, 23, 15,
+ 15, 17, 19, 20, 19, 19, 14, 12, 16, 17, 17, 17, 17, 13, 8, 13, 13, 13, 14,
+ 14, 11, 6, 9, 9, 9, 9, 9, 8, 6, 7, 7, 8, 8, 8, 5, 8, 9, 5,
+ 6, 6, 7, 5, 8, 10, 12, 13, 13, 13, 15, 3, 9, 10, 11, 11, 11, 14, 3,
+ 8, 8, 8, 8, 8, 9, 3, 5, 4, 4, 4, 5, 5, 2, 0, 0, 0, 0, 2,
+ 4, 3, 5, 6, 5, 3, 1, 4, 7, 10, 10, 6, 5, 2, 4, 4, 5, 6, 7,
+ 9, 11, 13, 4, 7, 7, 7, 8, 9, 11, 4, 9, 9, 8, 5, 7, 8, 4, 8,
+ 10, 10, 5, 3, 4, 2, 7, 12, 12, 7, 2, 3, 3, 9, 15, 15, 10, 4, 2,
+ 8, 12, 16, 16, 12, 6, 1, 11, 17, 16, 11, 7, 10, 12, 11, 18, 19, 13, 7,
+ 8, 9, 11, 17, 21, 14, 8, 4, 6, 11, 17, 21, 15, 9, 4, 2, 10, 16, 21,
+ 17, 12, 6, 1, 10, 16, 20, 19, 14, 8, 2, 13, 18, 20, 20, 16, 10, 4, 27,
+ 30, 33, 37, 36, 35, 31, 25, 29, 32, 36, 37, 35, 30, 23, 27, 31, 35, 35, 32,
+ 28, 22, 25, 29, 31, 34, 30, 24, 19, 22, 24, 27, 31, 28, 25, 16, 18, 21, 25,
+ 29, 28, 25, 13, 16, 20, 23, 27, 27, 25, 23, 28, 30, 34, 33, 31, 29, 22, 26,
+ 30, 33, 34, 29, 26, 20, 24, 28, 30, 31, 28, 23, 19, 22, 24, 27, 28, 26, 20,
+ 17, 19, 21, 24, 24, 23, 20, 14, 16, 19, 22, 21, 22, 20, 10, 14, 18, 20, 21,
+ 21, 21, 20, 24, 26, 27, 29, 27, 23, 19, 22, 25, 26, 26, 25, 21, 18, 21, 23,
+ 24, 24, 24, 19, 15, 20, 20, 20, 21, 20, 16, 10, 16, 16, 17, 16, 16, 16, 7,
+ 14, 14, 15, 15, 15, 16, 8, 11, 13, 14, 14, 14, 14, 18, 18, 19, 20, 22, 22,
+ 15, 15, 17, 18, 19, 20, 20, 13, 11, 16, 16, 16, 16, 16, 12, 8, 14, 12, 13,
+ 13, 13, 11, 6, 10, 9, 9, 9, 9, 7, 6, 7, 7, 7, 7, 8, 4, 8, 8,
+ 5, 6, 6, 6, 4, 7, 10, 12, 13, 13, 13, 14, 3, 9, 10, 11, 11, 10, 13,
+ 3, 8, 8, 8, 8, 8, 9, 3, 5, 4, 4, 4, 4, 4, 2, 0, 0, 0, 0,
+ 2, 3, 3, 5, 6, 4, 2, 1, 3, 6, 9, 9, 6, 4, 2, 2, 4, 5, 6,
+ 7, 9, 12, 13, 4, 7, 7, 7, 8, 9, 10, 4, 8, 9, 8, 5, 6, 7, 4,
+ 8, 9, 9, 5, 3, 3, 2, 7, 11, 11, 7, 2, 3, 3, 9, 15, 15, 9, 3,
+ 2, 7, 12, 17, 17, 11, 5, 1, 11, 17, 16, 11, 7, 10, 11, 11, 17, 19, 12,
+ 7, 8, 9, 11, 17, 20, 14, 8, 4, 5, 11, 17, 21, 14, 9, 3, 2, 10, 15,
+ 21, 17, 11, 5, 1, 10, 16, 21, 21, 14, 7, 2, 12, 18, 22, 22, 16, 10, 4,
+ 26, 30, 33, 39, 37, 35, 31, 25, 29, 32, 37, 37, 33, 30, 23, 26, 32, 34, 36,
+ 32, 28, 22, 24, 28, 31, 33, 30, 25, 19, 21, 25, 27, 30, 28, 25, 16, 19, 21,
+ 25, 28, 28, 25, 14, 17, 20, 24, 27, 28, 25, 23, 28, 30, 34, 34, 31, 27, 22,
+ 26, 29, 32, 33, 30, 26, 21, 24, 28, 30, 31, 28, 24, 20, 22, 25, 28, 29, 26,
+ 20, 17, 19, 21, 23, 24, 23, 20, 14, 16, 19, 21, 22, 23, 20, 12, 15, 18, 21,
+ 21, 21, 20, 21, 24, 27, 27, 29, 27, 23, 19, 23, 26, 26, 26, 25, 21, 18, 22,
+ 23, 23, 24, 24, 19, 15, 20, 20, 20, 20, 20, 15, 10, 16, 17, 17, 17, 16, 15,
+ 7, 14, 15, 15, 15, 15, 14, 7, 12, 14, 14, 14, 14, 14, 18, 19, 20, 20, 22,
+ 21, 15, 15, 17, 18, 20, 20, 19, 13, 12, 16, 16, 16, 16, 16, 12, 8, 14, 13,
+ 12, 13, 13, 11, 6, 10, 9, 9, 9, 9, 7, 6, 8, 8, 7, 7, 8, 4, 7,
+ 5, 6, 6, 6, 6, 4, 7, 11, 12, 12, 13, 13, 15, 4, 9, 10, 11, 11, 10,
+ 13, 4, 8, 8, 8, 8, 8, 8, 4, 5, 5, 4, 4, 4, 4, 2, 0, 0, 0,
+ 0, 2, 3, 3, 4, 4, 3, 2, 2, 3, 4, 5, 5, 4, 4, 2, 2, 4, 5,
+ 6, 7, 9, 11, 13, 4, 7, 7, 7, 8, 9, 10, 4, 9, 8, 7, 5, 6, 7,
+ 4, 8, 9, 8, 5, 3, 3, 2, 7, 11, 9, 6, 2, 2, 3, 9, 14, 12, 8,
+ 3, 2, 4, 9, 14, 14, 10, 5, 1, 10, 14, 12, 9, 8, 10, 11, 10, 14, 14,
+ 10, 7, 8, 8, 10, 14, 15, 10, 7, 4, 5, 10, 13, 15, 11, 8, 3, 2, 10,
+ 13, 16, 14, 10, 5, 1, 10, 15, 18, 17, 12, 7, 2, 11, 15, 18, 18, 13, 9,
+ 3, 24, 27, 31, 35, 36, 33, 29, 22, 26, 30, 34, 36, 32, 28, 21, 25, 28, 32,
+ 33, 29, 25, 19, 22, 26, 29, 31, 27, 23, 17, 19, 21, 25, 29, 26, 23, 15, 17,
+ 19, 23, 27, 26, 22, 13, 16, 19, 22, 26, 26, 23, 21, 25, 28, 32, 31, 28, 25,
+ 20, 22, 27, 31, 31, 28, 23, 19, 22, 26, 28, 28, 25, 21, 17, 20, 23, 25, 25,
+ 23, 18, 15, 17, 19, 22, 22, 21, 18, 13, 15, 17, 20, 20, 21, 19, 10, 14, 17,
+ 19, 19, 19, 18, 19, 22, 23, 25, 27, 24, 20, 18, 21, 23, 24, 24, 23, 18, 17,
+ 19, 21, 22, 21, 20, 16, 15, 17, 18, 18, 18, 18, 13, 10, 14, 15, 14, 14, 15,
+ 13, 6, 13, 14, 13, 13, 13, 13, 6, 12, 12, 12, 12, 12, 12, 17, 17, 17, 18,
+ 19, 19, 13, 14, 15, 16, 16, 17, 17, 10, 11, 14, 14, 14, 14, 13, 9, 7, 12,
+ 11, 10, 10, 10, 9, 5, 8, 8, 7, 7, 7, 5, 5, 6, 6, 6, 6, 6, 3,
+ 6, 4, 4, 4, 4, 4, 3, 7, 8, 9, 10, 11, 11, 13, 2, 7, 8, 8, 9,
+ 8, 10, 2, 6, 6, 5, 6, 6, 6, 2, 3, 3, 2, 2, 3, 3, 1, 3, 2,
+ 2, 2, 0, 2, 2, 5, 5, 4, 2, 1, 2, 3, 6, 7, 4, 3, 2, 2, 3,
+ 4, 5, 7, 8, 10, 11, 3, 4, 5, 6, 7, 7, 8, 3, 6, 5, 4, 4, 4,
+ 5, 3, 5, 5, 3, 2, 1, 2, 3, 7, 7, 5, 4, 2, 1, 5, 7, 10, 7,
+ 4, 3, 0, 6, 8, 10, 8, 5, 3, 1, 6, 8, 7, 5, 7, 8, 9, 6, 8,
+ 7, 6, 6, 6, 6, 6, 8, 9, 5, 3, 3, 3, 6, 8, 10, 7, 4, 2, 0,
+ 7, 10, 11, 8, 5, 3, 2, 8, 10, 11, 10, 6, 4, 2, 9, 10, 11, 11, 7,
+ 4, 3, 19, 22, 26, 30, 30, 27, 23, 17, 21, 25, 29, 29, 25, 22, 16, 20, 23,
+ 26, 28, 24, 20, 15, 17, 21, 24, 26, 23, 18, 12, 14, 17, 20, 24, 22, 18, 10,
+ 13, 16, 19, 22, 22, 18, 8, 11, 15, 18, 20, 21, 18, 16, 20, 23, 27, 25, 23,
+ 20, 15, 18, 22, 26, 26, 22, 18, 14, 17, 21, 23, 23, 20, 16, 12, 15, 18, 21,
+ 20, 19, 14, 9, 12, 15, 17, 17, 17, 14, 8, 11, 14, 16, 16, 16, 14, 6, 9,
+ 13, 15, 14, 15, 14, 14, 17, 19, 19, 21, 20, 15, 12, 15, 17, 18, 19, 18, 13,
+ 11, 14, 16, 16, 16, 16, 11, 10, 12, 13, 12, 13, 13, 9, 6, 9, 10, 10, 10,
+ 10, 9, 5, 8, 9, 9, 9, 9, 9, 6, 7, 8, 8, 8, 8, 8, 11, 11, 12,
+ 13, 14, 15, 9, 10, 10, 10, 11, 11, 12, 7, 8, 9, 9, 9, 8, 8, 5, 4,
+ 6, 5, 5, 5, 5, 4, 3, 3, 3, 3, 3, 2, 2, 4, 3, 2, 2, 2, 2,
+ 1, 6, 7, 4, 3, 2, 1, 0, 3, 3, 5, 6, 7, 8, 9, 2, 3, 4, 5,
+ 6, 6, 7, 2, 3, 3, 3, 3, 3, 4, 2, 5, 3, 2, 2, 2, 1, 3, 6,
+ 4, 3, 3, 2, 0, 4, 7, 6, 4, 3, 2, 2, 6, 8, 8, 6, 4, 3, 2,
+ 6, 6, 5, 5, 6, 7, 8, 6, 7, 5, 5, 5, 5, 5, 6, 8, 6, 4, 3,
+ 2, 2, 6, 8, 6, 5, 4, 3, 1, 6, 7, 9, 8, 5, 3, 2, 7, 9, 13,
+ 11, 7, 4, 2, 8, 10, 14, 13, 9, 5, 3, 8, 11, 9, 6, 5, 7, 7, 8,
+ 11, 11, 7, 4, 4, 4, 8, 11, 13, 7, 4, 3, 2, 8, 10, 14, 8, 5, 3,
+ 2, 8, 11, 15, 13, 8, 5, 3, 9, 13, 17, 17, 10, 6, 3, 11, 15, 18, 18,
+ 12, 7, 4, 23, 25, 27, 31, 31, 29, 25, 23, 24, 26, 29, 30, 27, 25, 21, 24,
+ 27, 27, 28, 25, 22, 20, 21, 24, 25, 26, 22, 16, 17, 19, 20, 21, 20, 18, 16,
+ 12, 14, 16, 16, 18, 18, 16, 8, 11, 14, 16, 18, 18, 16, 21, 24, 25, 28, 27,
+ 26, 24, 20, 23, 25, 27, 27, 24, 23, 18, 22, 25, 24, 25, 22, 20, 17, 20, 22,
+ 22, 21, 20, 15, 14, 16, 18, 19, 16, 15, 14, 9, 12, 14, 15, 14, 15, 15, 6,
+ 8, 12, 14, 14, 15, 15, 19, 22, 23, 24, 24, 24, 21, 16, 20, 22, 23, 24, 23,
+ 21, 15, 19, 21, 22, 22, 21, 19, 14, 17, 18, 18, 18, 19, 14, 9, 13, 14, 15,
+ 14, 14, 13, 4, 9, 10, 11, 12, 13, 13, 7, 7, 8, 10, 11, 12, 13, 15, 17,
+ 18, 20, 20, 21, 16, 14, 15, 16, 18, 18, 18, 14, 11, 13, 15, 15, 15, 15, 12,
+ 7, 11, 11, 11, 12, 11, 10, 5, 7, 7, 7, 6, 7, 6, 3, 3, 3, 3, 5,
+ 6, 6, 6, 6, 4, 2, 3, 4, 5, 8, 10, 11, 13, 14, 15, 16, 5, 9, 10,
+ 12, 12, 13, 15, 5, 8, 9, 9, 10, 10, 11, 5, 6, 6, 6, 6, 6, 5, 3,
+ 3, 3, 3, 3, 2, 4, 0, 3, 3, 2, 2, 2, 5, 4, 4, 5, 3, 3, 3,
+ 3, 7, 8, 8, 9, 11, 14, 15, 7, 9, 8, 9, 9, 11, 13, 7, 9, 9, 9,
+ 7, 8, 10, 7, 8, 8, 8, 6, 5, 5, 5, 7, 9, 7, 5, 3, 2, 3, 5,
+ 10, 8, 6, 3, 2, 4, 7, 10, 10, 7, 4, 2, 10, 13, 13, 10, 9, 12, 13,
+ 10, 14, 14, 11, 9, 9, 11, 10, 13, 16, 11, 8, 7, 8, 10, 13, 15, 10, 8,
+ 6, 2, 9, 11, 14, 11, 7, 4, 2, 7, 10, 13, 12, 8, 5, 3, 7, 11, 13,
+ 13, 9, 6, 3, 30, 34, 36, 38, 39, 38, 34, 29, 33, 36, 38, 38, 35, 33, 27,
+ 31, 33, 36, 37, 34, 30, 26, 29, 31, 35, 35, 30, 24, 23, 26, 27, 30, 29, 27,
+ 24, 18, 22, 23, 25, 27, 26, 24, 14, 18, 21, 24, 26, 26, 24, 27, 31, 35, 37,
+ 36, 35, 31, 25, 30, 33, 36, 36, 33, 29, 24, 28, 31, 34, 34, 31, 28, 23, 26,
+ 30, 32, 29, 28, 22, 19, 23, 25, 27, 25, 24, 22, 15, 18, 20, 22, 23, 24, 22,
+ 11, 15, 19, 22, 22, 22, 22, 24, 29, 30, 32, 33, 32, 28, 22, 26, 30, 31, 30,
+ 31, 26, 22, 25, 28, 28, 28, 28, 23, 19, 23, 24, 25, 25, 25, 18, 13, 19, 21,
+ 21, 19, 19, 19, 7, 15, 16, 16, 18, 18, 19, 7, 11, 14, 15, 16, 17, 17, 21,
+ 23, 24, 26, 27, 27, 21, 18, 21, 22, 24, 24, 24, 19, 14, 20, 20, 21, 21, 21,
+ 17, 11, 17, 17, 17, 17, 17, 15, 9, 12, 12, 13, 12, 12, 11, 6, 8, 8, 9,
+ 9, 10, 8, 7, 5, 6, 7, 8, 8, 8, 11, 14, 16, 17, 18, 19, 20, 7, 13,
+ 14, 16, 17, 16, 19, 7, 13, 13, 13, 14, 14, 15, 7, 9, 10, 9, 9, 9, 8,
+ 5, 3, 5, 5, 4, 5, 7, 3, 0, 2, 2, 1, 3, 7, 3, 3, 3, 2, 2,
+ 2, 5, 7, 9, 10, 12, 15, 18, 20, 7, 7, 9, 11, 13, 16, 16, 7, 8, 8,
+ 8, 10, 12, 12, 7, 7, 7, 7, 6, 7, 6, 5, 5, 6, 5, 3, 2, 5, 2,
+ 3, 5, 4, 3, 2, 4, 3, 5, 6, 6, 4, 2, 3, 9, 11, 11, 9, 12, 16,
+ 17, 9, 11, 12, 10, 10, 13, 15, 9, 11, 13, 9, 7, 9, 11, 9, 10, 12, 8,
+ 6, 5, 4, 7, 9, 10, 8, 4, 2, 3, 5, 7, 9, 8, 4, 3, 2, 4, 6,
+ 9, 9, 5, 3, 2, 32, 36, 40, 42, 43, 41, 37, 31, 35, 39, 42, 42, 39, 35,
+ 29, 33, 38, 40, 41, 37, 33, 28, 30, 35, 37, 39, 34, 27, 25, 27, 30, 32, 33,
+ 30, 27, 21, 23, 26, 28, 31, 31, 27, 16, 20, 24, 27, 30, 30, 27, 29, 34, 37,
+ 39, 39, 37, 34, 28, 33, 35, 38, 39, 35, 32, 26, 30, 34, 36, 36, 33, 28, 25,
+ 28, 31, 33, 34, 30, 24, 21, 24, 27, 29, 27, 26, 24, 16, 20, 23, 25, 25, 27,
+ 24, 13, 17, 21, 24, 24, 25, 24, 26, 30, 32, 34, 35, 34, 29, 25, 28, 31, 33,
+ 33, 31, 26, 22, 26, 30, 30, 29, 29, 24, 19, 25, 26, 26, 27, 25, 20, 13, 21,
+ 22, 23, 20, 21, 20, 8, 17, 18, 18, 18, 19, 20, 7, 14, 16, 16, 17, 17, 18,
+ 23, 24, 25, 26, 28, 29, 21, 19, 22, 24, 25, 26, 26, 19, 15, 21, 22, 22, 22,
+ 22, 18, 11, 18, 18, 18, 18, 17, 16, 9, 13, 15, 14, 13, 12, 12, 7, 8, 10,
+ 10, 10, 11, 9, 7, 5, 8, 9, 9, 10, 7, 11, 16, 17, 18, 19, 19, 21, 7,
+ 15, 16, 17, 18, 17, 20, 7, 13, 14, 14, 14, 14, 14, 7, 9, 11, 10, 10, 9,
+ 8, 5, 3, 6, 6, 4, 5, 6, 3, 2, 0, 2, 3, 4, 6, 4, 3, 3, 2,
+ 2, 3, 5, 7, 10, 12, 13, 16, 18, 20, 7, 9, 11, 12, 14, 16, 16, 7, 7,
+ 9, 9, 11, 13, 12, 7, 6, 7, 7, 7, 7, 6, 5, 4, 9, 9, 5, 3, 5,
+ 3, 5, 11, 11, 7, 2, 5, 1, 7, 11, 12, 9, 4, 4, 9, 15, 14, 10, 14,
+ 17, 18, 8, 15, 16, 10, 12, 14, 14, 9, 14, 18, 11, 8, 10, 11, 8, 14, 18,
+ 12, 7, 5, 5, 7, 13, 17, 14, 9, 4, 4, 7, 13, 16, 14, 11, 6, 3, 8,
+ 13, 15, 15, 12, 8, 2, 30, 33, 38, 42, 43, 38, 36, 29, 33, 37, 41, 41, 38,
+ 35, 28, 31, 35, 39, 40, 36, 31, 26, 29, 33, 35, 38, 33, 27, 23, 26, 28, 30,
+ 32, 30, 27, 19, 22, 25, 27, 31, 30, 27, 16, 19, 22, 26, 30, 29, 27, 27, 31,
+ 34, 39, 38, 35, 32, 26, 30, 34, 38, 37, 34, 30, 26, 29, 32, 35, 34, 31, 26,
+ 23, 25, 30, 32, 31, 29, 23, 20, 23, 26, 28, 26, 26, 22, 16, 19, 22, 23, 23,
+ 25, 23, 12, 17, 20, 23, 23, 24, 23, 25, 29, 30, 31, 34, 32, 27, 23, 27, 29,
+ 31, 31, 29, 25, 22, 26, 28, 28, 28, 27, 22, 18, 23, 25, 25, 25, 23, 18, 12,
+ 20, 21, 21, 20, 19, 18, 7, 16, 17, 17, 17, 18, 18, 6, 14, 16, 16, 16, 16,
+ 16, 21, 23, 24, 25, 26, 26, 20, 18, 22, 23, 23, 24, 23, 17, 14, 20, 21, 20,
+ 21, 21, 16, 10, 17, 17, 17, 17, 16, 14, 8, 12, 13, 13, 12, 11, 11, 7, 8,
+ 10, 10, 9, 10, 7, 6, 5, 8, 8, 8, 8, 5, 9, 15, 15, 17, 17, 17, 19,
+ 6, 13, 14, 15, 15, 14, 17, 6, 12, 12, 12, 12, 11, 12, 6, 8, 9, 9, 8,
+ 7, 7, 4, 2, 5, 4, 3, 4, 5, 2, 2, 2, 0, 2, 3, 4, 3, 2, 3,
+ 2, 2, 2, 4, 6, 9, 10, 11, 13, 16, 17, 6, 7, 9, 10, 12, 13, 14, 6,
+ 7, 7, 8, 9, 10, 9, 6, 6, 7, 7, 5, 6, 4, 4, 5, 9, 9, 4, 2,
+ 3, 2, 5, 11, 11, 6, 1, 3, 2, 6, 11, 13, 8, 3, 3, 9, 15, 14, 9,
+ 12, 14, 15, 9, 15, 17, 10, 10, 12, 12, 9, 15, 18, 11, 7, 9, 9, 9, 14,
+ 19, 12, 7, 4, 3, 7, 13, 19, 15, 8, 3, 2, 7, 13, 18, 17, 10, 5, 2,
+ 8, 14, 19, 18, 13, 7, 2, 29, 31, 36, 40, 40, 38, 34, 27, 30, 35, 39, 38,
+ 36, 32, 25, 29, 33, 37, 38, 33, 30, 24, 27, 31, 34, 36, 32, 26, 21, 24, 26,
+ 30, 32, 30, 25, 18, 20, 23, 27, 30, 28, 26, 16, 18, 22, 25, 29, 29, 26, 26,
+ 29, 33, 37, 35, 34, 30, 25, 28, 32, 35, 36, 32, 27, 22, 27, 30, 33, 33, 30,
+ 25, 22, 24, 26, 30, 30, 27, 21, 19, 21, 23, 26, 25, 25, 22, 16, 18, 20, 23,
+ 24, 24, 21, 12, 16, 19, 22, 22, 23, 21, 23, 26, 28, 29, 31, 29, 25, 22, 25,
+ 27, 29, 28, 27, 23, 20, 24, 26, 26, 26, 25, 20, 17, 22, 22, 22, 23, 22, 16,
+ 11, 18, 19, 19, 18, 18, 16, 7, 16, 17, 16, 16, 17, 16, 7, 15, 15, 15, 15,
+ 16, 15, 20, 21, 21, 22, 24, 24, 17, 17, 20, 20, 21, 21, 21, 15, 13, 19, 19,
+ 18, 18, 18, 13, 9, 16, 15, 15, 15, 14, 12, 7, 11, 11, 11, 10, 10, 9, 7,
+ 8, 10, 9, 9, 9, 6, 7, 5, 8, 8, 8, 8, 4, 8, 13, 14, 14, 15, 14,
+ 16, 5, 12, 12, 13, 13, 11, 15, 5, 10, 10, 10, 10, 9, 10, 5, 6, 7, 6,
+ 6, 5, 5, 3, 1, 3, 2, 2, 2, 3, 2, 1, 3, 2, 0, 2, 3, 4, 2,
+ 4, 2, 2, 1, 3, 4, 6, 7, 9, 10, 13, 14, 4, 5, 6, 8, 9, 10, 11,
+ 4, 7, 7, 6, 7, 7, 7, 4, 6, 7, 7, 3, 4, 3, 2, 5, 10, 8, 5,
+ 1, 2, 0, 6, 11, 11, 6, 2, 2, 3, 7, 12, 12, 8, 3, 2, 9, 12, 11,
+ 8, 9, 11, 12, 9, 13, 12, 9, 8, 9, 9, 9, 13, 14, 9, 6, 6, 7, 9,
+ 12, 14, 10, 7, 2, 2, 8, 12, 15, 13, 8, 3, 2, 8, 13, 16, 15, 10, 5,
+ 1, 8, 13, 16, 16, 11, 7, 2, 26, 28, 32, 36, 37, 34, 31, 24, 27, 31, 36,
+ 36, 33, 29, 23, 26, 29, 33, 34, 32, 27, 21, 24, 27, 31, 33, 29, 24, 18, 21,
+ 24, 27, 31, 27, 24, 16, 18, 22, 25, 28, 27, 24, 15, 17, 21, 23, 27, 27, 24,
+ 23, 27, 30, 35, 33, 31, 27, 21, 26, 29, 32, 32, 28, 25, 21, 23, 28, 30, 31,
+ 28, 23, 20, 22, 24, 27, 27, 25, 20, 17, 18, 21, 24, 24, 23, 20, 15, 17, 19,
+ 22, 22, 22, 20, 12, 16, 18, 20, 20, 21, 19, 21, 24, 25, 27, 28, 27, 22, 19,
+ 22, 25, 25, 26, 24, 20, 18, 21, 23, 23, 23, 22, 18, 16, 19, 20, 20, 20, 19,
+ 15, 10, 16, 16, 16, 16, 16, 14, 7, 15, 16, 15, 15, 15, 14, 7, 14, 14, 13,
+ 14, 14, 13, 18, 19, 18, 20, 21, 21, 14, 15, 17, 18, 18, 19, 18, 12, 12, 16,
+ 16, 16, 15, 15, 11, 8, 14, 13, 12, 12, 11, 10, 6, 10, 9, 9, 8, 8, 7,
+ 6, 8, 8, 7, 7, 7, 4, 7, 4, 6, 6, 6, 6, 3, 8, 10, 11, 12, 12,
+ 12, 13, 3, 9, 9, 10, 11, 9, 11, 3, 8, 7, 7, 7, 7, 7, 3, 4, 4,
+ 3, 3, 3, 3, 1, 1, 1, 1, 2, 1, 2, 2, 3, 4, 3, 2, 0, 3, 3,
+ 5, 5, 3, 3, 2, 2, 3, 5, 6, 7, 8, 10, 11, 3, 4, 6, 7, 7, 8,
+ 8, 3, 4, 4, 4, 5, 5, 6, 3, 4, 4, 3, 2, 2, 2, 2, 5, 6, 4,
+ 3, 2, 2, 3, 6, 8, 6, 3, 2, 1, 5, 7, 8, 7, 4, 3, 0, 5, 7,
+ 6, 6, 8, 9, 10, 5, 7, 7, 6, 6, 7, 7, 6, 7, 8, 4, 3, 4, 4,
+ 5, 7, 8, 5, 3, 2, 1, 5, 8, 9, 7, 4, 2, 0, 6, 8, 10, 9, 5,
+ 3, 2, 7, 9, 10, 10, 6, 4, 3, 20, 24, 29, 32, 32, 29, 25, 20, 23, 27,
+ 31, 31, 28, 25, 18, 21, 25, 29, 30, 25, 22, 16, 19, 22, 26, 28, 23, 20, 13,
+ 16, 19, 22, 26, 23, 19, 11, 14, 17, 20, 24, 23, 20, 10, 13, 16, 19, 22, 23,
+ 20, 18, 21, 25, 29, 28, 26, 22, 16, 20, 24, 28, 28, 24, 20, 15, 19, 23, 25,
+ 24, 23, 18, 14, 17, 20, 22, 22, 20, 16, 11, 13, 16, 18, 19, 18, 16, 10, 12,
+ 15, 17, 18, 17, 15, 7, 11, 14, 16, 16, 16, 16, 15, 19, 21, 21, 23, 21, 17,
+ 14, 17, 20, 21, 21, 20, 15, 13, 16, 18, 17, 18, 18, 14, 12, 14, 15, 15, 15,
+ 15, 11, 8, 11, 12, 12, 12, 12, 11, 5, 10, 11, 10, 10, 10, 11, 5, 8, 9,
+ 9, 10, 9, 9, 14, 13, 14, 14, 16, 17, 10, 11, 11, 12, 13, 13, 14, 8, 9,
+ 10, 11, 11, 10, 10, 7, 5, 8, 7, 7, 7, 7, 6, 4, 5, 5, 4, 4, 4,
+ 3, 5, 3, 3, 3, 3, 3, 3, 6, 5, 3, 2, 2, 2, 2, 4, 5, 6, 7,
+ 8, 9, 10, 2, 4, 5, 6, 6, 7, 8, 2, 3, 4, 4, 4, 4, 5, 2, 3,
+ 2, 2, 2, 1, 2, 1, 6, 4, 3, 3, 2, 2, 4, 7, 7, 5, 3, 2, 0,
+ 6, 8, 8, 5, 4, 3, 1, 5, 5, 5, 5, 6, 8, 9, 4, 6, 5, 5, 5,
+ 6, 6, 5, 7, 5, 4, 3, 3, 3, 5, 7, 7, 5, 4, 3, 0, 6, 8, 8,
+ 6, 4, 3, 1, 7, 9, 10, 8, 5, 3, 2, 8, 9, 11, 10, 6, 4, 2, 8,
+ 10, 7, 5, 6, 7, 7, 8, 9, 9, 6, 5, 5, 5, 8, 10, 10, 5, 4, 2,
+ 2, 8, 10, 11, 7, 5, 3, 2, 9, 10, 12, 10, 6, 4, 3, 9, 10, 14, 13,
+ 8, 5, 3, 9, 11, 14, 14, 9, 5, 3, 27, 30, 32, 34, 33, 31, 30, 26, 29,
+ 32, 33, 33, 30, 28, 25, 29, 30, 31, 31, 27, 24, 24, 25, 27, 28, 28, 23, 17,
+ 20, 23, 24, 23, 21, 18, 17, 17, 19, 19, 17, 19, 18, 17, 11, 13, 16, 18, 18,
+ 18, 17, 25, 27, 31, 32, 31, 30, 30, 24, 28, 30, 31, 31, 29, 26, 22, 25, 28,
+ 30, 28, 26, 23, 21, 24, 26, 26, 25, 21, 16, 18, 20, 22, 22, 17, 16, 16, 13,
+ 16, 17, 16, 16, 16, 16, 7, 11, 14, 16, 15, 16, 16, 22, 26, 28, 29, 30, 29,
+ 26, 21, 25, 27, 28, 28, 27, 24, 19, 23, 25, 26, 26, 25, 20, 18, 21, 22, 22,
+ 22, 20, 15, 11, 17, 18, 18, 15, 15, 14, 5, 12, 13, 13, 13, 13, 15, 4, 7,
+ 10, 11, 12, 13, 13, 19, 20, 22, 23, 25, 25, 22, 17, 19, 20, 22, 23, 23, 20,
+ 14, 18, 19, 19, 19, 18, 16, 11, 16, 15, 15, 15, 13, 13, 8, 11, 11, 10, 8,
+ 8, 9, 5, 5, 5, 5, 6, 7, 7, 4, 3, 2, 4, 5, 5, 7, 13, 16, 18,
+ 19, 20, 22, 22, 9, 15, 17, 18, 18, 19, 20, 9, 15, 15, 16, 16, 16, 14, 9,
+ 11, 12, 12, 11, 9, 7, 7, 6, 7, 6, 4, 3, 7, 4, 3, 4, 3, 4, 3,
+ 6, 0, 3, 4, 3, 4, 4, 4, 10, 11, 13, 14, 17, 20, 21, 10, 10, 12, 13,
+ 16, 17, 18, 10, 11, 11, 11, 12, 14, 13, 10, 10, 10, 9, 8, 7, 6, 8, 8,
+ 9, 8, 5, 4, 5, 5, 7, 9, 8, 5, 4, 3, 4, 5, 8, 9, 7, 5, 3,
+ 12, 14, 14, 13, 15, 19, 20, 12, 14, 15, 13, 13, 15, 17, 12, 14, 16, 12, 10,
+ 11, 11, 12, 13, 15, 11, 9, 6, 3, 10, 12, 14, 11, 7, 4, 3, 9, 10, 13,
+ 12, 8, 5, 4, 7, 10, 12, 13, 9, 6, 4, 34, 37, 39, 42, 42, 40, 38, 32,
+ 35, 39, 42, 42, 39, 36, 31, 34, 37, 39, 39, 36, 31, 29, 32, 34, 37, 36, 31,
+ 25, 26, 29, 30, 31, 30, 27, 25, 22, 24, 25, 25, 28, 27, 25, 16, 19, 21, 25,
+ 27, 27, 25, 30, 34, 38, 41, 40, 37, 35, 29, 33, 37, 39, 40, 36, 33, 27, 32,
+ 35, 37, 36, 33, 30, 26, 29, 33, 34, 32, 28, 23, 23, 26, 28, 29, 27, 24, 23,
+ 17, 21, 23, 22, 24, 24, 23, 11, 16, 20, 22, 23, 24, 23, 27, 31, 34, 35, 36,
+ 35, 30, 26, 29, 33, 34, 35, 33, 29, 23, 29, 31, 31, 31, 30, 25, 21, 26, 27,
+ 28, 27, 25, 20, 15, 22, 24, 23, 20, 20, 20, 9, 18, 18, 17, 17, 18, 20, 6,
+ 13, 15, 15, 16, 17, 18, 25, 26, 27, 28, 30, 31, 25, 20, 24, 26, 27, 27, 27,
+ 23, 17, 23, 24, 24, 24, 23, 21, 13, 20, 20, 20, 20, 18, 17, 11, 14, 15, 15,
+ 12, 12, 13, 8, 8, 11, 9, 10, 11, 10, 6, 4, 7, 7, 8, 9, 9, 13, 19,
+ 20, 22, 22, 24, 25, 10, 19, 19, 21, 21, 21, 22, 10, 17, 18, 18, 18, 17, 17,
+ 10, 12, 15, 14, 13, 11, 9, 7, 6, 10, 9, 5, 6, 7, 4, 3, 3, 2, 2,
+ 5, 8, 3, 0, 3, 2, 2, 3, 7, 10, 14, 15, 17, 20, 22, 24, 10, 12, 14,
+ 16, 18, 19, 20, 10, 10, 12, 12, 15, 16, 14, 10, 9, 8, 9, 10, 9, 7, 8,
+ 7, 7, 6, 4, 3, 7, 5, 5, 7, 5, 4, 2, 6, 2, 4, 6, 6, 4, 3,
+ 5, 10, 13, 13, 13, 17, 21, 22, 10, 13, 13, 12, 16, 18, 19, 10, 13, 14, 11,
+ 11, 13, 13, 10, 12, 13, 10, 8, 7, 6, 8, 10, 11, 9, 5, 3, 5, 7, 8,
+ 9, 8, 5, 3, 3, 5, 7, 9, 9, 6, 4, 2, 36, 39, 42, 47, 46, 43, 40,
+ 33, 38, 41, 46, 46, 42, 38, 33, 35, 40, 43, 43, 39, 35, 31, 33, 37, 39, 41,
+ 36, 31, 27, 30, 34, 35, 35, 32, 30, 23, 26, 28, 30, 33, 33, 30, 19, 22, 26,
+ 29, 32, 32, 30, 32, 36, 41, 44, 42, 41, 38, 31, 34, 39, 42, 42, 39, 35, 29,
+ 34, 37, 39, 40, 38, 32, 28, 31, 35, 36, 36, 32, 26, 23, 27, 30, 32, 30, 29,
+ 26, 18, 23, 26, 27, 27, 27, 27, 13, 20, 23, 26, 26, 26, 26, 30, 34, 35, 37,
+ 38, 38, 32, 27, 31, 35, 36, 36, 35, 30, 25, 30, 34, 34, 33, 32, 27, 21, 28,
+ 29, 29, 29, 28, 22, 15, 24, 26, 25, 23, 23, 22, 9, 20, 21, 20, 20, 21, 22,
+ 8, 17, 18, 19, 19, 20, 20, 25, 28, 29, 31, 31, 32, 26, 20, 26, 28, 29, 29,
+ 29, 23, 17, 24, 25, 26, 25, 25, 21, 14, 21, 22, 21, 22, 20, 18, 11, 15, 18,
+ 18, 15, 14, 14, 9, 10, 13, 13, 13, 13, 11, 8, 5, 10, 10, 11, 12, 8, 13,
+ 20, 20, 22, 23, 22, 25, 10, 18, 19, 21, 21, 20, 23, 9, 16, 18, 17, 17, 17,
+ 17, 10, 11, 14, 13, 13, 10, 10, 7, 5, 10, 9, 6, 7, 8, 5, 3, 3, 3,
+ 3, 5, 8, 4, 3, 0, 2, 3, 5, 7, 8, 14, 15, 17, 19, 22, 23, 9, 12,
+ 14, 15, 17, 19, 19, 9, 9, 12, 12, 14, 15, 14, 9, 8, 8, 8, 10, 9, 7,
+ 6, 5, 7, 7, 3, 5, 7, 4, 3, 8, 7, 5, 4, 7, 2, 4, 8, 8, 6,
+ 2, 5, 9, 12, 12, 13, 17, 20, 21, 9, 12, 14, 11, 15, 17, 18, 9, 12, 15,
+ 9, 11, 13, 13, 9, 11, 14, 10, 7, 7, 6, 7, 10, 13, 11, 6, 2, 5, 5,
+ 10, 11, 10, 7, 4, 4, 5, 8, 10, 10, 8, 6, 3, 32, 36, 39, 43, 44, 41,
+ 38, 31, 35, 38, 43, 44, 39, 35, 29, 33, 37, 40, 40, 38, 33, 28, 30, 34, 37,
+ 38, 34, 29, 25, 28, 30, 33, 35, 31, 29, 22, 24, 26, 29, 32, 31, 28, 18, 21,
+ 24, 28, 31, 32, 29, 29, 34, 38, 40, 41, 37, 34, 28, 32, 35, 39, 39, 36, 31,
+ 27, 30, 35, 38, 37, 33, 29, 25, 28, 31, 34, 33, 31, 24, 21, 25, 27, 29, 28,
+ 27, 24, 17, 21, 23, 25, 26, 27, 24, 13, 18, 22, 24, 24, 25, 25, 27, 30, 33,
+ 34, 35, 33, 29, 25, 29, 31, 33, 34, 32, 27, 23, 28, 30, 30, 30, 29, 24, 19,
+ 25, 25, 26, 26, 26, 20, 13, 21, 22, 23, 21, 20, 19, 8, 17, 19, 19, 19, 19,
+ 19, 7, 16, 17, 18, 18, 19, 18, 23, 25, 26, 27, 29, 28, 21, 18, 23, 24, 26,
+ 26, 25, 19, 15, 22, 23, 22, 22, 21, 18, 12, 19, 19, 19, 18, 17, 16, 9, 13,
+ 15, 15, 13, 12, 12, 8, 9, 11, 11, 11, 11, 9, 8, 5, 10, 9, 10, 10, 6,
+ 11, 16, 18, 19, 20, 18, 21, 7, 15, 16, 17, 17, 16, 19, 7, 12, 14, 14, 13,
+ 13, 14, 7, 8, 11, 10, 10, 7, 8, 5, 2, 6, 6, 4, 4, 6, 3, 2, 2,
+ 2, 2, 3, 5, 3, 2, 2, 0, 2, 3, 4, 6, 10, 11, 13, 15, 16, 19, 6,
+ 9, 10, 11, 13, 14, 15, 6, 7, 8, 9, 10, 11, 10, 6, 5, 6, 5, 6, 7,
+ 5, 4, 3, 7, 7, 3, 3, 4, 2, 3, 10, 10, 5, 2, 4, 1, 4, 10, 12,
+ 7, 2, 3, 7, 13, 12, 10, 13, 16, 16, 7, 13, 15, 9, 11, 13, 13, 7, 13,
+ 17, 10, 8, 10, 10, 7, 12, 17, 11, 5, 5, 4, 6, 11, 17, 13, 7, 2, 3,
+ 5, 12, 17, 16, 9, 4, 3, 6, 12, 17, 17, 11, 6, 2, 30, 35, 39, 42, 42,
+ 39, 36, 29, 34, 38, 41, 42, 38, 34, 28, 31, 36, 39, 41, 36, 33, 27, 28, 32,
+ 36, 38, 33, 28, 24, 25, 29, 33, 34, 31, 28, 20, 22, 26, 28, 32, 31, 28, 18,
+ 20, 24, 27, 30, 31, 28, 28, 31, 35, 40, 39, 35, 32, 26, 30, 34, 38, 39, 34,
+ 30, 25, 28, 32, 36, 36, 32, 27, 24, 26, 29, 33, 33, 30, 23, 20, 23, 26, 28,
+ 28, 26, 23, 16, 20, 23, 25, 26, 26, 24, 13, 19, 21, 24, 24, 24, 23, 25, 29,
+ 30, 32, 33, 31, 27, 23, 27, 29, 31, 30, 30, 25, 21, 26, 28, 29, 28, 27, 22,
+ 18, 25, 25, 25, 25, 24, 18, 12, 20, 22, 21, 21, 19, 18, 8, 18, 18, 19, 19,
+ 18, 18, 8, 16, 18, 17, 18, 18, 16, 20, 24, 24, 25, 26, 25, 19, 17, 22, 22,
+ 23, 24, 22, 17, 14, 20, 21, 20, 20, 20, 15, 11, 17, 17, 17, 17, 16, 14, 8,
+ 12, 14, 14, 13, 12, 11, 7, 9, 11, 11, 11, 11, 7, 7, 6, 10, 9, 9, 10,
+ 5, 9, 15, 16, 17, 18, 15, 18, 6, 14, 15, 15, 16, 13, 16, 6, 11, 12, 12,
+ 12, 10, 12, 6, 6, 9, 9, 8, 6, 7, 4, 2, 5, 4, 4, 3, 4, 3, 2,
+ 2, 2, 2, 2, 4, 4, 2, 3, 2, 0, 2, 3, 5, 8, 9, 10, 12, 14, 16,
+ 5, 6, 8, 9, 11, 11, 12, 5, 5, 6, 6, 8, 9, 8, 4, 4, 6, 5, 4,
+ 5, 4, 2, 4, 7, 7, 3, 2, 3, 2, 4, 10, 9, 4, 1, 3, 2, 5, 10,
+ 11, 6, 2, 2, 7, 11, 10, 7, 11, 13, 14, 7, 11, 11, 8, 9, 11, 11, 7,
+ 11, 13, 8, 6, 7, 8, 7, 11, 13, 9, 5, 3, 3, 6, 10, 14, 12, 7, 2,
+ 3, 6, 11, 15, 14, 8, 3, 2, 7, 12, 15, 15, 10, 5, 1, 27, 31, 36, 39,
+ 39, 36, 33, 27, 30, 34, 38, 39, 36, 32, 24, 27, 34, 36, 37, 33, 30, 24, 26,
+ 29, 32, 36, 32, 26, 21, 23, 26, 29, 32, 29, 26, 18, 20, 23, 26, 30, 29, 26,
+ 17, 19, 22, 25, 28, 29, 26, 24, 28, 32, 37, 35, 33, 29, 24, 27, 32, 34, 36,
+ 30, 27, 23, 26, 29, 32, 31, 30, 25, 22, 23, 27, 29, 29, 27, 22, 18, 20, 23,
+ 26, 25, 24, 21, 16, 18, 20, 23, 23, 24, 22, 12, 17, 20, 22, 22, 23, 21, 23,
+ 26, 28, 29, 30, 28, 23, 21, 25, 27, 28, 28, 27, 22, 20, 23, 26, 25, 26, 25,
+ 20, 17, 22, 21, 21, 21, 21, 16, 11, 18, 18, 18, 18, 18, 16, 8, 16, 17, 16,
+ 17, 16, 16, 8, 15, 15, 15, 15, 15, 14, 20, 21, 21, 22, 23, 23, 15, 16, 19,
+ 20, 20, 21, 19, 14, 13, 18, 18, 18, 17, 17, 12, 9, 15, 14, 14, 13, 13, 12,
+ 7, 11, 11, 10, 10, 10, 8, 7, 9, 10, 9, 9, 9, 5, 8, 5, 8, 8, 8,
+ 8, 3, 8, 12, 13, 14, 14, 13, 15, 4, 11, 12, 12, 12, 10, 13, 4, 9, 9,
+ 9, 9, 8, 9, 4, 5, 6, 5, 5, 4, 5, 3, 2, 2, 2, 2, 2, 3, 3,
+ 2, 3, 2, 1, 2, 3, 4, 3, 5, 3, 2, 0, 3, 3, 6, 7, 8, 9, 11,
+ 13, 3, 5, 7, 8, 8, 9, 10, 3, 4, 5, 5, 6, 6, 6, 3, 3, 3, 2,
+ 2, 3, 3, 1, 4, 6, 4, 3, 0, 3, 2, 6, 8, 5, 4, 2, 2, 4, 7,
+ 8, 7, 4, 2, 2, 4, 6, 6, 7, 9, 10, 11, 4, 6, 7, 6, 7, 8, 8,
+ 4, 6, 7, 4, 4, 4, 6, 4, 6, 8, 4, 2, 1, 2, 3, 8, 9, 7, 4,
+ 2, 2, 6, 9, 9, 9, 5, 3, 0, 7, 9, 9, 9, 6, 4, 2, 22, 26, 29,
+ 33, 33, 30, 27, 21, 24, 29, 33, 32, 29, 26, 19, 23, 27, 30, 32, 28, 24, 18,
+ 21, 24, 27, 30, 26, 21, 15, 17, 21, 24, 27, 24, 21, 13, 15, 18, 22, 25, 25,
+ 21, 12, 15, 18, 20, 24, 24, 21, 20, 23, 27, 31, 29, 26, 23, 18, 22, 26, 29,
+ 29, 26, 21, 17, 20, 24, 27, 27, 24, 20, 16, 18, 21, 24, 23, 22, 17, 13, 15,
+ 17, 20, 20, 20, 17, 12, 13, 16, 18, 19, 19, 16, 9, 12, 16, 18, 17, 18, 17,
+ 18, 20, 23, 24, 25, 23, 19, 17, 19, 21, 22, 22, 21, 17, 15, 18, 20, 20, 20,
+ 19, 15, 14, 16, 16, 16, 16, 16, 12, 9, 13, 14, 13, 13, 13, 12, 6, 12, 13,
+ 12, 12, 12, 12, 6, 10, 10, 11, 11, 11, 10, 15, 15, 16, 17, 18, 18, 11, 14,
+ 14, 14, 15, 15, 15, 9, 11, 12, 13, 12, 12, 11, 8, 6, 11, 9, 9, 9, 9,
+ 7, 4, 7, 6, 6, 5, 5, 4, 5, 5, 5, 5, 4, 4, 2, 6, 4, 3, 3,
+ 3, 3, 2, 6, 7, 7, 8, 9, 10, 11, 2, 5, 6, 7, 7, 8, 9, 2, 4,
+ 5, 4, 5, 5, 5, 2, 2, 2, 2, 1, 2, 2, 1, 4, 3, 3, 2, 1, 2,
+ 3, 5, 5, 3, 3, 2, 1, 4, 7, 6, 4, 3, 3, 0, 4, 4, 5, 6, 7,
+ 8, 10, 3, 5, 5, 6, 6, 6, 7, 4, 6, 5, 4, 4, 3, 4, 4, 6, 5,
+ 4, 3, 2, 1, 4, 7, 8, 5, 4, 3, 0, 5, 7, 9, 7, 4, 3, 2, 6,
+ 8, 9, 8, 5, 3, 2, 7, 9, 7, 5, 6, 8, 8, 7, 9, 8, 5, 5, 5,
+ 5, 7, 9, 9, 5, 3, 2, 3, 7, 8, 11, 6, 4, 3, 1, 8, 9, 11, 9,
+ 5, 3, 2, 8, 9, 11, 11, 6, 4, 2, 8, 10, 12, 12, 8, 5, 3, 24, 27,
+ 29, 32, 31, 29, 26, 23, 26, 28, 30, 32, 29, 25, 22, 24, 27, 29, 31, 29, 25,
+ 21, 23, 26, 28, 31, 28, 27, 20, 23, 25, 28, 31, 29, 26, 19, 23, 25, 27, 30,
+ 29, 27, 16, 20, 23, 26, 29, 28, 27, 22, 24, 27, 30, 28, 25, 24, 21, 24, 26,
+ 29, 28, 26, 24, 19, 22, 25, 28, 27, 25, 24, 18, 22, 24, 27, 26, 25, 23, 18,
+ 21, 24, 27, 26, 26, 24, 16, 19, 22, 24, 25, 25, 24, 13, 16, 21, 24, 25, 24,
+ 24, 19, 22, 23, 24, 24, 23, 19, 18, 20, 23, 23, 24, 23, 19, 17, 20, 22, 22,
+ 22, 23, 19, 16, 19, 21, 21, 22, 23, 20, 14, 18, 19, 20, 21, 22, 19, 11, 16,
+ 18, 19, 19, 20, 20, 13, 14, 16, 17, 19, 19, 18, 17, 18, 17, 18, 18, 17, 8,
+ 15, 15, 16, 16, 17, 16, 9, 13, 14, 15, 15, 15, 15, 10, 9, 14, 14, 14, 15,
+ 15, 11, 9, 12, 12, 13, 13, 14, 8, 10, 10, 10, 11, 11, 12, 6, 13, 13, 10,
+ 9, 10, 10, 6, 8, 9, 9, 9, 10, 6, 8, 4, 7, 7, 8, 8, 6, 9, 4,
+ 6, 5, 6, 6, 5, 7, 4, 4, 4, 4, 5, 5, 7, 5, 4, 4, 4, 4, 3,
+ 6, 7, 7, 7, 6, 4, 3, 4, 10, 10, 8, 6, 5, 3, 4, 0, 2, 2, 2,
+ 2, 5, 7, 0, 3, 2, 2, 1, 4, 8, 0, 4, 3, 2, 1, 3, 6, 0, 3,
+ 3, 2, 1, 2, 5, 2, 3, 5, 5, 3, 3, 4, 5, 6, 9, 8, 5, 3, 3,
+ 8, 9, 10, 9, 6, 4, 3, 4, 7, 6, 3, 2, 3, 6, 4, 7, 7, 4, 2,
+ 2, 5, 4, 7, 9, 4, 2, 0, 5, 4, 7, 10, 6, 3, 2, 3, 4, 7, 11,
+ 9, 5, 3, 3, 5, 9, 12, 11, 7, 4, 3, 7, 11, 12, 13, 8, 5, 3, 25,
+ 29, 32, 33, 34, 31, 29, 25, 27, 31, 34, 34, 32, 29, 23, 26, 29, 32, 34, 31,
+ 29, 21, 24, 28, 32, 34, 33, 30, 21, 24, 28, 32, 35, 33, 30, 20, 23, 27, 31,
+ 34, 33, 30, 18, 23, 25, 30, 33, 33, 31, 23, 25, 29, 31, 30, 27, 25, 21, 25,
+ 28, 31, 30, 27, 25, 21, 24, 27, 30, 29, 27, 25, 19, 22, 27, 28, 29, 29, 25,
+ 20, 22, 26, 29, 29, 28, 26, 19, 21, 24, 28, 28, 28, 25, 15, 19, 23, 27, 27,
+ 27, 26, 21, 25, 25, 26, 26, 24, 19, 19, 22, 25, 24, 25, 24, 19, 18, 22, 23,
+ 23, 24, 24, 20, 17, 21, 22, 23, 23, 23, 20, 14, 20, 22, 21, 22, 22, 21, 12,
+ 18, 20, 21, 21, 22, 20, 13, 16, 18, 19, 20, 20, 18, 19, 19, 19, 19, 19, 18,
+ 7, 16, 17, 18, 17, 18, 16, 9, 13, 16, 16, 17, 16, 15, 10, 10, 16, 16, 15,
+ 16, 15, 12, 11, 14, 15, 15, 14, 14, 9, 11, 12, 13, 13, 13, 13, 6, 14, 14,
+ 10, 11, 12, 12, 6, 8, 10, 10, 11, 10, 6, 7, 5, 9, 9, 9, 9, 6, 9,
+ 5, 8, 7, 7, 7, 5, 6, 5, 6, 6, 6, 7, 4, 6, 6, 5, 5, 5, 5,
+ 4, 6, 8, 9, 10, 9, 6, 5, 5, 11, 14, 14, 11, 8, 6, 4, 2, 0, 1,
+ 1, 3, 5, 6, 2, 2, 2, 2, 2, 4, 6, 2, 4, 4, 4, 1, 4, 7, 2,
+ 5, 7, 7, 4, 3, 5, 3, 7, 12, 11, 9, 6, 4, 6, 12, 16, 13, 11, 7,
+ 4, 12, 16, 16, 14, 12, 8, 5, 6, 11, 11, 6, 1, 3, 5, 6, 12, 12, 8,
+ 2, 3, 5, 6, 11, 13, 9, 4, 2, 5, 6, 12, 14, 12, 8, 4, 4, 8, 13,
+ 16, 15, 12, 8, 5, 10, 15, 17, 16, 13, 10, 6, 14, 17, 18, 17, 14, 11, 7,
+ 24, 27, 31, 33, 34, 31, 28, 23, 27, 29, 33, 34, 31, 28, 22, 25, 29, 32, 33,
+ 31, 28, 21, 25, 28, 31, 35, 32, 29, 22, 24, 28, 31, 35, 33, 30, 21, 24, 26,
+ 30, 34, 33, 29, 18, 21, 25, 29, 33, 33, 30, 21, 24, 28, 31, 29, 26, 24, 21,
+ 24, 27, 30, 30, 26, 23, 20, 23, 26, 28, 30, 27, 25, 19, 22, 26, 29, 29, 28,
+ 24, 20, 22, 26, 28, 28, 28, 25, 18, 21, 24, 27, 28, 27, 25, 16, 19, 23, 26,
+ 26, 27, 24, 20, 23, 24, 24, 24, 22, 17, 19, 21, 23, 24, 23, 22, 17, 18, 21,
+ 22, 23, 23, 21, 18, 16, 21, 22, 22, 23, 22, 18, 14, 20, 22, 22, 22, 22, 18,
+ 12, 18, 20, 20, 21, 21, 19, 15, 17, 19, 19, 19, 19, 17, 18, 17, 17, 17, 18,
+ 15, 6, 16, 16, 16, 16, 17, 14, 7, 12, 15, 16, 15, 15, 14, 9, 9, 16, 15,
+ 15, 15, 14, 12, 10, 14, 14, 15, 14, 13, 9, 12, 12, 12, 13, 13, 12, 6, 15,
+ 16, 11, 11, 12, 11, 5, 8, 10, 10, 10, 9, 4, 6, 4, 9, 8, 8, 8, 4,
+ 7, 4, 8, 7, 7, 7, 4, 6, 4, 6, 7, 6, 7, 4, 5, 6, 6, 6, 6,
+ 6, 5, 5, 8, 10, 12, 10, 7, 6, 5, 12, 16, 15, 12, 9, 7, 5, 2, 1,
+ 0, 1, 2, 3, 4, 2, 2, 2, 2, 2, 3, 4, 2, 4, 4, 4, 1, 3, 5,
+ 2, 6, 8, 8, 5, 4, 5, 4, 8, 13, 13, 9, 7, 5, 8, 13, 19, 18, 12,
+ 8, 5, 13, 18, 20, 19, 14, 9, 6, 6, 12, 11, 5, 1, 2, 4, 6, 12, 13,
+ 7, 1, 2, 4, 6, 12, 15, 9, 5, 2, 4, 6, 13, 18, 13, 9, 6, 5, 9,
+ 15, 21, 19, 13, 9, 6, 12, 18, 24, 23, 16, 10, 7, 16, 21, 25, 25, 18, 12,
+ 8, 24, 27, 30, 33, 33, 31, 26, 23, 26, 30, 33, 33, 31, 27, 21, 24, 28, 32,
+ 33, 31, 28, 21, 24, 29, 32, 35, 32, 29, 21, 24, 28, 31, 35, 33, 30, 20, 23,
+ 27, 30, 34, 32, 28, 19, 21, 26, 29, 33, 33, 29, 21, 25, 27, 31, 28, 26, 22,
+ 21, 23, 27, 30, 28, 26, 23, 19, 22, 26, 28, 29, 28, 23, 19, 23, 26, 29, 29,
+ 28, 24, 20, 23, 25, 28, 29, 28, 25, 18, 22, 24, 28, 28, 27, 25, 16, 19, 23,
+ 27, 27, 26, 24, 20, 23, 23, 24, 23, 21, 16, 19, 21, 23, 23, 23, 21, 16, 18,
+ 20, 22, 22, 22, 22, 17, 16, 21, 22, 22, 22, 22, 18, 15, 20, 22, 22, 22, 21,
+ 17, 13, 19, 20, 20, 21, 20, 18, 16, 18, 19, 19, 19, 19, 17, 18, 17, 17, 17,
+ 17, 14, 5, 16, 17, 16, 16, 15, 13, 6, 13, 15, 16, 15, 15, 13, 9, 9, 16,
+ 15, 15, 15, 13, 12, 11, 15, 14, 15, 14, 13, 8, 12, 13, 13, 13, 13, 12, 6,
+ 16, 17, 13, 11, 12, 11, 5, 8, 10, 10, 9, 8, 3, 5, 5, 9, 8, 8, 7,
+ 3, 6, 5, 8, 7, 7, 7, 3, 5, 5, 6, 7, 7, 7, 4, 5, 6, 6, 7,
+ 7, 7, 7, 5, 9, 12, 13, 11, 9, 7, 5, 14, 17, 17, 13, 10, 8, 6, 2,
+ 1, 1, 0, 2, 3, 4, 2, 3, 2, 2, 2, 3, 3, 2, 4, 4, 5, 2, 3,
+ 4, 2, 6, 9, 9, 6, 5, 5, 4, 9, 14, 13, 10, 8, 6, 9, 14, 20, 18,
+ 12, 9, 7, 15, 19, 21, 20, 15, 10, 7, 6, 9, 7, 4, 0, 2, 3, 6, 9,
+ 8, 5, 2, 2, 3, 6, 9, 10, 8, 5, 3, 4, 6, 11, 15, 12, 9, 7, 6,
+ 10, 14, 19, 18, 14, 10, 8, 13, 19, 23, 23, 16, 11, 8, 18, 22, 24, 24, 19,
+ 13, 9, 20, 23, 27, 30, 30, 28, 24, 19, 23, 26, 29, 29, 27, 24, 18, 21, 25,
+ 28, 31, 29, 26, 17, 21, 25, 28, 31, 29, 26, 18, 22, 24, 28, 32, 29, 26, 18,
+ 21, 24, 27, 32, 29, 25, 16, 19, 23, 26, 29, 29, 26, 18, 21, 24, 27, 27, 22,
+ 19, 18, 20, 23, 26, 26, 23, 20, 17, 19, 22, 26, 26, 24, 21, 16, 19, 23, 26,
+ 26, 25, 22, 17, 20, 23, 26, 25, 25, 21, 15, 18, 22, 24, 24, 24, 21, 17, 18,
+ 20, 23, 23, 23, 21, 17, 19, 19, 20, 21, 17, 13, 16, 18, 19, 19, 19, 18, 14,
+ 15, 18, 19, 20, 20, 19, 14, 15, 18, 20, 19, 20, 19, 15, 13, 17, 19, 18, 19,
+ 19, 16, 12, 16, 17, 18, 17, 18, 15, 19, 19, 16, 16, 17, 16, 15, 15, 14, 13,
+ 14, 14, 12, 3, 14, 13, 13, 13, 12, 11, 4, 11, 12, 12, 13, 12, 11, 7, 8,
+ 13, 13, 12, 12, 12, 9, 9, 12, 12, 11, 11, 10, 7, 12, 14, 12, 11, 10, 10,
+ 5, 19, 20, 15, 12, 9, 9, 6, 7, 6, 6, 6, 5, 2, 3, 3, 5, 5, 4,
+ 4, 3, 4, 3, 4, 3, 4, 4, 3, 4, 3, 3, 4, 5, 5, 5, 5, 5, 9,
+ 9, 9, 9, 8, 6, 10, 15, 16, 13, 10, 8, 6, 17, 20, 19, 15, 12, 9, 7,
+ 2, 3, 2, 2, 0, 2, 3, 2, 4, 3, 2, 2, 2, 3, 2, 6, 4, 4, 4,
+ 3, 4, 2, 7, 8, 8, 7, 6, 6, 7, 12, 14, 13, 12, 9, 7, 12, 16, 20,
+ 18, 13, 10, 8, 18, 21, 22, 19, 15, 11, 8, 7, 9, 5, 3, 2, 1, 2, 7,
+ 9, 7, 3, 2, 2, 3, 7, 9, 9, 6, 5, 5, 4, 7, 9, 13, 11, 9, 8,
+ 8, 9, 14, 17, 17, 14, 11, 8, 15, 18, 21, 22, 16, 13, 9, 19, 23, 23, 23,
+ 18, 14, 10, 16, 20, 23, 26, 25, 23, 20, 15, 18, 21, 25, 25, 24, 20, 14, 17,
+ 21, 25, 26, 24, 21, 14, 18, 21, 24, 28, 25, 22, 14, 18, 20, 25, 28, 26, 22,
+ 13, 17, 20, 23, 27, 26, 23, 17, 17, 19, 22, 25, 26, 22, 14, 17, 20, 23, 22,
+ 19, 15, 14, 16, 19, 22, 22, 20, 17, 12, 15, 18, 22, 22, 20, 17, 12, 15, 19,
+ 22, 22, 21, 18, 12, 16, 18, 21, 22, 21, 18, 12, 15, 18, 21, 21, 20, 18, 19,
+ 18, 17, 19, 19, 20, 18, 13, 15, 16, 16, 17, 14, 11, 12, 14, 15, 15, 16, 15,
+ 11, 11, 13, 14, 16, 16, 16, 12, 11, 13, 15, 15, 15, 15, 13, 9, 13, 15, 14,
+ 15, 15, 13, 14, 15, 14, 14, 14, 14, 13, 22, 20, 17, 13, 13, 13, 12, 11, 10,
+ 9, 9, 9, 9, 2, 10, 9, 9, 8, 9, 8, 3, 8, 8, 8, 9, 9, 9, 4,
+ 6, 8, 8, 9, 9, 8, 7, 7, 8, 9, 9, 9, 8, 6, 14, 16, 14, 13, 10,
+ 8, 7, 21, 23, 18, 13, 11, 9, 7, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 3, 2, 2, 2, 3, 4, 3, 4, 2, 5, 6, 7, 7, 7, 7, 6,
+ 11, 12, 11, 11, 9, 7, 13, 18, 19, 15, 13, 10, 8, 20, 22, 21, 17, 14, 11,
+ 9, 5, 5, 3, 3, 2, 0, 2, 5, 6, 4, 3, 2, 2, 3, 5, 8, 6, 6,
+ 5, 5, 4, 5, 9, 11, 10, 9, 9, 8, 9, 14, 17, 15, 14, 12, 8, 15, 19,
+ 23, 20, 15, 12, 10, 21, 23, 24, 21, 17, 13, 10, 9, 11, 7, 4, 3, 2, 1,
+ 9, 11, 9, 5, 3, 3, 3, 9, 11, 11, 8, 7, 6, 6, 9, 12, 15, 13, 11,
+ 10, 10, 12, 16, 20, 19, 16, 13, 11, 17, 21, 24, 24, 19, 15, 11, 21, 24, 25,
+ 25, 20, 16, 12, 12, 15, 18, 20, 20, 17, 15, 11, 14, 17, 20, 20, 18, 16, 10,
+ 13, 16, 20, 21, 20, 17, 10, 13, 17, 20, 23, 20, 18, 10, 14, 16, 20, 24, 21,
+ 18, 11, 13, 16, 19, 23, 22, 18, 18, 17, 16, 18, 21, 22, 18, 11, 13, 16, 18,
+ 17, 14, 11, 9, 12, 15, 17, 16, 15, 13, 8, 11, 15, 18, 17, 16, 14, 7, 11,
+ 15, 18, 17, 17, 14, 8, 11, 14, 17, 17, 16, 14, 12, 14, 15, 16, 16, 16, 14,
+ 20, 19, 17, 16, 15, 15, 14, 8, 11, 12, 12, 12, 10, 7, 7, 10, 11, 11, 11,
+ 10, 7, 7, 9, 10, 11, 11, 11, 8, 6, 9, 11, 11, 11, 11, 9, 7, 10, 11,
+ 11, 11, 11, 9, 16, 16, 15, 13, 11, 9, 9, 22, 21, 17, 14, 11, 9, 8, 6,
+ 6, 5, 5, 5, 6, 1, 6, 5, 4, 4, 4, 4, 2, 5, 3, 4, 5, 4, 4,
+ 3, 8, 3, 5, 6, 6, 6, 6, 8, 9, 9, 10, 10, 9, 7, 16, 17, 15, 14,
+ 11, 9, 7, 23, 24, 18, 15, 12, 10, 8, 3, 2, 2, 2, 2, 1, 1, 5, 3,
+ 2, 2, 2, 2, 2, 4, 4, 3, 4, 5, 5, 4, 5, 7, 7, 8, 9, 8, 7,
+ 8, 13, 13, 13, 12, 11, 8, 15, 19, 20, 17, 14, 12, 9, 22, 24, 23, 18, 16,
+ 13, 10, 7, 6, 4, 3, 3, 2, 0, 7, 8, 5, 4, 3, 3, 3, 7, 10, 7,
+ 8, 7, 6, 6, 7, 11, 13, 13, 11, 10, 9, 11, 16, 20, 19, 16, 13, 10, 17,
+ 21, 26, 24, 19, 14, 11, 22, 25, 28, 25, 21, 16, 12, 10, 13, 10, 6, 3, 2,
+ 2, 11, 13, 12, 7, 4, 4, 4, 11, 13, 16, 11, 9, 8, 7, 11, 15, 21, 17,
+ 14, 12, 11, 14, 21, 24, 24, 20, 16, 12, 19, 24, 30, 30, 22, 17, 13, 25, 29,
+ 31, 30, 25, 19, 14, 23, 27, 29, 32, 32, 30, 27, 22, 25, 28, 31, 31, 29, 26,
+ 21, 24, 27, 29, 31, 28, 26, 20, 23, 26, 29, 30, 29, 27, 20, 23, 26, 28, 30,
+ 29, 26, 19, 21, 25, 27, 30, 28, 27, 16, 20, 23, 27, 29, 28, 27, 22, 24, 27,
+ 29, 29, 26, 24, 20, 24, 27, 28, 28, 26, 24, 20, 22, 26, 28, 26, 25, 22, 18,
+ 22, 24, 27, 26, 26, 24, 18, 21, 24, 26, 26, 26, 24, 16, 19, 22, 24, 25, 25,
+ 24, 13, 17, 21, 24, 24, 24, 24, 19, 22, 23, 25, 25, 23, 20, 18, 21, 22, 23,
+ 23, 23, 19, 17, 20, 22, 22, 22, 22, 19, 15, 19, 21, 21, 22, 22, 20, 14, 18,
+ 20, 20, 21, 21, 20, 11, 16, 18, 18, 19, 20, 20, 13, 14, 16, 17, 18, 19, 19,
+ 17, 17, 17, 18, 18, 18, 8, 15, 15, 16, 16, 17, 16, 9, 13, 15, 15, 15, 16,
+ 15, 9, 9, 13, 14, 14, 14, 14, 12, 9, 12, 13, 13, 13, 13, 8, 10, 10, 10,
+ 11, 12, 12, 6, 13, 13, 9, 9, 10, 10, 6, 7, 9, 9, 9, 10, 6, 7, 4,
+ 7, 8, 8, 8, 6, 9, 4, 6, 6, 6, 6, 5, 7, 4, 4, 4, 4, 5, 4,
+ 7, 5, 4, 4, 4, 4, 3, 6, 7, 7, 7, 6, 4, 3, 5, 10, 10, 8, 6,
+ 5, 3, 3, 0, 2, 2, 2, 2, 5, 7, 0, 3, 2, 2, 2, 4, 8, 0, 4,
+ 3, 2, 1, 3, 6, 0, 3, 3, 2, 1, 2, 5, 2, 3, 5, 5, 3, 3, 3,
+ 5, 6, 9, 8, 5, 3, 3, 8, 8, 10, 9, 6, 4, 3, 4, 7, 6, 3, 2,
+ 3, 6, 4, 7, 7, 4, 2, 2, 5, 4, 7, 9, 4, 2, 0, 4, 4, 7, 10,
+ 5, 3, 2, 3, 4, 7, 11, 9, 5, 3, 3, 5, 9, 12, 11, 7, 4, 3, 7,
+ 11, 12, 12, 8, 5, 3, 28, 31, 34, 36, 36, 33, 31, 26, 29, 33, 36, 37, 35,
+ 32, 25, 29, 32, 35, 37, 34, 31, 24, 27, 31, 34, 36, 34, 32, 24, 27, 31, 34,
+ 37, 36, 31, 23, 25, 30, 33, 36, 35, 32, 20, 24, 28, 32, 35, 35, 32, 25, 29,
+ 32, 34, 34, 31, 27, 24, 27, 30, 33, 32, 30, 27, 22, 26, 29, 32, 32, 30, 28,
+ 22, 25, 29, 32, 32, 31, 28, 23, 24, 28, 32, 31, 31, 28, 20, 24, 27, 30, 30,
+ 31, 29, 17, 20, 25, 29, 29, 29, 28, 23, 26, 27, 28, 28, 26, 21, 22, 25, 27,
+ 26, 27, 25, 21, 21, 24, 25, 26, 25, 27, 22, 18, 23, 26, 24, 26, 25, 22, 16,
+ 22, 24, 24, 25, 24, 23, 13, 20, 22, 23, 23, 24, 22, 14, 17, 20, 22, 22, 23,
+ 21, 20, 20, 21, 22, 22, 19, 10, 18, 19, 20, 20, 21, 19, 11, 15, 18, 18, 18,
+ 19, 17, 12, 11, 18, 18, 18, 18, 17, 14, 12, 16, 17, 17, 17, 16, 11, 12, 14,
+ 14, 15, 15, 15, 8, 14, 13, 12, 13, 14, 13, 8, 10, 12, 13, 13, 12, 7, 10,
+ 6, 11, 11, 11, 11, 7, 10, 6, 10, 9, 9, 9, 7, 9, 6, 8, 8, 8, 9,
+ 6, 7, 7, 5, 7, 7, 7, 4, 7, 8, 7, 9, 8, 5, 4, 6, 10, 12, 12,
+ 9, 7, 5, 5, 3, 2, 2, 3, 4, 6, 8, 3, 0, 1, 1, 3, 7, 8, 3,
+ 2, 2, 2, 2, 5, 8, 3, 3, 5, 5, 2, 3, 6, 4, 5, 8, 7, 7, 5,
+ 5, 5, 9, 11, 9, 8, 6, 4, 11, 13, 12, 10, 8, 7, 4, 4, 8, 7, 4,
+ 3, 4, 7, 4, 8, 7, 5, 2, 4, 7, 4, 7, 9, 6, 2, 4, 6, 4, 8,
+ 10, 7, 6, 4, 4, 6, 9, 10, 9, 8, 7, 4, 8, 10, 12, 12, 9, 7, 5,
+ 11, 12, 13, 13, 10, 8, 6, 26, 29, 33, 35, 36, 33, 29, 25, 29, 32, 35, 35,
+ 33, 30, 23, 27, 31, 33, 35, 35, 30, 22, 26, 30, 34, 37, 34, 31, 23, 25, 29,
+ 33, 37, 34, 31, 23, 25, 29, 33, 37, 35, 31, 20, 24, 27, 32, 34, 34, 31, 23,
+ 27, 30, 33, 32, 28, 25, 23, 26, 29, 32, 32, 29, 25, 22, 24, 28, 31, 31, 29,
+ 26, 21, 25, 28, 31, 31, 29, 27, 21, 23, 27, 31, 31, 30, 26, 20, 23, 26, 29,
+ 30, 30, 26, 17, 21, 25, 27, 28, 29, 27, 22, 25, 26, 26, 26, 23, 19, 21, 24,
+ 25, 25, 25, 24, 18, 20, 22, 24, 24, 24, 24, 19, 17, 23, 24, 24, 25, 24, 20,
+ 15, 22, 23, 23, 24, 23, 20, 13, 20, 22, 22, 22, 22, 20, 15, 18, 20, 21, 21,
+ 21, 19, 19, 19, 19, 19, 19, 16, 7, 17, 18, 18, 18, 18, 15, 9, 13, 17, 17,
+ 17, 17, 15, 10, 10, 17, 17, 17, 17, 15, 13, 12, 16, 16, 16, 16, 14, 10, 12,
+ 14, 14, 14, 15, 14, 7, 15, 15, 12, 13, 13, 13, 5, 9, 11, 12, 11, 10, 5,
+ 7, 6, 11, 10, 10, 9, 5, 8, 6, 9, 9, 9, 8, 5, 7, 5, 7, 8, 8,
+ 8, 4, 6, 7, 5, 7, 7, 7, 5, 5, 9, 9, 11, 9, 7, 6, 5, 12, 14,
+ 14, 10, 8, 7, 5, 2, 2, 2, 2, 3, 4, 5, 2, 1, 0, 1, 2, 4, 5,
+ 2, 2, 2, 2, 2, 4, 6, 2, 4, 6, 6, 4, 4, 5, 4, 7, 11, 11, 9,
+ 7, 5, 7, 12, 17, 15, 11, 8, 5, 12, 16, 19, 18, 13, 9, 6, 4, 10, 9,
+ 4, 2, 3, 4, 4, 10, 12, 6, 1, 3, 4, 4, 10, 14, 8, 3, 3, 4, 4,
+ 11, 17, 12, 7, 5, 5, 7, 13, 19, 17, 12, 9, 6, 10, 17, 22, 21, 15, 10,
+ 7, 15, 19, 23, 23, 17, 11, 8, 26, 28, 32, 35, 35, 32, 29, 25, 27, 31, 34,
+ 35, 31, 29, 23, 26, 30, 34, 35, 33, 29, 22, 25, 30, 33, 36, 34, 31, 23, 25,
+ 29, 33, 37, 35, 29, 23, 25, 28, 32, 35, 34, 32, 20, 23, 27, 31, 34, 34, 31,
+ 23, 26, 29, 32, 31, 27, 23, 22, 25, 29, 32, 30, 27, 24, 22, 24, 28, 31, 31,
+ 29, 25, 21, 24, 27, 31, 31, 30, 25, 21, 24, 27, 30, 30, 29, 26, 20, 23, 26,
+ 30, 29, 29, 25, 18, 21, 24, 28, 27, 28, 25, 22, 24, 25, 25, 25, 23, 17, 20,
+ 22, 25, 25, 24, 22, 17, 19, 22, 24, 24, 24, 22, 17, 17, 23, 23, 23, 24, 23,
+ 19, 15, 23, 23, 24, 23, 23, 19, 13, 20, 22, 22, 22, 22, 19, 15, 19, 20, 20,
+ 21, 21, 18, 20, 19, 18, 18, 18, 15, 6, 17, 18, 18, 17, 17, 14, 8, 13, 17,
+ 17, 17, 16, 14, 10, 10, 17, 17, 17, 17, 14, 13, 12, 16, 16, 16, 15, 14, 10,
+ 13, 14, 14, 14, 14, 13, 7, 16, 16, 13, 13, 13, 13, 5, 9, 12, 11, 11, 9,
+ 4, 6, 6, 10, 10, 9, 8, 4, 8, 6, 9, 8, 9, 8, 4, 6, 5, 7, 9,
+ 8, 8, 4, 6, 7, 6, 7, 7, 7, 6, 5, 9, 10, 12, 10, 8, 7, 5, 13,
+ 16, 15, 12, 9, 8, 6, 2, 2, 2, 2, 2, 3, 4, 2, 1, 1, 0, 2, 3,
+ 4, 2, 2, 3, 3, 2, 3, 4, 2, 4, 7, 7, 5, 5, 5, 4, 8, 12, 11,
+ 9, 8, 6, 8, 13, 18, 16, 11, 8, 6, 14, 18, 20, 18, 13, 9, 7, 5, 8,
+ 7, 3, 1, 2, 3, 5, 8, 8, 4, 0, 2, 4, 5, 8, 10, 7, 4, 3, 4,
+ 5, 10, 14, 11, 8, 6, 6, 8, 13, 18, 17, 12, 9, 7, 11, 17, 22, 22, 15,
+ 11, 8, 16, 21, 23, 23, 18, 12, 8, 22, 25, 28, 31, 32, 29, 26, 21, 23, 27,
+ 31, 32, 27, 25, 19, 23, 26, 29, 32, 29, 27, 19, 23, 26, 30, 32, 30, 27, 20,
+ 22, 26, 29, 32, 31, 28, 19, 22, 25, 29, 32, 30, 27, 17, 20, 24, 27, 31, 31,
+ 27, 20, 23, 26, 29, 28, 24, 21, 18, 22, 25, 28, 27, 24, 21, 18, 20, 24, 27,
+ 27, 26, 21, 17, 21, 25, 27, 26, 25, 22, 19, 21, 24, 26, 26, 26, 22, 16, 20,
+ 22, 25, 26, 26, 23, 16, 18, 21, 24, 25, 24, 22, 18, 20, 21, 22, 23, 19, 15,
+ 17, 19, 21, 21, 21, 20, 15, 17, 19, 20, 20, 20, 20, 15, 15, 19, 20, 20, 21,
+ 20, 16, 14, 19, 20, 20, 20, 19, 16, 12, 17, 18, 19, 19, 18, 16, 17, 17, 17,
+ 17, 17, 17, 16, 16, 15, 15, 15, 16, 13, 4, 14, 14, 14, 14, 14, 12, 5, 12,
+ 14, 13, 14, 13, 12, 8, 8, 14, 14, 13, 13, 12, 10, 10, 13, 13, 13, 12, 11,
+ 7, 12, 12, 11, 11, 11, 11, 5, 17, 18, 14, 10, 10, 10, 5, 7, 8, 7, 7,
+ 7, 3, 4, 3, 7, 6, 6, 6, 3, 5, 3, 6, 5, 6, 5, 3, 4, 3, 4,
+ 5, 5, 5, 4, 4, 5, 7, 8, 8, 8, 7, 5, 9, 13, 14, 12, 9, 7, 6,
+ 15, 18, 18, 13, 11, 8, 6, 1, 2, 2, 2, 2, 2, 3, 2, 3, 2, 2, 0,
+ 3, 3, 2, 4, 3, 3, 3, 2, 3, 2, 6, 8, 7, 6, 5, 5, 6, 10, 13,
+ 12, 10, 8, 6, 11, 16, 19, 16, 12, 9, 7, 16, 20, 21, 18, 14, 10, 7, 6,
+ 7, 5, 3, 1, 2, 2, 6, 7, 7, 3, 2, 2, 3, 5, 7, 8, 5, 4, 3,
+ 3, 6, 8, 12, 10, 8, 7, 7, 9, 12, 16, 16, 13, 10, 7, 13, 17, 20, 21,
+ 15, 12, 8, 18, 21, 21, 21, 17, 13, 9, 17, 21, 24, 28, 28, 26, 22, 17, 19,
+ 24, 28, 28, 24, 21, 15, 19, 22, 25, 27, 25, 22, 15, 18, 21, 25, 29, 26, 22,
+ 15, 18, 21, 24, 28, 26, 23, 14, 17, 20, 24, 28, 26, 23, 15, 16, 19, 23, 25,
+ 27, 23, 16, 19, 22, 25, 24, 22, 18, 14, 18, 21, 23, 23, 21, 16, 13, 16, 20,
+ 22, 22, 20, 18, 13, 16, 20, 23, 22, 22, 19, 13, 16, 19, 22, 22, 21, 19, 12,
+ 14, 18, 21, 21, 21, 19, 17, 16, 17, 19, 20, 20, 18, 14, 16, 17, 18, 19, 17,
+ 13, 13, 15, 16, 17, 17, 16, 12, 12, 14, 15, 16, 16, 16, 12, 11, 14, 16, 16,
+ 16, 16, 13, 11, 14, 15, 15, 15, 15, 13, 12, 13, 14, 14, 14, 14, 13, 19, 18,
+ 14, 13, 13, 13, 12, 12, 11, 10, 11, 12, 12, 4, 11, 10, 10, 10, 10, 10, 3,
+ 9, 9, 9, 9, 9, 9, 4, 6, 9, 9, 9, 9, 9, 7, 7, 8, 8, 8, 8,
+ 7, 4, 12, 14, 12, 10, 8, 7, 4, 18, 20, 14, 12, 9, 7, 5, 4, 3, 3,
+ 3, 4, 3, 4, 1, 2, 2, 2, 2, 2, 3, 1, 1, 1, 2, 2, 2, 3, 1,
+ 5, 5, 5, 5, 4, 4, 5, 10, 9, 9, 9, 7, 5, 12, 15, 16, 13, 10, 8,
+ 6, 17, 20, 18, 14, 11, 9, 6, 4, 5, 3, 3, 2, 2, 3, 4, 6, 4, 3,
+ 2, 0, 2, 4, 7, 5, 5, 3, 3, 2, 4, 8, 9, 8, 7, 6, 5, 8, 12,
+ 14, 13, 11, 9, 6, 12, 16, 20, 17, 12, 10, 7, 18, 21, 21, 19, 14, 11, 8,
+ 8, 10, 6, 4, 3, 2, 2, 8, 10, 8, 5, 3, 2, 1, 8, 10, 10, 7, 5,
+ 4, 3, 9, 10, 14, 11, 9, 8, 7, 10, 14, 17, 16, 14, 11, 8, 14, 18, 21,
+ 21, 16, 12, 9, 19, 21, 23, 22, 18, 13, 10, 14, 16, 21, 24, 24, 21, 18, 13,
+ 16, 19, 23, 24, 20, 17, 11, 14, 18, 21, 23, 20, 18, 10, 14, 18, 21, 24, 21,
+ 18, 11, 14, 17, 20, 24, 22, 18, 10, 13, 16, 19, 23, 22, 18, 15, 14, 15, 18,
+ 22, 21, 18, 11, 14, 18, 21, 20, 17, 14, 11, 14, 17, 20, 20, 16, 13, 9, 12,
+ 15, 18, 18, 16, 14, 9, 12, 15, 18, 17, 17, 14, 9, 12, 15, 17, 17, 17, 14,
+ 11, 12, 13, 16, 16, 16, 14, 18, 16, 14, 15, 15, 15, 14, 10, 12, 14, 14, 16,
+ 13, 9, 9, 11, 12, 13, 13, 12, 8, 7, 10, 12, 11, 11, 12, 8, 7, 10, 11,
+ 11, 11, 11, 9, 6, 9, 11, 11, 10, 10, 9, 13, 13, 12, 10, 9, 9, 9, 19,
+ 17, 15, 11, 9, 8, 8, 8, 7, 6, 7, 8, 9, 3, 7, 6, 6, 6, 6, 6,
+ 2, 6, 5, 5, 5, 4, 4, 2, 4, 5, 4, 4, 4, 4, 4, 7, 8, 8, 7,
+ 7, 6, 4, 14, 15, 13, 11, 8, 7, 5, 19, 20, 15, 12, 9, 7, 6, 2, 2,
+ 1, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2,
+ 3, 6, 6, 6, 6, 5, 5, 7, 11, 11, 10, 9, 8, 5, 13, 16, 16, 13, 11,
+ 9, 7, 19, 20, 19, 15, 12, 10, 7, 7, 6, 4, 3, 3, 3, 3, 7, 7, 5,
+ 4, 3, 2, 0, 7, 9, 6, 6, 4, 3, 3, 7, 10, 10, 10, 8, 7, 6, 9,
+ 14, 16, 15, 12, 10, 7, 15, 18, 22, 20, 14, 10, 8, 19, 21, 23, 21, 16, 12,
+ 9, 10, 12, 9, 5, 3, 3, 2, 10, 12, 11, 6, 3, 2, 2, 10, 12, 13, 9,
+ 6, 5, 4, 10, 13, 17, 13, 11, 9, 8, 13, 16, 21, 20, 16, 12, 9, 16, 21,
+ 25, 24, 18, 13, 10, 20, 23, 25, 26, 20, 15, 11, 24, 26, 29, 32, 32, 30, 26,
+ 23, 25, 28, 31, 31, 29, 26, 22, 24, 27, 29, 31, 28, 26, 20, 23, 26, 29, 30,
+ 29, 27, 20, 23, 26, 29, 31, 28, 27, 19, 22, 24, 27, 30, 29, 26, 16, 20, 24,
+ 26, 29, 29, 27, 22, 24, 27, 29, 28, 26, 23, 21, 24, 27, 29, 27, 25, 24, 19,
+ 23, 25, 27, 27, 25, 23, 18, 21, 25, 27, 25, 25, 25, 18, 21, 24, 27, 26, 25,
+ 25, 16, 19, 23, 25, 25, 25, 24, 13, 16, 20, 24, 24, 25, 24, 19, 23, 23, 24,
+ 25, 23, 19, 18, 21, 22, 23, 24, 22, 20, 17, 20, 22, 22, 22, 23, 19, 15, 19,
+ 21, 21, 22, 22, 20, 14, 19, 19, 20, 21, 22, 20, 11, 17, 18, 19, 20, 20, 19,
+ 13, 14, 16, 17, 18, 19, 19, 17, 17, 17, 18, 18, 18, 7, 15, 16, 16, 16, 17,
+ 16, 8, 13, 15, 15, 15, 15, 15, 10, 9, 14, 14, 14, 14, 14, 12, 9, 12, 12,
+ 13, 13, 14, 8, 10, 10, 10, 11, 11, 12, 6, 13, 13, 9, 9, 10, 10, 6, 8,
+ 8, 9, 10, 10, 6, 8, 4, 7, 7, 8, 8, 6, 9, 4, 6, 6, 5, 6, 5,
+ 8, 4, 4, 4, 4, 5, 5, 6, 5, 4, 4, 4, 4, 3, 6, 7, 7, 7, 6,
+ 4, 3, 5, 10, 10, 8, 6, 4, 3, 4, 0, 2, 2, 2, 2, 5, 7, 0, 3,
+ 2, 2, 2, 4, 8, 0, 4, 2, 2, 1, 4, 6, 0, 3, 3, 2, 1, 2, 5,
+ 2, 3, 5, 5, 3, 3, 4, 5, 6, 9, 8, 5, 3, 3, 8, 9, 10, 9, 6,
+ 4, 3, 4, 7, 6, 3, 2, 3, 6, 4, 7, 8, 4, 2, 2, 5, 4, 7, 9,
+ 4, 2, 0, 5, 4, 7, 10, 6, 3, 2, 3, 4, 7, 10, 9, 5, 4, 3, 5,
+ 9, 12, 11, 7, 4, 3, 7, 11, 12, 12, 8, 5, 3, 30, 32, 35, 39, 38, 37,
+ 32, 28, 32, 34, 37, 39, 36, 33, 27, 31, 33, 37, 38, 36, 32, 26, 29, 32, 35,
+ 38, 37, 34, 25, 28, 33, 36, 38, 37, 35, 24, 27, 31, 35, 38, 37, 35, 22, 25,
+ 31, 34, 36, 37, 34, 27, 31, 33, 36, 36, 32, 29, 26, 29, 33, 35, 34, 32, 28,
+ 24, 28, 32, 34, 35, 33, 30, 24, 27, 31, 34, 34, 33, 30, 23, 26, 29, 33, 32,
+ 33, 30, 21, 25, 29, 32, 32, 33, 30, 19, 22, 27, 30, 31, 31, 30, 24, 28, 28,
+ 29, 30, 28, 23, 23, 27, 28, 29, 29, 28, 22, 22, 25, 27, 28, 28, 28, 23, 20,
+ 25, 26, 27, 28, 27, 25, 17, 24, 25, 26, 26, 26, 24, 14, 23, 24, 24, 25, 26,
+ 24, 15, 19, 21, 23, 24, 25, 23, 22, 23, 23, 23, 23, 21, 11, 19, 21, 21, 21,
+ 22, 20, 12, 16, 20, 20, 20, 20, 19, 13, 12, 20, 19, 20, 19, 19, 16, 13, 17,
+ 18, 19, 19, 18, 12, 13, 15, 16, 17, 17, 17, 9, 15, 13, 14, 15, 16, 16, 8,
+ 10, 14, 14, 14, 14, 9, 11, 7, 12, 13, 13, 12, 9, 12, 7, 11, 11, 11, 11,
+ 7, 10, 7, 8, 10, 10, 10, 8, 9, 8, 6, 9, 8, 9, 6, 8, 9, 8, 8,
+ 7, 7, 4, 7, 11, 10, 9, 7, 5, 3, 6, 4, 3, 4, 4, 6, 8, 10, 4,
+ 2, 2, 2, 4, 7, 9, 4, 0, 1, 1, 3, 6, 8, 4, 1, 2, 1, 2, 4,
+ 7, 4, 3, 4, 4, 4, 3, 7, 6, 6, 8, 6, 5, 4, 5, 8, 9, 9, 6,
+ 5, 4, 4, 2, 5, 3, 2, 3, 6, 9, 2, 5, 4, 3, 2, 5, 9, 2, 5,
+ 5, 3, 0, 4, 7, 2, 5, 6, 2, 2, 3, 5, 2, 4, 7, 5, 4, 4, 4,
+ 5, 6, 8, 8, 5, 4, 4, 8, 8, 9, 9, 6, 5, 4, 29, 33, 36, 39, 39,
+ 36, 33, 28, 31, 34, 38, 38, 36, 32, 26, 29, 34, 37, 38, 35, 32, 25, 28, 31,
+ 36, 39, 36, 33, 25, 29, 31, 36, 39, 37, 34, 24, 27, 30, 34, 38, 37, 33, 22,
+ 26, 29, 33, 36, 37, 33, 26, 29, 34, 36, 34, 32, 29, 25, 29, 32, 36, 35, 32,
+ 27, 24, 27, 30, 34, 34, 31, 28, 23, 26, 30, 33, 33, 32, 28, 24, 27, 30, 32,
+ 32, 31, 29, 22, 25, 29, 32, 32, 32, 29, 19, 23, 27, 30, 30, 30, 28, 24, 27,
+ 28, 29, 30, 26, 22, 23, 25, 28, 28, 28, 27, 21, 22, 25, 27, 26, 27, 26, 21,
+ 18, 25, 25, 25, 26, 25, 21, 16, 24, 25, 25, 26, 25, 22, 13, 22, 24, 24, 24,
+ 24, 22, 15, 20, 22, 23, 23, 23, 20, 22, 22, 22, 22, 23, 19, 10, 18, 20, 21,
+ 22, 21, 18, 11, 15, 20, 20, 20, 19, 17, 12, 11, 19, 19, 19, 18, 16, 15, 13,
+ 17, 18, 18, 19, 16, 11, 13, 15, 16, 16, 16, 15, 8, 15, 14, 14, 15, 15, 14,
+ 6, 10, 14, 14, 14, 12, 6, 10, 7, 13, 12, 13, 11, 6, 11, 7, 11, 11, 11,
+ 10, 5, 8, 7, 8, 10, 10, 9, 5, 8, 8, 6, 9, 9, 8, 5, 6, 9, 8,
+ 9, 7, 7, 5, 6, 11, 12, 12, 9, 6, 5, 5, 3, 4, 4, 5, 4, 5, 7,
+ 3, 2, 2, 3, 3, 5, 6, 3, 1, 0, 0, 3, 5, 7, 2, 2, 4, 4, 2,
+ 4, 5, 4, 5, 9, 8, 8, 5, 5, 6, 10, 15, 13, 10, 6, 4, 10, 14, 17,
+ 15, 11, 8, 4, 2, 8, 8, 2, 3, 4, 6, 2, 8, 10, 4, 2, 4, 6, 2,
+ 8, 12, 5, 1, 3, 5, 2, 9, 14, 9, 5, 4, 5, 5, 11, 16, 14, 10, 8,
+ 4, 8, 14, 19, 18, 13, 9, 5, 13, 18, 19, 19, 15, 10, 6, 28, 32, 36, 38,
+ 38, 35, 33, 27, 30, 35, 37, 39, 36, 31, 26, 29, 33, 36, 39, 34, 32, 24, 27,
+ 31, 35, 37, 35, 32, 25, 27, 31, 35, 37, 36, 33, 23, 26, 30, 33, 37, 35, 33,
+ 21, 25, 29, 31, 35, 35, 32, 25, 29, 33, 37, 35, 31, 27, 25, 29, 31, 36, 34,
+ 32, 27, 24, 26, 31, 33, 33, 30, 26, 23, 25, 29, 32, 32, 30, 26, 22, 25, 29,
+ 32, 32, 31, 26, 21, 25, 27, 31, 30, 29, 27, 19, 22, 26, 30, 29, 28, 27, 24,
+ 26, 29, 29, 30, 27, 20, 22, 25, 28, 28, 28, 26, 20, 21, 23, 26, 26, 26, 24,
+ 19, 18, 25, 25, 24, 25, 23, 19, 16, 23, 24, 24, 24, 23, 20, 13, 22, 23, 23,
+ 23, 23, 20, 15, 20, 21, 21, 21, 22, 18, 20, 21, 21, 22, 23, 18, 11, 18, 20,
+ 20, 21, 21, 17, 11, 14, 19, 18, 19, 18, 15, 12, 11, 18, 18, 17, 18, 15, 14,
+ 12, 16, 17, 17, 17, 14, 11, 13, 15, 16, 16, 16, 13, 7, 14, 14, 14, 14, 15,
+ 13, 5, 9, 14, 13, 15, 12, 7, 11, 6, 13, 12, 13, 12, 6, 11, 6, 10, 11,
+ 10, 9, 4, 7, 6, 7, 10, 9, 8, 4, 7, 8, 6, 8, 8, 7, 4, 4, 9,
+ 8, 9, 8, 6, 4, 4, 11, 12, 12, 9, 6, 5, 4, 2, 4, 4, 5, 4, 6,
+ 8, 2, 2, 2, 3, 3, 5, 6, 2, 1, 0, 0, 3, 3, 5, 2, 2, 4, 4,
+ 2, 3, 4, 4, 5, 9, 8, 7, 5, 4, 6, 10, 15, 13, 9, 6, 4, 10, 14,
+ 16, 15, 10, 7, 4, 3, 7, 6, 2, 3, 5, 6, 3, 7, 7, 4, 2, 3, 4,
+ 3, 7, 8, 5, 1, 2, 4, 3, 8, 11, 9, 4, 4, 4, 5, 11, 15, 14, 9,
+ 7, 4, 8, 14, 19, 19, 12, 8, 5, 13, 17, 20, 20, 14, 9, 6, 24, 29, 32,
+ 35, 36, 33, 29, 23, 27, 31, 34, 35, 32, 28, 22, 25, 28, 33, 34, 31, 27, 20,
+ 22, 26, 30, 32, 30, 28, 21, 23, 26, 30, 34, 31, 27, 20, 22, 26, 29, 33, 31,
+ 27, 17, 21, 24, 27, 31, 32, 28, 22, 25, 30, 33, 31, 28, 25, 21, 23, 27, 31,
+ 32, 28, 23, 20, 22, 27, 29, 28, 26, 22, 19, 21, 24, 27, 27, 26, 22, 20, 21,
+ 24, 27, 27, 27, 22, 18, 20, 23, 25, 26, 26, 23, 14, 18, 22, 25, 25, 24, 22,
+ 20, 23, 24, 25, 27, 23, 19, 19, 21, 24, 24, 25, 22, 17, 18, 21, 22, 22, 22,
+ 21, 16, 16, 20, 21, 21, 21, 21, 16, 14, 20, 20, 20, 20, 19, 16, 11, 17, 19,
+ 19, 19, 19, 16, 14, 15, 17, 17, 18, 18, 15, 18, 18, 17, 18, 19, 17, 9, 15,
+ 16, 16, 17, 17, 15, 9, 12, 16, 15, 15, 14, 12, 8, 9, 16, 14, 13, 13, 12,
+ 11, 10, 14, 13, 13, 12, 11, 8, 11, 11, 11, 11, 11, 10, 4, 14, 14, 10, 10,
+ 10, 10, 3, 7, 9, 10, 10, 10, 6, 9, 4, 9, 8, 9, 9, 4, 9, 4, 7,
+ 6, 6, 6, 3, 5, 4, 6, 6, 5, 5, 2, 4, 6, 5, 5, 5, 5, 4, 3,
+ 7, 10, 11, 9, 6, 5, 3, 12, 14, 14, 10, 8, 6, 4, 1, 1, 1, 2, 4,
+ 5, 7, 1, 2, 2, 2, 3, 3, 4, 1, 3, 4, 3, 0, 2, 4, 1, 5, 5,
+ 4, 4, 3, 3, 3, 7, 10, 9, 8, 6, 4, 8, 12, 16, 13, 9, 6, 4, 13,
+ 16, 18, 15, 11, 7, 5, 5, 6, 5, 3, 2, 4, 5, 4, 6, 6, 4, 2, 2,
+ 3, 4, 6, 7, 3, 2, 1, 2, 4, 7, 9, 7, 6, 4, 4, 6, 9, 13, 13,
+ 10, 7, 5, 10, 14, 17, 17, 13, 9, 6, 15, 18, 18, 18, 14, 10, 6, 20, 24,
+ 28, 32, 32, 29, 25, 19, 23, 26, 31, 31, 28, 24, 17, 21, 25, 28, 30, 26, 23,
+ 17, 19, 23, 26, 29, 26, 23, 16, 18, 21, 25, 29, 27, 23, 15, 18, 21, 24, 27,
+ 26, 23, 12, 16, 19, 23, 26, 27, 23, 17, 21, 25, 29, 28, 24, 21, 16, 20, 24,
+ 28, 27, 24, 20, 16, 18, 23, 25, 25, 21, 18, 15, 17, 20, 23, 22, 22, 19, 14,
+ 17, 20, 22, 22, 22, 18, 12, 16, 18, 21, 21, 21, 18, 13, 14, 17, 20, 20, 20,
+ 19, 15, 18, 21, 22, 23, 20, 16, 14, 17, 19, 21, 21, 19, 15, 14, 16, 18, 18,
+ 18, 18, 13, 13, 16, 16, 16, 16, 16, 13, 12, 15, 16, 16, 16, 15, 13, 10, 12,
+ 14, 14, 14, 14, 13, 15, 15, 12, 12, 13, 13, 12, 14, 13, 13, 14, 15, 15, 7,
+ 13, 12, 12, 13, 13, 13, 6, 10, 11, 11, 10, 10, 10, 5, 6, 10, 9, 9, 9,
+ 9, 7, 7, 9, 8, 8, 8, 8, 4, 10, 10, 9, 7, 7, 7, 2, 15, 16, 11,
+ 8, 6, 5, 3, 5, 5, 6, 6, 7, 6, 7, 1, 3, 4, 5, 5, 4, 6, 1,
+ 2, 2, 2, 2, 2, 3, 1, 3, 2, 2, 2, 2, 2, 4, 7, 7, 6, 6, 4,
+ 2, 9, 12, 12, 10, 7, 5, 3, 14, 16, 15, 11, 9, 6, 4, 3, 4, 3, 3,
+ 3, 5, 6, 3, 5, 4, 3, 3, 3, 3, 3, 6, 5, 4, 2, 0, 2, 4, 6,
+ 6, 5, 4, 3, 3, 6, 9, 11, 10, 8, 6, 4, 10, 13, 16, 14, 10, 7, 4,
+ 14, 16, 18, 15, 11, 8, 5, 7, 8, 7, 4, 3, 4, 4, 6, 8, 8, 5, 3,
+ 2, 2, 7, 8, 9, 5, 3, 2, 1, 7, 8, 11, 8, 6, 5, 4, 8, 11, 14,
+ 14, 11, 8, 5, 12, 15, 18, 18, 13, 9, 6, 16, 18, 19, 19, 14, 10, 7, 17,
+ 20, 25, 28, 28, 26, 21, 15, 19, 23, 27, 28, 25, 20, 14, 18, 21, 25, 27, 22,
+ 19, 12, 16, 19, 22, 24, 22, 19, 12, 14, 17, 21, 24, 23, 19, 10, 14, 16, 20,
+ 23, 22, 19, 11, 12, 15, 19, 22, 22, 19, 14, 17, 22, 25, 24, 22, 17, 13, 16,
+ 20, 24, 23, 20, 16, 12, 15, 19, 21, 21, 18, 15, 10, 13, 16, 18, 19, 17, 14,
+ 10, 13, 15, 18, 18, 18, 15, 9, 11, 14, 17, 17, 17, 14, 13, 12, 13, 15, 16,
+ 16, 14, 12, 15, 16, 18, 19, 17, 13, 10, 13, 16, 16, 17, 16, 12, 9, 12, 14,
+ 14, 14, 14, 10, 8, 11, 12, 12, 12, 12, 10, 7, 10, 11, 11, 11, 11, 10, 11,
+ 10, 10, 10, 10, 10, 9, 14, 13, 10, 9, 9, 9, 8, 9, 9, 10, 11, 12, 13,
+ 7, 8, 8, 9, 9, 10, 10, 5, 7, 6, 7, 7, 6, 7, 3, 4, 6, 5, 5,
+ 5, 5, 4, 6, 6, 5, 4, 5, 4, 2, 11, 11, 9, 7, 5, 4, 2, 14, 14,
+ 10, 8, 6, 4, 3, 0, 1, 2, 3, 4, 5, 6, 2, 2, 2, 3, 3, 3, 4,
+ 2, 3, 3, 2, 2, 1, 2, 2, 6, 4, 3, 3, 3, 2, 6, 9, 8, 7, 6,
+ 5, 2, 10, 12, 12, 9, 7, 6, 3, 13, 14, 14, 11, 8, 6, 4, 6, 6, 5,
+ 4, 4, 4, 6, 6, 8, 6, 4, 3, 2, 3, 6, 8, 7, 5, 3, 2, 0, 6,
+ 9, 8, 6, 5, 4, 3, 8, 11, 13, 11, 9, 6, 4, 11, 14, 17, 15, 10, 7,
+ 5, 14, 16, 18, 16, 12, 8, 6, 9, 10, 8, 6, 4, 4, 4, 10, 10, 10, 6,
+ 4, 2, 2, 9, 10, 11, 6, 4, 3, 2, 9, 11, 14, 10, 7, 6, 5, 10, 13,
+ 17, 15, 11, 8, 6, 13, 16, 20, 19, 13, 9, 6, 16, 19, 20, 20, 15, 11, 7,
+ 24, 27, 29, 31, 31, 31, 27, 23, 26, 28, 31, 32, 28, 27, 21, 24, 27, 30, 31,
+ 28, 25, 20, 24, 27, 29, 30, 29, 27, 20, 23, 26, 28, 30, 28, 26, 18, 22, 24,
+ 27, 31, 29, 26, 16, 20, 24, 26, 29, 28, 26, 21, 24, 28, 29, 28, 26, 23, 21,
+ 23, 25, 28, 28, 26, 23, 19, 23, 26, 28, 26, 26, 24, 18, 21, 25, 26, 26, 26,
+ 25, 17, 21, 24, 26, 25, 26, 24, 16, 19, 22, 25, 26, 25, 24, 13, 16, 21, 24,
+ 24, 25, 24, 19, 23, 24, 24, 25, 23, 20, 18, 21, 22, 23, 23, 23, 19, 17, 21,
+ 22, 22, 22, 23, 19, 15, 20, 21, 21, 22, 22, 20, 14, 18, 20, 20, 21, 21, 20,
+ 11, 16, 18, 19, 20, 20, 20, 13, 14, 16, 17, 18, 19, 19, 17, 17, 18, 18, 18,
+ 18, 8, 15, 15, 16, 17, 17, 16, 9, 13, 15, 15, 15, 15, 15, 10, 9, 13, 14,
+ 14, 15, 15, 12, 9, 12, 13, 13, 13, 14, 8, 10, 10, 10, 11, 11, 12, 6, 13,
+ 13, 9, 9, 10, 11, 6, 8, 8, 9, 9, 10, 6, 8, 4, 7, 8, 8, 8, 6,
+ 9, 4, 6, 5, 6, 6, 5, 7, 4, 4, 4, 4, 5, 5, 7, 5, 4, 4, 4,
+ 4, 3, 6, 7, 7, 7, 6, 4, 3, 5, 10, 10, 8, 6, 4, 3, 4, 0, 2,
+ 2, 2, 2, 5, 7, 0, 3, 2, 2, 2, 4, 8, 0, 4, 3, 2, 1, 4, 6,
+ 0, 3, 3, 2, 1, 2, 5, 2, 3, 5, 5, 3, 3, 4, 5, 6, 9, 8, 4,
+ 3, 3, 8, 9, 10, 9, 6, 4, 3, 4, 7, 6, 3, 2, 3, 6, 4, 7, 8,
+ 4, 2, 2, 5, 4, 7, 9, 4, 2, 0, 5, 4, 7, 10, 6, 3, 2, 3, 4,
+ 7, 11, 9, 5, 3, 3, 5, 9, 12, 12, 7, 4, 3, 7, 10, 12, 13, 8, 5,
+ 3, 30, 34, 37, 40, 39, 38, 34, 29, 34, 37, 40, 40, 38, 34, 28, 32, 35, 39,
+ 38, 38, 34, 27, 29, 34, 37, 39, 36, 33, 25, 28, 32, 35, 39, 37, 33, 23, 28,
+ 31, 34, 38, 37, 34, 21, 25, 29, 33, 36, 36, 34, 28, 32, 35, 37, 37, 34, 31,
+ 26, 31, 34, 37, 36, 33, 31, 25, 29, 33, 34, 35, 33, 30, 24, 28, 31, 33, 33,
+ 33, 30, 23, 26, 29, 32, 33, 32, 30, 20, 24, 28, 31, 32, 32, 30, 18, 22, 26,
+ 30, 30, 30, 28, 25, 30, 32, 32, 32, 30, 25, 24, 27, 30, 31, 32, 30, 25, 22,
+ 26, 28, 29, 29, 28, 24, 20, 25, 26, 27, 27, 27, 24, 16, 23, 25, 26, 26, 27,
+ 24, 13, 22, 24, 23, 25, 25, 24, 13, 18, 21, 22, 23, 23, 22, 23, 24, 24, 25,
+ 26, 23, 14, 19, 22, 22, 23, 23, 22, 14, 16, 21, 21, 21, 23, 20, 15, 12, 19,
+ 19, 19, 19, 18, 15, 12, 17, 18, 18, 18, 17, 12, 12, 14, 15, 16, 16, 16, 9,
+ 14, 12, 13, 14, 15, 15, 7, 10, 15, 16, 16, 15, 11, 13, 7, 13, 14, 15, 14,
+ 10, 13, 7, 11, 12, 12, 12, 8, 12, 7, 8, 10, 10, 10, 7, 8, 7, 5, 8,
+ 8, 8, 5, 8, 8, 7, 6, 6, 6, 3, 8, 10, 9, 8, 6, 4, 3, 6, 3,
+ 5, 6, 6, 7, 9, 11, 3, 3, 4, 4, 6, 8, 10, 3, 1, 2, 2, 4, 6,
+ 9, 3, 0, 2, 2, 2, 5, 7, 3, 2, 3, 3, 3, 3, 6, 5, 5, 7, 5,
+ 3, 3, 4, 7, 8, 7, 5, 4, 3, 3, 2, 4, 4, 2, 5, 7, 10, 2, 4,
+ 5, 3, 4, 6, 9, 2, 5, 6, 3, 2, 5, 6, 2, 4, 5, 3, 1, 2, 5,
+ 2, 4, 6, 5, 3, 3, 3, 4, 5, 7, 7, 4, 3, 3, 7, 7, 8, 9, 5,
+ 3, 3, 33, 36, 41, 43, 44, 41, 38, 31, 35, 39, 43, 44, 41, 37, 30, 35, 37,
+ 41, 43, 38, 35, 29, 33, 35, 39, 41, 38, 34, 27, 29, 33, 38, 40, 37, 33, 25,
+ 28, 32, 36, 40, 38, 34, 23, 26, 32, 34, 37, 38, 35, 30, 34, 38, 40, 40, 37,
+ 33, 29, 33, 36, 40, 40, 37, 32, 26, 32, 35, 39, 38, 35, 32, 26, 29, 32, 36,
+ 35, 33, 31, 25, 27, 30, 34, 34, 33, 29, 22, 26, 29, 33, 32, 32, 30, 19, 24,
+ 27, 31, 31, 32, 29, 27, 31, 34, 34, 34, 32, 27, 26, 29, 32, 33, 33, 32, 26,
+ 24, 28, 30, 31, 31, 30, 24, 20, 27, 27, 28, 28, 27, 23, 16, 25, 26, 27, 27,
+ 26, 23, 13, 24, 24, 25, 25, 26, 23, 14, 21, 23, 23, 24, 24, 22, 23, 25, 26,
+ 27, 28, 25, 17, 20, 24, 25, 26, 25, 22, 16, 15, 23, 23, 23, 23, 21, 17, 12,
+ 20, 20, 20, 19, 18, 16, 12, 17, 19, 19, 19, 16, 13, 13, 14, 17, 17, 17, 16,
+ 9, 14, 12, 15, 16, 16, 15, 7, 11, 17, 18, 19, 17, 12, 16, 8, 15, 16, 17,
+ 16, 10, 16, 8, 12, 14, 14, 13, 8, 12, 8, 8, 11, 11, 10, 7, 9, 8, 5,
+ 10, 10, 9, 5, 6, 8, 7, 7, 7, 7, 4, 8, 10, 9, 8, 6, 6, 3, 5,
+ 3, 7, 8, 9, 8, 10, 13, 3, 5, 6, 7, 7, 9, 10, 3, 2, 4, 4, 5,
+ 6, 8, 3, 2, 0, 0, 3, 6, 6, 3, 2, 5, 5, 4, 3, 5, 5, 6, 11,
+ 10, 6, 3, 5, 7, 11, 13, 11, 8, 4, 4, 1, 7, 7, 4, 7, 9, 10, 1,
+ 7, 9, 3, 6, 7, 9, 1, 7, 11, 4, 3, 5, 6, 1, 7, 11, 6, 1, 2,
+ 5, 1, 7, 12, 10, 7, 4, 3, 4, 10, 13, 13, 10, 5, 3, 9, 13, 14, 15,
+ 11, 7, 3, 33, 36, 40, 42, 44, 41, 37, 30, 34, 38, 44, 43, 40, 35, 29, 33,
+ 38, 41, 42, 38, 34, 28, 31, 35, 37, 40, 37, 32, 26, 28, 32, 35, 39, 36, 33,
+ 25, 27, 30, 34, 38, 36, 33, 23, 26, 30, 33, 36, 36, 33, 29, 33, 36, 41, 38,
+ 36, 32, 28, 32, 36, 39, 38, 35, 31, 26, 30, 33, 37, 36, 33, 29, 25, 27, 32,
+ 34, 34, 31, 28, 24, 26, 30, 32, 33, 32, 28, 22, 25, 28, 31, 31, 31, 28, 18,
+ 23, 27, 29, 30, 30, 27, 27, 31, 33, 33, 34, 31, 25, 25, 28, 31, 32, 32, 30,
+ 23, 22, 27, 29, 30, 30, 28, 22, 19, 25, 26, 26, 27, 26, 21, 15, 25, 25, 26,
+ 25, 24, 21, 12, 23, 24, 24, 24, 23, 21, 13, 20, 21, 22, 23, 22, 19, 22, 24,
+ 25, 25, 27, 23, 16, 18, 22, 24, 24, 24, 21, 15, 14, 21, 23, 22, 21, 18, 15,
+ 12, 19, 19, 19, 19, 15, 15, 12, 16, 19, 18, 17, 14, 12, 13, 14, 16, 16, 16,
+ 14, 8, 13, 12, 14, 15, 15, 14, 5, 10, 17, 17, 18, 16, 12, 16, 7, 15, 16,
+ 16, 15, 9, 16, 7, 11, 14, 13, 12, 7, 11, 7, 7, 11, 10, 9, 4, 7, 7,
+ 5, 10, 9, 8, 3, 5, 8, 7, 7, 7, 7, 3, 5, 10, 9, 8, 5, 5, 2,
+ 4, 2, 7, 8, 8, 8, 10, 13, 2, 5, 6, 7, 7, 8, 10, 2, 1, 4, 4,
+ 4, 5, 6, 2, 2, 0, 0, 2, 4, 5, 3, 2, 5, 5, 4, 2, 4, 5, 6,
+ 11, 10, 6, 3, 3, 7, 10, 13, 12, 7, 4, 3, 2, 6, 6, 4, 7, 9, 10,
+ 1, 6, 7, 3, 6, 6, 7, 1, 6, 8, 4, 2, 3, 5, 1, 6, 8, 5, 1,
+ 2, 3, 1, 7, 11, 11, 6, 4, 3, 4, 10, 15, 15, 9, 5, 2, 9, 13, 16,
+ 16, 11, 6, 3, 28, 32, 36, 40, 41, 36, 33, 27, 31, 35, 39, 40, 35, 32, 26,
+ 29, 34, 36, 39, 35, 30, 24, 27, 31, 34, 36, 32, 29, 22, 25, 28, 31, 34, 32,
+ 29, 21, 24, 26, 30, 33, 32, 29, 19, 22, 25, 28, 33, 31, 29, 26, 29, 34, 37,
+ 35, 33, 29, 25, 28, 32, 36, 35, 32, 28, 23, 27, 31, 34, 33, 30, 25, 22, 25,
+ 28, 30, 30, 28, 24, 20, 22, 25, 28, 28, 27, 23, 19, 22, 24, 27, 27, 27, 24,
+ 16, 19, 23, 26, 25, 26, 24, 23, 26, 29, 29, 31, 28, 23, 22, 26, 28, 28, 29,
+ 26, 21, 20, 23, 26, 25, 26, 25, 19, 17, 22, 22, 22, 23, 22, 18, 13, 21, 21,
+ 21, 21, 21, 17, 11, 19, 20, 20, 20, 19, 17, 12, 17, 18, 19, 19, 19, 16, 20,
+ 21, 22, 22, 24, 22, 15, 16, 20, 20, 21, 22, 19, 13, 13, 18, 19, 18, 18, 16,
+ 13, 10, 16, 15, 15, 15, 13, 12, 9, 14, 15, 14, 14, 12, 9, 10, 12, 12, 12,
+ 12, 11, 6, 12, 10, 11, 11, 11, 11, 4, 8, 14, 14, 15, 14, 11, 14, 5, 12,
+ 12, 13, 13, 8, 13, 5, 10, 10, 10, 10, 6, 8, 5, 5, 7, 6, 6, 3, 5,
+ 5, 4, 5, 5, 5, 2, 3, 6, 6, 7, 5, 3, 2, 4, 8, 9, 9, 6, 4,
+ 2, 3, 1, 4, 5, 6, 8, 9, 12, 1, 2, 4, 5, 6, 7, 8, 1, 2, 2,
+ 2, 4, 4, 5, 1, 2, 4, 2, 0, 3, 4, 2, 4, 6, 6, 4, 2, 3, 4,
+ 8, 12, 10, 6, 3, 2, 8, 11, 13, 11, 7, 4, 2, 4, 5, 4, 4, 6, 8,
+ 9, 4, 5, 5, 4, 5, 6, 6, 4, 6, 6, 3, 2, 3, 3, 3, 7, 8, 4,
+ 3, 1, 2, 4, 6, 9, 9, 7, 4, 2, 6, 10, 13, 13, 9, 5, 2, 11, 13,
+ 14, 14, 10, 6, 3, 25, 28, 32, 36, 36, 34, 29, 23, 26, 31, 35, 35, 31, 28,
+ 22, 25, 29, 33, 33, 30, 25, 20, 23, 27, 30, 33, 28, 24, 17, 20, 23, 26, 29,
+ 27, 24, 16, 19, 22, 25, 28, 27, 24, 14, 17, 21, 24, 28, 27, 24, 21, 25, 29,
+ 32, 32, 28, 25, 21, 24, 28, 31, 32, 27, 23, 18, 22, 27, 29, 29, 25, 22, 19,
+ 20, 24, 27, 26, 24, 20, 16, 18, 20, 23, 23, 22, 20, 14, 17, 19, 22, 22, 22,
+ 19, 11, 14, 18, 21, 21, 21, 20, 19, 22, 25, 26, 27, 25, 20, 18, 21, 24, 25,
+ 24, 23, 19, 17, 20, 22, 22, 22, 22, 16, 15, 18, 18, 18, 19, 19, 14, 11, 16,
+ 16, 16, 16, 16, 14, 9, 14, 15, 15, 15, 15, 14, 9, 12, 13, 13, 14, 14, 13,
+ 17, 17, 18, 18, 20, 19, 12, 15, 15, 17, 17, 17, 16, 10, 11, 15, 15, 15, 14,
+ 14, 9, 7, 12, 11, 11, 11, 11, 9, 7, 10, 9, 9, 9, 8, 6, 8, 7, 8,
+ 8, 8, 7, 3, 9, 9, 6, 6, 6, 6, 3, 7, 9, 9, 10, 11, 10, 12, 3,
+ 8, 8, 9, 9, 7, 10, 3, 6, 6, 6, 6, 5, 6, 3, 3, 3, 2, 2, 2,
+ 3, 2, 4, 3, 3, 3, 1, 3, 5, 7, 7, 6, 4, 2, 2, 7, 9, 9, 7,
+ 5, 3, 2, 2, 3, 4, 5, 6, 8, 10, 2, 3, 4, 5, 5, 6, 7, 2, 4,
+ 4, 3, 3, 3, 4, 2, 5, 5, 4, 3, 0, 2, 2, 6, 8, 6, 5, 3, 2,
+ 6, 9, 12, 9, 6, 4, 1, 9, 11, 12, 11, 7, 5, 2, 6, 8, 6, 4, 6,
+ 7, 8, 6, 8, 7, 5, 5, 5, 5, 6, 8, 9, 4, 2, 2, 3, 6, 9, 11,
+ 5, 3, 2, 1, 6, 8, 10, 10, 7, 5, 2, 8, 11, 13, 12, 8, 5, 3, 10,
+ 13, 14, 14, 10, 6, 4, 20, 24, 28, 32, 31, 29, 25, 19, 23, 27, 31, 31, 28,
+ 24, 18, 21, 25, 28, 31, 27, 22, 17, 19, 22, 26, 28, 25, 19, 13, 16, 19, 22,
+ 26, 23, 20, 11, 15, 17, 20, 24, 23, 19, 10, 13, 16, 19, 23, 23, 20, 18, 22,
+ 25, 29, 28, 25, 21, 17, 21, 24, 27, 28, 24, 20, 15, 19, 23, 25, 24, 22, 18,
+ 14, 17, 19, 22, 22, 20, 16, 11, 14, 16, 18, 19, 19, 15, 10, 12, 15, 18, 18,
+ 18, 15, 7, 10, 14, 16, 16, 16, 15, 15, 19, 20, 22, 23, 21, 17, 14, 17, 20,
+ 21, 21, 19, 15, 13, 16, 18, 18, 18, 18, 13, 12, 14, 15, 14, 14, 14, 10, 9,
+ 11, 12, 12, 12, 11, 10, 5, 10, 11, 10, 11, 11, 11, 6, 8, 10, 9, 9, 9,
+ 9, 13, 13, 14, 15, 16, 16, 10, 12, 12, 12, 13, 13, 13, 8, 9, 10, 11, 10,
+ 10, 10, 7, 5, 8, 7, 7, 7, 7, 6, 4, 5, 5, 4, 4, 4, 3, 5, 4,
+ 3, 3, 3, 3, 2, 6, 6, 3, 2, 2, 2, 1, 4, 5, 6, 7, 7, 9, 10,
+ 2, 4, 5, 6, 6, 7, 8, 2, 3, 4, 4, 4, 4, 5, 2, 3, 3, 2, 2,
+ 1, 2, 1, 6, 4, 3, 3, 2, 1, 5, 6, 6, 4, 3, 2, 0, 5, 7, 7,
+ 5, 4, 3, 1, 5, 5, 5, 5, 6, 8, 9, 5, 6, 5, 5, 5, 5, 6, 5,
+ 7, 5, 4, 3, 3, 3, 5, 7, 6, 5, 4, 3, 0, 6, 8, 8, 6, 4, 3,
+ 1, 6, 9, 11, 8, 5, 3, 2, 7, 9, 11, 10, 6, 4, 2, 7, 9, 7, 5,
+ 6, 7, 7, 8, 10, 9, 5, 5, 5, 5, 8, 10, 10, 5, 3, 2, 2, 8, 9,
+ 11, 7, 4, 3, 2, 8, 9, 12, 10, 6, 4, 2, 8, 10, 14, 13, 8, 4, 3,
+ 9, 12, 14, 14, 9, 5, 3, 27, 31, 32, 35, 35, 33, 30, 25, 29, 33, 35, 35,
+ 32, 29, 23, 27, 30, 33, 34, 31, 29, 23, 26, 28, 31, 33, 28, 26, 20, 23, 25,
+ 28, 30, 28, 26, 18, 22, 24, 27, 30, 28, 26, 15, 19, 22, 26, 28, 28, 26, 24,
+ 28, 30, 33, 32, 30, 27, 23, 27, 30, 31, 31, 29, 26, 21, 25, 28, 31, 30, 28,
+ 26, 20, 24, 27, 28, 28, 25, 23, 18, 20, 23, 25, 25, 26, 23, 16, 18, 22, 25,
+ 25, 24, 23, 12, 15, 20, 23, 24, 24, 23, 22, 25, 27, 27, 28, 26, 23, 20, 24,
+ 26, 28, 28, 26, 22, 18, 23, 25, 25, 24, 26, 22, 17, 20, 22, 22, 22, 24, 20,
+ 12, 18, 19, 20, 20, 21, 19, 9, 15, 17, 18, 19, 20, 19, 10, 12, 15, 16, 17,
+ 19, 19, 18, 20, 20, 22, 22, 22, 12, 16, 18, 19, 20, 21, 20, 13, 13, 17, 17,
+ 18, 18, 18, 13, 9, 14, 14, 14, 15, 15, 12, 8, 12, 12, 12, 12, 13, 8, 9,
+ 9, 9, 10, 11, 11, 6, 10, 10, 7, 8, 9, 9, 7, 9, 11, 12, 13, 13, 10,
+ 12, 4, 9, 10, 11, 11, 9, 12, 4, 8, 8, 8, 9, 8, 10, 4, 4, 5, 5,
+ 5, 6, 6, 3, 2, 2, 2, 2, 3, 6, 5, 5, 5, 4, 2, 2, 6, 8, 8,
+ 6, 4, 2, 1, 5, 2, 3, 4, 4, 7, 9, 11, 2, 4, 4, 4, 6, 8, 9,
+ 2, 4, 4, 4, 3, 6, 8, 2, 3, 3, 3, 2, 2, 6, 0, 3, 4, 3, 2,
+ 1, 5, 3, 4, 7, 5, 2, 1, 3, 6, 7, 8, 7, 3, 2, 1, 5, 8, 7,
+ 5, 5, 8, 9, 5, 8, 9, 6, 4, 6, 9, 5, 8, 10, 6, 4, 3, 6, 5,
+ 7, 10, 5, 2, 2, 3, 4, 6, 9, 7, 3, 2, 1, 4, 7, 10, 9, 5, 2,
+ 1, 5, 9, 10, 10, 6, 3, 1, 34, 37, 40, 44, 42, 41, 38, 33, 35, 39, 43,
+ 43, 40, 37, 30, 34, 38, 41, 43, 39, 36, 29, 32, 35, 39, 41, 37, 33, 26, 30,
+ 32, 35, 39, 34, 33, 23, 26, 30, 33, 36, 37, 33, 21, 25, 29, 33, 36, 36, 34,
+ 31, 35, 38, 42, 40, 37, 35, 29, 33, 37, 40, 41, 36, 33, 28, 31, 34, 38, 38,
+ 36, 31, 26, 29, 33, 35, 35, 34, 29, 23, 26, 29, 32, 31, 31, 28, 20, 24, 27,
+ 30, 31, 33, 29, 17, 21, 26, 29, 30, 30, 29, 27, 31, 33, 36, 36, 33, 29, 26,
+ 30, 32, 34, 34, 33, 28, 24, 28, 31, 32, 33, 32, 27, 21, 27, 27, 28, 29, 28,
+ 24, 15, 23, 25, 25, 25, 25, 25, 12, 21, 23, 24, 24, 24, 24, 12, 18, 21, 22,
+ 22, 23, 22, 25, 26, 27, 29, 28, 28, 19, 20, 23, 26, 26, 27, 24, 18, 16, 22,
+ 24, 24, 24, 23, 17, 12, 20, 20, 20, 21, 20, 17, 11, 16, 17, 17, 17, 17, 13,
+ 11, 13, 15, 15, 16, 15, 10, 12, 10, 12, 14, 14, 14, 8, 12, 17, 18, 19, 20,
+ 16, 19, 8, 15, 17, 18, 18, 13, 18, 8, 13, 15, 15, 15, 11, 15, 8, 8, 11,
+ 10, 11, 8, 9, 6, 3, 8, 7, 8, 6, 7, 7, 5, 4, 5, 5, 5, 8, 8,
+ 7, 5, 3, 3, 4, 7, 3, 7, 8, 10, 11, 14, 16, 3, 6, 7, 8, 10, 12,
+ 13, 3, 3, 5, 5, 7, 9, 11, 3, 2, 2, 2, 4, 6, 8, 3, 0, 3, 2,
+ 1, 4, 7, 3, 3, 4, 3, 2, 3, 7, 5, 6, 5, 4, 2, 1, 5, 4, 6,
+ 6, 6, 10, 12, 14, 4, 6, 7, 5, 8, 10, 11, 3, 6, 7, 4, 4, 7, 9,
+ 4, 5, 7, 3, 2, 3, 5, 2, 4, 5, 4, 2, 1, 5, 3, 4, 6, 5, 3,
+ 2, 4, 4, 5, 6, 6, 3, 2, 2, 38, 41, 46, 49, 49, 47, 45, 37, 41, 44,
+ 48, 50, 45, 41, 35, 38, 43, 46, 48, 44, 40, 33, 36, 39, 43, 46, 43, 36, 31,
+ 33, 37, 40, 43, 41, 36, 27, 30, 35, 38, 41, 40, 36, 25, 28, 33, 37, 40, 40,
+ 37, 35, 40, 42, 46, 46, 42, 37, 33, 37, 42, 46, 46, 41, 37, 31, 36, 40, 43,
+ 44, 40, 35, 30, 33, 37, 39, 40, 38, 33, 25, 30, 33, 36, 37, 36, 32, 22, 27,
+ 32, 34, 35, 35, 32, 18, 25, 30, 33, 33, 34, 32, 31, 36, 38, 39, 41, 37, 32,
+ 29, 35, 37, 38, 39, 36, 31, 27, 33, 34, 36, 36, 35, 29, 23, 31, 32, 32, 33,
+ 31, 25, 17, 27, 28, 28, 28, 27, 25, 14, 25, 26, 27, 27, 27, 26, 13, 22, 25,
+ 25, 26, 26, 24, 26, 30, 31, 32, 33, 31, 23, 22, 28, 30, 31, 30, 29, 22, 19,
+ 25, 27, 28, 28, 26, 22, 15, 22, 24, 24, 24, 22, 20, 13, 17, 21, 21, 21, 18,
+ 17, 13, 14, 18, 19, 19, 18, 13, 13, 11, 17, 17, 18, 17, 10, 14, 21, 22, 24,
+ 23, 19, 23, 10, 19, 21, 21, 21, 15, 22, 10, 15, 19, 18, 18, 13, 17, 10, 9,
+ 15, 15, 15, 10, 12, 8, 5, 12, 12, 11, 7, 9, 9, 6, 9, 9, 9, 7, 8,
+ 9, 7, 7, 7, 7, 7, 7, 5, 11, 13, 13, 14, 17, 20, 5, 8, 11, 12, 13,
+ 14, 16, 5, 4, 9, 9, 10, 11, 12, 5, 3, 5, 5, 6, 8, 8, 4, 3, 0,
+ 0, 3, 6, 7, 4, 3, 6, 5, 1, 5, 8, 6, 5, 7, 6, 2, 4, 6, 4,
+ 5, 5, 9, 13, 15, 16, 4, 6, 7, 8, 11, 13, 14, 4, 5, 9, 5, 8, 9,
+ 11, 4, 5, 9, 4, 3, 6, 7, 2, 4, 7, 5, 2, 3, 7, 2, 5, 8, 7,
+ 4, 3, 5, 4, 7, 8, 8, 5, 1, 4, 36, 40, 45, 47, 48, 45, 42, 35, 40,
+ 43, 48, 47, 44, 40, 33, 37, 42, 45, 46, 43, 39, 32, 35, 39, 43, 44, 40, 35,
+ 30, 32, 35, 39, 41, 38, 35, 27, 29, 32, 36, 40, 38, 34, 23, 28, 32, 34, 38,
+ 38, 34, 34, 38, 41, 45, 46, 41, 37, 33, 35, 40, 44, 44, 38, 35, 31, 35, 39,
+ 42, 42, 38, 33, 28, 32, 35, 39, 38, 35, 31, 24, 29, 32, 34, 36, 33, 30, 20,
+ 27, 30, 33, 33, 32, 30, 17, 24, 29, 32, 31, 31, 30, 31, 35, 36, 38, 38, 37,
+ 30, 28, 34, 36, 36, 38, 35, 29, 25, 32, 35, 34, 34, 33, 26, 21, 29, 31, 31,
+ 30, 30, 23, 15, 26, 27, 28, 27, 25, 24, 12, 23, 26, 27, 25, 25, 24, 12, 21,
+ 24, 24, 24, 24, 22, 25, 30, 29, 32, 31, 29, 23, 20, 27, 29, 29, 29, 26, 21,
+ 17, 26, 27, 26, 26, 23, 20, 14, 21, 23, 23, 22, 20, 19, 12, 17, 20, 20, 19,
+ 16, 15, 12, 13, 18, 18, 18, 15, 12, 12, 10, 16, 16, 17, 15, 8, 12, 20, 22,
+ 22, 21, 17, 22, 9, 18, 19, 21, 20, 14, 21, 9, 14, 18, 17, 17, 11, 16, 9,
+ 8, 15, 14, 13, 8, 11, 8, 4, 12, 11, 9, 5, 8, 7, 5, 9, 9, 8, 4,
+ 6, 8, 6, 7, 7, 7, 4, 5, 5, 11, 12, 13, 13, 15, 19, 5, 7, 11, 11,
+ 12, 13, 14, 5, 4, 9, 8, 9, 10, 11, 5, 3, 5, 5, 6, 6, 6, 3, 2,
+ 0, 0, 2, 4, 5, 3, 3, 6, 5, 1, 3, 5, 5, 5, 7, 7, 2, 3, 4,
+ 4, 5, 5, 9, 12, 14, 15, 4, 5, 5, 8, 10, 12, 12, 4, 5, 6, 5, 7,
+ 8, 9, 4, 4, 6, 3, 3, 4, 5, 2, 4, 7, 6, 1, 3, 4, 1, 5, 11,
+ 11, 4, 1, 4, 4, 8, 12, 12, 6, 1, 3, 33, 37, 41, 43, 44, 41, 38, 31,
+ 35, 39, 43, 44, 39, 36, 29, 34, 37, 42, 42, 38, 33, 28, 31, 34, 39, 41, 36,
+ 30, 26, 28, 31, 35, 37, 33, 31, 23, 24, 28, 32, 35, 34, 30, 21, 24, 27, 30,
+ 35, 34, 30, 30, 33, 38, 41, 40, 37, 32, 29, 31, 36, 40, 41, 37, 31, 28, 30,
+ 35, 37, 37, 34, 29, 26, 28, 31, 34, 34, 32, 26, 22, 25, 27, 30, 31, 29, 25,
+ 19, 23, 26, 29, 29, 28, 25, 14, 21, 25, 27, 27, 27, 25, 28, 31, 33, 34, 35,
+ 33, 28, 26, 30, 32, 33, 33, 31, 25, 22, 28, 30, 29, 30, 29, 23, 19, 27, 27,
+ 26, 28, 25, 19, 13, 22, 24, 23, 23, 22, 19, 10, 20, 22, 21, 21, 21, 20, 9,
+ 18, 20, 20, 20, 20, 18, 23, 26, 26, 27, 27, 27, 20, 18, 24, 25, 25, 26, 23,
+ 18, 15, 22, 23, 23, 22, 21, 17, 12, 19, 19, 19, 20, 17, 15, 10, 14, 16, 15,
+ 15, 13, 12, 9, 10, 15, 14, 14, 12, 9, 9, 7, 13, 13, 13, 12, 6, 11, 18,
+ 18, 19, 19, 15, 19, 7, 16, 16, 18, 17, 12, 18, 7, 12, 15, 14, 14, 10, 13,
+ 7, 7, 11, 11, 10, 6, 8, 5, 2, 7, 7, 6, 4, 5, 5, 3, 5, 4, 5,
+ 3, 4, 5, 4, 3, 3, 3, 3, 4, 3, 9, 9, 10, 12, 13, 17, 3, 7, 9,
+ 9, 11, 11, 12, 3, 4, 8, 7, 8, 8, 9, 3, 3, 4, 4, 4, 5, 4, 2,
+ 1, 3, 2, 0, 3, 4, 2, 3, 5, 4, 2, 2, 4, 3, 4, 5, 5, 3, 1,
+ 3, 4, 5, 6, 9, 11, 13, 13, 4, 5, 6, 8, 9, 10, 10, 4, 5, 6, 6,
+ 6, 7, 7, 4, 3, 5, 2, 2, 3, 3, 2, 3, 6, 5, 3, 1, 3, 2, 5,
+ 7, 6, 3, 2, 3, 4, 6, 7, 7, 4, 2, 2, 26, 31, 35, 39, 39, 35, 33,
+ 26, 30, 33, 39, 38, 36, 31, 24, 29, 32, 36, 37, 33, 29, 23, 26, 30, 33, 34,
+ 31, 26, 20, 24, 26, 29, 32, 29, 26, 18, 20, 23, 26, 30, 30, 26, 17, 18, 22,
+ 26, 29, 29, 26, 24, 28, 32, 36, 35, 33, 29, 24, 26, 31, 35, 34, 30, 27, 22,
+ 25, 29, 33, 31, 29, 24, 21, 24, 27, 29, 29, 27, 21, 18, 20, 23, 25, 25, 24,
+ 21, 16, 19, 21, 23, 23, 24, 21, 13, 17, 20, 23, 22, 22, 22, 22, 25, 28, 28,
+ 30, 28, 24, 21, 24, 27, 27, 28, 27, 22, 20, 23, 25, 25, 25, 25, 20, 17, 21,
+ 22, 21, 22, 22, 16, 11, 18, 18, 18, 18, 18, 16, 7, 17, 17, 16, 16, 17, 16,
+ 8, 15, 15, 15, 16, 16, 14, 20, 20, 20, 21, 23, 23, 16, 16, 19, 20, 20, 21,
+ 19, 14, 13, 18, 18, 18, 17, 17, 13, 9, 15, 15, 14, 14, 14, 11, 7, 12, 11,
+ 10, 10, 10, 8, 7, 8, 10, 9, 9, 9, 5, 7, 5, 8, 8, 7, 8, 3, 8,
+ 12, 13, 14, 14, 13, 15, 4, 11, 11, 12, 13, 10, 13, 4, 10, 9, 9, 9, 8,
+ 9, 4, 5, 6, 6, 5, 4, 5, 3, 2, 2, 2, 2, 2, 3, 3, 2, 3, 2,
+ 1, 2, 3, 4, 3, 5, 3, 2, 0, 3, 3, 6, 7, 8, 9, 11, 13, 3, 5,
+ 7, 8, 8, 9, 10, 3, 3, 5, 5, 6, 6, 6, 3, 3, 3, 2, 2, 3, 3,
+ 1, 4, 6, 4, 3, 0, 3, 2, 5, 8, 5, 3, 2, 2, 4, 7, 8, 7, 4,
+ 2, 2, 4, 6, 6, 7, 8, 10, 11, 4, 6, 6, 6, 7, 8, 8, 4, 6, 7,
+ 4, 4, 4, 6, 4, 6, 8, 4, 2, 1, 2, 3, 8, 9, 7, 4, 2, 2, 6,
+ 8, 10, 9, 5, 3, 0, 7, 9, 10, 9, 6, 4, 2, 21, 26, 31, 32, 34, 32,
+ 27, 20, 24, 28, 32, 34, 30, 25, 19, 22, 27, 30, 32, 28, 23, 18, 21, 24, 28,
+ 30, 26, 21, 15, 18, 21, 24, 27, 24, 21, 13, 15, 18, 21, 24, 24, 20, 12, 14,
+ 17, 21, 24, 25, 21, 20, 23, 26, 31, 29, 27, 24, 19, 22, 26, 29, 29, 26, 22,
+ 17, 21, 24, 27, 27, 24, 19, 16, 18, 21, 24, 23, 22, 17, 13, 15, 17, 20, 20,
+ 20, 17, 12, 13, 16, 19, 19, 19, 17, 9, 12, 15, 18, 17, 18, 17, 18, 20, 22,
+ 23, 25, 23, 18, 16, 19, 21, 22, 23, 21, 16, 15, 18, 19, 19, 19, 19, 15, 14,
+ 16, 16, 16, 16, 16, 12, 9, 12, 13, 13, 12, 13, 12, 6, 12, 12, 12, 11, 12,
+ 12, 6, 10, 11, 10, 11, 11, 10, 15, 15, 15, 16, 17, 18, 11, 13, 13, 14, 15,
+ 15, 15, 9, 10, 12, 12, 12, 12, 11, 8, 6, 11, 9, 8, 8, 8, 7, 4, 7,
+ 6, 6, 5, 5, 4, 5, 5, 5, 4, 4, 4, 2, 6, 4, 3, 3, 3, 3, 2,
+ 6, 7, 7, 8, 9, 10, 11, 2, 5, 6, 7, 7, 8, 9, 2, 4, 5, 4, 5,
+ 5, 5, 2, 2, 2, 2, 1, 2, 2, 1, 4, 4, 3, 2, 2, 2, 3, 5, 5,
+ 4, 2, 2, 1, 4, 6, 7, 4, 3, 3, 0, 3, 4, 5, 6, 7, 8, 10, 4,
+ 5, 5, 6, 6, 6, 7, 4, 7, 5, 4, 4, 4, 4, 4, 6, 5, 4, 3, 2,
+ 1, 4, 7, 8, 5, 4, 3, 0, 6, 7, 9, 7, 4, 3, 2, 6, 8, 10, 8,
+ 5, 3, 2, 7, 9, 7, 5, 6, 8, 8, 7, 9, 8, 5, 5, 5, 5, 7, 9,
+ 10, 5, 3, 2, 3, 7, 8, 11, 6, 4, 3, 1, 7, 9, 11, 8, 5, 3, 2,
+ 8, 9, 12, 11, 6, 4, 2, 8, 10, 12, 12, 8, 5, 3, 31, 34, 37, 39, 39,
+ 37, 34, 28, 33, 36, 38, 38, 35, 32, 27, 31, 34, 37, 37, 35, 31, 26, 28, 32,
+ 34, 35, 32, 27, 22, 25, 29, 30, 32, 29, 27, 18, 22, 24, 26, 30, 29, 27, 15,
+ 19, 23, 26, 29, 28, 27, 26, 31, 35, 36, 36, 34, 32, 26, 30, 34, 36, 37, 33,
+ 31, 24, 28, 31, 34, 34, 31, 28, 22, 26, 30, 32, 30, 30, 24, 20, 22, 25, 28,
+ 27, 26, 24, 16, 19, 22, 24, 25, 25, 24, 12, 16, 20, 24, 24, 25, 24, 24, 29,
+ 31, 31, 33, 30, 27, 23, 26, 29, 30, 31, 31, 26, 21, 26, 28, 28, 29, 29, 25,
+ 19, 23, 25, 24, 25, 25, 20, 12, 19, 20, 21, 21, 21, 20, 8, 16, 18, 18, 19,
+ 20, 20, 8, 13, 16, 16, 18, 18, 19, 21, 22, 24, 25, 25, 27, 18, 18, 20, 21,
+ 24, 23, 24, 17, 14, 19, 21, 21, 21, 21, 16, 11, 17, 17, 17, 17, 17, 15, 8,
+ 12, 12, 13, 13, 13, 11, 7, 9, 9, 10, 11, 12, 8, 9, 7, 7, 8, 9, 10,
+ 8, 10, 14, 15, 16, 17, 17, 18, 6, 12, 13, 15, 15, 14, 17, 6, 11, 11, 11,
+ 12, 11, 14, 6, 7, 8, 8, 8, 8, 7, 4, 2, 3, 3, 3, 5, 7, 3, 2,
+ 2, 2, 0, 3, 7, 5, 5, 4, 2, 2, 2, 5, 5, 6, 8, 9, 12, 15, 17,
+ 5, 5, 7, 8, 11, 13, 14, 5, 6, 6, 6, 8, 10, 11, 5, 5, 5, 5, 4,
+ 6, 6, 3, 3, 4, 3, 2, 2, 6, 0, 4, 5, 4, 2, 1, 5, 4, 4, 6,
+ 5, 2, 2, 3, 7, 9, 9, 8, 10, 13, 15, 7, 9, 10, 8, 8, 11, 13, 7,
+ 9, 11, 7, 6, 7, 9, 7, 8, 10, 6, 4, 3, 5, 5, 7, 9, 6, 3, 1,
+ 3, 4, 5, 9, 8, 3, 2, 1, 5, 6, 9, 9, 4, 2, 1, 37, 41, 44, 47,
+ 48, 44, 42, 35, 39, 43, 47, 48, 44, 40, 33, 38, 41, 45, 45, 42, 38, 32, 36,
+ 38, 42, 45, 40, 34, 29, 31, 35, 39, 39, 36, 34, 24, 28, 31, 33, 37, 37, 34,
+ 22, 25, 29, 32, 36, 36, 34, 33, 36, 41, 46, 42, 42, 39, 31, 36, 41, 44, 45,
+ 42, 36, 30, 34, 38, 41, 41, 39, 35, 29, 32, 36, 38, 39, 35, 30, 25, 29, 31,
+ 34, 33, 33, 30, 20, 25, 28, 32, 31, 31, 28, 16, 22, 26, 31, 30, 30, 30, 30,
+ 35, 36, 37, 39, 37, 33, 28, 33, 35, 36, 37, 36, 31, 26, 31, 33, 34, 34, 34,
+ 29, 22, 29, 30, 31, 31, 30, 24, 16, 24, 26, 26, 25, 26, 25, 11, 21, 23, 24,
+ 24, 24, 25, 10, 19, 21, 23, 23, 23, 22, 26, 29, 30, 31, 32, 33, 24, 21, 26,
+ 29, 30, 31, 29, 23, 18, 25, 27, 27, 27, 26, 21, 14, 22, 23, 23, 23, 22, 19,
+ 12, 16, 19, 19, 18, 17, 16, 11, 12, 15, 16, 16, 16, 12, 10, 8, 13, 14, 14,
+ 15, 9, 13, 20, 21, 22, 24, 21, 24, 10, 18, 20, 20, 22, 18, 22, 10, 15, 17,
+ 17, 18, 15, 17, 10, 9, 14, 14, 13, 10, 12, 8, 4, 9, 9, 9, 8, 9, 5,
+ 3, 5, 5, 6, 6, 9, 7, 5, 3, 3, 4, 5, 7, 6, 12, 13, 14, 17, 19,
+ 21, 6, 10, 11, 13, 15, 17, 18, 6, 6, 9, 10, 12, 13, 14, 6, 5, 6, 6,
+ 8, 9, 8, 4, 3, 3, 3, 3, 6, 7, 4, 0, 4, 3, 1, 4, 7, 3, 3,
+ 5, 3, 2, 3, 6, 6, 8, 8, 10, 15, 17, 19, 6, 8, 9, 9, 13, 15, 16,
+ 6, 8, 9, 7, 9, 11, 12, 6, 7, 8, 5, 5, 7, 6, 4, 5, 6, 5, 2,
+ 3, 7, 2, 4, 5, 4, 2, 1, 5, 3, 5, 5, 5, 3, 2, 4, 42, 47, 50,
+ 55, 55, 54, 48, 41, 45, 49, 54, 55, 52, 45, 39, 43, 47, 51, 52, 50, 45, 38,
+ 40, 45, 48, 51, 45, 39, 34, 38, 41, 44, 46, 42, 40, 31, 34, 38, 41, 44, 42,
+ 39, 27, 30, 35, 40, 42, 43, 40, 40, 43, 48, 51, 52, 48, 44, 38, 43, 47, 51,
+ 50, 46, 42, 35, 40, 45, 48, 48, 46, 42, 34, 38, 42, 44, 44, 42, 35, 29, 35,
+ 38, 41, 40, 38, 34, 22, 31, 34, 37, 37, 38, 34, 19, 29, 33, 35, 36, 37, 36,
+ 37, 40, 44, 44, 45, 44, 39, 33, 40, 42, 42, 43, 42, 37, 29, 38, 40, 41, 40,
+ 39, 34, 25, 35, 37, 36, 37, 35, 30, 20, 30, 33, 33, 32, 29, 29, 14, 26, 30,
+ 30, 29, 30, 29, 13, 23, 27, 29, 28, 28, 27, 29, 34, 36, 37, 39, 38, 30, 25,
+ 32, 34, 36, 36, 34, 27, 21, 30, 33, 32, 33, 30, 26, 18, 26, 29, 28, 29, 26,
+ 25, 16, 20, 25, 25, 24, 20, 21, 14, 14, 22, 21, 21, 20, 17, 14, 10, 19, 20,
+ 20, 19, 13, 17, 27, 28, 29, 29, 25, 30, 13, 22, 27, 27, 27, 22, 28, 13, 18,
+ 24, 23, 24, 18, 22, 13, 12, 20, 20, 19, 13, 17, 11, 7, 16, 15, 14, 9, 12,
+ 9, 5, 11, 11, 11, 8, 10, 9, 6, 8, 10, 10, 8, 9, 9, 15, 18, 20, 20,
+ 23, 26, 9, 12, 17, 18, 19, 20, 22, 9, 8, 15, 15, 16, 17, 17, 9, 7, 11,
+ 11, 12, 11, 11, 7, 5, 6, 6, 5, 8, 9, 5, 4, 0, 1, 4, 6, 9, 5,
+ 3, 2, 2, 3, 6, 8, 7, 8, 11, 16, 19, 21, 22, 7, 7, 9, 14, 18, 18,
+ 18, 8, 7, 8, 11, 14, 15, 15, 7, 6, 6, 7, 10, 10, 9, 6, 4, 5, 3,
+ 3, 5, 8, 4, 2, 4, 4, 2, 5, 7, 3, 2, 4, 4, 2, 4, 6, 42, 45,
+ 48, 52, 53, 51, 46, 40, 42, 47, 52, 52, 48, 43, 38, 42, 46, 49, 52, 47, 42,
+ 35, 40, 44, 46, 48, 45, 38, 32, 37, 39, 44, 44, 42, 38, 29, 33, 36, 39, 42,
+ 41, 37, 26, 30, 34, 38, 41, 41, 38, 38, 42, 46, 49, 48, 46, 43, 37, 40, 45,
+ 49, 48, 44, 40, 35, 38, 43, 47, 46, 42, 35, 33, 38, 40, 43, 41, 39, 33, 26,
+ 34, 36, 39, 38, 36, 32, 22, 29, 32, 36, 35, 35, 32, 17, 28, 32, 35, 34, 34,
+ 33, 35, 40, 42, 42, 44, 41, 35, 31, 39, 39, 42, 42, 39, 33, 28, 37, 39, 38,
+ 38, 37, 30, 24, 33, 34, 35, 35, 32, 28, 18, 28, 31, 31, 30, 29, 28, 13, 24,
+ 29, 28, 29, 28, 28, 13, 21, 27, 27, 27, 27, 25, 27, 34, 34, 35, 37, 33, 27,
+ 23, 30, 33, 34, 33, 31, 26, 20, 27, 32, 31, 30, 27, 24, 16, 24, 28, 28, 27,
+ 23, 23, 14, 17, 24, 23, 23, 18, 19, 13, 13, 21, 21, 20, 17, 16, 12, 9, 19,
+ 20, 19, 17, 11, 16, 24, 26, 27, 26, 23, 27, 12, 20, 25, 26, 24, 19, 26, 12,
+ 16, 23, 23, 21, 15, 20, 12, 10, 19, 19, 17, 10, 15, 10, 6, 15, 15, 12, 7,
+ 11, 9, 5, 11, 11, 11, 6, 8, 8, 5, 7, 9, 9, 6, 7, 8, 14, 18, 18,
+ 18, 19, 24, 8, 9, 16, 16, 17, 17, 19, 8, 6, 14, 13, 14, 14, 14, 8, 5,
+ 10, 9, 10, 9, 9, 6, 3, 5, 5, 4, 6, 7, 4, 3, 1, 0, 3, 5, 7,
+ 4, 3, 1, 2, 2, 4, 6, 6, 7, 11, 14, 17, 19, 20, 6, 7, 10, 13, 15,
+ 16, 16, 6, 6, 9, 10, 12, 13, 13, 6, 5, 5, 6, 8, 8, 7, 4, 4, 4,
+ 3, 3, 4, 6, 3, 2, 6, 6, 1, 4, 5, 2, 3, 6, 6, 2, 3, 4, 34,
+ 39, 42, 47, 46, 43, 40, 34, 37, 42, 46, 46, 43, 37, 32, 35, 40, 44, 45, 41,
+ 36, 31, 33, 37, 40, 42, 38, 32, 28, 30, 33, 37, 38, 36, 33, 25, 27, 29, 33,
+ 37, 36, 32, 23, 25, 28, 32, 35, 36, 32, 32, 35, 40, 44, 44, 40, 36, 32, 35,
+ 38, 43, 42, 38, 33, 30, 33, 38, 40, 40, 36, 31, 28, 31, 34, 37, 36, 34, 28,
+ 22, 29, 30, 34, 33, 30, 27, 19, 25, 27, 30, 30, 30, 28, 15, 25, 27, 29, 29,
+ 30, 27, 30, 34, 37, 36, 36, 34, 29, 27, 32, 34, 35, 36, 33, 28, 24, 30, 33,
+ 32, 32, 30, 25, 21, 29, 29, 30, 30, 27, 23, 15, 24, 26, 26, 25, 23, 22, 11,
+ 22, 24, 23, 23, 22, 22, 10, 19, 22, 22, 22, 22, 20, 25, 28, 28, 29, 30, 29,
+ 23, 20, 26, 27, 28, 28, 25, 20, 16, 24, 26, 25, 25, 22, 19, 13, 20, 22, 21,
+ 22, 18, 18, 11, 15, 19, 18, 17, 14, 14, 10, 11, 17, 15, 15, 14, 11, 10, 7,
+ 15, 14, 14, 13, 7, 12, 20, 20, 21, 21, 17, 22, 9, 17, 19, 20, 19, 14, 20,
+ 9, 13, 17, 17, 16, 11, 15, 9, 8, 14, 13, 12, 7, 10, 7, 3, 10, 9, 8,
+ 4, 7, 6, 3, 7, 6, 6, 3, 5, 5, 4, 5, 5, 4, 4, 4, 5, 10, 12,
+ 12, 13, 15, 19, 5, 8, 11, 11, 12, 13, 14, 4, 4, 10, 9, 9, 10, 10, 5,
+ 3, 6, 6, 6, 6, 5, 3, 2, 1, 1, 2, 3, 4, 2, 1, 4, 3, 0, 3,
+ 4, 3, 2, 4, 3, 2, 2, 3, 5, 5, 8, 10, 12, 14, 15, 5, 5, 7, 10,
+ 11, 12, 12, 5, 5, 6, 7, 8, 9, 9, 5, 4, 4, 4, 4, 5, 4, 3, 2,
+ 5, 4, 2, 2, 4, 1, 4, 5, 5, 2, 2, 3, 3, 4, 5, 5, 3, 0, 2,
+ 28, 33, 37, 40, 41, 38, 35, 28, 31, 36, 40, 39, 37, 33, 26, 29, 35, 38, 38,
+ 34, 31, 26, 28, 31, 35, 37, 33, 28, 23, 25, 28, 30, 34, 31, 27, 19, 22, 24,
+ 28, 32, 31, 27, 18, 21, 23, 27, 30, 31, 28, 27, 30, 34, 38, 36, 35, 30, 26,
+ 30, 32, 37, 37, 33, 28, 25, 28, 31, 34, 33, 32, 26, 23, 26, 28, 30, 30, 28,
+ 22, 20, 22, 25, 27, 27, 26, 22, 17, 20, 22, 25, 25, 25, 22, 13, 19, 21, 24,
+ 24, 25, 22, 25, 28, 29, 30, 31, 30, 25, 23, 26, 29, 30, 29, 28, 23, 21, 26,
+ 27, 27, 27, 27, 21, 18, 23, 23, 23, 24, 23, 17, 12, 20, 20, 20, 20, 19, 17,
+ 8, 19, 19, 18, 18, 18, 17, 8, 16, 17, 17, 17, 17, 15, 21, 22, 22, 23, 24,
+ 23, 17, 17, 21, 22, 22, 23, 21, 15, 13, 19, 20, 20, 19, 18, 14, 10, 16, 16,
+ 16, 16, 15, 13, 8, 12, 13, 12, 12, 11, 10, 8, 9, 11, 11, 10, 10, 6, 8,
+ 6, 10, 9, 9, 9, 4, 9, 14, 15, 16, 16, 13, 17, 5, 13, 13, 14, 14, 10,
+ 15, 6, 11, 11, 11, 11, 8, 10, 6, 6, 8, 7, 7, 5, 6, 4, 2, 4, 3,
+ 3, 3, 3, 3, 2, 2, 1, 2, 2, 3, 4, 2, 4, 2, 1, 2, 3, 3, 7,
+ 8, 9, 10, 12, 14, 3, 6, 7, 8, 9, 10, 10, 3, 4, 6, 6, 6, 7, 7,
+ 3, 3, 3, 3, 3, 4, 3, 1, 3, 5, 3, 2, 1, 3, 1, 5, 6, 4, 3,
+ 0, 3, 2, 5, 6, 6, 3, 2, 2, 4, 6, 6, 7, 9, 11, 12, 4, 7, 6,
+ 7, 8, 9, 9, 4, 6, 6, 5, 5, 5, 6, 4, 5, 6, 3, 2, 2, 2, 2,
+ 5, 7, 6, 3, 2, 2, 5, 6, 8, 7, 4, 2, 1, 5, 7, 8, 8, 5, 3,
+ 0, 24, 28, 32, 36, 35, 32, 29, 23, 26, 30, 34, 36, 32, 28, 21, 25, 29, 32,
+ 34, 31, 25, 19, 22, 26, 30, 32, 28, 23, 17, 20, 22, 26, 28, 27, 23, 15, 17,
+ 20, 23, 27, 26, 23, 14, 16, 19, 22, 25, 26, 23, 21, 24, 28, 34, 32, 28, 26,
+ 20, 24, 28, 31, 31, 28, 24, 18, 22, 26, 29, 28, 26, 22, 18, 20, 23, 25, 26,
+ 23, 19, 15, 17, 20, 22, 21, 21, 18, 13, 15, 18, 20, 20, 21, 19, 11, 13, 17,
+ 20, 19, 19, 18, 18, 22, 24, 25, 27, 25, 20, 17, 20, 23, 24, 24, 23, 19, 17,
+ 19, 22, 22, 21, 22, 17, 14, 18, 18, 18, 18, 18, 14, 10, 14, 15, 15, 15, 15,
+ 14, 6, 13, 14, 13, 13, 14, 13, 6, 12, 12, 12, 12, 13, 12, 17, 17, 17, 18,
+ 20, 20, 13, 14, 15, 16, 17, 17, 17, 11, 11, 14, 14, 14, 14, 13, 10, 7, 12,
+ 11, 11, 11, 10, 9, 5, 9, 8, 7, 7, 7, 5, 6, 7, 7, 6, 6, 6, 3,
+ 7, 4, 5, 5, 5, 5, 3, 7, 9, 9, 10, 11, 11, 13, 2, 7, 8, 9, 9,
+ 8, 11, 2, 6, 6, 6, 6, 6, 7, 2, 3, 3, 2, 2, 3, 3, 1, 2, 2,
+ 2, 2, 0, 2, 2, 4, 5, 4, 2, 1, 2, 3, 6, 7, 4, 3, 2, 2, 3,
+ 4, 5, 7, 8, 10, 11, 3, 4, 5, 6, 7, 7, 8, 3, 5, 4, 4, 4, 5,
+ 5, 3, 4, 5, 3, 2, 1, 2, 3, 6, 8, 5, 4, 2, 2, 4, 8, 10, 7,
+ 4, 3, 0, 6, 9, 10, 8, 5, 3, 1, 6, 8, 6, 5, 7, 8, 9, 6, 8,
+ 8, 6, 6, 6, 7, 6, 8, 9, 5, 3, 3, 4, 6, 7, 10, 7, 5, 2, 0,
+ 7, 9, 10, 8, 5, 3, 2, 8, 10, 11, 10, 6, 4, 2, 9, 10, 11, 11, 7,
+ 5, 3, 33, 37, 40, 43, 42, 41, 37, 32, 36, 40, 42, 42, 40, 36, 30, 34, 38,
+ 40, 41, 37, 34, 29, 32, 36, 37, 38, 33, 27, 25, 28, 32, 33, 32, 29, 28, 22,
+ 24, 27, 28, 32, 29, 28, 16, 20, 24, 27, 29, 30, 28, 30, 34, 38, 40, 40, 38,
+ 35, 29, 33, 37, 42, 40, 37, 34, 27, 31, 35, 38, 36, 34, 30, 26, 29, 32, 34,
+ 34, 32, 25, 23, 26, 28, 31, 28, 27, 25, 18, 22, 24, 25, 27, 26, 25, 13, 17,
+ 21, 24, 24, 25, 24, 27, 32, 33, 35, 36, 35, 31, 26, 29, 33, 34, 35, 34, 29,
+ 24, 29, 31, 31, 31, 31, 26, 20, 26, 27, 28, 28, 26, 21, 14, 22, 24, 24, 22,
+ 21, 21, 8, 17, 19, 19, 19, 20, 21, 7, 14, 17, 17, 18, 19, 19, 24, 25, 26,
+ 28, 30, 30, 24, 20, 24, 25, 27, 27, 27, 22, 17, 23, 24, 24, 24, 23, 20, 13,
+ 19, 20, 20, 20, 19, 17, 11, 14, 16, 16, 14, 13, 14, 8, 9, 11, 10, 11, 12,
+ 10, 7, 5, 8, 9, 10, 10, 9, 12, 18, 19, 20, 22, 22, 23, 9, 17, 18, 19,
+ 20, 19, 22, 9, 15, 16, 16, 16, 16, 16, 9, 11, 13, 13, 12, 10, 10, 6, 5,
+ 8, 8, 4, 6, 8, 4, 3, 1, 2, 3, 5, 8, 3, 2, 2, 1, 2, 4, 7,
+ 8, 12, 14, 15, 18, 21, 22, 8, 10, 12, 14, 16, 18, 18, 8, 8, 10, 10, 13,
+ 14, 14, 8, 7, 7, 7, 8, 9, 7, 6, 5, 6, 5, 3, 4, 7, 3, 3, 5,
+ 4, 3, 2, 6, 0, 3, 5, 5, 3, 2, 5, 8, 11, 11, 11, 16, 19, 20, 9,
+ 11, 12, 10, 14, 16, 17, 9, 11, 12, 9, 10, 12, 13, 9, 10, 11, 8, 6, 6,
+ 6, 7, 8, 10, 7, 4, 2, 5, 5, 7, 8, 7, 4, 3, 3, 4, 5, 7, 8,
+ 4, 3, 2, 39, 43, 46, 51, 52, 48, 44, 38, 42, 45, 50, 49, 46, 43, 36, 39,
+ 44, 48, 48, 45, 39, 35, 38, 42, 44, 45, 40, 34, 31, 35, 36, 40, 41, 37, 34,
+ 27, 30, 33, 34, 38, 38, 34, 22, 26, 29, 34, 37, 37, 33, 36, 40, 46, 48, 48,
+ 43, 41, 34, 38, 42, 47, 47, 42, 38, 32, 36, 41, 43, 44, 41, 35, 31, 34, 38,
+ 41, 40, 37, 30, 27, 32, 34, 36, 34, 33, 30, 21, 27, 30, 31, 32, 33, 30, 15,
+ 23, 25, 30, 30, 30, 29, 33, 36, 40, 41, 42, 40, 35, 31, 35, 38, 40, 41, 39,
+ 33, 28, 34, 37, 38, 36, 36, 31, 24, 31, 33, 33, 33, 31, 26, 17, 27, 28, 29,
+ 27, 26, 26, 12, 22, 25, 24, 24, 25, 26, 10, 18, 22, 23, 23, 24, 24, 27, 31,
+ 32, 34, 35, 35, 28, 23, 30, 31, 32, 32, 33, 25, 20, 28, 30, 29, 29, 28, 24,
+ 16, 24, 25, 26, 25, 22, 22, 13, 18, 21, 21, 18, 18, 18, 12, 11, 17, 16, 16,
+ 17, 14, 10, 7, 15, 14, 15, 15, 11, 15, 23, 24, 25, 26, 25, 28, 12, 20, 22,
+ 24, 24, 22, 26, 12, 17, 21, 21, 20, 18, 20, 12, 11, 17, 16, 16, 12, 14, 10,
+ 5, 13, 12, 9, 8, 10, 7, 5, 7, 6, 7, 7, 9, 5, 4, 4, 4, 5, 7,
+ 8, 9, 16, 18, 19, 22, 23, 25, 9, 13, 16, 18, 20, 20, 22, 9, 9, 14, 14,
+ 16, 17, 16, 9, 8, 10, 10, 12, 11, 9, 7, 6, 5, 5, 4, 7, 9, 4, 3,
+ 3, 3, 2, 5, 9, 4, 0, 3, 4, 2, 4, 7, 9, 9, 10, 15, 19, 22, 22,
+ 9, 9, 10, 13, 18, 19, 19, 9, 9, 11, 11, 13, 15, 14, 9, 8, 10, 7, 9,
+ 9, 7, 7, 7, 8, 6, 3, 4, 7, 5, 5, 6, 5, 3, 3, 6, 2, 4, 5,
+ 5, 3, 2, 5, 44, 47, 53, 55, 56, 52, 48, 42, 47, 51, 53, 55, 51, 47, 41,
+ 45, 50, 51, 54, 50, 44, 39, 42, 44, 49, 50, 47, 40, 36, 39, 42, 46, 46, 42,
+ 39, 31, 34, 38, 40, 44, 43, 39, 27, 31, 35, 39, 42, 43, 39, 40, 45, 49, 52,
+ 52, 48, 45, 39, 44, 47, 52, 50, 47, 42, 36, 42, 45, 48, 49, 45, 39, 35, 39,
+ 42, 44, 44, 41, 34, 28, 36, 39, 41, 39, 38, 35, 23, 32, 35, 36, 37, 36, 35,
+ 18, 29, 32, 36, 35, 36, 34, 36, 42, 44, 45, 47, 44, 38, 33, 41, 41, 45, 45,
+ 43, 37, 30, 39, 40, 41, 41, 39, 34, 25, 35, 37, 37, 37, 36, 30, 20, 31, 34,
+ 34, 31, 30, 30, 15, 26, 30, 29, 30, 29, 30, 13, 22, 28, 28, 28, 29, 28, 29,
+ 35, 36, 38, 39, 38, 31, 26, 34, 36, 37, 37, 35, 29, 22, 31, 34, 33, 34, 31,
+ 27, 18, 27, 30, 29, 30, 26, 25, 16, 20, 26, 25, 24, 21, 22, 15, 14, 22, 21,
+ 21, 20, 17, 14, 10, 19, 20, 20, 19, 14, 17, 25, 28, 30, 29, 26, 31, 14, 23,
+ 27, 28, 28, 23, 29, 14, 19, 25, 25, 24, 20, 24, 14, 13, 21, 20, 21, 13, 16,
+ 12, 8, 16, 16, 13, 10, 13, 9, 5, 11, 11, 12, 9, 11, 9, 6, 8, 10, 10,
+ 8, 10, 10, 16, 19, 21, 22, 24, 27, 10, 12, 18, 20, 21, 22, 23, 9, 8, 17,
+ 16, 17, 18, 18, 10, 8, 13, 12, 13, 12, 11, 8, 5, 7, 7, 5, 8, 10, 6,
+ 5, 2, 1, 4, 7, 9, 5, 3, 0, 2, 4, 6, 8, 9, 8, 12, 17, 21, 23,
+ 24, 9, 8, 11, 16, 20, 20, 20, 9, 8, 9, 13, 15, 16, 16, 8, 7, 7, 8,
+ 11, 11, 9, 6, 5, 5, 3, 4, 5, 8, 4, 3, 4, 4, 2, 5, 7, 3, 2,
+ 4, 4, 2, 4, 6, 44, 48, 53, 55, 55, 53, 49, 41, 48, 50, 54, 54, 52, 48,
+ 40, 44, 49, 52, 53, 49, 44, 39, 42, 47, 48, 51, 47, 42, 35, 39, 42, 45, 46,
+ 44, 40, 31, 34, 38, 42, 44, 43, 40, 27, 33, 36, 39, 43, 43, 40, 41, 44, 48,
+ 53, 53, 48, 43, 39, 45, 48, 51, 50, 47, 42, 36, 42, 46, 48, 48, 46, 39, 33,
+ 40, 43, 46, 45, 42, 35, 28, 36, 40, 41, 40, 38, 35, 22, 32, 35, 39, 38, 37,
+ 35, 18, 29, 34, 37, 35, 36, 36, 36, 41, 44, 45, 46, 44, 37, 34, 39, 43, 44,
+ 43, 41, 35, 29, 39, 41, 41, 40, 39, 33, 26, 37, 37, 37, 37, 36, 30, 19, 31,
+ 34, 34, 32, 30, 29, 15, 26, 31, 30, 30, 29, 29, 14, 21, 30, 29, 29, 28, 27,
+ 30, 36, 38, 38, 38, 36, 30, 25, 33, 35, 37, 35, 33, 28, 21, 30, 34, 34, 32,
+ 30, 26, 17, 24, 31, 30, 30, 24, 25, 15, 19, 26, 27, 24, 20, 21, 15, 14, 24,
+ 23, 22, 19, 17, 14, 10, 20, 22, 21, 18, 14, 16, 25, 29, 30, 27, 24, 30, 13,
+ 22, 27, 28, 26, 20, 28, 13, 17, 25, 24, 23, 17, 23, 13, 12, 21, 21, 19, 12,
+ 17, 11, 7, 17, 17, 13, 8, 13, 10, 6, 12, 13, 12, 7, 10, 9, 6, 8, 12,
+ 11, 7, 9, 9, 14, 19, 20, 19, 22, 26, 9, 10, 18, 18, 18, 19, 22, 9, 6,
+ 16, 15, 15, 16, 16, 9, 5, 12, 12, 11, 11, 10, 7, 4, 6, 7, 5, 7, 8,
+ 5, 3, 2, 2, 3, 5, 8, 5, 3, 2, 0, 3, 5, 7, 7, 7, 12, 16, 18,
+ 20, 22, 7, 7, 11, 14, 17, 17, 18, 7, 7, 9, 12, 13, 14, 14, 7, 6, 5,
+ 8, 9, 9, 8, 5, 4, 3, 2, 4, 5, 7, 3, 2, 5, 4, 2, 4, 6, 3,
+ 1, 5, 5, 1, 4, 5, 38, 42, 47, 47, 48, 47, 43, 36, 41, 44, 48, 49, 45,
+ 41, 34, 39, 43, 45, 47, 44, 38, 33, 36, 40, 43, 45, 41, 34, 31, 33, 37, 39,
+ 41, 39, 35, 26, 29, 32, 35, 39, 37, 35, 23, 28, 31, 34, 38, 38, 34, 34, 39,
+ 43, 47, 46, 41, 38, 34, 38, 41, 45, 44, 40, 36, 31, 35, 40, 43, 42, 39, 33,
+ 29, 34, 36, 40, 39, 36, 30, 24, 30, 33, 36, 35, 33, 30, 21, 27, 30, 32, 32,
+ 32, 29, 16, 26, 29, 31, 31, 31, 30, 31, 35, 38, 39, 39, 37, 31, 29, 35, 37,
+ 38, 38, 35, 30, 26, 32, 35, 35, 36, 33, 27, 22, 31, 32, 31, 32, 30, 24, 16,
+ 26, 27, 28, 27, 25, 24, 11, 24, 25, 26, 25, 24, 24, 11, 20, 24, 24, 24, 24,
+ 22, 25, 30, 30, 31, 32, 30, 24, 21, 29, 28, 30, 31, 28, 22, 18, 26, 28, 28,
+ 27, 24, 21, 15, 21, 24, 25, 24, 20, 20, 12, 15, 21, 20, 20, 16, 16, 11, 12,
+ 18, 18, 17, 15, 12, 11, 8, 17, 17, 16, 15, 9, 13, 21, 23, 24, 23, 19, 24,
+ 10, 19, 22, 22, 21, 16, 23, 10, 14, 19, 19, 18, 13, 17, 10, 9, 16, 16, 15,
+ 8, 12, 8, 4, 12, 12, 10, 5, 9, 7, 4, 9, 8, 8, 4, 6, 7, 4, 6,
+ 7, 7, 4, 5, 6, 12, 14, 15, 15, 17, 20, 6, 8, 12, 13, 14, 14, 16, 6,
+ 5, 11, 10, 11, 11, 12, 6, 4, 8, 7, 7, 7, 6, 4, 2, 2, 2, 3, 4,
+ 5, 2, 2, 3, 3, 2, 3, 5, 3, 2, 4, 3, 0, 3, 4, 5, 5, 10, 12,
+ 14, 16, 17, 5, 6, 9, 11, 12, 13, 13, 5, 6, 8, 9, 9, 10, 10, 5, 5,
+ 4, 5, 6, 6, 5, 3, 2, 3, 3, 1, 3, 4, 2, 3, 5, 4, 2, 2, 4,
+ 2, 4, 5, 4, 3, 2, 3, 31, 36, 40, 43, 43, 40, 36, 30, 33, 38, 43, 43,
+ 39, 35, 28, 32, 37, 39, 42, 37, 33, 28, 30, 33, 36, 40, 35, 29, 25, 27, 29,
+ 33, 35, 32, 29, 21, 24, 26, 30, 34, 32, 29, 20, 21, 26, 29, 32, 32, 29, 29,
+ 32, 36, 40, 38, 35, 33, 29, 31, 35, 38, 37, 35, 30, 26, 30, 33, 36, 36, 32,
+ 29, 25, 28, 30, 33, 33, 31, 24, 20, 25, 27, 29, 29, 27, 24, 18, 22, 25, 27,
+ 27, 27, 24, 14, 21, 24, 26, 26, 26, 24, 26, 30, 32, 33, 34, 32, 27, 25, 29,
+ 30, 31, 31, 30, 25, 23, 27, 29, 29, 29, 28, 22, 18, 26, 25, 26, 26, 24, 19,
+ 13, 22, 22, 22, 22, 21, 19, 9, 20, 20, 20, 20, 20, 19, 9, 17, 20, 18, 19,
+ 19, 17, 23, 24, 24, 26, 27, 25, 19, 18, 23, 23, 25, 24, 22, 17, 14, 22, 22,
+ 21, 22, 19, 16, 11, 18, 19, 18, 18, 16, 14, 9, 13, 15, 14, 14, 12, 11, 9,
+ 10, 13, 12, 12, 12, 8, 9, 7, 12, 11, 11, 11, 5, 10, 17, 17, 18, 18, 15,
+ 19, 7, 15, 16, 16, 16, 12, 17, 6, 12, 13, 13, 13, 9, 12, 7, 6, 10, 9,
+ 9, 6, 7, 5, 2, 6, 5, 5, 3, 5, 4, 2, 4, 3, 3, 3, 4, 4, 3,
+ 2, 2, 2, 2, 3, 4, 9, 9, 10, 11, 13, 16, 4, 7, 9, 9, 10, 11, 12,
+ 4, 4, 8, 7, 8, 8, 8, 4, 3, 4, 4, 4, 5, 4, 2, 1, 4, 3, 1,
+ 2, 3, 2, 3, 6, 4, 2, 2, 3, 2, 5, 6, 5, 3, 0, 3, 4, 5, 6,
+ 8, 10, 12, 13, 4, 5, 6, 8, 9, 10, 10, 4, 5, 6, 5, 6, 6, 7, 4,
+ 4, 6, 3, 2, 3, 3, 2, 4, 8, 5, 3, 0, 3, 3, 6, 7, 7, 4, 2,
+ 2, 5, 7, 7, 8, 5, 2, 2, 25, 28, 34, 37, 37, 34, 31, 24, 28, 32, 36,
+ 37, 32, 29, 23, 26, 30, 34, 36, 32, 27, 22, 24, 28, 31, 34, 30, 24, 19, 21,
+ 24, 27, 31, 28, 24, 17, 18, 21, 24, 29, 28, 24, 15, 17, 21, 24, 27, 27, 24,
+ 23, 27, 31, 34, 33, 30, 27, 22, 25, 29, 33, 33, 28, 25, 20, 24, 28, 31, 30,
+ 27, 24, 20, 22, 24, 28, 27, 26, 20, 17, 18, 21, 23, 24, 23, 20, 15, 17, 19,
+ 21, 22, 22, 20, 12, 16, 19, 21, 21, 21, 20, 21, 24, 26, 27, 28, 26, 22, 20,
+ 23, 24, 25, 26, 25, 20, 19, 21, 23, 23, 23, 22, 18, 16, 20, 20, 20, 19, 19,
+ 14, 10, 16, 16, 16, 16, 16, 14, 7, 16, 15, 15, 15, 15, 15, 7, 14, 14, 14,
+ 14, 14, 13, 18, 19, 19, 20, 21, 21, 14, 15, 17, 18, 19, 19, 18, 12, 12, 16,
+ 16, 16, 15, 15, 11, 8, 14, 13, 12, 12, 12, 10, 6, 10, 9, 9, 8, 8, 7,
+ 6, 8, 8, 7, 7, 7, 4, 7, 4, 6, 6, 6, 6, 3, 8, 11, 11, 12, 13,
+ 12, 14, 3, 9, 10, 10, 11, 9, 12, 4, 8, 8, 7, 7, 7, 8, 3, 4, 4,
+ 4, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 2, 3, 4, 3, 2, 0, 2, 3,
+ 5, 5, 3, 2, 2, 2, 3, 5, 6, 7, 8, 10, 12, 3, 4, 6, 7, 7, 8,
+ 9, 3, 4, 4, 4, 5, 5, 6, 3, 3, 4, 3, 2, 2, 2, 1, 5, 6, 4,
+ 3, 2, 2, 3, 6, 8, 6, 3, 2, 1, 5, 7, 8, 7, 4, 3, 0, 5, 7,
+ 6, 6, 8, 9, 10, 5, 7, 6, 6, 6, 7, 7, 5, 7, 8, 4, 3, 4, 4,
+ 5, 7, 8, 5, 3, 2, 1, 5, 8, 9, 7, 4, 3, 0, 6, 8, 10, 9, 5,
+ 3, 2, 7, 9, 10, 10, 6, 4, 2, 31, 36, 39, 41, 40, 39, 36, 31, 35, 38,
+ 41, 41, 38, 36, 30, 33, 36, 40, 42, 39, 36, 29, 32, 36, 39, 41, 39, 38, 28,
+ 31, 35, 38, 42, 40, 37, 28, 30, 34, 38, 41, 42, 36, 25, 29, 33, 37, 40, 40,
+ 37, 29, 32, 36, 40, 38, 35, 32, 28, 32, 35, 38, 38, 34, 32, 27, 30, 34, 38,
+ 38, 34, 32, 25, 29, 34, 36, 37, 36, 32, 25, 29, 32, 36, 36, 36, 33, 24, 28,
+ 32, 35, 36, 35, 32, 21, 26, 30, 34, 34, 35, 32, 27, 31, 31, 32, 33, 30, 25,
+ 27, 29, 30, 30, 32, 29, 25, 24, 28, 29, 30, 30, 30, 25, 21, 27, 28, 29, 30,
+ 30, 25, 19, 27, 28, 28, 29, 30, 26, 15, 24, 26, 28, 28, 28, 26, 16, 22, 25,
+ 26, 26, 26, 25, 24, 25, 25, 25, 25, 22, 13, 22, 23, 23, 25, 24, 21, 14, 17,
+ 22, 22, 23, 22, 20, 15, 14, 22, 21, 22, 23, 20, 17, 15, 19, 20, 21, 21, 19,
+ 14, 15, 17, 19, 19, 19, 18, 10, 16, 14, 17, 17, 18, 17, 8, 12, 16, 16, 17,
+ 15, 10, 12, 9, 15, 15, 15, 14, 10, 13, 9, 13, 13, 13, 13, 8, 12, 9, 9,
+ 13, 13, 12, 9, 10, 10, 7, 11, 11, 11, 6, 8, 10, 9, 8, 9, 9, 5, 8,
+ 12, 11, 9, 7, 7, 4, 7, 4, 6, 6, 6, 7, 9, 10, 4, 4, 4, 5, 5,
+ 9, 10, 4, 2, 2, 3, 5, 7, 9, 4, 2, 1, 1, 3, 6, 8, 5, 4, 4,
+ 4, 4, 4, 7, 7, 6, 8, 6, 4, 4, 6, 9, 9, 9, 7, 5, 4, 5, 0,
+ 4, 3, 1, 4, 7, 9, 0, 4, 4, 2, 4, 6, 8, 0, 4, 5, 2, 2, 5,
+ 8, 0, 3, 4, 2, 2, 4, 6, 2, 4, 5, 5, 5, 4, 5, 5, 6, 8, 8,
+ 5, 5, 4, 8, 8, 9, 9, 6, 5, 4, 37, 41, 45, 47, 47, 45, 41, 37, 40,
+ 44, 46, 48, 46, 42, 36, 39, 42, 47, 47, 46, 43, 34, 37, 41, 45, 48, 46, 42,
+ 34, 37, 41, 45, 49, 46, 42, 33, 36, 41, 44, 47, 45, 42, 30, 35, 38, 43, 47,
+ 46, 42, 36, 39, 42, 45, 43, 41, 36, 34, 37, 41, 44, 44, 39, 36, 33, 36, 40,
+ 42, 43, 40, 36, 32, 36, 38, 43, 42, 42, 37, 30, 35, 38, 42, 41, 40, 37, 28,
+ 34, 38, 41, 41, 40, 37, 25, 31, 36, 40, 40, 40, 37, 33, 36, 38, 38, 37, 34,
+ 28, 31, 34, 36, 38, 36, 34, 28, 28, 34, 36, 36, 35, 34, 29, 25, 34, 34, 36,
+ 36, 34, 30, 22, 32, 34, 35, 35, 34, 30, 19, 30, 33, 33, 33, 32, 30, 18, 28,
+ 31, 31, 32, 32, 28, 28, 31, 31, 30, 29, 25, 16, 25, 29, 29, 30, 28, 24, 18,
+ 21, 28, 28, 28, 27, 23, 20, 17, 26, 29, 27, 27, 23, 23, 18, 23, 27, 27, 26,
+ 22, 19, 19, 20, 25, 25, 25, 21, 15, 18, 17, 23, 24, 24, 21, 11, 16, 22, 22,
+ 22, 16, 12, 16, 12, 19, 21, 21, 16, 11, 18, 13, 16, 19, 19, 16, 9, 15, 13,
+ 12, 19, 19, 15, 11, 14, 14, 10, 17, 17, 13, 8, 11, 13, 11, 14, 14, 12, 7,
+ 10, 14, 13, 12, 13, 11, 6, 8, 7, 11, 12, 9, 9, 11, 13, 7, 8, 10, 8,
+ 7, 9, 12, 7, 5, 8, 7, 6, 8, 10, 7, 4, 7, 6, 6, 8, 10, 8, 6,
+ 5, 5, 4, 6, 9, 9, 8, 8, 7, 5, 6, 8, 11, 9, 8, 7, 6, 5, 7,
+ 4, 0, 2, 4, 6, 9, 11, 4, 0, 2, 3, 5, 7, 10, 4, 0, 3, 2, 6,
+ 9, 10, 4, 1, 3, 2, 4, 5, 8, 4, 3, 5, 5, 5, 5, 7, 6, 6, 8,
+ 8, 6, 5, 6, 7, 9, 8, 8, 7, 5, 6, 36, 40, 42, 44, 44, 42, 39, 35,
+ 38, 42, 44, 45, 42, 38, 34, 37, 41, 44, 45, 42, 40, 33, 37, 41, 45, 46, 43,
+ 40, 33, 36, 40, 43, 47, 44, 39, 32, 35, 39, 43, 46, 44, 40, 30, 34, 38, 41,
+ 45, 44, 39, 34, 37, 40, 43, 41, 36, 34, 32, 35, 39, 42, 42, 37, 33, 31, 35,
+ 38, 40, 41, 37, 34, 31, 35, 37, 41, 41, 39, 34, 29, 35, 36, 40, 39, 39, 35,
+ 27, 33, 36, 39, 39, 38, 35, 24, 31, 35, 38, 37, 37, 35, 31, 34, 36, 35, 35,
+ 31, 24, 29, 33, 34, 35, 34, 31, 24, 27, 33, 34, 34, 32, 31, 26, 23, 33, 33,
+ 34, 33, 31, 27, 21, 31, 33, 33, 33, 29, 27, 18, 29, 32, 31, 31, 30, 28, 18,
+ 27, 30, 30, 31, 29, 25, 27, 29, 28, 28, 27, 22, 14, 22, 28, 27, 27, 25, 20,
+ 16, 19, 26, 27, 27, 25, 20, 18, 15, 24, 27, 26, 25, 20, 21, 17, 22, 25, 25,
+ 24, 19, 17, 17, 19, 24, 25, 23, 18, 14, 18, 16, 22, 22, 22, 18, 10, 14, 20,
+ 20, 20, 14, 8, 14, 11, 18, 19, 19, 14, 8, 15, 11, 14, 19, 18, 13, 7, 14,
+ 11, 11, 18, 18, 13, 7, 13, 13, 9, 16, 16, 12, 6, 9, 12, 11, 14, 14, 11,
+ 6, 7, 14, 13, 12, 12, 10, 6, 7, 6, 10, 11, 7, 5, 7, 10, 6, 7, 9,
+ 7, 5, 7, 9, 6, 3, 8, 6, 5, 7, 8, 6, 4, 7, 6, 4, 6, 7, 7,
+ 6, 5, 5, 6, 6, 7, 9, 8, 11, 11, 8, 6, 7, 11, 10, 12, 12, 10, 6,
+ 6, 3, 2, 0, 3, 4, 6, 8, 3, 2, 2, 2, 4, 5, 7, 3, 2, 3, 2,
+ 4, 6, 7, 3, 2, 6, 4, 4, 5, 6, 4, 5, 10, 11, 9, 6, 6, 6, 10,
+ 15, 16, 11, 7, 6, 9, 14, 17, 17, 13, 8, 6, 28, 32, 36, 38, 38, 36, 32,
+ 29, 31, 35, 38, 39, 36, 32, 27, 31, 34, 37, 39, 36, 33, 26, 30, 34, 37, 40,
+ 37, 34, 27, 30, 33, 36, 40, 38, 34, 26, 29, 32, 36, 39, 38, 34, 24, 27, 30,
+ 35, 37, 38, 34, 28, 30, 34, 36, 34, 30, 27, 26, 28, 32, 35, 34, 31, 27, 25,
+ 28, 32, 34, 34, 32, 28, 24, 28, 31, 35, 34, 32, 28, 24, 28, 31, 34, 34, 34,
+ 29, 23, 27, 29, 33, 33, 33, 29, 21, 25, 28, 31, 32, 31, 29, 26, 29, 29, 28,
+ 29, 25, 19, 25, 26, 28, 28, 28, 25, 19, 23, 26, 27, 28, 28, 25, 20, 19, 26,
+ 27, 28, 27, 26, 22, 17, 25, 27, 26, 26, 25, 22, 15, 25, 25, 25, 26, 24, 22,
+ 16, 22, 24, 24, 24, 24, 20, 22, 23, 22, 21, 21, 17, 8, 18, 22, 21, 21, 20,
+ 16, 10, 15, 21, 21, 21, 20, 16, 13, 12, 20, 21, 20, 19, 16, 16, 14, 18, 21,
+ 20, 19, 15, 12, 15, 16, 18, 18, 17, 15, 9, 16, 15, 16, 17, 16, 14, 6, 11,
+ 15, 15, 14, 10, 5, 8, 7, 14, 13, 13, 10, 5, 10, 8, 11, 12, 12, 10, 5,
+ 9, 7, 9, 12, 12, 10, 5, 8, 9, 7, 11, 11, 9, 6, 6, 10, 9, 10, 9,
+ 8, 6, 5, 13, 13, 13, 10, 8, 7, 5, 3, 6, 5, 4, 3, 4, 6, 3, 4,
+ 4, 3, 3, 4, 5, 3, 2, 2, 2, 3, 4, 6, 3, 2, 4, 4, 4, 5, 5,
+ 5, 6, 9, 9, 8, 7, 5, 7, 10, 16, 14, 10, 7, 6, 11, 15, 17, 15, 12,
+ 8, 6, 2, 4, 3, 0, 2, 3, 4, 2, 4, 4, 2, 2, 3, 5, 2, 4, 5,
+ 3, 2, 3, 4, 2, 6, 9, 7, 6, 5, 5, 5, 9, 13, 12, 11, 8, 6, 9,
+ 14, 18, 18, 13, 10, 7, 14, 18, 19, 19, 15, 11, 7, 24, 27, 30, 33, 32, 30,
+ 27, 24, 26, 29, 32, 33, 31, 27, 22, 25, 28, 31, 33, 31, 29, 21, 25, 28, 32,
+ 34, 32, 29, 22, 25, 27, 31, 35, 33, 29, 21, 24, 27, 30, 34, 33, 29, 19, 22,
+ 25, 29, 33, 33, 28, 21, 24, 28, 30, 29, 25, 23, 21, 24, 27, 30, 29, 25, 22,
+ 20, 22, 26, 29, 28, 27, 23, 20, 22, 26, 29, 28, 27, 24, 21, 23, 26, 29, 29,
+ 27, 23, 19, 21, 24, 28, 27, 27, 24, 16, 20, 23, 26, 26, 26, 24, 20, 22, 23,
+ 23, 23, 20, 15, 19, 22, 22, 23, 22, 20, 16, 19, 20, 22, 23, 22, 21, 17, 16,
+ 22, 23, 23, 23, 21, 18, 15, 21, 21, 21, 22, 21, 18, 13, 19, 20, 20, 21, 20,
+ 17, 17, 18, 19, 19, 19, 20, 17, 19, 17, 16, 17, 16, 13, 5, 16, 16, 16, 16,
+ 15, 12, 6, 13, 16, 15, 16, 15, 13, 9, 10, 16, 16, 15, 15, 13, 12, 11, 14,
+ 14, 14, 14, 12, 8, 12, 13, 13, 13, 13, 12, 6, 17, 18, 13, 12, 12, 11, 5,
+ 8, 10, 9, 9, 8, 3, 5, 5, 9, 8, 8, 7, 3, 6, 5, 8, 7, 7, 7,
+ 3, 5, 5, 6, 7, 7, 7, 5, 5, 6, 7, 7, 7, 8, 7, 5, 9, 12, 14,
+ 12, 9, 8, 6, 15, 18, 17, 13, 10, 8, 6, 2, 1, 1, 1, 2, 3, 3, 2,
+ 3, 2, 1, 1, 3, 4, 2, 3, 3, 3, 2, 3, 4, 2, 5, 7, 7, 6, 5,
+ 6, 5, 10, 13, 12, 11, 8, 6, 10, 15, 19, 17, 12, 9, 7, 16, 19, 21, 18,
+ 14, 10, 8, 4, 6, 4, 2, 0, 2, 3, 4, 6, 5, 3, 1, 2, 3, 4, 6,
+ 7, 5, 5, 3, 4, 4, 8, 12, 9, 8, 7, 7, 8, 12, 16, 15, 13, 10, 8,
+ 13, 16, 20, 21, 15, 11, 9, 18, 22, 21, 21, 17, 13, 9, 19, 23, 25, 29, 28,
+ 25, 22, 19, 21, 24, 27, 28, 26, 23, 17, 20, 24, 27, 30, 27, 24, 17, 20, 24,
+ 28, 30, 29, 25, 18, 20, 23, 27, 31, 28, 25, 16, 19, 22, 26, 29, 29, 25, 16,
+ 18, 22, 25, 28, 29, 26, 18, 20, 23, 25, 23, 21, 18, 16, 19, 23, 25, 25, 21,
+ 19, 15, 18, 21, 25, 24, 22, 20, 15, 19, 22, 25, 24, 23, 20, 16, 18, 21, 24,
+ 24, 23, 21, 14, 17, 20, 23, 23, 23, 20, 18, 18, 19, 22, 22, 22, 21, 15, 18,
+ 19, 19, 19, 16, 13, 15, 17, 18, 18, 18, 17, 13, 15, 16, 18, 18, 18, 18, 14,
+ 14, 17, 18, 18, 18, 18, 15, 12, 15, 17, 17, 17, 17, 15, 13, 15, 16, 17, 16,
+ 16, 15, 20, 19, 16, 15, 15, 15, 13, 15, 13, 12, 12, 12, 11, 2, 14, 12, 12,
+ 11, 11, 10, 3, 11, 11, 11, 12, 11, 11, 6, 7, 11, 11, 11, 11, 10, 9, 9,
+ 10, 10, 10, 10, 10, 6, 13, 14, 13, 11, 9, 9, 6, 19, 21, 16, 13, 10, 8,
+ 7, 6, 5, 5, 5, 4, 2, 3, 2, 4, 4, 3, 3, 2, 3, 2, 2, 2, 3,
+ 3, 3, 4, 2, 3, 5, 5, 6, 6, 6, 5, 10, 10, 10, 10, 8, 7, 11, 16,
+ 17, 15, 11, 9, 7, 18, 21, 20, 15, 13, 10, 8, 3, 4, 2, 2, 1, 2, 2,
+ 3, 4, 3, 2, 2, 2, 3, 3, 6, 4, 5, 5, 4, 4, 3, 8, 9, 9, 8,
+ 7, 7, 8, 12, 15, 14, 13, 10, 8, 13, 17, 20, 19, 14, 11, 8, 19, 22, 23,
+ 21, 16, 12, 9, 7, 9, 6, 3, 2, 0, 2, 7, 9, 7, 4, 3, 2, 2, 7,
+ 9, 9, 7, 6, 5, 5, 7, 11, 14, 12, 10, 9, 8, 10, 15, 18, 17, 15, 13,
+ 9, 15, 19, 22, 23, 17, 13, 10, 20, 23, 23, 24, 19, 15, 11, 15, 18, 21, 24,
+ 25, 22, 18, 14, 17, 20, 24, 25, 22, 20, 13, 16, 20, 23, 26, 24, 21, 12, 16,
+ 20, 23, 26, 24, 21, 13, 16, 19, 23, 27, 24, 21, 12, 16, 18, 22, 25, 24, 21,
+ 17, 16, 17, 21, 24, 24, 21, 13, 16, 19, 21, 21, 18, 15, 12, 15, 18, 20, 20,
+ 18, 15, 11, 14, 17, 20, 21, 19, 16, 10, 14, 18, 21, 21, 19, 17, 11, 14, 17,
+ 20, 20, 20, 17, 12, 13, 16, 19, 19, 19, 16, 19, 17, 16, 18, 18, 18, 17, 11,
+ 14, 15, 15, 16, 14, 10, 10, 13, 14, 14, 14, 13, 10, 9, 11, 13, 14, 14, 14,
+ 11, 9, 12, 14, 14, 14, 14, 12, 8, 11, 13, 13, 13, 13, 12, 14, 14, 14, 13,
+ 12, 12, 12, 22, 20, 16, 13, 11, 11, 11, 9, 9, 8, 8, 9, 9, 2, 8, 7,
+ 7, 7, 7, 7, 2, 6, 6, 7, 7, 7, 7, 3, 6, 6, 7, 7, 7, 7, 6,
+ 7, 8, 9, 9, 9, 7, 6, 14, 16, 14, 12, 10, 8, 6, 21, 22, 17, 13, 10,
+ 8, 7, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
+ 4, 4, 3, 3, 2, 6, 6, 7, 7, 7, 6, 6, 11, 12, 11, 11, 9, 7, 13,
+ 17, 18, 15, 12, 10, 7, 20, 22, 21, 16, 13, 11, 8, 6, 5, 4, 3, 2, 1,
+ 2, 5, 7, 4, 3, 3, 2, 2, 6, 9, 6, 6, 5, 4, 4, 6, 10, 10, 10,
+ 9, 8, 7, 9, 14, 16, 15, 13, 11, 8, 15, 19, 22, 19, 15, 12, 9, 20, 23,
+ 24, 22, 16, 13, 10, 9, 11, 7, 4, 3, 2, 0, 10, 11, 9, 5, 3, 3, 3,
+ 9, 11, 12, 8, 7, 6, 5, 9, 13, 16, 13, 11, 10, 9, 12, 16, 20, 19, 16,
+ 13, 10, 17, 20, 24, 24, 18, 14, 11, 21, 25, 25, 25, 20, 16, 12, 32, 36, 39,
+ 42, 41, 39, 35, 31, 35, 37, 41, 41, 40, 35, 29, 33, 36, 39, 41, 39, 36, 29,
+ 32, 35, 39, 42, 40, 37, 29, 31, 35, 39, 42, 41, 37, 27, 31, 34, 38, 42, 40,
+ 36, 24, 28, 33, 37, 40, 41, 37, 30, 33, 36, 38, 38, 35, 31, 28, 31, 36, 38,
+ 37, 34, 32, 27, 31, 34, 37, 37, 35, 31, 25, 30, 34, 37, 36, 35, 33, 26, 29,
+ 33, 36, 36, 36, 33, 24, 29, 31, 35, 36, 35, 32, 21, 26, 30, 34, 34, 34, 32,
+ 27, 30, 31, 32, 33, 30, 24, 25, 29, 31, 32, 31, 30, 25, 25, 27, 28, 30, 31,
+ 30, 25, 21, 27, 28, 30, 29, 30, 26, 19, 26, 29, 30, 29, 28, 25, 15, 25, 26,
+ 28, 27, 27, 25, 16, 23, 24, 25, 27, 27, 25, 24, 24, 25, 25, 25, 22, 13, 21,
+ 23, 24, 24, 24, 21, 14, 17, 22, 22, 23, 22, 20, 15, 14, 22, 21, 22, 22, 20,
+ 18, 14, 19, 20, 21, 21, 19, 14, 15, 17, 19, 19, 19, 18, 11, 16, 14, 16, 18,
+ 18, 17, 8, 12, 16, 17, 17, 15, 10, 12, 9, 15, 15, 15, 14, 10, 13, 9, 13,
+ 13, 13, 12, 8, 11, 9, 10, 13, 13, 12, 9, 10, 10, 7, 11, 11, 10, 6, 8,
+ 10, 9, 8, 9, 9, 5, 8, 12, 11, 9, 7, 7, 4, 7, 4, 6, 6, 6, 7,
+ 9, 11, 4, 4, 4, 5, 6, 9, 10, 4, 2, 2, 3, 4, 7, 9, 4, 2, 1,
+ 1, 4, 6, 8, 5, 4, 4, 4, 4, 4, 7, 7, 6, 8, 6, 5, 4, 6, 8,
+ 9, 9, 7, 5, 4, 5, 0, 4, 3, 2, 4, 7, 9, 0, 4, 4, 2, 3, 7,
+ 9, 0, 4, 5, 2, 2, 6, 8, 0, 4, 5, 2, 2, 4, 6, 2, 4, 5, 5,
+ 5, 4, 5, 4, 6, 8, 8, 5, 5, 4, 8, 8, 9, 9, 6, 5, 4, 37, 41,
+ 45, 47, 47, 44, 41, 36, 40, 43, 47, 47, 44, 41, 35, 38, 43, 46, 47, 44, 41,
+ 33, 37, 41, 45, 48, 45, 42, 35, 37, 41, 45, 48, 46, 43, 33, 37, 40, 43, 47,
+ 46, 43, 31, 35, 39, 43, 46, 46, 43, 34, 39, 43, 44, 44, 39, 36, 34, 37, 42,
+ 44, 43, 41, 36, 33, 36, 40, 43, 43, 39, 36, 31, 36, 40, 43, 43, 41, 37, 30,
+ 36, 39, 42, 42, 42, 38, 28, 34, 37, 40, 40, 41, 37, 25, 32, 37, 40, 39, 39,
+ 37, 32, 36, 38, 37, 37, 34, 28, 31, 35, 37, 37, 38, 34, 28, 28, 33, 34, 36,
+ 36, 34, 28, 25, 34, 35, 34, 35, 34, 30, 23, 33, 35, 34, 35, 34, 29, 18, 30,
+ 32, 34, 34, 32, 30, 18, 28, 32, 31, 33, 32, 28, 29, 30, 31, 31, 30, 25, 16,
+ 25, 30, 30, 30, 28, 23, 18, 21, 28, 28, 29, 27, 22, 20, 17, 26, 28, 28, 27,
+ 23, 22, 18, 23, 27, 27, 27, 22, 18, 19, 20, 25, 25, 25, 21, 15, 18, 17, 22,
+ 23, 24, 21, 11, 16, 22, 23, 22, 17, 12, 16, 12, 20, 21, 21, 16, 11, 18, 13,
+ 16, 20, 19, 15, 9, 15, 12, 12, 19, 19, 15, 11, 14, 14, 10, 17, 17, 13, 8,
+ 11, 13, 11, 15, 15, 13, 7, 10, 14, 13, 12, 13, 11, 6, 9, 7, 11, 12, 9,
+ 9, 11, 13, 7, 8, 10, 8, 7, 10, 12, 7, 5, 8, 7, 6, 8, 10, 7, 4,
+ 7, 6, 6, 8, 10, 8, 6, 5, 5, 5, 6, 9, 9, 8, 7, 7, 5, 6, 8,
+ 11, 9, 8, 7, 6, 5, 7, 4, 0, 2, 4, 6, 9, 11, 4, 0, 2, 3, 5,
+ 7, 10, 4, 0, 3, 2, 5, 9, 10, 4, 1, 3, 2, 4, 5, 8, 4, 3, 5,
+ 5, 5, 5, 7, 6, 6, 8, 8, 6, 5, 6, 7, 8, 8, 8, 6, 5, 6, 39,
+ 41, 46, 47, 47, 45, 41, 37, 41, 43, 47, 46, 43, 40, 37, 39, 42, 46, 48, 45,
+ 42, 35, 38, 43, 46, 48, 46, 43, 36, 39, 43, 45, 49, 46, 44, 35, 38, 42, 45,
+ 48, 47, 43, 32, 35, 40, 43, 47, 47, 43, 36, 39, 43, 45, 43, 39, 36, 35, 38,
+ 42, 43, 44, 40, 35, 34, 37, 42, 44, 42, 40, 36, 32, 37, 41, 43, 42, 40, 37,
+ 31, 37, 40, 43, 43, 41, 37, 29, 35, 39, 42, 42, 40, 37, 25, 33, 38, 41, 40,
+ 39, 37, 35, 37, 37, 38, 38, 33, 26, 31, 36, 37, 37, 36, 33, 27, 29, 35, 37,
+ 37, 36, 33, 28, 25, 34, 36, 37, 36, 33, 30, 23, 34, 36, 36, 35, 32, 30, 19,
+ 31, 34, 35, 35, 32, 30, 19, 29, 33, 33, 33, 31, 27, 29, 31, 31, 31, 30, 24,
+ 16, 25, 30, 31, 30, 27, 22, 18, 21, 28, 29, 30, 27, 22, 20, 18, 26, 29, 28,
+ 28, 22, 23, 19, 23, 28, 28, 26, 21, 19, 19, 20, 26, 26, 26, 20, 16, 19, 17,
+ 24, 24, 24, 20, 12, 16, 23, 23, 22, 16, 10, 16, 13, 20, 22, 21, 15, 10, 17,
+ 13, 16, 21, 20, 15, 9, 16, 12, 13, 21, 19, 15, 9, 15, 14, 11, 19, 19, 13,
+ 7, 11, 15, 12, 16, 16, 12, 7, 9, 15, 14, 14, 15, 11, 7, 8, 7, 12, 13,
+ 8, 7, 9, 12, 8, 7, 12, 8, 6, 8, 10, 7, 4, 10, 7, 6, 8, 10, 7,
+ 5, 9, 7, 5, 7, 9, 9, 7, 7, 5, 6, 7, 8, 10, 9, 9, 10, 7, 6,
+ 7, 12, 10, 11, 11, 9, 6, 7, 4, 2, 2, 3, 5, 7, 9, 4, 2, 0, 3,
+ 5, 6, 9, 4, 2, 2, 2, 5, 8, 9, 4, 2, 5, 3, 4, 6, 8, 5, 4,
+ 9, 10, 8, 6, 7, 7, 8, 14, 14, 11, 7, 6, 8, 12, 15, 15, 12, 8, 6,
+ 31, 35, 38, 40, 41, 38, 34, 30, 34, 37, 40, 41, 37, 34, 29, 31, 36, 39, 41,
+ 39, 35, 28, 32, 36, 38, 42, 39, 36, 29, 32, 35, 39, 42, 40, 36, 28, 32, 34,
+ 38, 40, 40, 36, 25, 29, 33, 36, 40, 40, 35, 30, 33, 36, 38, 36, 32, 28, 28,
+ 31, 34, 37, 36, 32, 29, 27, 30, 34, 36, 36, 34, 30, 26, 30, 34, 36, 36, 34,
+ 30, 26, 30, 33, 36, 36, 35, 31, 24, 29, 32, 34, 35, 34, 30, 22, 26, 31, 34,
+ 34, 33, 30, 28, 30, 31, 31, 31, 27, 20, 26, 29, 29, 30, 29, 27, 21, 24, 29,
+ 29, 30, 30, 27, 22, 20, 28, 30, 30, 29, 27, 24, 18, 28, 29, 29, 29, 27, 23,
+ 16, 26, 28, 28, 28, 26, 23, 17, 24, 25, 26, 27, 26, 21, 24, 25, 24, 24, 22,
+ 18, 10, 20, 23, 23, 24, 22, 17, 12, 16, 22, 22, 22, 21, 16, 15, 13, 21, 22,
+ 22, 21, 17, 18, 14, 19, 22, 21, 20, 16, 14, 15, 17, 19, 20, 19, 16, 10, 17,
+ 15, 18, 18, 18, 16, 7, 12, 17, 16, 16, 11, 5, 11, 8, 15, 15, 15, 11, 6,
+ 12, 9, 12, 14, 14, 11, 5, 10, 9, 9, 14, 13, 11, 5, 10, 10, 8, 13, 12,
+ 10, 6, 7, 11, 10, 10, 10, 9, 5, 6, 13, 12, 11, 9, 8, 6, 5, 4, 7,
+ 7, 5, 4, 5, 7, 4, 5, 5, 5, 3, 5, 6, 4, 3, 4, 4, 4, 5, 6,
+ 4, 3, 3, 3, 4, 5, 5, 6, 5, 8, 8, 8, 6, 5, 8, 9, 14, 13, 10,
+ 7, 5, 10, 13, 16, 14, 11, 8, 5, 2, 3, 2, 2, 3, 4, 5, 2, 3, 3,
+ 0, 2, 4, 5, 2, 3, 4, 2, 2, 4, 5, 2, 5, 8, 6, 5, 5, 5, 4,
+ 8, 12, 12, 10, 8, 6, 7, 12, 17, 18, 13, 9, 6, 12, 17, 19, 18, 14, 10,
+ 7, 25, 29, 32, 35, 35, 31, 29, 24, 28, 30, 35, 35, 32, 28, 23, 26, 30, 33,
+ 35, 32, 30, 23, 26, 30, 33, 36, 33, 30, 24, 25, 29, 32, 36, 34, 30, 22, 25,
+ 28, 32, 35, 33, 30, 20, 23, 27, 31, 33, 34, 30, 24, 26, 30, 33, 30, 28, 23,
+ 23, 25, 28, 32, 31, 28, 24, 22, 23, 28, 31, 30, 27, 24, 21, 24, 27, 30, 30,
+ 28, 25, 21, 25, 27, 30, 29, 29, 25, 20, 23, 25, 29, 29, 29, 25, 17, 20, 24,
+ 27, 28, 27, 25, 22, 24, 25, 26, 26, 22, 17, 21, 22, 24, 25, 24, 23, 17, 20,
+ 22, 24, 24, 24, 22, 17, 17, 23, 24, 23, 24, 23, 18, 15, 22, 23, 22, 23, 22,
+ 18, 13, 20, 21, 21, 22, 21, 18, 15, 18, 19, 20, 20, 21, 17, 20, 19, 18, 18,
+ 19, 15, 7, 16, 18, 18, 17, 17, 14, 8, 14, 18, 17, 16, 17, 14, 10, 10, 17,
+ 17, 16, 16, 14, 13, 12, 16, 16, 15, 16, 13, 9, 12, 14, 14, 14, 14, 12, 6,
+ 15, 16, 12, 12, 13, 12, 5, 9, 11, 11, 11, 9, 4, 6, 5, 10, 9, 9, 8,
+ 3, 8, 5, 9, 8, 8, 8, 3, 6, 5, 7, 9, 8, 7, 4, 6, 7, 6, 7,
+ 7, 7, 6, 4, 9, 10, 12, 10, 8, 6, 5, 13, 16, 16, 11, 9, 7, 5, 2,
+ 2, 1, 2, 2, 3, 4, 2, 2, 1, 0, 2, 3, 4, 2, 2, 2, 2, 2, 3,
+ 4, 2, 4, 6, 6, 5, 5, 5, 4, 8, 11, 10, 9, 7, 5, 8, 13, 18, 15,
+ 11, 8, 6, 14, 18, 19, 17, 12, 9, 7, 4, 5, 3, 2, 1, 2, 3, 4, 5,
+ 4, 2, 0, 2, 4, 4, 5, 6, 4, 3, 2, 3, 4, 7, 10, 8, 7, 6, 6,
+ 7, 11, 14, 14, 11, 9, 7, 11, 15, 19, 19, 14, 10, 7, 16, 19, 20, 20, 16,
+ 12, 8, 21, 24, 27, 31, 32, 29, 25, 19, 23, 27, 30, 31, 28, 24, 18, 22, 26,
+ 29, 30, 28, 25, 18, 21, 24, 28, 31, 28, 25, 19, 21, 24, 27, 31, 29, 25, 17,
+ 20, 23, 26, 30, 29, 25, 15, 19, 22, 26, 29, 29, 26, 19, 21, 25, 29, 28, 25,
+ 21, 18, 20, 24, 28, 27, 24, 20, 17, 20, 22, 26, 25, 23, 21, 16, 20, 22, 25,
+ 25, 24, 21, 17, 19, 22, 24, 24, 24, 21, 15, 18, 21, 23, 24, 23, 21, 16, 16,
+ 19, 22, 22, 22, 21, 17, 19, 21, 22, 22, 20, 16, 16, 18, 19, 21, 21, 19, 14,
+ 16, 17, 19, 19, 19, 18, 15, 14, 18, 18, 18, 18, 18, 16, 13, 17, 18, 18, 17,
+ 18, 15, 11, 15, 17, 17, 17, 16, 15, 17, 16, 15, 15, 16, 16, 14, 16, 14, 14,
+ 14, 15, 14, 6, 14, 13, 13, 13, 13, 12, 5, 11, 12, 12, 12, 11, 11, 6, 8,
+ 12, 12, 12, 11, 11, 9, 9, 11, 11, 11, 10, 10, 6, 11, 11, 11, 9, 9, 9,
+ 4, 17, 18, 13, 10, 8, 8, 4, 7, 7, 6, 6, 7, 4, 5, 2, 5, 5, 5,
+ 5, 3, 5, 2, 4, 3, 3, 3, 2, 3, 2, 3, 3, 3, 4, 4, 4, 5, 8,
+ 8, 7, 8, 6, 4, 9, 13, 14, 12, 9, 7, 5, 16, 18, 17, 13, 10, 8, 5,
+ 2, 3, 2, 2, 2, 3, 4, 2, 4, 3, 2, 2, 2, 2, 2, 5, 4, 3, 2,
+ 2, 2, 2, 6, 7, 6, 6, 5, 5, 6, 10, 13, 11, 10, 8, 5, 11, 15, 18,
+ 16, 12, 9, 6, 16, 19, 19, 18, 13, 10, 7, 6, 8, 5, 3, 2, 2, 3, 6,
+ 7, 7, 4, 2, 0, 2, 6, 7, 8, 5, 4, 3, 3, 6, 9, 12, 9, 8, 7,
+ 6, 8, 12, 16, 15, 12, 10, 7, 13, 16, 20, 21, 15, 11, 8, 18, 21, 20, 21,
+ 16, 12, 9, 17, 20, 24, 28, 28, 25, 22, 16, 19, 23, 27, 28, 24, 20, 14, 17,
+ 21, 24, 26, 24, 20, 13, 17, 20, 23, 27, 24, 21, 14, 17, 20, 23, 27, 25, 21,
+ 12, 15, 19, 22, 26, 24, 21, 14, 14, 18, 21, 25, 24, 21, 14, 18, 22, 25, 24,
+ 21, 18, 13, 17, 20, 23, 23, 20, 16, 13, 15, 18, 21, 21, 19, 16, 12, 15, 18,
+ 20, 21, 19, 16, 12, 15, 18, 20, 20, 20, 17, 10, 13, 16, 19, 20, 19, 17, 16,
+ 15, 15, 18, 18, 18, 17, 13, 16, 17, 18, 19, 17, 13, 12, 14, 16, 16, 17, 15,
+ 11, 10, 13, 14, 14, 14, 14, 11, 10, 13, 14, 14, 14, 14, 12, 9, 12, 14, 13,
+ 13, 14, 12, 12, 12, 12, 12, 13, 12, 12, 18, 17, 13, 11, 11, 11, 11, 11, 10,
+ 10, 11, 12, 12, 5, 10, 9, 9, 9, 10, 10, 3, 8, 8, 8, 8, 7, 7, 3,
+ 5, 8, 7, 7, 7, 7, 5, 7, 7, 7, 6, 6, 6, 3, 12, 13, 11, 9, 7,
+ 6, 4, 18, 18, 14, 10, 8, 6, 4, 2, 2, 2, 3, 3, 4, 4, 1, 1, 0,
+ 1, 2, 2, 3, 1, 2, 2, 2, 1, 1, 2, 1, 5, 5, 4, 4, 4, 3, 5,
+ 10, 9, 8, 8, 6, 4, 11, 14, 14, 12, 10, 7, 5, 17, 19, 17, 13, 10, 8,
+ 5, 5, 5, 4, 3, 3, 3, 4, 5, 7, 4, 4, 3, 1, 2, 5, 8, 6, 4,
+ 3, 2, 2, 5, 9, 9, 7, 6, 6, 5, 9, 12, 14, 12, 10, 8, 5, 12, 16,
+ 19, 16, 12, 9, 6, 17, 19, 20, 18, 14, 10, 7, 9, 10, 7, 5, 3, 2, 3,
+ 10, 10, 9, 5, 4, 2, 0, 9, 10, 11, 7, 5, 4, 3, 10, 11, 14, 10, 8,
+ 7, 6, 10, 14, 17, 16, 13, 10, 8, 14, 17, 21, 20, 15, 11, 8, 18, 20, 22,
+ 22, 17, 12, 9, 32, 35, 38, 41, 41, 38, 36, 30, 34, 37, 41, 41, 39, 37, 29,
+ 32, 37, 40, 42, 39, 37, 28, 32, 36, 39, 42, 39, 37, 28, 31, 35, 39, 42, 41,
+ 36, 28, 30, 34, 38, 41, 40, 36, 25, 29, 33, 37, 40, 39, 37, 29, 33, 36, 39,
+ 38, 35, 32, 28, 32, 35, 39, 37, 35, 31, 27, 31, 34, 37, 37, 35, 31, 25, 30,
+ 33, 36, 36, 36, 33, 25, 30, 33, 36, 35, 36, 32, 24, 27, 31, 35, 36, 35, 33,
+ 21, 26, 30, 34, 34, 34, 32, 27, 31, 31, 32, 32, 30, 25, 26, 29, 31, 32, 32,
+ 30, 25, 24, 28, 30, 30, 30, 30, 24, 21, 28, 28, 30, 30, 29, 26, 18, 26, 28,
+ 29, 29, 29, 25, 15, 25, 27, 27, 28, 28, 25, 16, 22, 25, 26, 26, 27, 25, 24,
+ 25, 24, 25, 25, 23, 13, 20, 23, 24, 24, 24, 21, 14, 18, 22, 23, 22, 22, 20,
+ 15, 14, 21, 21, 22, 22, 20, 18, 14, 19, 20, 21, 21, 19, 14, 15, 17, 18, 19,
+ 19, 18, 11, 16, 14, 16, 17, 18, 17, 8, 12, 16, 17, 17, 15, 10, 13, 9, 15,
+ 15, 15, 13, 10, 14, 9, 13, 13, 13, 12, 8, 12, 9, 9, 12, 12, 12, 9, 10,
+ 10, 7, 11, 11, 10, 6, 8, 10, 9, 8, 8, 9, 5, 8, 12, 10, 9, 7, 7,
+ 4, 7, 4, 6, 6, 6, 7, 9, 11, 4, 4, 4, 5, 6, 9, 10, 4, 2, 2,
+ 3, 4, 7, 9, 4, 2, 1, 1, 3, 6, 8, 5, 4, 4, 4, 4, 4, 7, 7,
+ 6, 8, 6, 5, 4, 6, 9, 9, 9, 7, 5, 4, 6, 0, 4, 3, 2, 4, 7,
+ 9, 0, 4, 4, 2, 4, 6, 8, 0, 4, 5, 2, 2, 6, 8, 0, 4, 4, 2,
+ 2, 4, 6, 2, 4, 5, 5, 5, 4, 5, 4, 6, 8, 8, 6, 5, 4, 8, 8,
+ 9, 9, 6, 5, 4, 38, 41, 44, 47, 47, 44, 41, 37, 40, 43, 47, 47, 45, 42,
+ 35, 39, 42, 46, 47, 45, 42, 34, 37, 42, 45, 48, 46, 42, 34, 38, 41, 45, 49,
+ 46, 43, 34, 36, 41, 44, 48, 46, 42, 32, 35, 38, 42, 46, 46, 42, 35, 39, 43,
+ 44, 42, 39, 36, 34, 38, 41, 45, 43, 39, 36, 33, 36, 39, 43, 42, 40, 36, 31,
+ 36, 39, 43, 41, 40, 37, 31, 35, 39, 43, 41, 41, 37, 28, 34, 38, 41, 40, 40,
+ 37, 24, 31, 36, 39, 40, 39, 37, 33, 36, 38, 38, 37, 34, 27, 32, 34, 37, 38,
+ 37, 33, 28, 28, 33, 36, 35, 36, 34, 28, 25, 34, 35, 36, 35, 34, 30, 22, 33,
+ 35, 35, 35, 33, 30, 19, 31, 32, 33, 33, 32, 29, 19, 28, 31, 32, 33, 31, 28,
+ 29, 31, 30, 30, 29, 25, 16, 24, 29, 29, 29, 28, 24, 18, 21, 28, 29, 29, 27,
+ 23, 19, 17, 26, 27, 28, 28, 23, 22, 18, 23, 27, 26, 26, 22, 19, 19, 19, 25,
+ 25, 24, 22, 14, 19, 17, 23, 24, 24, 20, 11, 16, 22, 21, 22, 17, 12, 16, 13,
+ 20, 21, 20, 16, 12, 17, 12, 16, 20, 19, 16, 9, 15, 12, 12, 19, 19, 15, 10,
+ 14, 14, 10, 18, 17, 14, 8, 11, 14, 11, 15, 15, 12, 7, 10, 15, 13, 12, 13,
+ 11, 6, 9, 7, 11, 12, 9, 9, 10, 13, 7, 8, 10, 8, 7, 10, 12, 7, 5,
+ 8, 7, 6, 8, 10, 7, 5, 7, 6, 6, 8, 9, 8, 6, 6, 5, 5, 6, 9,
+ 9, 8, 7, 7, 5, 6, 8, 11, 9, 8, 7, 6, 5, 7, 4, 0, 2, 4, 6,
+ 9, 11, 4, 0, 2, 3, 5, 7, 10, 4, 0, 3, 2, 5, 9, 9, 4, 1, 3,
+ 2, 4, 5, 8, 4, 3, 5, 5, 5, 5, 7, 6, 6, 8, 8, 6, 5, 6, 7,
+ 9, 8, 8, 6, 5, 6, 41, 44, 47, 50, 50, 47, 43, 40, 43, 48, 50, 51, 47,
+ 44, 39, 42, 46, 48, 51, 46, 44, 37, 41, 45, 48, 51, 48, 45, 38, 41, 45, 49,
+ 51, 49, 45, 37, 40, 44, 47, 51, 49, 46, 34, 38, 42, 46, 49, 48, 44, 39, 42,
+ 46, 48, 46, 42, 38, 38, 41, 45, 48, 46, 42, 37, 37, 40, 43, 46, 45, 43, 39,
+ 33, 40, 43, 46, 46, 43, 39, 33, 40, 43, 45, 45, 44, 39, 30, 38, 40, 44, 44,
+ 43, 38, 27, 36, 40, 43, 42, 41, 39, 37, 40, 41, 40, 40, 37, 29, 34, 39, 39,
+ 41, 38, 36, 29, 31, 38, 40, 38, 38, 36, 30, 27, 37, 38, 40, 37, 36, 32, 25,
+ 35, 38, 38, 37, 35, 32, 21, 33, 36, 36, 36, 34, 32, 21, 30, 35, 35, 36, 33,
+ 29, 31, 34, 33, 34, 31, 27, 19, 26, 32, 34, 33, 31, 25, 20, 23, 30, 32, 31,
+ 30, 24, 22, 19, 27, 31, 32, 29, 23, 25, 20, 25, 31, 30, 28, 22, 21, 21, 22,
+ 29, 28, 27, 22, 17, 21, 18, 26, 27, 26, 21, 14, 18, 26, 26, 25, 18, 13, 19,
+ 14, 22, 25, 24, 17, 12, 20, 14, 18, 23, 23, 16, 10, 18, 14, 15, 22, 22, 15,
+ 10, 17, 15, 12, 21, 20, 14, 9, 13, 16, 13, 18, 18, 14, 8, 10, 16, 14, 15,
+ 17, 12, 8, 9, 9, 13, 15, 10, 9, 11, 15, 9, 9, 14, 10, 8, 10, 13, 9,
+ 5, 12, 8, 7, 9, 11, 9, 6, 11, 8, 7, 9, 10, 11, 8, 9, 6, 6, 7,
+ 9, 11, 9, 8, 8, 6, 7, 9, 12, 11, 9, 9, 8, 6, 7, 5, 3, 3, 5,
+ 7, 9, 12, 5, 3, 2, 4, 6, 8, 11, 5, 3, 0, 4, 7, 9, 11, 5, 3,
+ 3, 3, 4, 7, 9, 6, 4, 7, 8, 7, 6, 8, 7, 6, 10, 11, 8, 6, 7,
+ 9, 10, 11, 11, 9, 7, 6, 34, 38, 42, 44, 45, 41, 37, 32, 37, 41, 44, 44,
+ 41, 37, 31, 35, 39, 43, 43, 41, 36, 30, 33, 37, 40, 43, 40, 37, 30, 33, 37,
+ 40, 43, 40, 38, 29, 32, 35, 40, 42, 41, 36, 26, 31, 35, 38, 42, 41, 38, 32,
+ 35, 39, 43, 40, 36, 33, 31, 34, 38, 41, 40, 37, 32, 30, 32, 36, 39, 38, 35,
+ 30, 27, 31, 34, 37, 38, 35, 31, 27, 31, 34, 38, 37, 36, 31, 25, 30, 33, 36,
+ 37, 35, 31, 21, 27, 32, 35, 35, 34, 31, 29, 33, 34, 35, 33, 31, 25, 27, 31,
+ 33, 35, 33, 30, 24, 24, 30, 31, 32, 31, 29, 23, 21, 30, 30, 30, 30, 28, 24,
+ 19, 28, 30, 30, 29, 28, 24, 16, 26, 28, 28, 29, 26, 25, 16, 24, 27, 26, 28,
+ 26, 22, 23, 27, 26, 28, 27, 22, 15, 20, 25, 26, 26, 26, 21, 15, 17, 23, 24,
+ 24, 23, 18, 16, 14, 23, 23, 23, 21, 18, 19, 15, 19, 23, 22, 21, 17, 15, 15,
+ 17, 21, 21, 20, 16, 11, 16, 15, 19, 19, 20, 16, 7, 13, 19, 18, 19, 15, 9,
+ 15, 9, 17, 18, 17, 14, 8, 15, 9, 13, 16, 15, 12, 6, 12, 9, 10, 15, 14,
+ 11, 6, 10, 10, 8, 14, 13, 10, 5, 7, 11, 9, 11, 11, 9, 4, 5, 12, 11,
+ 9, 10, 8, 4, 5, 4, 9, 9, 8, 6, 8, 11, 4, 6, 8, 7, 5, 7, 9,
+ 4, 3, 5, 5, 3, 5, 6, 4, 3, 4, 4, 3, 4, 5, 6, 4, 5, 5, 6,
+ 4, 5, 7, 6, 11, 10, 7, 5, 5, 9, 11, 12, 12, 9, 6, 4, 2, 2, 2,
+ 3, 5, 7, 8, 2, 2, 2, 2, 4, 5, 7, 2, 2, 3, 0, 3, 5, 6, 2,
+ 3, 6, 4, 3, 3, 4, 3, 5, 9, 10, 8, 5, 4, 5, 9, 15, 15, 11, 7,
+ 4, 9, 14, 17, 17, 12, 8, 5, 27, 33, 36, 39, 39, 37, 33, 27, 30, 35, 38,
+ 40, 37, 32, 25, 29, 34, 37, 38, 34, 30, 25, 27, 32, 35, 37, 34, 32, 25, 27,
+ 30, 34, 37, 35, 31, 24, 26, 29, 33, 36, 34, 31, 21, 24, 28, 32, 35, 34, 32,
+ 26, 29, 34, 37, 35, 32, 28, 24, 29, 32, 35, 36, 32, 27, 24, 26, 30, 33, 33,
+ 30, 25, 23, 25, 28, 31, 31, 29, 26, 23, 25, 28, 31, 31, 28, 26, 21, 24, 27,
+ 30, 30, 29, 26, 18, 22, 25, 28, 29, 28, 26, 24, 26, 29, 30, 30, 27, 22, 22,
+ 25, 28, 28, 29, 26, 20, 21, 24, 26, 26, 26, 25, 18, 17, 24, 24, 24, 25, 24,
+ 19, 16, 23, 23, 23, 23, 22, 19, 13, 21, 22, 22, 22, 20, 19, 14, 19, 21, 21,
+ 21, 21, 17, 21, 22, 21, 22, 23, 20, 13, 17, 20, 21, 21, 21, 18, 12, 14, 19,
+ 19, 18, 19, 15, 12, 11, 19, 17, 17, 17, 14, 13, 12, 16, 16, 17, 16, 13, 10,
+ 13, 14, 15, 15, 15, 13, 7, 14, 13, 13, 13, 13, 13, 4, 9, 14, 14, 15, 13,
+ 9, 13, 6, 13, 12, 13, 12, 6, 12, 6, 10, 10, 10, 9, 4, 8, 6, 7, 9,
+ 9, 8, 4, 6, 7, 5, 8, 7, 7, 3, 4, 8, 7, 8, 7, 6, 3, 4, 10,
+ 11, 11, 8, 5, 4, 3, 2, 4, 5, 5, 5, 7, 9, 2, 2, 3, 4, 4, 5,
+ 7, 2, 0, 1, 1, 2, 3, 4, 2, 2, 3, 2, 2, 2, 3, 4, 5, 8, 7,
+ 6, 4, 3, 5, 9, 14, 12, 8, 5, 3, 10, 13, 16, 13, 9, 6, 3, 3, 6,
+ 4, 2, 5, 6, 7, 2, 5, 5, 2, 3, 4, 5, 3, 6, 7, 3, 0, 3, 4,
+ 2, 5, 7, 5, 4, 3, 3, 4, 7, 11, 11, 8, 6, 4, 7, 11, 15, 15, 11,
+ 7, 4, 12, 16, 16, 16, 12, 8, 5, 24, 29, 32, 36, 35, 32, 29, 23, 27, 31,
+ 35, 34, 31, 28, 21, 25, 29, 33, 34, 30, 26, 20, 22, 26, 30, 32, 29, 27, 19,
+ 22, 24, 28, 32, 30, 27, 18, 21, 23, 27, 31, 30, 27, 16, 19, 23, 26, 29, 30,
+ 26, 22, 25, 28, 33, 31, 28, 25, 20, 24, 28, 31, 31, 27, 24, 19, 22, 26, 30,
+ 29, 26, 22, 18, 20, 23, 26, 26, 25, 21, 18, 20, 22, 26, 26, 25, 22, 16, 19,
+ 22, 25, 24, 24, 22, 13, 17, 20, 23, 23, 24, 22, 19, 22, 24, 26, 26, 24, 20,
+ 18, 21, 23, 24, 25, 23, 18, 17, 20, 22, 21, 22, 21, 16, 15, 19, 19, 19, 20,
+ 19, 15, 13, 17, 19, 18, 18, 18, 15, 11, 16, 17, 17, 18, 17, 16, 13, 14, 15,
+ 16, 16, 17, 14, 17, 17, 17, 18, 19, 18, 10, 15, 16, 17, 17, 17, 15, 9, 11,
+ 15, 15, 15, 14, 13, 8, 8, 14, 13, 12, 12, 11, 10, 9, 12, 12, 11, 11, 10,
+ 6, 10, 10, 10, 10, 10, 9, 3, 13, 14, 10, 8, 9, 8, 3, 7, 9, 10, 10,
+ 11, 8, 10, 3, 7, 8, 9, 9, 5, 9, 3, 6, 6, 6, 6, 3, 5, 3, 4,
+ 4, 4, 3, 2, 3, 5, 5, 4, 4, 4, 3, 3, 7, 9, 10, 8, 6, 4, 2,
+ 11, 14, 13, 10, 7, 4, 2, 0, 2, 2, 3, 4, 6, 8, 0, 4, 3, 3, 3,
+ 4, 5, 0, 4, 3, 2, 1, 2, 3, 0, 5, 5, 3, 3, 2, 2, 3, 7, 9,
+ 8, 7, 4, 2, 7, 12, 15, 12, 9, 5, 3, 12, 15, 16, 14, 10, 7, 4, 6,
+ 9, 6, 3, 3, 5, 6, 6, 9, 8, 4, 2, 3, 4, 6, 9, 11, 5, 3, 0,
+ 3, 6, 7, 9, 6, 4, 4, 3, 6, 9, 12, 12, 9, 6, 4, 9, 13, 16, 16,
+ 11, 8, 4, 14, 17, 17, 17, 13, 9, 5, 20, 23, 27, 31, 30, 28, 24, 19, 22,
+ 26, 31, 31, 27, 24, 17, 21, 25, 28, 29, 26, 22, 16, 19, 22, 25, 27, 24, 21,
+ 14, 17, 20, 23, 27, 25, 22, 14, 16, 19, 23, 26, 25, 21, 11, 14, 18, 22, 25,
+ 25, 22, 17, 20, 25, 29, 28, 25, 21, 16, 20, 24, 28, 27, 23, 20, 15, 18, 22,
+ 24, 24, 22, 18, 14, 16, 19, 21, 21, 19, 17, 13, 16, 18, 20, 20, 21, 17, 11,
+ 14, 17, 20, 20, 20, 17, 12, 12, 16, 19, 19, 18, 17, 15, 18, 21, 22, 23, 21,
+ 16, 14, 17, 19, 20, 21, 20, 15, 13, 16, 18, 18, 18, 17, 13, 12, 14, 14, 14,
+ 15, 15, 12, 10, 13, 14, 14, 14, 14, 12, 9, 11, 12, 12, 13, 12, 12, 13, 12,
+ 11, 11, 11, 11, 11, 13, 13, 13, 14, 15, 15, 8, 11, 11, 12, 13, 13, 13, 7,
+ 9, 10, 11, 10, 10, 10, 6, 5, 9, 8, 7, 7, 7, 6, 7, 7, 7, 7, 6,
+ 6, 3, 9, 9, 8, 6, 5, 5, 2, 13, 13, 9, 7, 5, 4, 2, 4, 5, 5,
+ 6, 7, 7, 8, 1, 3, 4, 5, 5, 5, 6, 1, 1, 2, 2, 2, 2, 3, 1,
+ 3, 2, 1, 2, 1, 2, 3, 6, 6, 5, 5, 3, 2, 8, 11, 11, 9, 6, 4,
+ 2, 12, 14, 13, 10, 8, 6, 3, 5, 5, 4, 4, 4, 6, 7, 5, 7, 4, 4,
+ 4, 3, 4, 5, 7, 5, 4, 2, 1, 2, 5, 7, 6, 5, 3, 3, 2, 6, 9,
+ 11, 9, 7, 6, 3, 9, 12, 15, 13, 9, 6, 4, 13, 15, 16, 14, 10, 7, 4,
+ 8, 10, 7, 4, 4, 5, 5, 8, 10, 9, 5, 3, 3, 3, 8, 10, 11, 6, 4,
+ 3, 0, 8, 9, 11, 7, 5, 4, 3, 8, 10, 14, 13, 9, 7, 5, 11, 14, 17,
+ 16, 12, 8, 6, 14, 16, 17, 17, 13, 9, 6, 32, 35, 38, 42, 42, 39, 35, 31,
+ 35, 38, 41, 41, 39, 37, 29, 33, 37, 40, 41, 39, 37, 29, 31, 35, 38, 42, 39,
+ 37, 28, 31, 35, 39, 42, 40, 37, 27, 31, 35, 37, 41, 40, 36, 24, 28, 33, 36,
+ 41, 39, 36, 29, 33, 37, 39, 38, 34, 32, 29, 32, 35, 38, 37, 35, 31, 27, 30,
+ 33, 38, 37, 34, 32, 26, 29, 33, 36, 36, 35, 33, 26, 29, 33, 35, 36, 36, 33,
+ 23, 28, 31, 35, 35, 35, 33, 21, 25, 29, 33, 34, 34, 32, 27, 31, 32, 32, 33,
+ 30, 24, 26, 28, 32, 31, 32, 29, 25, 24, 28, 29, 30, 30, 29, 24, 21, 28, 28,
+ 29, 30, 30, 26, 19, 26, 29, 28, 29, 28, 26, 15, 24, 27, 27, 28, 27, 26, 16,
+ 22, 25, 25, 27, 26, 25, 24, 25, 25, 25, 25, 22, 13, 21, 23, 24, 24, 24, 21,
+ 14, 18, 22, 23, 23, 22, 20, 15, 13, 22, 22, 22, 23, 20, 17, 15, 20, 21, 21,
+ 21, 19, 14, 15, 17, 19, 19, 19, 18, 11, 16, 14, 16, 17, 18, 17, 8, 12, 17,
+ 16, 17, 14, 10, 13, 9, 15, 15, 15, 14, 10, 14, 9, 12, 13, 13, 13, 8, 12,
+ 9, 10, 13, 12, 12, 9, 10, 10, 7, 11, 11, 10, 6, 8, 10, 9, 8, 9, 9,
+ 5, 8, 12, 10, 9, 7, 7, 4, 7, 4, 6, 6, 6, 7, 9, 10, 4, 4, 4,
+ 5, 5, 9, 10, 4, 2, 2, 3, 4, 7, 9, 4, 2, 1, 1, 4, 6, 8, 5,
+ 4, 4, 4, 4, 4, 7, 7, 6, 8, 6, 4, 4, 6, 9, 9, 8, 7, 5, 4,
+ 5, 0, 4, 3, 2, 4, 7, 9, 0, 4, 4, 2, 4, 6, 8, 0, 4, 5, 2,
+ 2, 6, 8, 0, 4, 4, 2, 2, 4, 6, 2, 4, 6, 5, 5, 4, 5, 4, 6,
+ 8, 8, 6, 5, 4, 8, 8, 9, 9, 6, 5, 4, 38, 42, 46, 49, 49, 46, 42,
+ 37, 41, 46, 48, 50, 45, 42, 36, 39, 44, 47, 48, 45, 40, 34, 38, 41, 45, 48,
+ 44, 41, 34, 37, 41, 45, 48, 45, 41, 32, 37, 40, 43, 47, 46, 42, 30, 34, 39,
+ 43, 46, 46, 42, 36, 40, 44, 46, 44, 41, 37, 34, 38, 42, 45, 45, 40, 37, 33,
+ 37, 42, 43, 44, 41, 38, 30, 36, 39, 42, 40, 39, 37, 30, 35, 38, 42, 42, 40,
+ 36, 27, 33, 36, 40, 41, 39, 38, 24, 31, 35, 39, 39, 37, 37, 32, 36, 39, 39,
+ 39, 36, 29, 32, 35, 37, 39, 37, 35, 29, 28, 34, 36, 37, 36, 35, 28, 25, 34,
+ 33, 34, 35, 33, 29, 22, 32, 34, 34, 34, 33, 30, 18, 29, 31, 33, 33, 32, 29,
+ 17, 27, 30, 31, 32, 31, 27, 28, 31, 32, 31, 31, 27, 19, 24, 30, 31, 31, 30,
+ 25, 19, 20, 27, 29, 28, 28, 24, 21, 16, 26, 27, 27, 26, 22, 22, 17, 22, 26,
+ 26, 25, 22, 18, 18, 19, 24, 24, 24, 21, 14, 17, 15, 22, 23, 23, 20, 11, 15,
+ 22, 23, 24, 18, 14, 19, 12, 20, 22, 21, 18, 13, 19, 12, 16, 19, 19, 17, 10,
+ 16, 12, 12, 18, 18, 15, 10, 13, 13, 9, 17, 16, 13, 8, 10, 12, 10, 14, 14,
+ 12, 7, 10, 13, 12, 11, 12, 10, 6, 8, 7, 11, 13, 11, 9, 12, 15, 7, 7,
+ 11, 10, 8, 11, 13, 7, 5, 9, 8, 7, 8, 11, 7, 4, 7, 6, 7, 9, 9,
+ 7, 5, 5, 4, 4, 6, 8, 8, 7, 6, 6, 4, 5, 8, 10, 8, 8, 6, 5,
+ 4, 6, 4, 1, 2, 6, 8, 10, 13, 4, 1, 2, 5, 7, 9, 10, 4, 1, 3,
+ 3, 5, 7, 9, 4, 0, 3, 1, 3, 5, 7, 4, 2, 4, 4, 4, 4, 6, 5,
+ 5, 7, 7, 5, 4, 6, 7, 7, 7, 7, 5, 4, 5, 45, 49, 52, 55, 56, 52,
+ 48, 43, 47, 51, 55, 54, 51, 47, 41, 45, 50, 52, 53, 50, 46, 40, 44, 47, 51,
+ 52, 49, 46, 38, 41, 45, 49, 52, 50, 46, 37, 41, 44, 48, 51, 50, 46, 34, 39,
+ 43, 47, 50, 49, 47, 42, 45, 49, 53, 51, 48, 43, 40, 45, 49, 53, 51, 47, 42,
+ 37, 43, 47, 51, 49, 46, 40, 35, 40, 44, 48, 46, 45, 40, 33, 41, 44, 46, 45,
+ 45, 40, 30, 39, 42, 45, 45, 44, 40, 26, 36, 40, 44, 43, 42, 40, 38, 43, 45,
+ 46, 45, 42, 34, 34, 41, 44, 44, 44, 40, 34, 31, 40, 42, 41, 41, 39, 33, 28,
+ 37, 39, 39, 38, 36, 33, 24, 36, 38, 40, 38, 35, 33, 21, 32, 37, 37, 37, 34,
+ 33, 19, 30, 34, 35, 35, 34, 30, 31, 37, 38, 37, 36, 32, 25, 27, 34, 36, 35,
+ 35, 30, 25, 24, 31, 35, 34, 33, 27, 25, 20, 28, 31, 32, 30, 25, 26, 20, 25,
+ 31, 31, 29, 23, 22, 21, 21, 29, 29, 28, 23, 19, 20, 17, 26, 28, 27, 22, 14,
+ 18, 27, 29, 29, 23, 19, 25, 15, 23, 27, 27, 22, 16, 25, 15, 19, 25, 25, 20,
+ 13, 20, 15, 15, 23, 23, 17, 11, 17, 15, 12, 21, 21, 15, 10, 14, 16, 12, 18,
+ 19, 15, 8, 12, 15, 13, 14, 17, 13, 8, 12, 10, 14, 19, 15, 13, 16, 21, 10,
+ 10, 17, 13, 12, 14, 18, 10, 6, 15, 11, 9, 11, 14, 10, 5, 11, 8, 7, 10,
+ 11, 10, 6, 9, 6, 5, 8, 10, 10, 8, 6, 5, 4, 6, 10, 11, 9, 7, 5,
+ 4, 6, 8, 5, 3, 6, 9, 12, 14, 16, 5, 3, 5, 8, 10, 12, 14, 5, 3,
+ 3, 6, 7, 9, 11, 5, 3, 0, 3, 6, 10, 10, 5, 3, 3, 3, 3, 5, 8,
+ 6, 5, 5, 6, 4, 5, 7, 8, 6, 6, 6, 5, 4, 6, 38, 43, 47, 49, 51,
+ 47, 43, 36, 41, 43, 50, 49, 46, 41, 35, 39, 44, 46, 47, 45, 39, 34, 37, 40,
+ 44, 46, 42, 38, 31, 34, 38, 42, 44, 42, 38, 30, 34, 36, 40, 44, 43, 37, 28,
+ 31, 35, 39, 42, 42, 38, 34, 40, 44, 47, 45, 42, 38, 34, 38, 42, 46, 45, 41,
+ 37, 32, 36, 40, 43, 43, 39, 34, 31, 34, 37, 40, 39, 38, 33, 27, 32, 35, 38,
+ 38, 37, 33, 24, 32, 34, 37, 37, 36, 33, 20, 29, 33, 36, 36, 35, 34, 33, 36,
+ 38, 38, 39, 36, 30, 29, 34, 38, 39, 39, 35, 29, 26, 33, 36, 36, 36, 34, 27,
+ 23, 31, 32, 33, 33, 31, 26, 19, 29, 32, 32, 31, 29, 25, 15, 28, 30, 31, 31,
+ 28, 25, 15, 25, 28, 29, 29, 27, 24, 26, 30, 31, 31, 32, 28, 21, 22, 29, 30,
+ 31, 30, 25, 20, 18, 26, 28, 29, 27, 23, 20, 16, 22, 25, 25, 25, 19, 20, 14,
+ 19, 25, 24, 23, 17, 16, 15, 16, 22, 22, 21, 18, 12, 15, 13, 20, 20, 20, 17,
+ 9, 14, 22, 23, 24, 20, 15, 20, 10, 19, 21, 22, 18, 13, 21, 11, 14, 19, 19,
+ 16, 10, 16, 10, 9, 17, 16, 12, 7, 12, 10, 7, 15, 15, 11, 7, 9, 11, 8,
+ 12, 12, 10, 5, 7, 11, 9, 10, 11, 9, 4, 6, 5, 11, 14, 12, 11, 13, 17,
+ 5, 7, 12, 11, 10, 11, 14, 6, 3, 9, 8, 7, 8, 10, 5, 3, 6, 5, 4,
+ 5, 7, 5, 3, 4, 3, 2, 4, 6, 6, 5, 7, 6, 4, 3, 7, 8, 7, 8,
+ 8, 5, 3, 4, 2, 2, 4, 7, 9, 12, 13, 2, 2, 3, 6, 8, 9, 10, 2,
+ 2, 3, 4, 5, 6, 8, 2, 1, 3, 0, 3, 5, 6, 2, 1, 5, 6, 4, 3,
+ 5, 3, 5, 11, 11, 7, 3, 4, 5, 9, 12, 13, 8, 4, 3, 32, 37, 41, 44,
+ 43, 41, 38, 30, 35, 39, 44, 44, 41, 36, 30, 34, 39, 41, 42, 38, 34, 28, 32,
+ 35, 38, 41, 37, 32, 26, 29, 31, 35, 38, 36, 32, 24, 27, 30, 34, 38, 36, 33,
+ 22, 25, 29, 34, 36, 36, 32, 30, 35, 38, 41, 41, 37, 33, 30, 32, 36, 40, 38,
+ 36, 31, 27, 31, 35, 38, 37, 34, 30, 25, 29, 32, 35, 35, 32, 27, 23, 26, 30,
+ 32, 32, 31, 28, 21, 25, 28, 30, 31, 30, 27, 18, 23, 27, 30, 29, 29, 28, 27,
+ 31, 33, 34, 35, 33, 26, 25, 29, 31, 33, 33, 30, 24, 23, 28, 31, 30, 31, 29,
+ 23, 19, 26, 27, 26, 28, 26, 21, 15, 24, 25, 25, 25, 24, 21, 13, 23, 24, 23,
+ 23, 23, 21, 12, 21, 22, 22, 23, 23, 19, 22, 25, 26, 27, 27, 25, 18, 19, 24,
+ 24, 25, 25, 22, 17, 15, 22, 23, 23, 22, 19, 16, 12, 19, 19, 19, 20, 16, 16,
+ 11, 16, 18, 17, 17, 14, 12, 12, 13, 16, 16, 16, 13, 9, 12, 11, 15, 14, 15,
+ 13, 6, 10, 17, 18, 18, 17, 13, 18, 7, 16, 16, 17, 16, 10, 17, 7, 12, 15,
+ 14, 13, 8, 12, 7, 7, 11, 10, 10, 5, 8, 7, 4, 9, 9, 8, 4, 5, 7,
+ 6, 6, 7, 6, 3, 5, 9, 8, 7, 5, 5, 2, 4, 3, 8, 9, 9, 9, 11,
+ 15, 3, 6, 7, 8, 8, 9, 11, 3, 2, 5, 4, 6, 6, 7, 3, 1, 1, 1,
+ 3, 3, 4, 2, 2, 3, 3, 2, 2, 4, 4, 5, 9, 8, 4, 2, 5, 6, 9,
+ 11, 9, 6, 2, 3, 2, 4, 4, 6, 8, 10, 11, 2, 4, 4, 5, 7, 8, 8,
+ 2, 4, 5, 3, 4, 4, 5, 2, 3, 7, 3, 0, 3, 4, 1, 4, 7, 7, 5,
+ 2, 3, 3, 8, 11, 12, 7, 3, 2, 8, 12, 13, 12, 8, 4, 2, 27, 32, 36,
+ 39, 40, 36, 33, 27, 30, 35, 39, 38, 35, 32, 25, 29, 33, 37, 38, 34, 30, 25,
+ 26, 30, 34, 36, 32, 27, 21, 24, 27, 30, 34, 31, 27, 19, 22, 25, 28, 32, 31,
+ 28, 18, 20, 24, 27, 30, 31, 27, 26, 29, 34, 37, 35, 33, 28, 24, 27, 31, 35,
+ 36, 31, 28, 23, 26, 30, 33, 32, 29, 24, 21, 24, 28, 30, 29, 28, 23, 19, 21,
+ 24, 26, 26, 26, 22, 17, 20, 22, 25, 26, 25, 22, 14, 18, 22, 25, 24, 24, 22,
+ 23, 26, 28, 29, 31, 28, 24, 22, 25, 27, 28, 28, 27, 21, 20, 25, 25, 26, 26,
+ 25, 19, 17, 22, 22, 22, 22, 22, 17, 13, 19, 20, 19, 20, 19, 16, 10, 18, 19,
+ 19, 18, 18, 17, 10, 15, 17, 17, 18, 17, 15, 20, 21, 21, 22, 23, 22, 15, 17,
+ 20, 19, 21, 21, 20, 14, 13, 19, 19, 18, 18, 17, 13, 9, 16, 15, 15, 14, 13,
+ 12, 8, 13, 13, 12, 12, 11, 8, 9, 11, 11, 11, 11, 10, 6, 10, 8, 9, 9,
+ 10, 9, 4, 8, 13, 13, 14, 15, 12, 15, 5, 12, 12, 13, 13, 9, 13, 5, 10,
+ 10, 9, 10, 7, 9, 5, 5, 6, 6, 6, 3, 5, 4, 2, 4, 3, 3, 3, 3,
+ 5, 5, 5, 4, 2, 2, 4, 6, 7, 7, 5, 3, 1, 3, 2, 4, 6, 7, 8,
+ 10, 12, 2, 3, 5, 6, 7, 8, 9, 2, 3, 4, 4, 4, 5, 6, 2, 2, 2,
+ 2, 1, 2, 3, 2, 3, 6, 4, 3, 1, 3, 3, 7, 10, 8, 5, 2, 3, 6,
+ 9, 10, 9, 6, 3, 2, 4, 6, 5, 5, 7, 9, 10, 4, 6, 6, 5, 6, 7,
+ 7, 3, 5, 7, 3, 3, 4, 5, 4, 5, 10, 5, 3, 0, 3, 4, 6, 8, 8,
+ 5, 3, 1, 6, 9, 12, 11, 7, 4, 1, 9, 11, 12, 12, 8, 5, 2, 24, 27,
+ 32, 36, 36, 33, 29, 23, 26, 30, 34, 35, 32, 28, 21, 25, 29, 32, 33, 30, 26,
+ 20, 22, 26, 29, 32, 28, 23, 17, 19, 22, 26, 28, 26, 23, 15, 17, 20, 23, 27,
+ 26, 22, 14, 16, 19, 22, 26, 26, 22, 21, 25, 28, 33, 31, 29, 25, 20, 24, 28,
+ 31, 31, 27, 23, 19, 22, 26, 28, 28, 25, 22, 18, 20, 23, 25, 25, 24, 19, 15,
+ 17, 19, 21, 22, 22, 18, 13, 15, 18, 20, 20, 21, 18, 11, 14, 17, 20, 19, 19,
+ 18, 19, 22, 24, 25, 27, 25, 20, 18, 21, 23, 24, 24, 23, 18, 17, 20, 21, 22,
+ 22, 21, 17, 15, 17, 18, 18, 18, 18, 13, 10, 14, 15, 15, 15, 15, 13, 6, 13,
+ 14, 14, 14, 14, 13, 7, 12, 13, 12, 12, 12, 12, 17, 17, 17, 18, 19, 19, 13,
+ 15, 15, 16, 16, 17, 17, 11, 11, 14, 15, 14, 14, 13, 10, 7, 12, 11, 10, 10,
+ 10, 9, 5, 9, 8, 8, 7, 7, 6, 6, 7, 6, 6, 6, 6, 3, 7, 4, 5,
+ 5, 5, 5, 3, 7, 9, 9, 10, 11, 11, 13, 2, 7, 8, 9, 9, 8, 11, 2,
+ 6, 6, 5, 6, 6, 6, 2, 3, 3, 2, 2, 2, 3, 1, 2, 2, 2, 2, 0,
+ 2, 2, 5, 4, 3, 2, 1, 2, 3, 6, 6, 4, 3, 2, 1, 3, 4, 5, 7,
+ 8, 9, 11, 3, 4, 5, 6, 6, 7, 8, 3, 6, 4, 4, 4, 4, 5, 3, 5,
+ 4, 3, 2, 1, 2, 3, 6, 6, 5, 3, 2, 1, 5, 7, 9, 7, 4, 2, 0,
+ 6, 7, 9, 8, 5, 3, 1, 6, 7, 6, 5, 7, 8, 9, 6, 8, 7, 5, 6,
+ 6, 6, 6, 8, 9, 4, 3, 3, 3, 6, 8, 9, 6, 4, 2, 0, 6, 8, 10,
+ 8, 4, 3, 2, 7, 9, 11, 10, 6, 3, 2, 7, 9, 11, 10, 7, 4, 2, 33,
+ 38, 42, 45, 45, 42, 39, 33, 37, 41, 45, 45, 43, 37, 30, 35, 39, 42, 43, 41,
+ 38, 30, 33, 36, 40, 43, 39, 36, 27, 30, 34, 38, 41, 40, 36, 26, 30, 33, 36,
+ 40, 39, 35, 23, 27, 32, 36, 39, 38, 36, 31, 36, 39, 42, 41, 38, 35, 29, 33,
+ 38, 41, 41, 37, 33, 28, 32, 36, 40, 39, 37, 32, 27, 30, 34, 36, 36, 36, 31,
+ 25, 28, 32, 34, 35, 34, 30, 22, 27, 29, 33, 34, 33, 31, 20, 24, 28, 33, 32,
+ 33, 31, 28, 32, 35, 34, 36, 34, 28, 27, 30, 32, 35, 35, 32, 28, 24, 29, 32,
+ 33, 31, 32, 27, 21, 28, 28, 30, 29, 29, 25, 17, 25, 27, 28, 28, 28, 25, 14,
+ 24, 26, 26, 26, 27, 26, 14, 21, 24, 25, 24, 25, 24, 24, 26, 27, 28, 29, 26,
+ 17, 20, 25, 26, 28, 28, 25, 17, 17, 23, 25, 24, 24, 23, 18, 13, 20, 21, 21,
+ 21, 19, 18, 13, 18, 20, 19, 19, 18, 14, 14, 15, 17, 18, 18, 18, 10, 14, 12,
+ 15, 16, 17, 16, 9, 12, 18, 19, 20, 19, 14, 17, 9, 16, 17, 18, 17, 12, 17,
+ 8, 13, 15, 15, 15, 10, 14, 9, 8, 12, 12, 12, 8, 10, 8, 5, 10, 10, 9,
+ 7, 8, 9, 7, 7, 8, 8, 5, 10, 10, 8, 7, 6, 6, 3, 8, 4, 8, 9,
+ 9, 9, 12, 14, 4, 6, 7, 8, 9, 10, 13, 4, 2, 5, 5, 6, 8, 10, 4,
+ 2, 1, 1, 4, 6, 8, 4, 2, 2, 2, 2, 3, 8, 5, 4, 6, 4, 3, 2,
+ 6, 7, 7, 6, 5, 3, 2, 5, 2, 4, 4, 5, 8, 10, 12, 2, 4, 5, 4,
+ 7, 8, 10, 2, 4, 5, 3, 4, 6, 7, 2, 4, 5, 2, 1, 4, 7, 0, 4,
+ 5, 3, 3, 2, 5, 3, 4, 6, 6, 4, 3, 4, 5, 6, 6, 6, 4, 3, 2,
+ 41, 45, 50, 51, 52, 50, 46, 40, 43, 48, 51, 53, 48, 44, 37, 41, 46, 50, 51,
+ 48, 43, 36, 39, 43, 46, 50, 46, 41, 33, 36, 41, 44, 47, 45, 41, 32, 35, 39,
+ 42, 46, 45, 42, 28, 34, 37, 41, 45, 45, 41, 37, 41, 47, 49, 48, 44, 41, 35,
+ 41, 44, 49, 48, 44, 40, 33, 39, 43, 47, 44, 42, 38, 33, 37, 40, 44, 42, 41,
+ 36, 29, 34, 37, 40, 41, 39, 36, 25, 33, 36, 39, 40, 38, 36, 22, 30, 35, 38,
+ 39, 38, 36, 34, 39, 42, 42, 42, 39, 34, 32, 37, 40, 41, 42, 38, 32, 29, 36,
+ 37, 38, 39, 37, 31, 25, 33, 35, 35, 35, 34, 29, 20, 31, 33, 33, 34, 32, 30,
+ 16, 29, 31, 31, 32, 31, 29, 16, 26, 30, 30, 31, 30, 27, 28, 33, 34, 34, 34,
+ 32, 23, 24, 31, 32, 34, 33, 29, 23, 20, 29, 31, 30, 30, 27, 23, 17, 24, 27,
+ 27, 27, 24, 23, 15, 20, 25, 26, 25, 21, 19, 16, 18, 23, 23, 24, 20, 15, 16,
+ 14, 21, 22, 22, 20, 11, 16, 24, 25, 26, 23, 18, 24, 12, 21, 23, 24, 22, 16,
+ 23, 12, 17, 21, 21, 19, 13, 19, 12, 11, 18, 17, 15, 10, 14, 11, 8, 16, 15,
+ 13, 9, 11, 12, 9, 13, 13, 12, 8, 10, 12, 10, 11, 12, 11, 8, 9, 7, 13,
+ 15, 14, 14, 16, 20, 7, 9, 14, 13, 12, 14, 16, 7, 4, 11, 11, 9, 11, 13,
+ 7, 4, 7, 7, 6, 8, 10, 6, 4, 4, 4, 3, 8, 9, 7, 5, 4, 3, 2,
+ 5, 9, 8, 7, 5, 4, 2, 4, 8, 4, 3, 5, 9, 12, 14, 16, 4, 3, 4,
+ 8, 11, 12, 14, 4, 3, 4, 5, 7, 9, 11, 4, 2, 3, 1, 4, 7, 8, 4,
+ 0, 4, 3, 2, 4, 8, 3, 3, 4, 5, 3, 3, 7, 5, 5, 5, 5, 3, 2,
+ 5, 47, 50, 55, 58, 58, 55, 52, 45, 50, 53, 58, 57, 54, 50, 43, 48, 52, 54,
+ 56, 52, 48, 42, 45, 49, 51, 55, 51, 45, 39, 42, 45, 49, 52, 50, 45, 36, 40,
+ 43, 47, 50, 49, 45, 32, 38, 43, 46, 49, 51, 45, 42, 47, 51, 55, 54, 50, 47,
+ 42, 45, 51, 55, 54, 49, 44, 40, 44, 48, 53, 51, 47, 43, 36, 43, 47, 49, 48,
+ 46, 40, 32, 39, 42, 46, 46, 44, 39, 28, 36, 41, 45, 43, 44, 40, 24, 35, 40,
+ 44, 43, 41, 40, 38, 43, 47, 47, 48, 45, 38, 36, 43, 46, 48, 47, 43, 37, 32,
+ 40, 44, 44, 44, 43, 35, 28, 38, 40, 40, 40, 38, 34, 22, 34, 37, 38, 37, 36,
+ 33, 19, 32, 37, 36, 36, 35, 33, 18, 28, 34, 35, 35, 33, 31, 32, 39, 39, 42,
+ 40, 36, 30, 28, 36, 38, 39, 38, 34, 28, 24, 33, 37, 36, 35, 29, 28, 21, 28,
+ 33, 33, 33, 27, 27, 18, 23, 31, 30, 29, 23, 23, 19, 19, 28, 28, 28, 23, 20,
+ 18, 15, 25, 27, 27, 22, 15, 18, 28, 31, 31, 27, 23, 29, 16, 24, 30, 30, 26,
+ 20, 28, 16, 20, 27, 26, 23, 17, 23, 16, 14, 23, 23, 19, 13, 19, 14, 10, 20,
+ 20, 16, 11, 15, 14, 10, 16, 18, 15, 9, 12, 13, 11, 13, 17, 14, 9, 11, 11,
+ 15, 21, 18, 17, 20, 25, 10, 10, 19, 18, 16, 17, 21, 11, 6, 16, 15, 13, 14,
+ 17, 10, 6, 12, 12, 9, 11, 12, 9, 5, 7, 7, 7, 9, 11, 9, 7, 5, 4,
+ 4, 7, 11, 10, 8, 5, 3, 3, 8, 9, 6, 5, 10, 13, 15, 18, 20, 6, 5,
+ 9, 12, 14, 16, 17, 5, 5, 7, 9, 10, 12, 14, 5, 4, 3, 5, 7, 8, 10,
+ 5, 4, 0, 2, 4, 7, 9, 5, 3, 3, 4, 3, 6, 8, 6, 4, 4, 4, 3,
+ 4, 7, 44, 47, 51, 55, 56, 53, 48, 43, 46, 50, 54, 55, 52, 45, 40, 44, 49,
+ 51, 54, 48, 45, 40, 42, 46, 50, 52, 48, 42, 35, 39, 42, 45, 48, 45, 41, 32,
+ 37, 39, 42, 46, 45, 41, 29, 34, 39, 42, 45, 46, 41, 42, 45, 49, 53, 51, 47,
+ 44, 40, 44, 48, 51, 50, 46, 42, 36, 41, 45, 47, 47, 45, 40, 33, 39, 43, 46,
+ 46, 43, 37, 28, 35, 39, 42, 41, 40, 36, 25, 34, 37, 40, 40, 40, 37, 21, 32,
+ 36, 39, 39, 38, 36, 36, 42, 43, 44, 46, 42, 37, 33, 39, 42, 44, 43, 41, 33,
+ 29, 38, 40, 40, 41, 40, 33, 26, 35, 37, 37, 38, 36, 29, 20, 31, 34, 34, 34,
+ 32, 31, 16, 28, 31, 33, 33, 30, 30, 15, 24, 31, 31, 31, 30, 27, 30, 35, 36,
+ 37, 37, 35, 29, 25, 33, 34, 36, 36, 31, 26, 22, 30, 34, 34, 32, 28, 26, 17,
+ 25, 30, 29, 29, 24, 25, 16, 19, 26, 26, 24, 21, 21, 15, 16, 25, 25, 25, 20,
+ 17, 15, 12, 22, 23, 23, 20, 13, 16, 26, 28, 29, 25, 22, 28, 13, 22, 26, 28,
+ 25, 19, 27, 13, 17, 25, 24, 22, 15, 21, 13, 12, 21, 21, 18, 11, 17, 12, 7,
+ 17, 17, 14, 8, 13, 11, 8, 14, 15, 13, 7, 10, 11, 9, 10, 13, 12, 7, 9,
+ 9, 14, 18, 18, 17, 19, 24, 9, 9, 17, 17, 16, 16, 19, 9, 6, 15, 14, 12,
+ 13, 15, 8, 5, 10, 10, 9, 10, 10, 7, 4, 5, 6, 5, 7, 8, 6, 5, 3,
+ 3, 4, 6, 8, 7, 6, 3, 2, 3, 6, 7, 5, 5, 10, 13, 15, 18, 20, 5,
+ 5, 9, 12, 14, 15, 16, 5, 5, 8, 10, 11, 11, 13, 5, 4, 3, 6, 7, 8,
+ 8, 3, 3, 2, 0, 3, 5, 7, 3, 2, 4, 5, 2, 5, 6, 4, 4, 6, 6,
+ 2, 4, 6, 37, 42, 45, 48, 48, 46, 42, 36, 40, 43, 47, 48, 44, 40, 34, 37,
+ 42, 46, 46, 42, 38, 32, 36, 41, 43, 44, 41, 34, 29, 33, 36, 39, 40, 37, 35,
+ 27, 29, 33, 35, 39, 37, 33, 24, 27, 30, 35, 37, 39, 34, 34, 38, 41, 46, 45,
+ 42, 37, 34, 37, 40, 45, 44, 40, 35, 31, 35, 39, 42, 41, 38, 34, 29, 33, 36,
+ 39, 38, 36, 29, 25, 30, 32, 35, 35, 33, 29, 20, 27, 29, 32, 32, 32, 29, 15,
+ 26, 29, 32, 31, 31, 29, 31, 34, 37, 39, 40, 37, 30, 28, 34, 36, 37, 37, 35,
+ 29, 25, 32, 35, 35, 34, 34, 27, 21, 30, 31, 31, 31, 29, 25, 16, 25, 28, 28,
+ 27, 25, 24, 12, 23, 25, 25, 25, 24, 24, 11, 19, 25, 23, 24, 24, 21, 25, 29,
+ 30, 31, 31, 30, 24, 21, 28, 29, 30, 30, 27, 21, 18, 25, 27, 27, 27, 23, 20,
+ 15, 22, 24, 23, 23, 20, 19, 12, 15, 21, 21, 19, 15, 16, 11, 12, 19, 18, 18,
+ 15, 12, 11, 8, 17, 16, 16, 15, 9, 13, 21, 23, 23, 22, 19, 24, 9, 18, 21,
+ 22, 21, 15, 22, 10, 14, 19, 18, 17, 12, 17, 10, 8, 16, 15, 14, 8, 11, 8,
+ 4, 12, 11, 10, 5, 8, 7, 4, 9, 8, 8, 4, 6, 6, 5, 6, 7, 7, 4,
+ 5, 5, 11, 13, 14, 14, 16, 20, 5, 8, 12, 13, 13, 14, 16, 5, 4, 11, 10,
+ 10, 11, 11, 5, 3, 7, 6, 7, 7, 6, 3, 2, 2, 1, 3, 4, 5, 3, 2,
+ 3, 3, 2, 3, 5, 4, 3, 4, 4, 1, 3, 4, 5, 5, 9, 11, 13, 15, 16,
+ 5, 5, 8, 10, 12, 12, 13, 5, 5, 7, 8, 8, 9, 10, 5, 4, 3, 4, 5,
+ 6, 4, 3, 2, 4, 3, 0, 3, 4, 2, 3, 5, 5, 2, 2, 3, 2, 5, 6,
+ 6, 3, 1, 3, 32, 35, 39, 42, 42, 40, 37, 30, 34, 38, 42, 43, 39, 35, 29,
+ 31, 37, 40, 41, 37, 33, 28, 29, 33, 38, 39, 35, 28, 25, 27, 30, 33, 36, 33,
+ 29, 22, 23, 26, 30, 34, 32, 29, 20, 22, 25, 29, 32, 32, 28, 29, 33, 36, 40,
+ 39, 36, 32, 28, 31, 35, 38, 39, 34, 30, 26, 30, 34, 36, 35, 32, 28, 26, 26,
+ 30, 33, 33, 30, 24, 21, 23, 27, 30, 29, 28, 23, 17, 22, 24, 27, 27, 26, 24,
+ 14, 20, 23, 26, 26, 26, 24, 27, 30, 31, 32, 35, 31, 27, 25, 29, 31, 32, 31,
+ 29, 24, 22, 27, 29, 28, 29, 28, 22, 19, 26, 26, 25, 25, 25, 19, 13, 22, 22,
+ 22, 22, 20, 18, 9, 20, 20, 20, 19, 20, 19, 8, 17, 19, 18, 18, 19, 17, 22,
+ 25, 25, 25, 27, 26, 19, 18, 23, 24, 24, 25, 23, 17, 14, 22, 22, 22, 21, 19,
+ 16, 11, 18, 18, 18, 18, 16, 14, 9, 13, 15, 14, 14, 12, 11, 9, 10, 13, 13,
+ 12, 12, 8, 8, 6, 12, 11, 11, 11, 5, 10, 17, 17, 18, 18, 15, 19, 7, 15,
+ 15, 16, 16, 12, 17, 7, 12, 13, 13, 13, 9, 12, 7, 6, 10, 9, 9, 5, 8,
+ 5, 2, 6, 5, 5, 3, 5, 4, 2, 4, 3, 3, 3, 4, 4, 3, 2, 2, 2,
+ 2, 3, 3, 9, 9, 10, 11, 13, 16, 3, 7, 8, 9, 10, 11, 12, 3, 4, 7,
+ 7, 7, 8, 8, 3, 3, 4, 4, 4, 4, 3, 2, 1, 4, 3, 1, 2, 3, 1,
+ 3, 5, 4, 2, 2, 3, 2, 4, 5, 5, 3, 0, 2, 4, 5, 6, 8, 10, 12,
+ 13, 4, 5, 6, 8, 9, 10, 10, 4, 5, 6, 5, 6, 6, 7, 4, 4, 5, 3,
+ 2, 3, 3, 2, 4, 7, 5, 3, 0, 3, 3, 5, 7, 6, 3, 2, 2, 4, 6,
+ 7, 7, 4, 2, 2, 26, 30, 34, 38, 38, 34, 31, 24, 29, 32, 36, 37, 33, 29,
+ 23, 26, 31, 35, 35, 32, 28, 22, 25, 28, 31, 33, 29, 24, 19, 21, 24, 28, 30,
+ 28, 24, 17, 19, 22, 25, 29, 28, 25, 16, 17, 20, 24, 27, 28, 24, 23, 27, 31,
+ 35, 33, 31, 27, 22, 25, 30, 33, 33, 30, 26, 21, 24, 28, 30, 31, 28, 24, 20,
+ 21, 25, 27, 28, 26, 20, 17, 19, 21, 24, 24, 23, 20, 15, 17, 20, 22, 22, 22,
+ 20, 12, 16, 19, 21, 21, 21, 20, 22, 24, 27, 27, 29, 26, 22, 20, 23, 25, 26,
+ 26, 25, 20, 18, 22, 23, 23, 24, 23, 18, 16, 20, 20, 20, 20, 20, 15, 11, 16,
+ 17, 17, 17, 16, 15, 7, 16, 15, 15, 15, 15, 14, 7, 14, 14, 14, 14, 14, 14,
+ 18, 18, 19, 20, 22, 21, 14, 15, 18, 18, 19, 19, 19, 13, 12, 17, 17, 16, 16,
+ 15, 11, 8, 15, 13, 12, 13, 12, 10, 6, 10, 10, 9, 9, 9, 7, 6, 8, 8,
+ 8, 7, 8, 4, 7, 4, 7, 6, 6, 6, 3, 7, 11, 11, 12, 13, 12, 14, 4,
+ 9, 10, 10, 11, 10, 12, 4, 8, 8, 7, 7, 7, 8, 4, 5, 4, 4, 4, 4,
+ 4, 2, 1, 1, 1, 1, 2, 3, 2, 3, 4, 3, 2, 0, 3, 3, 4, 5, 3,
+ 3, 2, 2, 3, 5, 6, 8, 9, 11, 12, 3, 4, 6, 7, 7, 8, 9, 3, 4,
+ 4, 5, 5, 5, 6, 3, 3, 4, 3, 2, 2, 2, 1, 5, 6, 4, 3, 2, 2,
+ 3, 7, 8, 6, 4, 2, 2, 5, 7, 9, 8, 4, 3, 0, 5, 7, 6, 6, 8,
+ 9, 10, 5, 7, 7, 6, 7, 7, 7, 5, 7, 8, 4, 3, 4, 5, 5, 6, 8,
+ 5, 3, 1, 2, 5, 8, 10, 8, 4, 3, 0, 7, 9, 10, 9, 5, 3, 1, 8,
+ 9, 10, 10, 6, 4, 2, 36, 41, 47, 48, 47, 47, 42, 36, 39, 44, 48, 49, 45,
+ 41, 34, 38, 42, 45, 47, 44, 39, 32, 35, 39, 42, 45, 41, 36, 29, 33, 35, 39,
+ 41, 39, 37, 26, 29, 32, 37, 41, 39, 36, 23, 27, 32, 34, 38, 39, 35, 33, 38,
+ 42, 45, 44, 42, 39, 31, 37, 41, 45, 44, 40, 38, 31, 35, 40, 42, 42, 40, 35,
+ 29, 33, 36, 39, 38, 38, 31, 25, 28, 32, 36, 35, 35, 32, 22, 27, 30, 32, 34,
+ 34, 31, 18, 24, 28, 32, 33, 32, 31, 31, 36, 37, 38, 39, 37, 32, 29, 33, 35,
+ 37, 38, 36, 31, 26, 32, 36, 35, 35, 35, 30, 22, 29, 31, 31, 31, 31, 26, 17,
+ 26, 27, 28, 27, 27, 26, 13, 24, 26, 26, 26, 27, 26, 12, 21, 23, 25, 24, 25,
+ 24, 27, 29, 30, 31, 32, 31, 23, 21, 27, 29, 30, 29, 28, 22, 19, 25, 27, 26,
+ 27, 25, 22, 14, 21, 24, 23, 23, 22, 20, 13, 17, 20, 19, 20, 18, 16, 12, 13,
+ 17, 18, 18, 17, 12, 12, 10, 15, 16, 17, 17, 10, 13, 20, 22, 22, 22, 19, 23,
+ 10, 18, 20, 20, 21, 16, 21, 10, 15, 18, 18, 18, 13, 17, 10, 9, 14, 14, 14,
+ 10, 12, 8, 4, 10, 10, 10, 8, 9, 7, 5, 7, 7, 8, 6, 9, 8, 7, 5,
+ 5, 6, 6, 8, 5, 11, 12, 13, 15, 17, 20, 5, 8, 10, 11, 13, 14, 16, 5,
+ 5, 8, 8, 10, 12, 13, 5, 4, 4, 4, 6, 8, 8, 4, 3, 2, 1, 2, 6,
+ 8, 4, 2, 4, 3, 1, 5, 8, 5, 5, 4, 3, 2, 4, 6, 4, 6, 6, 9,
+ 13, 16, 17, 4, 6, 7, 7, 11, 13, 14, 5, 6, 7, 4, 8, 9, 11, 4, 5,
+ 6, 3, 3, 5, 7, 3, 3, 5, 3, 2, 3, 7, 0, 4, 5, 5, 2, 1, 5,
+ 3, 5, 5, 5, 3, 1, 4, 42, 48, 52, 54, 55, 52, 49, 41, 46, 50, 54, 53,
+ 50, 46, 40, 44, 48, 52, 53, 50, 45, 38, 41, 46, 50, 52, 47, 41, 35, 39, 41,
+ 46, 47, 46, 42, 31, 35, 37, 42, 46, 44, 41, 28, 33, 38, 41, 44, 44, 41, 39,
+ 45, 48, 53, 51, 47, 44, 39, 43, 47, 50, 50, 47, 42, 36, 43, 46, 49, 48, 44,
+ 40, 34, 39, 44, 45, 46, 42, 36, 29, 35, 38, 41, 41, 40, 37, 24, 33, 36, 39,
+ 39, 39, 36, 20, 31, 34, 38, 38, 37, 37, 36, 40, 43, 43, 46, 43, 37, 34, 39,
+ 42, 43, 43, 41, 36, 30, 39, 40, 41, 41, 39, 33, 25, 35, 37, 37, 37, 35, 30,
+ 20, 31, 33, 33, 33, 32, 31, 15, 28, 31, 31, 31, 31, 30, 15, 25, 29, 30, 30,
+ 30, 27, 30, 35, 37, 37, 38, 36, 28, 25, 33, 34, 36, 35, 32, 27, 21, 30, 34,
+ 32, 32, 29, 26, 18, 26, 30, 29, 30, 26, 25, 16, 19, 26, 25, 25, 21, 21, 15,
+ 16, 23, 24, 24, 21, 17, 15, 12, 21, 22, 22, 20, 13, 17, 25, 27, 29, 27, 23,
+ 29, 14, 22, 26, 27, 25, 20, 27, 13, 18, 23, 23, 22, 17, 21, 13, 12, 20, 20,
+ 18, 12, 16, 12, 7, 16, 16, 14, 9, 13, 11, 7, 13, 13, 13, 8, 10, 10, 8,
+ 10, 12, 11, 8, 9, 9, 15, 18, 19, 18, 21, 24, 9, 10, 17, 17, 17, 18, 20,
+ 9, 6, 14, 14, 14, 15, 16, 9, 5, 10, 10, 10, 11, 11, 7, 4, 5, 5, 5,
+ 8, 10, 6, 4, 2, 2, 4, 6, 10, 7, 5, 3, 2, 3, 7, 8, 6, 6, 9,
+ 14, 16, 19, 21, 6, 6, 8, 13, 15, 16, 18, 6, 6, 6, 10, 12, 13, 14, 6,
+ 5, 5, 5, 8, 9, 9, 4, 3, 3, 2, 3, 5, 9, 4, 0, 3, 4, 1, 6,
+ 7, 3, 3, 4, 4, 2, 4, 6, 49, 54, 58, 61, 61, 58, 53, 48, 51, 57, 60,
+ 60, 57, 53, 46, 50, 54, 57, 59, 54, 51, 44, 47, 51, 55, 56, 51, 46, 40, 44,
+ 47, 51, 53, 49, 46, 36, 41, 44, 46, 50, 50, 46, 32, 38, 42, 46, 49, 48, 47,
+ 45, 49, 54, 58, 56, 54, 48, 44, 48, 52, 58, 56, 52, 47, 41, 47, 51, 55, 54,
+ 50, 44, 38, 44, 48, 52, 50, 48, 41, 33, 42, 44, 48, 46, 46, 41, 27, 37, 41,
+ 44, 45, 43, 40, 23, 34, 39, 43, 42, 42, 41, 41, 46, 50, 50, 51, 49, 41, 38,
+ 45, 48, 50, 49, 46, 40, 33, 44, 46, 47, 46, 44, 37, 30, 40, 43, 43, 43, 41,
+ 34, 24, 34, 39, 39, 39, 35, 34, 19, 30, 36, 36, 36, 35, 35, 17, 26, 34, 35,
+ 34, 34, 32, 34, 42, 41, 43, 42, 41, 34, 29, 38, 41, 41, 40, 36, 33, 25, 34,
+ 39, 39, 36, 33, 31, 21, 29, 36, 35, 33, 28, 29, 19, 23, 31, 32, 29, 24, 26,
+ 18, 18, 28, 29, 27, 23, 22, 17, 14, 25, 26, 26, 23, 17, 20, 29, 33, 34, 30,
+ 28, 33, 17, 25, 32, 32, 29, 24, 31, 17, 21, 29, 30, 26, 20, 26, 17, 15, 25,
+ 26, 22, 15, 21, 15, 10, 20, 22, 17, 11, 17, 13, 9, 16, 19, 15, 10, 14, 13,
+ 9, 11, 17, 15, 9, 11, 12, 17, 24, 23, 22, 24, 30, 12, 12, 22, 22, 20, 21,
+ 25, 12, 9, 19, 19, 17, 18, 20, 12, 8, 14, 16, 13, 13, 14, 10, 6, 7, 11,
+ 7, 10, 12, 9, 5, 4, 6, 5, 8, 10, 8, 6, 4, 5, 5, 7, 10, 8, 7,
+ 16, 18, 20, 23, 24, 8, 8, 13, 17, 19, 20, 20, 8, 7, 11, 14, 16, 16, 17,
+ 8, 6, 5, 11, 11, 12, 10, 6, 4, 3, 4, 5, 7, 10, 5, 3, 0, 1, 3,
+ 6, 9, 5, 3, 2, 2, 3, 5, 8, 48, 52, 55, 59, 59, 57, 52, 47, 50, 55,
+ 59, 59, 56, 50, 44, 48, 52, 56, 58, 53, 49, 43, 46, 50, 53, 54, 51, 44, 38,
+ 44, 46, 49, 52, 49, 44, 34, 40, 43, 45, 50, 48, 43, 30, 37, 41, 44, 48, 47,
+ 43, 45, 51, 53, 56, 56, 51, 47, 44, 47, 53, 55, 56, 50, 45, 40, 47, 51, 54,
+ 52, 48, 44, 36, 43, 48, 50, 49, 44, 40, 32, 39, 45, 47, 44, 43, 39, 26, 36,
+ 40, 43, 42, 42, 39, 21, 33, 38, 42, 42, 41, 40, 40, 45, 47, 48, 50, 48, 40,
+ 37, 44, 48, 46, 49, 46, 38, 32, 42, 46, 45, 46, 43, 37, 28, 40, 42, 43, 41,
+ 40, 33, 23, 35, 37, 38, 36, 34, 33, 17, 29, 35, 35, 35, 34, 34, 16, 24, 33,
+ 33, 33, 32, 31, 32, 39, 40, 43, 42, 40, 34, 28, 38, 40, 41, 40, 36, 32, 24,
+ 33, 38, 38, 37, 33, 30, 20, 28, 34, 34, 33, 28, 28, 19, 21, 31, 30, 28, 24,
+ 25, 17, 17, 27, 27, 26, 22, 21, 16, 13, 23, 26, 26, 21, 17, 19, 29, 33, 34,
+ 30, 27, 33, 16, 24, 32, 32, 29, 24, 31, 16, 21, 29, 29, 26, 20, 26, 16, 15,
+ 24, 25, 22, 14, 20, 14, 10, 19, 21, 17, 10, 17, 12, 8, 14, 17, 15, 9, 13,
+ 12, 8, 10, 16, 14, 9, 11, 11, 17, 23, 23, 22, 24, 29, 12, 12, 21, 22, 21,
+ 21, 24, 12, 9, 19, 19, 17, 18, 20, 11, 7, 14, 15, 13, 12, 13, 10, 5, 7,
+ 10, 7, 9, 11, 8, 5, 4, 6, 5, 7, 10, 7, 5, 4, 4, 4, 7, 9, 8,
+ 8, 16, 18, 21, 23, 24, 8, 8, 14, 18, 20, 20, 20, 8, 8, 11, 16, 15, 15,
+ 16, 8, 7, 6, 11, 11, 11, 10, 6, 5, 4, 5, 5, 6, 9, 4, 4, 1, 0,
+ 3, 6, 8, 4, 3, 2, 2, 2, 5, 7, 41, 45, 48, 52, 51, 50, 45, 39, 42,
+ 46, 52, 51, 47, 43, 37, 41, 45, 49, 49, 46, 41, 36, 39, 41, 46, 47, 44, 36,
+ 33, 36, 38, 42, 43, 40, 37, 29, 32, 35, 39, 42, 41, 37, 25, 30, 34, 37, 40,
+ 41, 37, 37, 42, 45, 49, 48, 44, 40, 36, 41, 44, 47, 47, 42, 37, 33, 38, 43,
+ 45, 44, 42, 36, 31, 37, 39, 41, 41, 38, 32, 26, 33, 35, 38, 38, 35, 32, 21,
+ 30, 33, 35, 34, 34, 31, 16, 28, 31, 34, 34, 33, 32, 34, 39, 40, 43, 42, 40,
+ 33, 31, 37, 40, 41, 40, 37, 33, 26, 35, 38, 39, 37, 36, 30, 24, 32, 33, 34,
+ 34, 32, 26, 17, 27, 31, 30, 30, 27, 26, 13, 24, 29, 28, 27, 26, 27, 12, 20,
+ 27, 27, 27, 26, 24, 27, 33, 32, 34, 34, 33, 27, 22, 30, 33, 32, 32, 29, 24,
+ 20, 27, 31, 31, 30, 26, 23, 17, 23, 27, 27, 26, 21, 22, 13, 17, 24, 23, 21,
+ 18, 18, 12, 12, 21, 20, 19, 17, 15, 12, 9, 19, 19, 19, 17, 11, 15, 23, 26,
+ 26, 24, 21, 26, 11, 20, 24, 25, 23, 18, 24, 11, 15, 22, 22, 20, 15, 19, 11,
+ 10, 19, 18, 16, 10, 13, 9, 5, 14, 14, 12, 6, 10, 8, 4, 10, 11, 10, 5,
+ 8, 8, 5, 7, 9, 8, 5, 6, 7, 13, 16, 17, 16, 19, 23, 7, 9, 15, 15,
+ 15, 16, 18, 7, 5, 13, 12, 13, 13, 13, 7, 4, 10, 9, 9, 9, 8, 5, 2,
+ 4, 4, 4, 5, 6, 3, 2, 2, 1, 2, 4, 6, 4, 3, 2, 2, 2, 4, 5,
+ 6, 6, 11, 13, 15, 17, 18, 6, 6, 11, 13, 14, 15, 15, 6, 6, 8, 10, 11,
+ 11, 11, 5, 5, 4, 6, 7, 7, 6, 3, 3, 3, 2, 2, 3, 5, 2, 1, 3,
+ 3, 0, 3, 4, 2, 3, 4, 4, 2, 3, 4, 33, 38, 42, 46, 45, 43, 37, 32,
+ 36, 40, 45, 44, 41, 37, 31, 34, 39, 42, 43, 39, 34, 30, 32, 36, 39, 40, 37,
+ 31, 27, 29, 31, 36, 37, 35, 31, 23, 26, 29, 33, 36, 34, 31, 22, 25, 28, 30,
+ 34, 34, 31, 31, 34, 38, 42, 42, 37, 34, 30, 33, 38, 41, 40, 36, 32, 29, 32,
+ 36, 38, 38, 36, 28, 27, 29, 32, 36, 35, 32, 27, 22, 26, 30, 32, 31, 30, 26,
+ 18, 23, 26, 29, 29, 29, 26, 15, 23, 25, 28, 28, 28, 26, 28, 31, 35, 34, 37,
+ 34, 28, 26, 31, 31, 34, 34, 32, 26, 23, 30, 31, 31, 31, 30, 23, 19, 28, 28,
+ 28, 28, 26, 21, 14, 24, 24, 24, 23, 23, 21, 10, 21, 23, 21, 22, 22, 21, 10,
+ 19, 21, 20, 21, 21, 18, 24, 26, 27, 27, 29, 27, 21, 19, 25, 26, 27, 27, 24,
+ 18, 16, 23, 24, 24, 24, 21, 18, 12, 19, 21, 20, 21, 18, 17, 10, 14, 17, 17,
+ 16, 13, 13, 10, 10, 15, 15, 14, 13, 10, 9, 7, 14, 13, 13, 12, 6, 11, 18,
+ 19, 20, 20, 16, 21, 8, 16, 18, 18, 18, 13, 19, 8, 12, 15, 15, 15, 11, 14,
+ 8, 7, 12, 12, 11, 6, 9, 6, 2, 8, 8, 7, 4, 6, 5, 3, 6, 5, 5,
+ 3, 5, 5, 3, 4, 4, 3, 3, 4, 4, 10, 10, 11, 13, 14, 18, 4, 8, 10,
+ 10, 12, 12, 13, 4, 4, 9, 8, 9, 9, 9, 4, 3, 5, 5, 5, 5, 4, 2,
+ 2, 2, 2, 2, 3, 4, 2, 1, 5, 4, 2, 2, 4, 3, 3, 5, 4, 2, 2,
+ 3, 5, 5, 8, 10, 11, 13, 14, 5, 5, 7, 9, 10, 11, 11, 5, 5, 6, 7,
+ 7, 8, 8, 5, 4, 5, 3, 3, 4, 3, 3, 3, 7, 5, 2, 2, 3, 1, 5,
+ 6, 6, 3, 0, 3, 4, 6, 6, 6, 4, 2, 2, 27, 31, 35, 38, 39, 36, 33,
+ 25, 30, 34, 39, 38, 35, 30, 25, 29, 32, 36, 37, 34, 29, 24, 26, 29, 32, 36,
+ 31, 26, 21, 24, 26, 29, 33, 30, 26, 18, 20, 23, 27, 30, 30, 25, 17, 19, 22,
+ 25, 29, 29, 26, 25, 28, 33, 35, 35, 33, 29, 24, 28, 32, 35, 35, 32, 27, 23,
+ 26, 30, 32, 32, 30, 25, 22, 24, 27, 29, 29, 26, 21, 19, 20, 23, 26, 26, 25,
+ 21, 16, 18, 21, 24, 23, 24, 21, 13, 17, 20, 22, 23, 22, 21, 22, 26, 28, 29,
+ 31, 28, 23, 22, 24, 26, 28, 28, 27, 22, 20, 23, 25, 25, 25, 25, 19, 17, 22,
+ 22, 22, 22, 21, 16, 11, 18, 19, 18, 18, 17, 16, 8, 17, 17, 17, 17, 16, 16,
+ 8, 16, 16, 15, 15, 16, 14, 20, 21, 20, 22, 24, 22, 16, 16, 20, 20, 21, 21,
+ 20, 14, 13, 18, 18, 18, 17, 17, 13, 9, 16, 15, 14, 14, 14, 12, 7, 12, 11,
+ 11, 10, 10, 8, 7, 9, 10, 9, 9, 9, 5, 8, 5, 8, 8, 8, 8, 3, 8,
+ 13, 13, 14, 15, 13, 15, 5, 11, 12, 12, 13, 10, 14, 5, 10, 10, 9, 9, 8,
+ 9, 5, 5, 6, 6, 5, 4, 5, 3, 1, 2, 2, 2, 2, 3, 3, 2, 3, 2,
+ 1, 2, 3, 4, 3, 4, 3, 2, 0, 2, 3, 6, 7, 8, 9, 11, 13, 3, 5,
+ 7, 8, 8, 9, 10, 3, 4, 5, 5, 6, 6, 6, 3, 3, 3, 2, 2, 3, 3,
+ 1, 4, 5, 4, 3, 0, 2, 1, 5, 7, 5, 3, 1, 2, 3, 6, 7, 6, 4,
+ 2, 2, 4, 6, 6, 7, 9, 10, 11, 4, 6, 6, 6, 8, 8, 8, 4, 6, 7,
+ 4, 4, 4, 6, 4, 6, 7, 4, 2, 1, 2, 3, 7, 8, 6, 3, 2, 1, 5,
+ 7, 9, 8, 4, 3, 0, 6, 7, 9, 9, 5, 3, 2, 38, 44, 48, 51, 51, 48,
+ 46, 38, 42, 46, 50, 51, 48, 44, 37, 41, 45, 47, 49, 44, 41, 34, 38, 41, 45,
+ 47, 43, 36, 32, 34, 38, 42, 42, 38, 37, 28, 30, 33, 36, 41, 38, 37, 23, 26,
+ 32, 35, 39, 40, 36, 37, 39, 45, 49, 49, 44, 41, 36, 39, 43, 47, 48, 44, 39,
+ 34, 37, 42, 45, 44, 42, 36, 31, 36, 38, 41, 42, 38, 32, 27, 31, 35, 38, 36,
+ 36, 31, 22, 28, 31, 33, 34, 35, 32, 18, 25, 29, 32, 33, 33, 32, 34, 36, 40,
+ 41, 43, 41, 37, 31, 34, 39, 40, 40, 39, 34, 28, 35, 37, 37, 37, 37, 32, 23,
+ 33, 34, 34, 34, 32, 27, 18, 28, 29, 30, 29, 28, 27, 13, 24, 26, 26, 27, 27,
+ 27, 11, 21, 24, 24, 25, 25, 25, 28, 32, 33, 34, 36, 35, 27, 23, 30, 31, 34,
+ 33, 31, 25, 20, 28, 29, 30, 30, 28, 24, 16, 23, 26, 26, 26, 24, 23, 14, 17,
+ 22, 22, 21, 19, 18, 12, 13, 17, 19, 18, 18, 15, 11, 9, 16, 16, 17, 17, 11,
+ 15, 23, 25, 26, 27, 23, 27, 12, 20, 23, 25, 25, 21, 25, 12, 16, 21, 21, 21,
+ 17, 20, 12, 11, 17, 17, 17, 12, 14, 10, 5, 13, 12, 11, 8, 11, 7, 4, 8,
+ 8, 9, 7, 10, 7, 5, 5, 6, 7, 7, 9, 7, 14, 16, 17, 19, 22, 24, 8,
+ 11, 15, 16, 18, 19, 20, 8, 7, 13, 13, 15, 15, 16, 7, 6, 9, 9, 11, 10,
+ 9, 5, 4, 4, 4, 4, 7, 9, 5, 3, 3, 2, 3, 5, 8, 4, 2, 3, 3,
+ 1, 5, 7, 8, 7, 9, 14, 18, 20, 21, 7, 7, 9, 12, 16, 17, 18, 7, 7,
+ 9, 9, 12, 14, 14, 8, 6, 8, 5, 7, 9, 7, 5, 5, 6, 4, 2, 4, 8,
+ 3, 3, 5, 4, 2, 4, 6, 0, 4, 5, 5, 3, 3, 5, 45, 50, 54, 59, 58,
+ 56, 50, 44, 48, 52, 57, 58, 51, 48, 42, 46, 51, 55, 54, 52, 46, 41, 44, 46,
+ 51, 53, 48, 41, 36, 41, 44, 47, 48, 45, 41, 32, 37, 40, 42, 45, 46, 42, 28,
+ 33, 37, 42, 45, 46, 42, 42, 47, 50, 54, 54, 50, 46, 39, 44, 50, 53, 53, 49,
+ 45, 39, 43, 48, 51, 50, 46, 41, 35, 41, 45, 47, 46, 43, 36, 30, 37, 41, 43,
+ 41, 40, 37, 24, 33, 38, 38, 40, 40, 37, 19, 31, 35, 38, 39, 38, 36, 38, 43,
+ 46, 46, 48, 45, 41, 34, 41, 46, 46, 47, 44, 38, 31, 41, 42, 44, 43, 40, 36,
+ 27, 38, 39, 40, 38, 37, 32, 21, 33, 35, 35, 34, 32, 31, 16, 26, 32, 31, 32,
+ 30, 32, 15, 24, 29, 31, 31, 30, 29, 31, 38, 40, 39, 40, 39, 32, 27, 35, 37,
+ 39, 38, 35, 30, 24, 31, 37, 36, 35, 32, 29, 19, 27, 31, 32, 32, 27, 27, 17,
+ 21, 28, 27, 26, 22, 22, 16, 15, 24, 23, 23, 21, 19, 15, 12, 21, 22, 22, 21,
+ 15, 18, 28, 30, 31, 30, 27, 31, 15, 24, 29, 30, 29, 24, 30, 15, 19, 26, 27,
+ 25, 19, 24, 15, 14, 22, 23, 21, 14, 19, 13, 8, 18, 18, 15, 10, 15, 11, 6,
+ 12, 14, 13, 8, 12, 10, 7, 9, 12, 11, 8, 10, 11, 16, 21, 22, 22, 24, 29,
+ 11, 12, 20, 20, 21, 22, 24, 11, 8, 17, 17, 18, 18, 18, 10, 7, 13, 13, 13,
+ 12, 12, 8, 5, 7, 8, 6, 9, 10, 6, 5, 2, 3, 4, 7, 10, 5, 4, 2,
+ 1, 4, 7, 9, 8, 8, 14, 18, 21, 23, 24, 8, 8, 12, 17, 20, 21, 21, 8,
+ 8, 10, 14, 16, 16, 16, 9, 7, 6, 9, 12, 11, 9, 6, 5, 4, 4, 5, 6,
+ 9, 4, 3, 3, 3, 3, 5, 8, 4, 0, 3, 3, 2, 5, 7, 49, 54, 57, 61,
+ 61, 57, 54, 47, 52, 57, 60, 62, 56, 53, 46, 50, 55, 58, 58, 55, 50, 45, 49,
+ 52, 55, 57, 52, 46, 40, 45, 49, 52, 53, 50, 46, 35, 41, 44, 47, 50, 50, 46,
+ 31, 38, 43, 45, 50, 51, 45, 47, 49, 55, 59, 57, 54, 50, 44, 50, 55, 58, 57,
+ 52, 46, 41, 47, 53, 55, 54, 51, 44, 39, 46, 50, 51, 50, 48, 40, 32, 41, 45,
+ 47, 46, 45, 40, 27, 37, 41, 43, 44, 44, 41, 22, 34, 40, 44, 42, 41, 41, 41,
+ 49, 50, 52, 52, 49, 43, 37, 46, 50, 50, 50, 47, 41, 33, 42, 47, 47, 47, 44,
+ 38, 29, 41, 43, 44, 42, 41, 35, 23, 35, 39, 40, 37, 36, 35, 19, 29, 37, 36,
+ 37, 34, 36, 18, 26, 35, 35, 34, 33, 32, 33, 40, 43, 45, 43, 41, 35, 30, 38,
+ 41, 42, 42, 38, 34, 25, 35, 40, 40, 39, 33, 32, 22, 30, 36, 35, 35, 30, 30,
+ 20, 23, 31, 32, 30, 24, 26, 18, 18, 28, 29, 28, 23, 23, 18, 14, 24, 27, 26,
+ 23, 18, 21, 29, 34, 36, 33, 29, 34, 17, 26, 33, 34, 31, 25, 33, 17, 21, 30,
+ 30, 28, 21, 27, 17, 16, 25, 27, 23, 15, 21, 15, 10, 20, 23, 18, 11, 17, 13,
+ 9, 15, 19, 16, 10, 14, 13, 9, 11, 18, 15, 10, 12, 12, 17, 25, 24, 23, 26,
+ 31, 12, 13, 23, 23, 22, 22, 26, 12, 9, 20, 20, 19, 19, 21, 12, 8, 14, 16,
+ 14, 14, 14, 11, 6, 8, 11, 7, 9, 12, 9, 5, 4, 6, 5, 8, 11, 7, 5,
+ 4, 5, 4, 7, 10, 9, 8, 17, 19, 22, 24, 26, 9, 8, 15, 19, 20, 21, 21,
+ 9, 8, 11, 16, 16, 17, 17, 9, 7, 7, 13, 12, 12, 11, 6, 5, 4, 6, 6,
+ 7, 10, 5, 4, 2, 2, 3, 7, 9, 4, 3, 0, 0, 3, 5, 8, 49, 53, 57,
+ 61, 62, 59, 55, 48, 53, 57, 60, 61, 57, 52, 45, 51, 53, 58, 59, 56, 51, 44,
+ 47, 52, 55, 57, 52, 46, 42, 45, 48, 51, 54, 49, 45, 35, 41, 45, 47, 50, 49,
+ 45, 31, 38, 41, 46, 49, 50, 46, 46, 52, 55, 59, 57, 54, 50, 46, 50, 54, 57,
+ 55, 53, 48, 41, 48, 52, 56, 53, 50, 44, 38, 46, 50, 52, 50, 47, 42, 31, 41,
+ 46, 48, 45, 45, 40, 27, 37, 41, 43, 43, 43, 40, 22, 35, 40, 42, 43, 41, 41,
+ 41, 48, 50, 51, 51, 49, 42, 36, 46, 48, 50, 49, 46, 41, 33, 43, 47, 48, 46,
+ 45, 38, 30, 41, 44, 44, 44, 40, 35, 24, 34, 39, 40, 38, 36, 35, 19, 30, 36,
+ 36, 36, 35, 35, 17, 24, 34, 35, 35, 33, 32, 33, 41, 43, 44, 44, 41, 35, 29,
+ 38, 42, 43, 40, 38, 33, 26, 34, 40, 40, 39, 33, 31, 22, 30, 36, 36, 34, 29,
+ 30, 20, 23, 31, 32, 30, 24, 26, 19, 18, 28, 29, 27, 23, 22, 17, 14, 24, 27,
+ 27, 22, 18, 21, 31, 34, 35, 32, 29, 35, 17, 26, 33, 33, 30, 25, 33, 17, 21,
+ 30, 30, 28, 21, 27, 17, 16, 25, 27, 24, 15, 22, 15, 11, 20, 22, 17, 11, 17,
+ 14, 9, 15, 18, 16, 10, 14, 13, 9, 10, 17, 15, 9, 12, 13, 17, 25, 24, 23,
+ 25, 31, 13, 13, 22, 23, 22, 23, 26, 13, 9, 20, 20, 18, 19, 21, 12, 8, 14,
+ 16, 14, 13, 14, 10, 6, 8, 12, 7, 10, 12, 9, 5, 4, 7, 5, 8, 10, 7,
+ 6, 4, 5, 5, 8, 10, 9, 8, 17, 19, 21, 24, 25, 9, 8, 15, 18, 20, 21,
+ 22, 9, 8, 11, 17, 17, 17, 17, 9, 7, 6, 12, 12, 12, 11, 6, 5, 4, 6,
+ 6, 7, 10, 5, 4, 2, 2, 4, 6, 9, 5, 3, 0, 0, 3, 5, 8, 43, 47,
+ 51, 55, 55, 53, 49, 40, 45, 50, 53, 53, 50, 45, 39, 44, 47, 51, 52, 48, 43,
+ 39, 42, 46, 47, 49, 46, 39, 35, 38, 41, 45, 46, 43, 39, 30, 34, 37, 40, 44,
+ 43, 38, 27, 32, 36, 39, 42, 43, 40, 40, 43, 48, 51, 51, 47, 43, 39, 42, 47,
+ 50, 50, 47, 41, 36, 41, 45, 47, 48, 44, 39, 33, 38, 42, 44, 44, 40, 34, 27,
+ 36, 38, 42, 39, 38, 35, 22, 32, 35, 37, 36, 37, 34, 18, 29, 34, 37, 37, 35,
+ 34, 36, 40, 43, 44, 45, 42, 37, 32, 38, 42, 44, 42, 41, 33, 28, 38, 40, 40,
+ 40, 38, 33, 25, 35, 36, 36, 36, 34, 29, 19, 30, 33, 33, 32, 30, 29, 14, 26,
+ 30, 29, 30, 28, 29, 13, 21, 29, 28, 29, 28, 26, 28, 36, 36, 37, 37, 35, 29,
+ 24, 32, 35, 36, 35, 31, 27, 20, 30, 33, 33, 32, 28, 25, 17, 24, 29, 28, 28,
+ 24, 24, 15, 18, 25, 26, 24, 19, 20, 14, 14, 23, 22, 22, 19, 17, 13, 10, 20,
+ 21, 21, 18, 12, 16, 25, 28, 29, 26, 23, 28, 13, 21, 26, 27, 25, 19, 27, 13,
+ 17, 24, 24, 22, 16, 21, 13, 12, 20, 19, 18, 11, 16, 11, 7, 16, 16, 13, 7,
+ 12, 10, 5, 12, 13, 12, 6, 9, 9, 6, 8, 11, 10, 6, 8, 8, 14, 19, 19,
+ 18, 20, 24, 8, 10, 17, 18, 17, 17, 20, 8, 6, 14, 14, 14, 14, 15, 8, 5,
+ 11, 11, 10, 10, 10, 6, 3, 5, 6, 4, 6, 8, 4, 3, 2, 2, 3, 5, 7,
+ 4, 3, 2, 1, 3, 4, 6, 6, 6, 13, 15, 17, 19, 20, 6, 6, 12, 14, 16,
+ 16, 17, 6, 6, 9, 12, 12, 12, 13, 6, 5, 5, 8, 8, 8, 7, 4, 3, 3,
+ 2, 3, 4, 6, 3, 2, 3, 2, 2, 4, 5, 3, 2, 3, 3, 0, 4, 5, 36,
+ 40, 45, 47, 47, 44, 40, 34, 38, 42, 46, 46, 43, 38, 32, 36, 41, 44, 45, 42,
+ 36, 31, 34, 38, 40, 43, 39, 33, 29, 32, 33, 37, 39, 36, 32, 25, 27, 31, 33,
+ 37, 36, 33, 23, 26, 29, 33, 36, 37, 33, 33, 36, 41, 45, 43, 40, 35, 31, 35,
+ 39, 44, 43, 39, 35, 30, 33, 38, 41, 39, 36, 32, 29, 31, 34, 37, 37, 35, 29,
+ 24, 29, 31, 34, 32, 32, 29, 19, 26, 28, 31, 30, 32, 28, 15, 24, 27, 30, 30,
+ 29, 28, 31, 34, 36, 37, 37, 36, 30, 28, 32, 34, 36, 36, 35, 28, 25, 31, 34,
+ 34, 34, 31, 25, 20, 29, 29, 29, 30, 28, 23, 15, 24, 26, 26, 25, 24, 23, 11,
+ 21, 23, 23, 24, 23, 23, 10, 19, 24, 23, 22, 23, 20, 25, 29, 28, 30, 30, 29,
+ 23, 20, 27, 28, 29, 29, 26, 21, 17, 24, 27, 26, 26, 22, 19, 14, 20, 23, 22,
+ 22, 19, 18, 11, 15, 19, 19, 18, 14, 15, 11, 11, 17, 16, 16, 14, 11, 10, 7,
+ 16, 15, 15, 14, 8, 12, 20, 21, 22, 21, 18, 22, 9, 17, 20, 20, 20, 15, 21,
+ 9, 14, 18, 17, 17, 12, 15, 9, 8, 15, 14, 13, 7, 10, 7, 3, 10, 10, 9,
+ 5, 7, 6, 3, 8, 7, 7, 4, 5, 6, 4, 5, 6, 5, 4, 5, 5, 11, 12,
+ 13, 14, 16, 19, 5, 8, 11, 12, 13, 13, 15, 5, 5, 10, 9, 10, 10, 10, 5,
+ 3, 7, 6, 6, 6, 5, 3, 2, 1, 1, 2, 4, 4, 2, 2, 4, 3, 0, 3,
+ 5, 3, 2, 4, 4, 2, 2, 4, 5, 5, 9, 11, 13, 15, 15, 5, 5, 8, 10,
+ 12, 12, 12, 5, 5, 7, 8, 8, 9, 9, 5, 4, 4, 4, 4, 5, 4, 3, 2,
+ 4, 4, 1, 2, 4, 1, 4, 5, 5, 3, 2, 3, 2, 5, 5, 5, 3, 0, 3,
+ 29, 34, 38, 41, 41, 39, 34, 28, 31, 36, 40, 40, 37, 33, 27, 31, 34, 38, 39,
+ 36, 30, 26, 28, 31, 34, 37, 32, 27, 23, 25, 27, 31, 33, 31, 27, 20, 22, 25,
+ 28, 33, 31, 27, 19, 21, 23, 27, 30, 30, 28, 27, 30, 34, 38, 38, 34, 31, 26,
+ 29, 32, 37, 36, 34, 29, 25, 27, 32, 35, 33, 31, 26, 23, 25, 28, 31, 30, 28,
+ 23, 20, 23, 25, 28, 27, 26, 22, 17, 20, 23, 25, 25, 25, 23, 14, 19, 22, 24,
+ 24, 24, 24, 25, 28, 29, 31, 32, 30, 26, 23, 27, 28, 29, 30, 27, 23, 22, 25,
+ 28, 27, 27, 26, 21, 18, 23, 24, 23, 23, 23, 17, 12, 20, 20, 19, 20, 18, 17,
+ 8, 19, 19, 18, 18, 18, 17, 8, 16, 18, 17, 17, 18, 15, 21, 23, 23, 23, 25,
+ 24, 17, 18, 21, 22, 22, 22, 22, 15, 14, 20, 20, 19, 20, 18, 14, 10, 17, 17,
+ 16, 16, 15, 13, 8, 13, 13, 12, 12, 11, 10, 8, 9, 12, 11, 10, 10, 7, 8,
+ 6, 10, 9, 9, 9, 4, 9, 15, 15, 16, 17, 14, 17, 6, 13, 13, 14, 14, 11,
+ 15, 6, 11, 11, 11, 11, 8, 10, 6, 6, 8, 8, 7, 5, 6, 4, 2, 4, 4,
+ 3, 3, 4, 3, 2, 2, 2, 2, 3, 4, 4, 2, 3, 2, 1, 2, 3, 3, 7,
+ 8, 9, 10, 12, 14, 3, 6, 8, 9, 9, 10, 10, 3, 4, 6, 6, 6, 7, 7,
+ 3, 3, 3, 3, 3, 4, 3, 1, 3, 5, 3, 2, 2, 3, 1, 4, 6, 4, 3,
+ 0, 3, 2, 5, 6, 5, 3, 2, 2, 4, 6, 6, 8, 9, 11, 12, 4, 6, 6,
+ 7, 8, 9, 9, 4, 6, 6, 5, 5, 5, 6, 4, 4, 6, 3, 2, 2, 3, 2,
+ 5, 7, 6, 3, 2, 2, 4, 6, 8, 7, 4, 3, 2, 5, 7, 8, 8, 5, 3,
+ 0,
+};
diff --git a/c-lib/extras/sharpyuv_risk_table.h b/c-lib/extras/sharpyuv_risk_table.h
@@ -0,0 +1,27 @@
+// Copyright 2023 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Precomputed data for 420 risk estimation.
+
+#ifndef WEBP_EXTRAS_SHARPYUV_RISK_TABLE_H_
+#define WEBP_EXTRAS_SHARPYUV_RISK_TABLE_H_
+
+#include "src/webp/types.h"
+
+extern const int kSharpYuvPrecomputedRiskYuvSampling;
+// Table of precomputed risk scores when chroma subsampling images with two
+// given colors.
+// Since precomputing values for all possible YUV colors would create a huge
+// table, the YUV space (i.e. [0, 255]^3) is reduced to
+// [0, kSharpYuvPrecomputedRiskYuvSampling-1]^3
+// where 255 maps to kSharpYuvPrecomputedRiskYuvSampling-1.
+// Table size: kSharpYuvPrecomputedRiskYuvSampling^6 bytes or 114 KiB
+extern const uint8_t kSharpYuvPrecomputedRisk[];
+
+#endif // WEBP_EXTRAS_SHARPYUV_RISK_TABLE_H_
diff --git a/c-lib/extras/vwebp_sdl.c b/c-lib/extras/vwebp_sdl.c
@@ -15,6 +15,7 @@
// Author: James Zern (jzern@google.com)
#include <stdio.h>
+#include <stdlib.h>
#ifdef HAVE_CONFIG_H
#include "webp/config.h"
@@ -30,7 +31,7 @@
#if defined(WEBP_HAVE_JUST_SDL_H)
#include <SDL.h>
#else
-#include <SDL/SDL.h>
+#include <SDL2/SDL.h>
#endif
static void ProcessEvents(void) {
@@ -49,19 +50,26 @@ static void ProcessEvents(void) {
}
}
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, char* argv[]) {
int c;
int ok = 0;
INIT_WARGV(argc, argv);
+ if (argc == 1) {
+ fprintf(stderr, "Usage: %s [-h] image.webp [more_files.webp...]\n",
+ argv[0]);
+ goto Error;
+ }
+
for (c = 1; c < argc; ++c) {
const char* file = NULL;
const uint8_t* webp = NULL;
size_t webp_size = 0;
if (!strcmp(argv[c], "-h")) {
printf("Usage: %s [-h] image.webp [more_files.webp...]\n", argv[0]);
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else {
file = (const char*)GET_WARGV(argv, c);
}
@@ -75,7 +83,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "File too large.\n");
goto Error;
}
- ok = WebpToSDL((const char*)webp, (int)webp_size);
+ ok = WebPToSDL((const char*)webp, (int)webp_size);
free((void*)webp);
if (!ok) {
WFPRINTF(stderr, "Error decoding file %s\n", (const W_CHAR*)file);
@@ -87,7 +95,7 @@ int main(int argc, char* argv[]) {
Error:
SDL_Quit();
- FREE_WARGV_AND_RETURN(ok ? 0 : 1);
+ FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
#else // !WEBP_HAVE_SDL
diff --git a/c-lib/extras/webp_quality.c b/c-lib/extras/webp_quality.c
@@ -11,10 +11,12 @@
#include <stdlib.h>
#include <string.h>
+#include "../examples/unicode.h"
+#include "src/webp/types.h"
#include "extras/extras.h"
#include "imageio/imageio_util.h"
-#include "../examples/unicode.h"
+// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
int main(int argc, const char* argv[]) {
int c;
int quiet = 0;
@@ -27,7 +29,7 @@ int main(int argc, const char* argv[]) {
quiet = 1;
} else if (!strcmp(argv[c], "-help") || !strcmp(argv[c], "-h")) {
printf("webp_quality [-h][-quiet] webp_files...\n");
- FREE_WARGV_AND_RETURN(0);
+ FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
} else {
const char* const filename = (const char*)GET_WARGV(argv, c);
const uint8_t* data = NULL;
@@ -50,5 +52,5 @@ int main(int argc, const char* argv[]) {
free((void*)data);
}
}
- FREE_WARGV_AND_RETURN(ok ? 0 : 1);
+ FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/c-lib/extras/webp_to_sdl.c b/c-lib/extras/webp_to_sdl.c
@@ -20,88 +20,75 @@
#include "webp_to_sdl.h"
#include <stdio.h>
+
#include "src/webp/decode.h"
#if defined(WEBP_HAVE_JUST_SDL_H)
#include <SDL.h>
#else
-#include <SDL/SDL.h>
+#include <SDL2/SDL.h>
#endif
static int init_ok = 0;
-int WebpToSDL(const char* data, unsigned int data_size) {
+int WebPToSDL(const char* data, unsigned int data_size) {
int ok = 0;
VP8StatusCode status;
- WebPDecoderConfig config;
- WebPBitstreamFeatures* const input = &config.input;
- WebPDecBuffer* const output = &config.output;
- SDL_Surface* screen = NULL;
- SDL_Surface* surface = NULL;
-
- if (!WebPInitDecoderConfig(&config)) {
- fprintf(stderr, "Library version mismatch!\n");
- return 0;
- }
+ WebPBitstreamFeatures input;
+ uint8_t* output = NULL;
+ SDL_Window* window = NULL;
+ SDL_Renderer* renderer = NULL;
+ SDL_Texture* texture = NULL;
+ int width, height;
if (!init_ok) {
SDL_Init(SDL_INIT_VIDEO);
init_ok = 1;
}
- status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input);
+ status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &input);
if (status != VP8_STATUS_OK) goto Error;
+ width = input.width;
+ height = input.height;
- screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE);
- if (screen == NULL) {
- fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!\n",
- input->width, input->height);
+ SDL_CreateWindowAndRenderer(width, height, 0, &window, &renderer);
+ if (window == NULL || renderer == NULL) {
+ fprintf(stderr, "Unable to create window or renderer!\n");
goto Error;
}
-
- surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
- input->width, input->height, 32,
- 0x000000ffu, // R mask
- 0x0000ff00u, // G mask
- 0x00ff0000u, // B mask
- 0xff000000u); // A mask
-
- if (surface == NULL) {
- fprintf(stderr, "Unable to create %dx%d RGBA surface!\n",
- input->width, input->height);
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,
+ "linear"); // make the scaled rendering look smoother.
+ SDL_RenderSetLogicalSize(renderer, width, height);
+
+ texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
+ SDL_TEXTUREACCESS_STREAMING, width, height);
+ if (texture == NULL) {
+ fprintf(stderr, "Unable to create %dx%d RGBA texture!\n", width, height);
goto Error;
}
- if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- output->colorspace = MODE_BGRA;
+ output = WebPDecodeBGRA((const uint8_t*)data, (size_t)data_size, &width,
+ &height);
#else
- output->colorspace = MODE_RGBA;
+ output = WebPDecodeRGBA((const uint8_t*)data, (size_t)data_size, &width,
+ &height);
#endif
- output->width = surface->w;
- output->height = surface->h;
- output->u.RGBA.rgba = surface->pixels;
- output->u.RGBA.stride = surface->pitch;
- output->u.RGBA.size = surface->pitch * surface->h;
- output->is_external_memory = 1;
-
- status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config);
- if (status != VP8_STATUS_OK) {
+ if (output == NULL) {
fprintf(stderr, "Error decoding image (%d)\n", status);
goto Error;
}
- if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
- if (SDL_BlitSurface(surface, NULL, screen, NULL) ||
- SDL_Flip(screen)) {
- goto Error;
- }
-
+ SDL_UpdateTexture(texture, NULL, output, width * sizeof(uint32_t));
+ SDL_RenderClear(renderer);
+ SDL_RenderCopy(renderer, texture, NULL, NULL);
+ SDL_RenderPresent(renderer);
ok = 1;
Error:
- SDL_FreeSurface(surface);
- SDL_FreeSurface(screen);
- WebPFreeDecBuffer(output);
+ // We should call SDL_DestroyWindow(window) but that makes .js fail.
+ SDL_DestroyRenderer(renderer);
+ SDL_DestroyTexture(texture);
+ WebPFree(output);
return ok;
}
diff --git a/c-lib/extras/webp_to_sdl.h b/c-lib/extras/webp_to_sdl.h
@@ -14,9 +14,9 @@
#ifndef WEBP_EXTRAS_WEBP_TO_SDL_H_
#define WEBP_EXTRAS_WEBP_TO_SDL_H_
-// Exports the method WebpToSDL(const char* data, int data_size) which decodes
+// Exports the method WebPToSDL(const char* data, int data_size) which decodes
// a WebP bitstream into an RGBA SDL surface.
// Return false on failure.
-extern int WebpToSDL(const char* data, unsigned int data_size);
+extern int WebPToSDL(const char* data, unsigned int data_size);
#endif // WEBP_EXTRAS_WEBP_TO_SDL_H_
diff --git a/c-lib/imageio/Android.mk b/c-lib/imageio/Android.mk
@@ -1,3 +1,5 @@
+# Ignore this file during non-NDK builds.
+ifdef NDK_ROOT
LOCAL_PATH := $(call my-dir)
################################################################################
@@ -52,3 +54,4 @@ LOCAL_STATIC_LIBRARIES := imageio_util
LOCAL_MODULE := imageenc
include $(BUILD_STATIC_LIBRARY)
+endif # NDK_ROOT
diff --git a/c-lib/imageio/image_dec.c b/c-lib/imageio/image_dec.c
@@ -9,7 +9,30 @@
//
// Generic image-type guessing.
+#include <stddef.h>
+
#include "./image_dec.h"
+#include "./metadata.h"
+#include "webp/encode.h"
+#include "webp/types.h"
+
+const char* WebPGetEnabledInputFileFormats(void) {
+ return "WebP"
+#ifdef WEBP_HAVE_JPEG
+ ", JPEG"
+#endif
+#ifdef WEBP_HAVE_PNG
+ ", PNG"
+#endif
+ ", PNM (PGM, PPM, PAM)"
+#ifdef WEBP_HAVE_TIFF
+ ", TIFF"
+#endif
+#ifdef HAVE_WINCODEC_H
+ ", Windows Imaging Component (WIC)"
+#endif
+ "";
+}
static WEBP_INLINE uint32_t GetBE32(const uint8_t buf[]) {
return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
diff --git a/c-lib/imageio/image_dec.h b/c-lib/imageio/image_dec.h
@@ -14,6 +14,8 @@
#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_
#define WEBP_IMAGEIO_IMAGE_DEC_H_
+#include <stddef.h>
+
#include "webp/types.h"
#ifdef HAVE_CONFIG_H
@@ -41,6 +43,9 @@ typedef enum {
WEBP_UNSUPPORTED_FORMAT
} WebPInputFileFormat;
+// Returns a comma separated list of enabled input formats.
+const char* WebPGetEnabledInputFileFormats(void);
+
// Try to infer the image format. 'data_size' should be larger than 12.
// Returns WEBP_UNSUPPORTED_FORMAT if format can't be guess safely.
WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
diff --git a/c-lib/imageio/image_enc.c b/c-lib/imageio/image_enc.c
@@ -12,6 +12,7 @@
#include "./image_enc.h"
#include <assert.h>
+#include <stdio.h>
#include <string.h>
#ifdef WEBP_HAVE_PNG
@@ -34,8 +35,10 @@
#include <wincodec.h>
#endif
-#include "./imageio_util.h"
#include "../examples/unicode.h"
+#include "./imageio_util.h"
+#include "webp/decode.h"
+#include "webp/types.h"
//------------------------------------------------------------------------------
// PNG
@@ -260,14 +263,20 @@ int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) {
// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose.
int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) {
- const uint32_t width = buffer->width;
- const uint32_t height = buffer->height;
- const uint8_t* rgba = buffer->u.RGBA.rgba;
- const int stride = buffer->u.RGBA.stride;
+ uint32_t width, height;
+ uint8_t* rgba;
+ int stride;
const uint32_t bytes_per_px = 2;
uint32_t y;
- if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+ if (fout == NULL || buffer == NULL) return 0;
+
+ width = buffer->width;
+ height = buffer->height;
+ rgba = buffer->u.RGBA.rgba;
+ stride = buffer->u.RGBA.stride;
+
+ if (rgba == NULL) return 0;
fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height);
for (y = 0; y < height; ++y) {
@@ -295,22 +304,29 @@ static void PutLE32(uint8_t* const dst, uint32_t value) {
#define BMP_HEADER_SIZE 54
#define BMP_HEADER_ALPHA_EXTRA_SIZE 16 // for alpha info
int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
- const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
- const int header_size =
- BMP_HEADER_SIZE + (has_alpha ? BMP_HEADER_ALPHA_EXTRA_SIZE : 0);
- const uint32_t width = buffer->width;
- const uint32_t height = buffer->height;
- const uint8_t* rgba = buffer->u.RGBA.rgba;
- const int stride = buffer->u.RGBA.stride;
- const uint32_t bytes_per_px = has_alpha ? 4 : 3;
+ int has_alpha, header_size;
+ uint32_t width, height;
+ uint8_t* rgba;
+ int stride;
uint32_t y;
- const uint32_t line_size = bytes_per_px * width;
- const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4
- const uint32_t image_size = bmp_stride * height;
- const uint32_t total_size = image_size + header_size;
+ uint32_t bytes_per_px, line_size, image_size, bmp_stride, total_size;
uint8_t bmp_header[BMP_HEADER_SIZE + BMP_HEADER_ALPHA_EXTRA_SIZE] = { 0 };
- if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+ if (fout == NULL || buffer == NULL) return 0;
+
+ has_alpha = WebPIsAlphaMode(buffer->colorspace);
+ header_size = BMP_HEADER_SIZE + (has_alpha ? BMP_HEADER_ALPHA_EXTRA_SIZE : 0);
+ width = buffer->width;
+ height = buffer->height;
+ rgba = buffer->u.RGBA.rgba;
+ stride = buffer->u.RGBA.stride;
+ bytes_per_px = has_alpha ? 4 : 3;
+ line_size = bytes_per_px * width;
+ bmp_stride = (line_size + 3) & ~3; // pad to 4
+ image_size = bmp_stride * height;
+ total_size = image_size + header_size;
+
+ if (rgba == NULL) return 0;
// bitmap file header
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
@@ -372,17 +388,14 @@ int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
- const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
- const uint32_t width = buffer->width;
- const uint32_t height = buffer->height;
- const uint8_t* rgba = buffer->u.RGBA.rgba;
- const int stride = buffer->u.RGBA.stride;
- const uint8_t bytes_per_px = has_alpha ? 4 : 3;
- const uint8_t assoc_alpha =
- WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2;
+ int has_alpha;
+ uint32_t width, height;
+ uint8_t* rgba;
+ int stride;
+ uint8_t bytes_per_px = 0;
+ const uint8_t assoc_alpha = 0;
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
- const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
- : NUM_IFD_ENTRIES - 1;
+ const uint8_t num_ifd_entries = 0;
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
0x49, 0x49, 0x2a, 0x00, // little endian signature
8, 0, 0, 0, // offset to the unique IFD that follows
@@ -416,7 +429,20 @@ int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
};
uint32_t y;
- if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+ if (fout == NULL || buffer == NULL) return 0;
+
+ has_alpha = WebPIsAlphaMode(buffer->colorspace);
+ width = buffer->width;
+ height = buffer->height;
+ rgba = buffer->u.RGBA.rgba;
+ stride = buffer->u.RGBA.stride;
+
+ if (rgba == NULL) return 0;
+
+ // Update bytes_per_px, num_ifd_entries and assoc_alpha.
+ tiff_header[38] = tiff_header[102] = bytes_per_px = has_alpha ? 4 : 3;
+ tiff_header[8] = has_alpha ? NUM_IFD_ENTRIES : NUM_IFD_ENTRIES - 1;
+ tiff_header[186] = WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2;
// Fill placeholders in IFD:
PutLE32(tiff_header + 10 + 8, width);
diff --git a/c-lib/imageio/imageio_util.c b/c-lib/imageio/imageio_util.c
@@ -16,8 +16,11 @@
#include <fcntl.h> // for _O_BINARY
#include <io.h> // for _setmode()
#endif
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+
+#include "webp/types.h"
#include "../examples/unicode.h"
// -----------------------------------------------------------------------------
@@ -89,6 +92,11 @@ int ImgIoUtilReadFile(const char* const file_name,
}
fseek(in, 0, SEEK_END);
file_size = ftell(in);
+ if (file_size == (size_t)-1) {
+ fclose(in);
+ WFPRINTF(stderr, "error getting size of '%s'\n", (const W_CHAR*)file_name);
+ return 0;
+ }
fseek(in, 0, SEEK_SET);
// we allocate one extra byte for the \0 terminator
file_data = (uint8_t*)WebPMalloc(file_size + 1);
diff --git a/c-lib/imageio/imageio_util.h b/c-lib/imageio/imageio_util.h
@@ -14,6 +14,7 @@
#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_
#include <stdio.h>
+
#include "webp/types.h"
#ifdef __cplusplus
diff --git a/c-lib/imageio/jpegdec.c b/c-lib/imageio/jpegdec.c
@@ -24,9 +24,10 @@
#include <stdlib.h>
#include <string.h>
-#include "webp/encode.h"
#include "./imageio_util.h"
#include "./metadata.h"
+#include "webp/encode.h"
+#include "webp/types.h"
// -----------------------------------------------------------------------------
// Metadata processing
@@ -206,8 +207,18 @@ struct my_error_mgr {
static void my_error_exit(j_common_ptr dinfo) {
struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err;
+ // The following code is disabled in fuzzing mode because:
+ // - the logs can be flooded due to invalid JPEG files
+ // - msg_code is wrongfully seen as uninitialized by msan when the libjpeg
+ // dependency is not built with sanitizers enabled
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ const int msg_code = myerr->pub.msg_code;
fprintf(stderr, "libjpeg error: ");
dinfo->err->output_message(dinfo);
+ if (msg_code == JERR_INPUT_EOF || msg_code == JERR_FILE_READ) {
+ fprintf(stderr, "`jpegtran -copy all` MAY be able to process this file.\n");
+ }
+#endif
longjmp(myerr->setjmp_buffer, 1);
}
diff --git a/c-lib/imageio/jpegdec.h b/c-lib/imageio/jpegdec.h
@@ -12,6 +12,8 @@
#ifndef WEBP_IMAGEIO_JPEGDEC_H_
#define WEBP_IMAGEIO_JPEGDEC_H_
+#include <stddef.h>
+
#include "webp/types.h"
#ifdef __cplusplus
diff --git a/c-lib/imageio/metadata.h b/c-lib/imageio/metadata.h
@@ -13,6 +13,8 @@
#ifndef WEBP_IMAGEIO_METADATA_H_
#define WEBP_IMAGEIO_METADATA_H_
+#include <stddef.h>
+
#include "webp/types.h"
#ifdef __cplusplus
diff --git a/c-lib/imageio/pngdec.c b/c-lib/imageio/pngdec.c
@@ -22,13 +22,15 @@
#define PNG_USER_MEM_SUPPORTED // for png_create_read_struct_2
#endif
#include <png.h>
+
#include <setjmp.h> // note: this must be included *after* png.h
#include <stdlib.h>
#include <string.h>
-#include "webp/encode.h"
#include "./imageio_util.h"
#include "./metadata.h"
+#include "webp/encode.h"
+#include "webp/types.h"
#define LOCAL_PNG_VERSION ((PNG_LIBPNG_VER_MAJOR << 8) | PNG_LIBPNG_VER_MINOR)
#define LOCAL_PNG_PREREQ(maj, min) \
@@ -139,6 +141,8 @@ static const struct {
{ "Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp) },
// Exiftool puts exif data in APP1 chunk, too.
{ "Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif) },
+ // ImageMagick uses lowercase app1.
+ { "Raw profile type app1", ProcessRawProfile, METADATA_OFFSET(exif) },
// XMP Specification Part 3, Section 3 #PNG
{ "XML:com.adobe.xmp", MetadataCopy, METADATA_OFFSET(xmp) },
{ NULL, NULL, 0 },
@@ -159,6 +163,20 @@ static int ExtractMetadataFromPNG(png_structp png,
png_textp text = NULL;
const png_uint_32 num = png_get_text(png, info, &text, NULL);
png_uint_32 i;
+
+#ifdef PNG_eXIf_SUPPORTED
+ // Look for an 'eXIf' tag. Preference is given to this tag as it's newer
+ // than the TextualData tags.
+ {
+ png_bytep exif;
+ png_uint_32 len;
+
+ if (png_get_eXIf_1(png, info, &len, &exif) == PNG_INFO_eXIf) {
+ if (!MetadataCopy((const char*)exif, len, &metadata->exif)) return 0;
+ }
+ }
+#endif // PNG_eXIf_SUPPORTED
+
// Look for EXIF / XMP metadata.
for (i = 0; i < num; ++i, ++text) {
int j;
@@ -192,6 +210,7 @@ static int ExtractMetadataFromPNG(png_structp png,
}
}
}
+#ifdef PNG_iCCP_SUPPORTED
// Look for an ICC profile.
{
png_charp name;
@@ -208,6 +227,7 @@ static int ExtractMetadataFromPNG(png_structp png,
if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0;
}
}
+#endif // PNG_iCCP_SUPPORTED
}
return 1;
}
@@ -235,7 +255,7 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
volatile png_infop end_info = NULL;
PNGReadContext context = { NULL, 0, 0 };
int color_type, bit_depth, interlaced;
- int has_alpha;
+ int num_channels;
int num_passes;
int p;
volatile int ok = 0;
@@ -293,9 +313,6 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
}
if (png_get_valid(png, info, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(png);
- has_alpha = 1;
- } else {
- has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA);
}
// Apply gamma correction if needed.
@@ -310,13 +327,16 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
if (!keep_alpha) {
png_set_strip_alpha(png);
- has_alpha = 0;
}
num_passes = png_set_interlace_handling(png);
png_read_update_info(png, info);
- stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb);
+ num_channels = png_get_channels(png, info);
+ if (num_channels != 3 && num_channels != 4) {
+ goto Error;
+ }
+ stride = (int64_t)num_channels * width * sizeof(*rgb);
if (stride != (int)stride ||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
goto Error;
@@ -341,8 +361,8 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
pic->width = (int)width;
pic->height = (int)height;
- ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, (int)stride)
- : WebPPictureImportRGB(pic, rgb, (int)stride);
+ ok = (num_channels == 4) ? WebPPictureImportRGBA(pic, rgb, (int)stride)
+ : WebPPictureImportRGB(pic, rgb, (int)stride);
if (!ok) {
goto Error;
diff --git a/c-lib/imageio/pngdec.h b/c-lib/imageio/pngdec.h
@@ -12,6 +12,8 @@
#ifndef WEBP_IMAGEIO_PNGDEC_H_
#define WEBP_IMAGEIO_PNGDEC_H_
+#include <stddef.h>
+
#include "webp/types.h"
#ifdef __cplusplus
diff --git a/c-lib/imageio/pnmdec.c b/c-lib/imageio/pnmdec.c
@@ -17,8 +17,13 @@
#include <stdlib.h>
#include <string.h>
-#include "webp/encode.h"
#include "./imageio_util.h"
+#include "webp/encode.h"
+#include "webp/types.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
typedef enum {
WIDTH_FLAG = 1 << 0,
@@ -111,8 +116,9 @@ static size_t ReadPAMFields(PNMInfo* const info, size_t off) {
break;
} else {
static const char kEllipsis[] = " ...";
+ const size_t kLen = strlen(kEllipsis) + 1; // +1 = trailing \0
int i;
- if (out_size > 20) sprintf(out + 20 - strlen(kEllipsis), kEllipsis);
+ if (out_size > 20) snprintf(out + 20 - kLen, kLen, kEllipsis);
for (i = 0; i < (int)strlen(out); ++i) {
// isprint() might trigger a "char-subscripts" warning if given a char.
if (!isprint((int)out[i])) out[i] = ' ';
diff --git a/c-lib/imageio/pnmdec.h b/c-lib/imageio/pnmdec.h
@@ -12,6 +12,8 @@
#ifndef WEBP_IMAGEIO_PNMDEC_H_
#define WEBP_IMAGEIO_PNMDEC_H_
+#include <stddef.h>
+
#include "webp/types.h"
#ifdef __cplusplus
diff --git a/c-lib/imageio/tiffdec.c b/c-lib/imageio/tiffdec.c
@@ -22,9 +22,10 @@
#ifdef WEBP_HAVE_TIFF
#include <tiffio.h>
-#include "webp/encode.h"
#include "./imageio_util.h"
#include "./metadata.h"
+#include "webp/encode.h"
+#include "webp/types.h"
static const struct {
ttag_t tag;
@@ -188,7 +189,9 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
goto End;
}
- if (samples_per_px < 3 || samples_per_px > 4) goto End; // not supported
+ if (!(samples_per_px == 1 || samples_per_px == 3 || samples_per_px == 4)) {
+ goto End; // not supported
+ }
if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &image_width) &&
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_height))) {
@@ -212,7 +215,7 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height)) {
if ((tile_width > 32 && tile_width / 2 > image_width) ||
(tile_height > 32 && tile_height / 2 > image_height) ||
- ImgIoUtilCheckSizeArgumentsOverflow(
+ !ImgIoUtilCheckSizeArgumentsOverflow(
(uint64_t)tile_width * sizeof(*raster), tile_height)) {
fprintf(stderr, "Error! TIFF tile dimension (%d x %d) is too large.\n",
tile_width, tile_height);
diff --git a/c-lib/imageio/tiffdec.h b/c-lib/imageio/tiffdec.h
@@ -12,6 +12,8 @@
#ifndef WEBP_IMAGEIO_TIFFDEC_H_
#define WEBP_IMAGEIO_TIFFDEC_H_
+#include <stddef.h>
+
#include "webp/types.h"
#ifdef __cplusplus
diff --git a/c-lib/imageio/webpdec.c b/c-lib/imageio/webpdec.c
@@ -13,18 +13,19 @@
#include "webp/config.h"
#endif
-#include "./webpdec.h"
-
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
-#include "webp/decode.h"
-#include "webp/demux.h"
-#include "webp/encode.h"
#include "../examples/unicode.h"
#include "./imageio_util.h"
#include "./metadata.h"
+#include "./webpdec.h"
+#include "webp/decode.h"
+#include "webp/demux.h"
+#include "webp/encode.h"
+#include "webp/mux_types.h"
+#include "webp/types.h"
//------------------------------------------------------------------------------
// WebP decoding
diff --git a/c-lib/imageio/webpdec.h b/c-lib/imageio/webpdec.h
@@ -12,7 +12,10 @@
#ifndef WEBP_IMAGEIO_WEBPDEC_H_
#define WEBP_IMAGEIO_WEBPDEC_H_
+#include <stddef.h>
+
#include "webp/decode.h"
+#include "webp/types.h"
#ifdef __cplusplus
extern "C" {
diff --git a/c-lib/infra/run_static_analysis.sh b/c-lib/infra/run_static_analysis.sh
@@ -85,10 +85,10 @@ scan_build make -j4
index="$(find "${OUTPUT_DIR}" -name index.html)"
if [[ -f "${index}" ]]; then
- mv "$(dirname "${index}")/"* .
+ mv "$(dirname "${index}")/"* "${OUTPUT_DIR}"
else
# make a empty report to wipe out any old bug reports.
- cat << EOT > index.html
+ cat << EOT > "${OUTPUT_DIR}/index.html"
<html>
<body>
No bugs reported.
diff --git a/c-lib/iosbuild.sh b/c-lib/iosbuild.sh
@@ -41,6 +41,7 @@ readonly TARGETDIR="${TOPDIR}/WebP.framework"
readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.framework"
readonly MUXTARGETDIR="${TOPDIR}/WebPMux.framework"
readonly DEMUXTARGETDIR="${TOPDIR}/WebPDemux.framework"
+readonly SHARPYUVTARGETDIR="${TOPDIR}/SharpYuv.framework"
readonly DEVELOPER=$(xcode-select --print-path)
readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
readonly LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
@@ -52,7 +53,7 @@ DEMUXLIBLIST=''
if [[ -z "${SDK}" ]]; then
echo "iOS SDK not available"
exit 1
-elif [[ ${SDK%%.*} -gt 8 ]]; then
+elif [[ ${SDK%%.*} -gt 8 && "${XCODE%%.*}" -lt 16 ]]; then
EXTRA_CFLAGS="-fembed-bitcode"
elif [[ ${SDK%%.*} -le 6 ]]; then
echo "You need iOS SDK version 6.0 or above"
@@ -63,7 +64,8 @@ echo "Xcode Version: ${XCODE}"
echo "iOS SDK Version: ${SDK}"
if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \
- || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" ]]; then
+ || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" \
+ || -e "${SHARPYUVTARGETDIR}" ]]; then
cat << EOF
WARNING: The following directories will be deleted:
WARNING: ${BUILDDIR}
@@ -71,14 +73,16 @@ WARNING: ${TARGETDIR}
WARNING: ${DECTARGETDIR}
WARNING: ${MUXTARGETDIR}
WARNING: ${DEMUXTARGETDIR}
+WARNING: ${SHARPYUVTARGETDIR}
WARNING: The build will continue in 5 seconds...
EOF
sleep 5
fi
rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} \
- ${MUXTARGETDIR} ${DEMUXTARGETDIR}
+ ${MUXTARGETDIR} ${DEMUXTARGETDIR} ${SHARPYUVTARGETDIR}
mkdir -p ${BUILDDIR} ${TARGETDIR}/Headers/ ${DECTARGETDIR}/Headers/ \
- ${MUXTARGETDIR}/Headers/ ${DEMUXTARGETDIR}/Headers/
+ ${MUXTARGETDIR}/Headers/ ${DEMUXTARGETDIR}/Headers/ \
+ ${SHARPYUVTARGETDIR}/Headers/
if [[ ! -e ${SRCDIR}/configure ]]; then
if ! (cd ${SRCDIR} && sh autogen.sh); then
@@ -133,18 +137,17 @@ for PLATFORM in ${PLATFORMS}; do
CFLAGS="${CFLAGS}"
set +x
- # run make only in the src/ directory to create libwebp.a/libwebpdecoder.a
- cd src/
- make V=0
- make install
+ # Build only the libraries, skip the examples.
+ make V=0 -C sharpyuv install
+ make V=0 -C src install
LIBLIST+=" ${ROOTDIR}/lib/libwebp.a"
DECLIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
MUXLIBLIST+=" ${ROOTDIR}/lib/libwebpmux.a"
DEMUXLIBLIST+=" ${ROOTDIR}/lib/libwebpdemux.a"
+ SHARPYUVLIBLIST+=" ${ROOTDIR}/lib/libsharpyuv.a"
make clean
- cd ..
export PATH=${OLDPATH}
done
@@ -167,4 +170,9 @@ cp -a ${SRCDIR}/src/webp/{decode,types,mux_types,demux}.h \
${DEMUXTARGETDIR}/Headers/
${LIPO} -create ${DEMUXLIBLIST} -output ${DEMUXTARGETDIR}/WebPDemux
+echo "SHARPYUVLIBLIST = ${SHARPYUVLIBLIST}"
+cp -a ${SRCDIR}/sharpyuv/{sharpyuv,sharpyuv_csp}.h \
+ ${SHARPYUVTARGETDIR}/Headers/
+${LIPO} -create ${SHARPYUVLIBLIST} -output ${SHARPYUVTARGETDIR}/SharpYuv
+
echo "SUCCESS"
diff --git a/c-lib/makefile.unix b/c-lib/makefile.unix
@@ -3,7 +3,8 @@
# It will not install the libraries system-wide, but just create the 'cwebp'
# and 'dwebp' tools in the examples/ directory, along with the static
# libraries 'src/libwebp.a', 'src/libwebpdecoder.a', 'src/mux/libwebpmux.a',
-# 'src/demux/libwebpdemux.a' and 'extras/libwebpextras.a'.
+# 'src/demux/libwebpdemux.a', 'extras/libwebpextras.a' and
+# 'sharpyuv/libsharpyuv.a'.
#
# To build the library and examples, use:
# make -f makefile.unix
@@ -36,13 +37,13 @@ else
endif
# SDL flags: use sdl-config if it exists
-SDL_CONFIG = $(shell sdl-config --version 2> /dev/null)
+SDL_CONFIG = $(shell sdl2-config --version 2> /dev/null)
ifneq ($(SDL_CONFIG),)
- SDL_LIBS = $(shell sdl-config --libs)
- SDL_FLAGS = $(shell sdl-config --cflags)
+ SDL_LIBS = $(shell sdl2-config --libs)
+ SDL_FLAGS = $(shell sdl2-config --cflags)
else
# use best-guess
- SDL_LIBS = -lSDL
+ SDL_LIBS = -lSDL2
SDL_FLAGS =
endif
@@ -125,6 +126,15 @@ endif
ANIM_UTIL_OBJS = \
examples/anim_util.o \
+SHARPYUV_OBJS = \
+ sharpyuv/sharpyuv.o \
+ sharpyuv/sharpyuv_cpu.o \
+ sharpyuv/sharpyuv_csp.o \
+ sharpyuv/sharpyuv_dsp.o \
+ sharpyuv/sharpyuv_gamma.o \
+ sharpyuv/sharpyuv_neon.o \
+ sharpyuv/sharpyuv_sse2.o \
+
DEC_OBJS = \
src/dec/alpha_dec.o \
src/dec/buffer_dec.o \
@@ -266,6 +276,7 @@ UTILS_DEC_OBJS = \
src/utils/color_cache_utils.o \
src/utils/filters_utils.o \
src/utils/huffman_utils.o \
+ src/utils/palette.o \
src/utils/quant_levels_dec_utils.o \
src/utils/random_utils.o \
src/utils/rescaler_utils.o \
@@ -280,13 +291,15 @@ UTILS_ENC_OBJS = \
EXTRA_OBJS = \
extras/extras.o \
extras/quality_estimate.o \
+ extras/sharpyuv_risk_table.o \
LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
-LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
- $(UTILS_ENC_OBJS)
+LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) \
+ $(DSP_ENC_OBJS) $(UTILS_ENC_OBJS)
LIBWEBPMUX_OBJS = $(MUX_OBJS)
LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS)
LIBWEBPEXTRA_OBJS = $(EXTRA_OBJS)
+LIBSHARPYUV_OBJS = $(SHARPYUV_OBJS)
HDRS_INSTALLED = \
src/webp/decode.h \
@@ -296,6 +309,11 @@ HDRS_INSTALLED = \
src/webp/mux_types.h \
src/webp/types.h \
+SHARPYUV_HDRS_INSTALLED = \
+ sharpyuv/sharpyuv.h \
+ sharpyuv/sharpyuv_cpu.h \
+ sharpyuv/sharpyuv_csp.h \
+
HDRS = \
src/dec/alphai_dec.h \
src/dec/common_dec.h \
@@ -304,6 +322,7 @@ HDRS = \
src/dec/vp8li_dec.h \
src/dec/webpi_dec.h \
src/dsp/common_sse2.h \
+ src/dsp/cpu.h \
src/dsp/dsp.h \
src/dsp/lossless.h \
src/dsp/lossless_common.h \
@@ -326,6 +345,7 @@ HDRS = \
src/utils/filters_utils.h \
src/utils/huffman_utils.h \
src/utils/huffman_encode_utils.h \
+ src/utils/palette.h \
src/utils/quant_levels_utils.h \
src/utils/quant_levels_dec_utils.h \
src/utils/random_utils.h \
@@ -334,6 +354,7 @@ HDRS = \
src/utils/utils.h \
src/webp/format_constants.h \
$(HDRS_INSTALLED) \
+ $(SHARPYUV_HDRS_INSTALLED) \
OUT_LIBS = examples/libexample_util.a
OUT_LIBS += imageio/libimageio_util.a
@@ -341,6 +362,7 @@ OUT_LIBS += imageio/libimagedec.a
OUT_LIBS += imageio/libimageenc.a
OUT_LIBS += src/libwebpdecoder.a
OUT_LIBS += src/libwebp.a
+OUT_LIBS += sharpyuv/libsharpyuv.a
EXTRA_LIB = extras/libwebpextras.a
OUT_EXAMPLES = examples/cwebp examples/dwebp
EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux \
@@ -386,6 +408,7 @@ src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS)
src/libwebp.a: $(LIBWEBP_OBJS)
src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS)
src/demux/libwebpdemux.a: $(LIBWEBPDEMUX_OBJS)
+sharpyuv/libsharpyuv.a: $(LIBSHARPYUV_OBJS)
%.a:
$(AR) $(ARFLAGS) $@ $^
@@ -403,6 +426,7 @@ examples/webpinfo: examples/webpinfo.o
examples/anim_diff: examples/libanim_util.a examples/libgifdec.a
examples/anim_diff: src/demux/libwebpdemux.a examples/libexample_util.a
examples/anim_diff: imageio/libimageio_util.a src/libwebp.a
+examples/anim_diff: sharpyuv/libsharpyuv.a
examples/anim_diff: override EXTRA_LIBS += $(GIF_LIBS)
examples/anim_diff: EXTRA_FLAGS += -DWEBP_HAVE_GIF
examples/anim_dump: examples/libanim_util.a examples/libgifdec.a
@@ -411,12 +435,14 @@ examples/anim_dump: examples/libexample_util.a
examples/anim_dump: imageio/libimageio_util.a
examples/anim_dump: imageio/libimageenc.a
examples/anim_dump: src/libwebp.a
+examples/anim_dump: sharpyuv/libsharpyuv.a
examples/anim_dump: override EXTRA_LIBS += $(GIF_LIBS) $(DWEBP_LIBS)
examples/cwebp: examples/libexample_util.a
examples/cwebp: imageio/libimagedec.a
examples/cwebp: src/demux/libwebpdemux.a
examples/cwebp: imageio/libimageio_util.a
examples/cwebp: src/libwebp.a
+examples/cwebp: sharpyuv/libsharpyuv.a
examples/cwebp: override EXTRA_LIBS += $(CWEBP_LIBS)
examples/dwebp: examples/libexample_util.a
examples/dwebp: imageio/libimagedec.a
@@ -424,13 +450,16 @@ examples/dwebp: src/demux/libwebpdemux.a
examples/dwebp: imageio/libimageenc.a
examples/dwebp: imageio/libimageio_util.a
examples/dwebp: src/libwebp.a
+examples/dwebp: sharpyuv/libsharpyuv.a
examples/dwebp: override EXTRA_LIBS += $(DWEBP_LIBS)
examples/gif2webp: examples/libexample_util.a imageio/libimageio_util.a
examples/gif2webp: examples/libgifdec.a src/mux/libwebpmux.a src/libwebp.a
+examples/gif2webp: sharpyuv/libsharpyuv.a
examples/gif2webp: override EXTRA_LIBS += $(GIF_LIBS)
examples/gif2webp: EXTRA_FLAGS += -DWEBP_HAVE_GIF
examples/vwebp: examples/libexample_util.a src/demux/libwebpdemux.a
examples/vwebp: imageio/libimageio_util.a src/libwebp.a
+examples/vwebp: sharpyuv/libsharpyuv.a
examples/vwebp: override EXTRA_LIBS += $(GL_LIBS)
examples/vwebp: EXTRA_FLAGS += -DWEBP_HAVE_GL
examples/webpmux: examples/libexample_util.a imageio/libimageio_util.a
@@ -438,7 +467,9 @@ examples/webpmux: src/mux/libwebpmux.a src/libwebpdecoder.a
examples/img2webp: examples/libexample_util.a imageio/libimageio_util.a
examples/img2webp: imageio/libimagedec.a
examples/img2webp: src/demux/libwebpdemux.a
-examples/img2webp: src/mux/libwebpmux.a src/libwebp.a
+examples/img2webp: src/mux/libwebpmux.a
+examples/img2webp: src/libwebp.a
+examples/img2webp: sharpyuv/libsharpyuv.a
examples/img2webp: override EXTRA_LIBS += $(CWEBP_LIBS)
examples/webpinfo: examples/libexample_util.a imageio/libimageio_util.a
examples/webpinfo: src/libwebpdecoder.a
@@ -448,16 +479,19 @@ extras/get_disto: imageio/libimagedec.a
extras/get_disto: src/demux/libwebpdemux.a
extras/get_disto: imageio/libimageio_util.a
extras/get_disto: src/libwebp.a
+extras/get_disto: sharpyuv/libsharpyuv.a
extras/get_disto: override EXTRA_LIBS += $(CWEBP_LIBS)
extras/webp_quality: extras/webp_quality.o
extras/webp_quality: imageio/libimageio_util.a
extras/webp_quality: $(EXTRA_LIB) src/libwebp.a
+extras/webp_quality: sharpyuv/libsharpyuv.a
extras/vwebp_sdl: extras/vwebp_sdl.o
extras/vwebp_sdl: extras/webp_to_sdl.o
extras/vwebp_sdl: imageio/libimageio_util.a
extras/vwebp_sdl: src/libwebp.a
+extras/vwebp_sdl: sharpyuv/libsharpyuv.a
extras/vwebp_sdl: EXTRA_FLAGS += -DWEBP_HAVE_SDL $(SDL_FLAGS)
extras/vwebp_sdl: override EXTRA_LIBS += $(SDL_LIBS)
@@ -468,12 +502,15 @@ dist: DESTDIR := dist
dist: OUT_EXAMPLES += $(EXTRA_EXAMPLES)
dist: all
$(INSTALL) -m755 -d $(DESTDIR)/include/webp \
- $(DESTDIR)/bin $(DESTDIR)/doc $(DESTDIR)/lib
+ $(DESTDIR)/include/webp/sharpyuv \
+ $(DESTDIR)/bin $(DESTDIR)/doc $(DESTDIR)/lib
$(INSTALL) -m755 -s $(OUT_EXAMPLES) $(DESTDIR)/bin
$(INSTALL) -m644 $(HDRS_INSTALLED) $(DESTDIR)/include/webp
+ $(INSTALL) -m644 $(SHARPYUV_HDRS_INSTALLED) $(DESTDIR)/include/webp/sharpyuv
$(INSTALL) -m644 src/libwebp.a $(DESTDIR)/lib
$(INSTALL) -m644 src/demux/libwebpdemux.a $(DESTDIR)/lib
$(INSTALL) -m644 src/mux/libwebpmux.a $(DESTDIR)/lib
+ $(INSTALL) -m644 sharpyuv/libsharpyuv.a $(DESTDIR)/lib
umask 022; \
for m in man/[cdv]webp.1 man/gif2webp.1 man/webpmux.1 \
man/img2webp.1 man/webpinfo.1; do \
@@ -489,6 +526,7 @@ clean:
examples/*.o examples/*~ \
extras/*.o extras/*~ \
imageio/*.o imageio/*~ \
+ sharpyuv/*.o sharpyuv/*~ \
src/dec/*.o src/dec/*~ \
src/demux/*.o src/demux/*~ \
src/dsp/*.o src/dsp/*~ \
diff --git a/c-lib/man/cwebp.1 b/c-lib/man/cwebp.1
@@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*-
-.TH CWEBP 1 "November 17, 2021"
+.TH CWEBP 1 "April 10, 2025"
.SH NAME
cwebp \- compress an image file to a WebP file
.SH SYNOPSIS
@@ -90,15 +90,25 @@ additional encoding possibilities and decide on the quality gain.
Lower value can result in faster processing time at the expense of
larger file size and lower compression quality.
.TP
-.BI \-resize " width height
-Resize the source to a rectangle with size \fBwidth\fP x \fBheight\fP.
-If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
-the value will be calculated preserving the aspect\-ratio.
-.TP
.BI \-crop " x_position y_position width height
Crop the source to a rectangle with top\-left corner at coordinates
(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
This cropping area must be fully contained within the source rectangle.
+Note: the cropping is applied \fIbefore\fP any scaling.
+.TP
+.BI \-resize " width height
+Resize the source to a rectangle with size \fBwidth\fP x \fBheight\fP.
+If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
+the value will be calculated preserving the aspect\-ratio. Note: scaling
+is applied \fIafter\fP cropping.
+.TP
+.BI \-resize_mode " string
+Specify the behavior of the \fB\-resize\fP option. Possible values are:
+\fBdown_only\fP, \fBup_only\fP, \fBalways\fP (default). \fBdown_only\fP will
+use the values specified by \fB\-resize\fP if \fIeither\fP the input width or
+height are larger than the given dimensions. Similarly, \fBup_only\fP will only
+resize if \fIeither\fP the input width or height are smaller than the given
+dimensions.
.TP
.B \-mt
Use multi\-threading for encoding, if possible.
@@ -133,7 +143,9 @@ are used, \fB\-size\fP value will prevail.
Set a maximum number of passes to use during the dichotomy used by
options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10, default is 1.
If options \fB\-size\fP or \fB\-psnr\fP were used, but \fB\-pass\fP wasn't
-specified, a default value of '6' passes will be used.
+specified, a default value of '6' passes will be used. If \fB\-pass\fP is
+specified, but neither \fB-size\fP nor \fB-psnr\fP are, a target PSNR of 40dB
+will be used.
.TP
.BI \-qrange " int int
Specifies the permissible interval for the quality factor. This is particularly
@@ -176,8 +188,8 @@ Disable strong filtering (if filtering is being used thanks to the
\fB\-f\fP option) and use simple filtering instead.
.TP
.B \-sharp_yuv
-Use more accurate and sharper RGB->YUV conversion if needed. Note that this
-process is slower than the default 'fast' RGB->YUV conversion.
+Use more accurate and sharper RGB->YUV conversion. Note that this process is
+slower than the default 'fast' RGB->YUV conversion.
.TP
.BI \-sns " int
Specify the amplitude of the spatial noise shaping. Spatial noise shaping
@@ -200,7 +212,8 @@ In the VP8 format, the so\-called control partition has a limit of 512k and
is used to store the following information: whether the macroblock is skipped,
which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16
mode, and finally the prediction modes to use for each of the sub\-blocks.
-For a very large image, 512k only leaves room to few bits per 16x16 macroblock.
+For a very large image, 512k only leaves room for a few bits per 16x16
+macroblock.
The absolute minimum is 4 bits per macroblock. Skip, segment, and mode
information can use up almost all these 4 bits (although the case is unlikely),
which is problematic for very large images. The partition_limit factor controls
@@ -209,7 +222,8 @@ useful in case the 512k limit is reached and the following message is displayed:
\fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP.
If using \fB\-partition_limit\fP is not enough to meet the 512k constraint, one
should use less segments in order to save more header bits per macroblock.
-See the \fB\-segments\fP option.
+See the \fB\-segments\fP option. Note the \fB-m\fP and \fB-q\fP options also
+influence the encoder's decisions and ability to hit this limit.
.SS LOGGING OPTIONS
These options control the level of output:
@@ -293,12 +307,12 @@ Note: each input format may not support all combinations.
.B \-noasm
Disable all assembly optimizations.
-.SH BUGS
-Please report all bugs to the issue tracker:
-https://bugs.chromium.org/p/webp
-.br
-Patches welcome! See this page to get started:
-https://www.webmproject.org/code/contribute/submitting\-patches/
+.SH EXIT STATUS
+If there were no problems during execution, \fBcwebp\fP exits with the value of
+the C constant \fBEXIT_SUCCESS\fP. This is usually zero.
+.PP
+If an error occurs, \fBcwebp\fP exits with the value of the C constant
+\fBEXIT_FAILURE\fP. This is usually one.
.SH EXAMPLES
cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
@@ -318,6 +332,13 @@ https://chromium.googlesource.com/webm/libwebp
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
for the Debian project (and may be used by others).
+.SH REPORTING BUGS
+Please report all bugs to the issue tracker:
+https://issues.webmproject.org
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
.SH SEE ALSO
.BR dwebp (1),
.BR gif2webp (1)
diff --git a/c-lib/man/dwebp.1 b/c-lib/man/dwebp.1
@@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*-
-.TH DWEBP 1 "November 17, 2021"
+.TH DWEBP 1 "July 18, 2024"
.SH NAME
dwebp \- decompress a WebP file to an image file
.SH SYNOPSIS
@@ -108,12 +108,12 @@ Print extra information (decoding time in particular).
.B \-noasm
Disable all assembly optimizations.
-.SH BUGS
-Please report all bugs to the issue tracker:
-https://bugs.chromium.org/p/webp
-.br
-Patches welcome! See this page to get started:
-https://www.webmproject.org/code/contribute/submitting\-patches/
+.SH EXIT STATUS
+If there were no problems during execution, \fBdwebp\fP exits with the value of
+the C constant \fBEXIT_SUCCESS\fP. This is usually zero.
+.PP
+If an error occurs, \fBdwebp\fP exits with the value of the C constant
+\fBEXIT_FAILURE\fP. This is usually one.
.SH EXAMPLES
dwebp picture.webp \-o output.png
@@ -133,6 +133,13 @@ https://chromium.googlesource.com/webm/libwebp
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
for the Debian project (and may be used by others).
+.SH REPORTING BUGS
+Please report all bugs to the issue tracker:
+https://issues.webmproject.org
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
.SH SEE ALSO
.BR cwebp (1),
.BR gif2webp (1),
diff --git a/c-lib/man/gif2webp.1 b/c-lib/man/gif2webp.1
@@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*-
-.TH GIF2WEBP 1 "November 17, 2021"
+.TH GIF2WEBP 1 "November 4, 2024"
.SH NAME
gif2webp \- Convert a GIF image to WebP
.SH SYNOPSIS
@@ -39,6 +39,18 @@ Encode the image using lossy compression.
Mixed compression mode: optimize compression of the image by picking either
lossy or lossless compression for each frame heuristically.
.TP
+.BI \-near_lossless " int
+Specify the level of near\-lossless image preprocessing. This option adjusts
+pixel values to help compressibility, but has minimal impact on the visual
+quality. It triggers lossless compression mode automatically. The range is 0
+(maximum preprocessing) to 100 (no preprocessing, the default). The typical
+value is around 60. Note that lossy with \fB\-q 100\fP can at times yield
+better results.
+.TP
+.B \-sharp_yuv
+Use more accurate and sharper RGB->YUV conversion. Note that this process is
+slower than the default 'fast' RGB->YUV conversion.
+.TP
.BI \-q " float
Specify the compression factor for RGB channels between 0 and 100. The default
is 75.
@@ -126,12 +138,12 @@ Print extra information.
.B \-quiet
Do not print anything.
-.SH BUGS
-Please report all bugs to the issue tracker:
-https://bugs.chromium.org/p/webp
-.br
-Patches welcome! See this page to get started:
-https://www.webmproject.org/code/contribute/submitting\-patches/
+.SH EXIT STATUS
+If there were no problems during execution, \fBgif2webp\fP exits with the value
+of the C constant \fBEXIT_SUCCESS\fP. This is usually zero.
+.PP
+If an error occurs, \fBgif2webp\fP exits with the value of the C constant
+\fBEXIT_FAILURE\fP. This is usually one.
.SH EXAMPLES
gif2webp picture.gif \-o picture.webp
@@ -155,6 +167,13 @@ https://chromium.googlesource.com/webm/libwebp
This manual page was written by Urvang Joshi <urvang@google.com>, for the
Debian project (and may be used by others).
+.SH REPORTING BUGS
+Please report all bugs to the issue tracker:
+https://issues.webmproject.org
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
.SH SEE ALSO
.BR cwebp (1),
.BR dwebp (1),
diff --git a/c-lib/man/img2webp.1 b/c-lib/man/img2webp.1
@@ -1,10 +1,10 @@
.\" Hey, EMACS: -*- nroff -*-
-.TH IMG2WEBP 1 "January 5, 2022"
+.TH IMG2WEBP 1 "November 26, 2024"
.SH NAME
img2webp \- create animated WebP file from a sequence of input images.
.SH SYNOPSIS
.B img2webp
-[file_options] [[frame_options] frame_file]...
+[file_options] [[frame_options] frame_file]... [\-o webp_file]
.br
.B img2webp argument_file_name
.br
@@ -44,6 +44,18 @@ Mixed compression mode: optimize compression of the image by picking either
lossy or lossless compression for each frame heuristically. This global
option disables the local option \fB-lossy\fP and \fB-lossless\fP .
.TP
+.BI \-near_lossless " int
+Specify the level of near\-lossless image preprocessing. This option adjusts
+pixel values to help compressibility, but has minimal impact on the visual
+quality. It triggers lossless compression mode automatically. The range is 0
+(maximum preprocessing) to 100 (no preprocessing, the default). The typical
+value is around 60. Note that lossy with \fB\-q 100\fP can at times yield
+better results.
+.TP
+.B \-sharp_yuv
+Use more accurate and sharper RGB->YUV conversion. Note that this process is
+slower than the default 'fast' RGB->YUV conversion.
+.TP
.BI \-loop " int
Specifies the number of times the animation should loop. Using '0'
means 'loop indefinitely'.
@@ -76,18 +88,27 @@ Specify the compression factor between 0 and 100. The default is 75.
Specify the compression method to use. This parameter controls the
trade off between encoding speed and the compressed file size and quality.
Possible values range from 0 to 6. Default value is 4.
+When higher values are used, the encoder will spend more time inspecting
+additional encoding possibilities and decide on the quality gain.
+Lower value can result in faster processing time at the expense of
+larger file size and lower compression quality.
+.TP
+.B \-exact, \-noexact
+Preserve or alter RGB values in transparent area. The default is
+\fB-noexact\fP, to help compressibility. Note \fB\-noexact\fP may cause
+artifacts in frames compressed with \fB\-lossy\fP.
+
+.SH EXIT STATUS
+If there were no problems during execution, \fBimg2webp\fP exits with the value
+of the C constant \fBEXIT_SUCCESS\fP. This is usually zero.
+.PP
+If an error occurs, \fBimg2webp\fP exits with the value of the C constant
+\fBEXIT_FAILURE\fP. This is usually one.
.SH EXAMPLE
img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp
.br
-.SH BUGS
-Please report all bugs to the issue tracker:
-https://bugs.chromium.org/p/webp
-.br
-Patches welcome! See this page to get started:
-https://www.webmproject.org/code/contribute/submitting\-patches/
-
.SH AUTHORS
\fBimg2webp\fP is a part of libwebp and was written by the WebP team.
.br
@@ -97,6 +118,13 @@ https://chromium.googlesource.com/webm/libwebp
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
for the Debian project (and may be used by others).
+.SH REPORTING BUGS
+Please report all bugs to the issue tracker:
+https://issues.webmproject.org
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
.SH SEE ALSO
.BR webpmux (1),
.BR gif2webp (1)
diff --git a/c-lib/man/vwebp.1 b/c-lib/man/vwebp.1
@@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*-
-.TH VWEBP 1 "November 17, 2021"
+.TH VWEBP 1 "July 18, 2024"
.SH NAME
vwebp \- decompress a WebP file and display it in a window
.SH SYNOPSIS
@@ -72,12 +72,12 @@ Disable blending and disposal process, for debugging purposes.
.B 'q' / 'Q' / ESC
Quit.
-.SH BUGS
-Please report all bugs to the issue tracker:
-https://bugs.chromium.org/p/webp
-.br
-Patches welcome! See this page to get started:
-https://www.webmproject.org/code/contribute/submitting\-patches/
+.SH EXIT STATUS
+If there were no problems during execution, \fBvwebp\fP exits with the value of
+the C constant \fBEXIT_SUCCESS\fP. This is usually zero.
+.PP
+If an error occurs, \fBvwebp\fP exits with the value of the C constant
+\fBEXIT_FAILURE\fP. This is usually one.
.SH EXAMPLES
vwebp picture.webp
@@ -94,6 +94,13 @@ https://chromium.googlesource.com/webm/libwebp
.PP
This manual page was written for the Debian project (and may be used by others).
+.SH REPORTING BUGS
+Please report all bugs to the issue tracker:
+https://issues.webmproject.org
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
.SH SEE ALSO
.BR dwebp (1)
.br
diff --git a/c-lib/man/webpinfo.1 b/c-lib/man/webpinfo.1
@@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*-
-.TH WEBPINFO 1 "November 17, 2021"
+.TH WEBPINFO 1 "July 18, 2024"
.SH NAME
webpinfo \- print out the chunk level structure of WebP files
along with basic integrity checks.
@@ -47,12 +47,12 @@ Detailed usage instructions.
Input files in WebP format. Input files must come last, following
options (if any). There can be multiple input files.
-.SH BUGS
-Please report all bugs to the issue tracker:
-https://bugs.chromium.org/p/webp
-.br
-Patches welcome! See this page to get started:
-https://www.webmproject.org/code/contribute/submitting\-patches/
+.SH EXIT STATUS
+If there were no problems during execution, \fBwebpinfo\fP exits with the value
+of the C constant \fBEXIT_SUCCESS\fP. This is usually zero.
+.PP
+If an error occurs, \fBwebpinfo\fP exits with the value of the C constant
+\fBEXIT_FAILURE\fP. This is usually one.
.SH EXAMPLES
.br
@@ -73,6 +73,13 @@ https://chromium.googlesource.com/webm/libwebp
This manual page was written by Hui Su <huisu@google.com>,
for the Debian project (and may be used by others).
+.SH REPORTING BUGS
+Please report all bugs to the issue tracker:
+https://issues.webmproject.org
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
.SH SEE ALSO
.BR webpmux (1)
.br
diff --git a/c-lib/man/webpmux.1 b/c-lib/man/webpmux.1
@@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*-
-.TH WEBPMUX 1 "November 17, 2021"
+.TH WEBPMUX 1 "July 18, 2024"
.SH NAME
webpmux \- create animated WebP files from non\-animated WebP images, extract
frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
@@ -186,12 +186,12 @@ Output file in WebP format.
.TP
The nature of EXIF, XMP and ICC data is not checked and is assumed to be valid.
-.SH BUGS
-Please report all bugs to the issue tracker:
-https://bugs.chromium.org/p/webp
-.br
-Patches welcome! See this page to get started:
-https://www.webmproject.org/code/contribute/submitting\-patches/
+.SH EXIT STATUS
+If there were no problems during execution, \fBwebpmux\fP exits with the value
+of the C constant \fBEXIT_SUCCESS\fP. This is usually zero.
+.PP
+If an error occurs, \fBwebpmux\fP exits with the value of the C constant
+\fBEXIT_FAILURE\fP. This is usually one.
.SH EXAMPLES
.P
@@ -262,6 +262,13 @@ https://chromium.googlesource.com/webm/libwebp
This manual page was written by Vikas Arora <vikaas.arora@gmail.com>,
for the Debian project (and may be used by others).
+.SH REPORTING BUGS
+Please report all bugs to the issue tracker:
+https://issues.webmproject.org
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
.SH SEE ALSO
.BR cwebp (1),
.BR dwebp (1),
diff --git a/c-lib/sharpyuv/Makefile.am b/c-lib/sharpyuv/Makefile.am
@@ -0,0 +1,41 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
+
+lib_LTLIBRARIES = libsharpyuv.la
+
+noinst_LTLIBRARIES =
+noinst_LTLIBRARIES += libsharpyuv_sse2.la
+noinst_LTLIBRARIES += libsharpyuv_neon.la
+
+libsharpyuvinclude_HEADERS =
+libsharpyuvinclude_HEADERS += sharpyuv.h
+libsharpyuvinclude_HEADERS += sharpyuv_csp.h
+noinst_HEADERS =
+noinst_HEADERS += ../src/dsp/cpu.c
+noinst_HEADERS += ../src/dsp/cpu.h
+noinst_HEADERS += ../src/webp/types.h
+
+libsharpyuv_sse2_la_SOURCES =
+libsharpyuv_sse2_la_SOURCES += sharpyuv_sse2.c
+libsharpyuv_sse2_la_CPPFLAGS = $(libsharpyuv_la_CPPFLAGS)
+libsharpyuv_sse2_la_CFLAGS = $(AM_CFLAGS) $(SSE2_FLAGS)
+
+libsharpyuv_neon_la_SOURCES =
+libsharpyuv_neon_la_SOURCES += sharpyuv_neon.c
+libsharpyuv_neon_la_CPPFLAGS = $(libsharpyuv_la_CPPFLAGS)
+libsharpyuv_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS)
+
+libsharpyuv_la_SOURCES =
+libsharpyuv_la_SOURCES += sharpyuv_cpu.c sharpyuv_cpu.h
+libsharpyuv_la_SOURCES += sharpyuv_csp.c sharpyuv_csp.h
+libsharpyuv_la_SOURCES += sharpyuv_dsp.c sharpyuv_dsp.h
+libsharpyuv_la_SOURCES += sharpyuv_gamma.c sharpyuv_gamma.h
+libsharpyuv_la_SOURCES += sharpyuv.c sharpyuv.h
+
+libsharpyuv_la_CPPFLAGS = $(AM_CPPFLAGS)
+libsharpyuv_la_LDFLAGS = -no-undefined -version-info 1:2:1 -lm
+libsharpyuv_la_LIBADD =
+libsharpyuv_la_LIBADD += libsharpyuv_sse2.la
+libsharpyuv_la_LIBADD += libsharpyuv_neon.la
+libsharpyuvincludedir = $(includedir)/webp/sharpyuv
+pkgconfig_DATA = libsharpyuv.pc
diff --git a/c-lib/sharpyuv/libsharpyuv.pc.in b/c-lib/sharpyuv/libsharpyuv.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/webp
+
+Name: libsharpyuv
+Description: Library for sharp RGB to YUV conversion
+Version: @PACKAGE_VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -l@webp_libname_prefix@sharpyuv
+Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
diff --git a/c-lib/sharpyuv/libsharpyuv.rc b/c-lib/sharpyuv/libsharpyuv.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,4,2
+ PRODUCTVERSION 0,0,4,2
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Google, Inc."
+ VALUE "FileDescription", "libsharpyuv DLL"
+ VALUE "FileVersion", "0.4.2"
+ VALUE "InternalName", "libsharpyuv.dll"
+ VALUE "LegalCopyright", "Copyright (C) 2025"
+ VALUE "OriginalFilename", "libsharpyuv.dll"
+ VALUE "ProductName", "SharpYuv Library"
+ VALUE "ProductVersion", "0.4.2"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
diff --git a/c-lib/sharpyuv/sharpyuv.c b/c-lib/sharpyuv/sharpyuv.c
@@ -0,0 +1,575 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Sharp RGB to YUV conversion.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "sharpyuv/sharpyuv.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sharpyuv/sharpyuv_cpu.h"
+#include "sharpyuv/sharpyuv_dsp.h"
+#include "sharpyuv/sharpyuv_gamma.h"
+#include "src/webp/types.h"
+
+//------------------------------------------------------------------------------
+
+int SharpYuvGetVersion(void) {
+ return SHARPYUV_VERSION;
+}
+
+//------------------------------------------------------------------------------
+// Sharp RGB->YUV conversion
+
+static const int kNumIterations = 4;
+
+#define YUV_FIX 16 // fixed-point precision for RGB->YUV
+static const int kYuvHalf = 1 << (YUV_FIX - 1);
+
+// Max bit depth so that intermediate calculations fit in 16 bits.
+static const int kMaxBitDepth = 14;
+
+// Returns the precision shift to use based on the input rgb_bit_depth.
+static int GetPrecisionShift(int rgb_bit_depth) {
+ // Try to add 2 bits of precision if it fits in kMaxBitDepth. Otherwise remove
+ // bits if needed.
+ return ((rgb_bit_depth + 2) <= kMaxBitDepth) ? 2
+ : (kMaxBitDepth - rgb_bit_depth);
+}
+
+typedef int16_t fixed_t; // signed type with extra precision for UV
+typedef uint16_t fixed_y_t; // unsigned type with extra precision for W
+
+//------------------------------------------------------------------------------
+
+static uint8_t clip_8b(fixed_t v) {
+ return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
+}
+
+static uint16_t clip(fixed_t v, int max) {
+ return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
+}
+
+static fixed_y_t clip_bit_depth(int y, int bit_depth) {
+ const int max = (1 << bit_depth) - 1;
+ return (!(y & ~max)) ? (fixed_y_t)y : (y < 0) ? 0 : max;
+}
+
+//------------------------------------------------------------------------------
+
+static int RGBToGray(int64_t r, int64_t g, int64_t b) {
+ const int64_t luma = 13933 * r + 46871 * g + 4732 * b + kYuvHalf;
+ return (int)(luma >> YUV_FIX);
+}
+
+static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
+ int rgb_bit_depth,
+ SharpYuvTransferFunctionType transfer_type) {
+ const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
+ const uint32_t A = SharpYuvGammaToLinear(a, bit_depth, transfer_type);
+ const uint32_t B = SharpYuvGammaToLinear(b, bit_depth, transfer_type);
+ const uint32_t C = SharpYuvGammaToLinear(c, bit_depth, transfer_type);
+ const uint32_t D = SharpYuvGammaToLinear(d, bit_depth, transfer_type);
+ return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth,
+ transfer_type);
+}
+
+static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
+ int rgb_bit_depth,
+ SharpYuvTransferFunctionType transfer_type) {
+ const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
+ int i = 0;
+ do {
+ const uint32_t R =
+ SharpYuvGammaToLinear(src[0 * w + i], bit_depth, transfer_type);
+ const uint32_t G =
+ SharpYuvGammaToLinear(src[1 * w + i], bit_depth, transfer_type);
+ const uint32_t B =
+ SharpYuvGammaToLinear(src[2 * w + i], bit_depth, transfer_type);
+ const uint32_t Y = RGBToGray(R, G, B);
+ dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth, transfer_type);
+ } while (++i < w);
+}
+
+static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
+ fixed_t* dst, int uv_w, int rgb_bit_depth,
+ SharpYuvTransferFunctionType transfer_type) {
+ int i = 0;
+ do {
+ const int r =
+ ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0],
+ src2[0 * uv_w + 1], rgb_bit_depth, transfer_type);
+ const int g =
+ ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0],
+ src2[2 * uv_w + 1], rgb_bit_depth, transfer_type);
+ const int b =
+ ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0],
+ src2[4 * uv_w + 1], rgb_bit_depth, transfer_type);
+ const int W = RGBToGray(r, g, b);
+ dst[0 * uv_w] = (fixed_t)(r - W);
+ dst[1 * uv_w] = (fixed_t)(g - W);
+ dst[2 * uv_w] = (fixed_t)(b - W);
+ dst += 1;
+ src1 += 2;
+ src2 += 2;
+ } while (++i < uv_w);
+}
+
+static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
+ int i = 0;
+ assert(w > 0);
+ do {
+ y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
+ } while (++i < w);
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0, int bit_depth) {
+ const int v0 = (A * 3 + B + 2) >> 2;
+ return clip_bit_depth(v0 + W0, bit_depth);
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE int Shift(int v, int shift) {
+ return (shift >= 0) ? (v << shift) : (v >> -shift);
+}
+
+static void ImportOneRow(const uint8_t* const r_ptr,
+ const uint8_t* const g_ptr,
+ const uint8_t* const b_ptr,
+ int rgb_step,
+ int rgb_bit_depth,
+ int pic_width,
+ fixed_y_t* const dst) {
+ // Convert the rgb_step from a number of bytes to a number of uint8_t or
+ // uint16_t values depending the bit depth.
+ const int step = (rgb_bit_depth > 8) ? rgb_step / 2 : rgb_step;
+ int i = 0;
+ const int w = (pic_width + 1) & ~1;
+ do {
+ const int off = i * step;
+ const int shift = GetPrecisionShift(rgb_bit_depth);
+ if (rgb_bit_depth == 8) {
+ dst[i + 0 * w] = Shift(r_ptr[off], shift);
+ dst[i + 1 * w] = Shift(g_ptr[off], shift);
+ dst[i + 2 * w] = Shift(b_ptr[off], shift);
+ } else {
+ dst[i + 0 * w] = Shift(((uint16_t*)r_ptr)[off], shift);
+ dst[i + 1 * w] = Shift(((uint16_t*)g_ptr)[off], shift);
+ dst[i + 2 * w] = Shift(((uint16_t*)b_ptr)[off], shift);
+ }
+ } while (++i < pic_width);
+ if (pic_width & 1) { // replicate rightmost pixel
+ dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
+ dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
+ dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];
+ }
+}
+
+static void InterpolateTwoRows(const fixed_y_t* const best_y,
+ const fixed_t* prev_uv,
+ const fixed_t* cur_uv,
+ const fixed_t* next_uv,
+ int w,
+ fixed_y_t* out1,
+ fixed_y_t* out2,
+ int rgb_bit_depth) {
+ const int uv_w = w >> 1;
+ const int len = (w - 1) >> 1; // length to filter
+ int k = 3;
+ const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
+ while (k-- > 0) { // process each R/G/B segments in turn
+ // special boundary case for i==0
+ out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0], bit_depth);
+ out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w], bit_depth);
+
+ SharpYuvFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1,
+ bit_depth);
+ SharpYuvFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1,
+ bit_depth);
+
+ // special boundary case for i == w - 1 when w is even
+ if (!(w & 1)) {
+ out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
+ best_y[w - 1 + 0], bit_depth);
+ out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
+ best_y[w - 1 + w], bit_depth);
+ }
+ out1 += w;
+ out2 += w;
+ prev_uv += uv_w;
+ cur_uv += uv_w;
+ next_uv += uv_w;
+ }
+}
+
+static WEBP_INLINE int RGBToYUVComponent(int r, int g, int b,
+ const int coeffs[4], int sfix) {
+ const int srounder = 1 << (YUV_FIX + sfix - 1);
+ const int luma = coeffs[0] * r + coeffs[1] * g + coeffs[2] * b +
+ coeffs[3] + srounder;
+ return (luma >> (YUV_FIX + sfix));
+}
+
+static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
+ uint8_t* y_ptr, int y_stride, uint8_t* u_ptr,
+ int u_stride, uint8_t* v_ptr, int v_stride,
+ int rgb_bit_depth,
+ int yuv_bit_depth, int width, int height,
+ const SharpYuvConversionMatrix* yuv_matrix) {
+ int i, j;
+ const fixed_t* const best_uv_base = best_uv;
+ const int w = (width + 1) & ~1;
+ const int h = (height + 1) & ~1;
+ const int uv_w = w >> 1;
+ const int uv_h = h >> 1;
+ const int sfix = GetPrecisionShift(rgb_bit_depth);
+ const int yuv_max = (1 << yuv_bit_depth) - 1;
+
+ best_uv = best_uv_base;
+ j = 0;
+ do {
+ i = 0;
+ do {
+ const int off = (i >> 1);
+ const int W = best_y[i];
+ const int r = best_uv[off + 0 * uv_w] + W;
+ const int g = best_uv[off + 1 * uv_w] + W;
+ const int b = best_uv[off + 2 * uv_w] + W;
+ const int y = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_y, sfix);
+ if (yuv_bit_depth <= 8) {
+ y_ptr[i] = clip_8b(y);
+ } else {
+ ((uint16_t*)y_ptr)[i] = clip(y, yuv_max);
+ }
+ } while (++i < width);
+ best_y += w;
+ best_uv += (j & 1) * 3 * uv_w;
+ y_ptr += y_stride;
+ } while (++j < height);
+
+ best_uv = best_uv_base;
+ j = 0;
+ do {
+ i = 0;
+ do {
+ // Note r, g and b values here are off by W, but a constant offset on all
+ // 3 components doesn't change the value of u and v with a YCbCr matrix.
+ const int r = best_uv[i + 0 * uv_w];
+ const int g = best_uv[i + 1 * uv_w];
+ const int b = best_uv[i + 2 * uv_w];
+ const int u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix);
+ const int v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix);
+ if (yuv_bit_depth <= 8) {
+ u_ptr[i] = clip_8b(u);
+ v_ptr[i] = clip_8b(v);
+ } else {
+ ((uint16_t*)u_ptr)[i] = clip(u, yuv_max);
+ ((uint16_t*)v_ptr)[i] = clip(v, yuv_max);
+ }
+ } while (++i < uv_w);
+ best_uv += 3 * uv_w;
+ u_ptr += u_stride;
+ v_ptr += v_stride;
+ } while (++j < uv_h);
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Main function
+
+static void* SafeMalloc(uint64_t nmemb, size_t size) {
+ const uint64_t total_size = nmemb * (uint64_t)size;
+ if (total_size != (size_t)total_size) return NULL;
+ return malloc((size_t)total_size);
+}
+
+#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((uint64_t)(W) * (H), sizeof(T)))
+
+static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
+ const uint8_t* b_ptr, int rgb_step, int rgb_stride,
+ int rgb_bit_depth, uint8_t* y_ptr, int y_stride,
+ uint8_t* u_ptr, int u_stride, uint8_t* v_ptr,
+ int v_stride, int yuv_bit_depth, int width,
+ int height,
+ const SharpYuvConversionMatrix* yuv_matrix,
+ SharpYuvTransferFunctionType transfer_type) {
+ // we expand the right/bottom border if needed
+ const int w = (width + 1) & ~1;
+ const int h = (height + 1) & ~1;
+ const int uv_w = w >> 1;
+ const int uv_h = h >> 1;
+ const int y_bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
+ uint64_t prev_diff_y_sum = ~0;
+ int j, iter;
+
+ // TODO(skal): allocate one big memory chunk. But for now, it's easier
+ // for valgrind debugging to have several chunks.
+ fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch
+ fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);
+ fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);
+ fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
+ fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
+ fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
+ fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
+ fixed_y_t* best_y = best_y_base;
+ fixed_y_t* target_y = target_y_base;
+ fixed_t* best_uv = best_uv_base;
+ fixed_t* target_uv = target_uv_base;
+ const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
+ int ok;
+ assert(w > 0);
+ assert(h > 0);
+
+ if (best_y_base == NULL || best_uv_base == NULL ||
+ target_y_base == NULL || target_uv_base == NULL ||
+ best_rgb_y == NULL || best_rgb_uv == NULL ||
+ tmp_buffer == NULL) {
+ ok = 0;
+ goto End;
+ }
+
+ // Import RGB samples to W/RGB representation.
+ for (j = 0; j < height; j += 2) {
+ const int is_last_row = (j == height - 1);
+ fixed_y_t* const src1 = tmp_buffer + 0 * w;
+ fixed_y_t* const src2 = tmp_buffer + 3 * w;
+
+ // prepare two rows of input
+ ImportOneRow(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, width,
+ src1);
+ if (!is_last_row) {
+ ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
+ rgb_step, rgb_bit_depth, width, src2);
+ } else {
+ memcpy(src2, src1, 3 * w * sizeof(*src2));
+ }
+ StoreGray(src1, best_y + 0, w);
+ StoreGray(src2, best_y + w, w);
+
+ UpdateW(src1, target_y, w, rgb_bit_depth, transfer_type);
+ UpdateW(src2, target_y + w, w, rgb_bit_depth, transfer_type);
+ UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth, transfer_type);
+ memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
+ best_y += 2 * w;
+ best_uv += 3 * uv_w;
+ target_y += 2 * w;
+ target_uv += 3 * uv_w;
+ r_ptr += 2 * rgb_stride;
+ g_ptr += 2 * rgb_stride;
+ b_ptr += 2 * rgb_stride;
+ }
+
+ // Iterate and resolve clipping conflicts.
+ for (iter = 0; iter < kNumIterations; ++iter) {
+ const fixed_t* cur_uv = best_uv_base;
+ const fixed_t* prev_uv = best_uv_base;
+ uint64_t diff_y_sum = 0;
+
+ best_y = best_y_base;
+ best_uv = best_uv_base;
+ target_y = target_y_base;
+ target_uv = target_uv_base;
+ j = 0;
+ do {
+ fixed_y_t* const src1 = tmp_buffer + 0 * w;
+ fixed_y_t* const src2 = tmp_buffer + 3 * w;
+ {
+ const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
+ InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w,
+ src1, src2, rgb_bit_depth);
+ prev_uv = cur_uv;
+ cur_uv = next_uv;
+ }
+
+ UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth, transfer_type);
+ UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth, transfer_type);
+ UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth, transfer_type);
+
+ // update two rows of Y and one row of RGB
+ diff_y_sum +=
+ SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w, y_bit_depth);
+ SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
+
+ best_y += 2 * w;
+ best_uv += 3 * uv_w;
+ target_y += 2 * w;
+ target_uv += 3 * uv_w;
+ j += 2;
+ } while (j < h);
+ // test exit condition
+ if (iter > 0) {
+ if (diff_y_sum < diff_y_threshold) break;
+ if (diff_y_sum > prev_diff_y_sum) break;
+ }
+ prev_diff_y_sum = diff_y_sum;
+ }
+
+ // final reconstruction
+ ok = ConvertWRGBToYUV(best_y_base, best_uv_base, y_ptr, y_stride, u_ptr,
+ u_stride, v_ptr, v_stride, rgb_bit_depth, yuv_bit_depth,
+ width, height, yuv_matrix);
+
+ End:
+ free(best_y_base);
+ free(best_uv_base);
+ free(target_y_base);
+ free(target_uv_base);
+ free(best_rgb_y);
+ free(best_rgb_uv);
+ free(tmp_buffer);
+ return ok;
+}
+
+#undef SAFE_ALLOC
+
+#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
+#include <pthread.h> // NOLINT
+
+#define LOCK_ACCESS \
+ static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \
+ if (pthread_mutex_lock(&sharpyuv_lock)) return
+#define UNLOCK_ACCESS_AND_RETURN \
+ do { \
+ (void)pthread_mutex_unlock(&sharpyuv_lock); \
+ return; \
+ } while (0)
+#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
+#define LOCK_ACCESS do {} while (0)
+#define UNLOCK_ACCESS_AND_RETURN return
+#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32)
+
+// Hidden exported init function.
+// By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed,
+// users can declare it as extern and call it with an alternate VP8CPUInfo
+// function.
+extern VP8CPUInfo SharpYuvGetCPUInfo;
+SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func);
+void SharpYuvInit(VP8CPUInfo cpu_info_func) {
+ static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used =
+ (VP8CPUInfo)&sharpyuv_last_cpuinfo_used;
+ LOCK_ACCESS;
+ // Only update SharpYuvGetCPUInfo when called from external code to avoid a
+ // race on reading the value in SharpYuvConvert().
+ if (cpu_info_func != (VP8CPUInfo)&SharpYuvGetCPUInfo) {
+ SharpYuvGetCPUInfo = cpu_info_func;
+ }
+ if (sharpyuv_last_cpuinfo_used == SharpYuvGetCPUInfo) {
+ UNLOCK_ACCESS_AND_RETURN;
+ }
+
+ SharpYuvInitDsp();
+ SharpYuvInitGammaTables();
+
+ sharpyuv_last_cpuinfo_used = SharpYuvGetCPUInfo;
+ UNLOCK_ACCESS_AND_RETURN;
+}
+
+int SharpYuvConvert(const void* r_ptr, const void* g_ptr, const void* b_ptr,
+ int rgb_step, int rgb_stride, int rgb_bit_depth,
+ void* y_ptr, int y_stride, void* u_ptr, int u_stride,
+ void* v_ptr, int v_stride, int yuv_bit_depth, int width,
+ int height, const SharpYuvConversionMatrix* yuv_matrix) {
+ SharpYuvOptions options;
+ options.yuv_matrix = yuv_matrix;
+ options.transfer_type = kSharpYuvTransferFunctionSrgb;
+ return SharpYuvConvertWithOptions(
+ 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, &options);
+}
+
+int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix* yuv_matrix,
+ SharpYuvOptions* options, int version) {
+ const int major = (version >> 24);
+ const int minor = (version >> 16) & 0xff;
+ if (options == NULL || yuv_matrix == NULL ||
+ (major == SHARPYUV_VERSION_MAJOR && major == 0 &&
+ minor != SHARPYUV_VERSION_MINOR) ||
+ (major != SHARPYUV_VERSION_MAJOR)) {
+ return 0;
+ }
+ options->yuv_matrix = yuv_matrix;
+ options->transfer_type = kSharpYuvTransferFunctionSrgb;
+ return 1;
+}
+
+int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr,
+ const void* b_ptr, int rgb_step, int rgb_stride,
+ int rgb_bit_depth, void* y_ptr, int y_stride,
+ void* u_ptr, int u_stride, void* v_ptr,
+ int v_stride, int yuv_bit_depth, int width,
+ int height, const SharpYuvOptions* options) {
+ const SharpYuvConversionMatrix* yuv_matrix = options->yuv_matrix;
+ SharpYuvTransferFunctionType transfer_type = options->transfer_type;
+ SharpYuvConversionMatrix scaled_matrix;
+ const int rgb_max = (1 << rgb_bit_depth) - 1;
+ const int rgb_round = 1 << (rgb_bit_depth - 1);
+ const int yuv_max = (1 << yuv_bit_depth) - 1;
+ const int sfix = GetPrecisionShift(rgb_bit_depth);
+
+ if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX ||
+ r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || y_ptr == NULL ||
+ u_ptr == NULL || v_ptr == NULL) {
+ return 0;
+ }
+ if (rgb_bit_depth != 8 && rgb_bit_depth != 10 && rgb_bit_depth != 12 &&
+ rgb_bit_depth != 16) {
+ return 0;
+ }
+ if (yuv_bit_depth != 8 && yuv_bit_depth != 10 && yuv_bit_depth != 12) {
+ return 0;
+ }
+ if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride % 2 != 0)) {
+ // Step/stride should be even for uint16_t buffers.
+ return 0;
+ }
+ if (yuv_bit_depth > 8 &&
+ (y_stride % 2 != 0 || u_stride % 2 != 0 || v_stride % 2 != 0)) {
+ // Stride should be even for uint16_t buffers.
+ return 0;
+ }
+ // The address of the function pointer is used to avoid a read race.
+ SharpYuvInit((VP8CPUInfo)&SharpYuvGetCPUInfo);
+
+ // Add scaling factor to go from rgb_bit_depth to yuv_bit_depth, to the
+ // rgb->yuv conversion matrix.
+ if (rgb_bit_depth == yuv_bit_depth) {
+ memcpy(&scaled_matrix, yuv_matrix, sizeof(scaled_matrix));
+ } else {
+ int i;
+ for (i = 0; i < 3; ++i) {
+ scaled_matrix.rgb_to_y[i] =
+ (yuv_matrix->rgb_to_y[i] * yuv_max + rgb_round) / rgb_max;
+ scaled_matrix.rgb_to_u[i] =
+ (yuv_matrix->rgb_to_u[i] * yuv_max + rgb_round) / rgb_max;
+ scaled_matrix.rgb_to_v[i] =
+ (yuv_matrix->rgb_to_v[i] * yuv_max + rgb_round) / rgb_max;
+ }
+ }
+ // Also incorporate precision change scaling.
+ scaled_matrix.rgb_to_y[3] = Shift(yuv_matrix->rgb_to_y[3], sfix);
+ 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(
+ (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/c-lib/sharpyuv/sharpyuv.h b/c-lib/sharpyuv/sharpyuv.h
@@ -0,0 +1,181 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Sharp RGB to YUV conversion.
+
+#ifndef WEBP_SHARPYUV_SHARPYUV_H_
+#define WEBP_SHARPYUV_SHARPYUV_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef SHARPYUV_EXTERN
+#ifdef WEBP_EXTERN
+#define SHARPYUV_EXTERN WEBP_EXTERN
+#else
+// This explicitly marks library functions and allows for changing the
+// signature for e.g., Windows DLL builds.
+#if defined(_WIN32) && defined(WEBP_DLL)
+#define SHARPYUV_EXTERN __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+#define SHARPYUV_EXTERN extern __attribute__((visibility("default")))
+#else
+#define SHARPYUV_EXTERN extern
+#endif /* defined(_WIN32) && defined(WEBP_DLL) */
+#endif /* WEBP_EXTERN */
+#endif /* SHARPYUV_EXTERN */
+
+#ifndef SHARPYUV_INLINE
+#ifdef WEBP_INLINE
+#define SHARPYUV_INLINE WEBP_INLINE
+#else
+#ifndef _MSC_VER
+#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
+ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
+#define SHARPYUV_INLINE inline
+#else
+#define SHARPYUV_INLINE
+#endif
+#else
+#define SHARPYUV_INLINE __forceinline
+#endif /* _MSC_VER */
+#endif /* WEBP_INLINE */
+#endif /* SHARPYUV_INLINE */
+
+// SharpYUV API version following the convention from semver.org
+#define SHARPYUV_VERSION_MAJOR 0
+#define SHARPYUV_VERSION_MINOR 4
+#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) \
+ (((MAJOR) << 24) | ((MINOR) << 16) | (PATCH))
+#define SHARPYUV_VERSION \
+ SHARPYUV_MAKE_VERSION(SHARPYUV_VERSION_MAJOR, SHARPYUV_VERSION_MINOR, \
+ SHARPYUV_VERSION_PATCH)
+
+// Returns the library's version number, packed in hexadecimal. See
+// SHARPYUV_VERSION.
+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 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];
+ int rgb_to_v[4];
+} SharpYuvConversionMatrix;
+
+typedef struct SharpYuvOptions SharpYuvOptions;
+
+// Enums for transfer functions, as defined in H.273,
+// https://www.itu.int/rec/T-REC-H.273-202107-I/en
+typedef enum SharpYuvTransferFunctionType {
+ // 0 is reserved
+ kSharpYuvTransferFunctionBt709 = 1,
+ // 2 is unspecified
+ // 3 is reserved
+ kSharpYuvTransferFunctionBt470M = 4,
+ kSharpYuvTransferFunctionBt470Bg = 5,
+ kSharpYuvTransferFunctionBt601 = 6,
+ kSharpYuvTransferFunctionSmpte240 = 7,
+ kSharpYuvTransferFunctionLinear = 8,
+ kSharpYuvTransferFunctionLog100 = 9,
+ kSharpYuvTransferFunctionLog100_Sqrt10 = 10,
+ kSharpYuvTransferFunctionIec61966 = 11,
+ kSharpYuvTransferFunctionBt1361 = 12,
+ kSharpYuvTransferFunctionSrgb = 13,
+ kSharpYuvTransferFunctionBt2020_10Bit = 14,
+ kSharpYuvTransferFunctionBt2020_12Bit = 15,
+ kSharpYuvTransferFunctionSmpte2084 = 16, // PQ
+ kSharpYuvTransferFunctionSmpte428 = 17,
+ kSharpYuvTransferFunctionHlg = 18,
+ kSharpYuvTransferFunctionNum
+} SharpYuvTransferFunctionType;
+
+// Converts RGB to YUV420 using a downsampling algorithm that minimizes
+// artefacts caused by chroma subsampling.
+// This is slower than standard downsampling (averaging of 4 UV values).
+// Assumes that the image will be upsampled using a bilinear filter. If nearest
+// neighbor is used instead, the upsampled image might look worse than with
+// standard downsampling.
+// r_ptr, g_ptr, b_ptr: pointers to the source r, g and b channels. Should point
+// to uint8_t buffers if rgb_bit_depth is 8, or uint16_t buffers otherwise.
+// rgb_step: distance in bytes between two horizontally adjacent pixels on the
+// r, g and b channels. If rgb_bit_depth is > 8, it should be a
+// multiple of 2.
+// rgb_stride: distance in bytes between two vertically adjacent pixels on the
+// r, g, and b channels. If rgb_bit_depth is > 8, it should be a
+// multiple of 2.
+// rgb_bit_depth: number of bits for each r/g/b value. One of: 8, 10, 12, 16.
+// Note: 16 bit input is truncated to 14 bits before conversion to yuv.
+// yuv_bit_depth: number of bits for each y/u/v value. One of: 8, 10, 12.
+// y_ptr, u_ptr, v_ptr: pointers to the destination y, u and v channels. Should
+// point to uint8_t buffers if yuv_bit_depth is 8, or uint16_t buffers
+// otherwise.
+// y_stride, u_stride, v_stride: distance in bytes between two vertically
+// 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,
+ const void* b_ptr, int rgb_step,
+ int rgb_stride, int rgb_bit_depth,
+ void* y_ptr, int y_stride, void* u_ptr,
+ int u_stride, void* v_ptr, int v_stride,
+ int yuv_bit_depth, int width, int height,
+ const SharpYuvConversionMatrix* yuv_matrix);
+
+struct SharpYuvOptions {
+ // This matrix cannot be NULL and can be initialized by
+ // SharpYuvComputeConversionMatrix.
+ const SharpYuvConversionMatrix* yuv_matrix;
+ SharpYuvTransferFunctionType transfer_type;
+};
+
+// Internal, version-checked, entry point
+SHARPYUV_EXTERN int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix*,
+ SharpYuvOptions*, int);
+
+// Should always be called, to initialize a fresh SharpYuvOptions
+// structure before modification. SharpYuvOptionsInit() must have succeeded
+// before using the 'options' object.
+static SHARPYUV_INLINE int SharpYuvOptionsInit(
+ const SharpYuvConversionMatrix* yuv_matrix, SharpYuvOptions* options) {
+ return SharpYuvOptionsInitInternal(yuv_matrix, options, SHARPYUV_VERSION);
+}
+
+SHARPYUV_EXTERN int SharpYuvConvertWithOptions(
+ const void* r_ptr, const void* g_ptr, const void* b_ptr, int rgb_step,
+ int rgb_stride, int rgb_bit_depth, void* y_ptr, int y_stride, void* u_ptr,
+ int u_stride, void* v_ptr, int v_stride, int yuv_bit_depth, int width,
+ int height, const SharpYuvOptions* options);
+
+// TODO(b/194336375): Add YUV444 to YUV420 conversion. Maybe also add 422
+// support (it's rarely used in practice, especially for images).
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_SHARPYUV_SHARPYUV_H_
diff --git a/c-lib/sharpyuv/sharpyuv_cpu.c b/c-lib/sharpyuv/sharpyuv_cpu.c
@@ -0,0 +1,14 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+#include "sharpyuv/sharpyuv_cpu.h"
+
+// Include src/dsp/cpu.c to create SharpYuvGetCPUInfo from VP8GetCPUInfo. The
+// function pointer is renamed in sharpyuv_cpu.h.
+#include "src/dsp/cpu.c"
diff --git a/c-lib/sharpyuv/sharpyuv_cpu.h b/c-lib/sharpyuv/sharpyuv_cpu.h
@@ -0,0 +1,22 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+#ifndef WEBP_SHARPYUV_SHARPYUV_CPU_H_
+#define WEBP_SHARPYUV_SHARPYUV_CPU_H_
+
+#include "sharpyuv/sharpyuv.h"
+
+// Avoid exporting SharpYuvGetCPUInfo in shared object / DLL builds.
+// SharpYuvInit() replaces the use of the function pointer.
+#undef WEBP_EXTERN
+#define WEBP_EXTERN extern
+#define VP8GetCPUInfo SharpYuvGetCPUInfo
+#include "src/dsp/cpu.h"
+
+#endif // WEBP_SHARPYUV_SHARPYUV_CPU_H_
diff --git a/c-lib/sharpyuv/sharpyuv_csp.c b/c-lib/sharpyuv/sharpyuv_csp.c
@@ -0,0 +1,116 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Colorspace utilities.
+
+#include "sharpyuv/sharpyuv_csp.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stddef.h>
+
+#include "sharpyuv/sharpyuv.h"
+
+static int ToFixed16(float f) { return (int)floor(f * (1 << 16) + 0.5f); }
+
+void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space,
+ SharpYuvConversionMatrix* matrix) {
+ const float kr = yuv_color_space->kr;
+ const float kb = yuv_color_space->kb;
+ const float kg = 1.0f - kr - kb;
+ 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 = cb;
+ float scale_v = cr;
+ float add_uv = (float)(128 << shift);
+ assert(yuv_color_space->bit_depth >= 8);
+
+ if (yuv_color_space->range == kSharpYuvRangeLimited) {
+ scale_y *= (219 << shift) / denom;
+ scale_u *= (224 << shift) / denom;
+ scale_v *= (224 << shift) / denom;
+ add_y = (float)(16 << shift);
+ }
+
+ matrix->rgb_to_y[0] = ToFixed16(kr * scale_y);
+ matrix->rgb_to_y[1] = ToFixed16(kg * scale_y);
+ matrix->rgb_to_y[2] = ToFixed16(kb * scale_y);
+ matrix->rgb_to_y[3] = ToFixed16(add_y);
+
+ matrix->rgb_to_u[0] = ToFixed16(-kr * scale_u);
+ matrix->rgb_to_u[1] = ToFixed16(-kg * scale_u);
+ matrix->rgb_to_u[2] = ToFixed16((1 - kb) * scale_u);
+ matrix->rgb_to_u[3] = ToFixed16(add_uv);
+
+ matrix->rgb_to_v[0] = ToFixed16((1 - kr) * scale_v);
+ matrix->rgb_to_v[1] = ToFixed16(-kg * scale_v);
+ matrix->rgb_to_v[2] = ToFixed16(-kb * scale_v);
+ matrix->rgb_to_v[3] = ToFixed16(add_uv);
+}
+
+// Matrices are in YUV_FIX fixed point precision.
+// 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 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 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 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 bit_depth=8 range=kSharpYuvRangeFull
+static const SharpYuvConversionMatrix kRec709FullMatrix = {
+ {13933, 46871, 4732, 0},
+ {-7509, -25259, 32768, 128 << 16},
+ {32768, -29763, -3005, 128 << 16},
+};
+
+const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix(
+ SharpYuvMatrixType matrix_type) {
+ switch (matrix_type) {
+ case kSharpYuvMatrixWebp:
+ return &kWebpMatrix;
+ case kSharpYuvMatrixRec601Limited:
+ return &kRec601LimitedMatrix;
+ case kSharpYuvMatrixRec601Full:
+ return &kRec601FullMatrix;
+ case kSharpYuvMatrixRec709Limited:
+ return &kRec709LimitedMatrix;
+ case kSharpYuvMatrixRec709Full:
+ return &kRec709FullMatrix;
+ case kSharpYuvMatrixNum:
+ return NULL;
+ }
+ return NULL;
+}
diff --git a/c-lib/sharpyuv/sharpyuv_csp.h b/c-lib/sharpyuv/sharpyuv_csp.h
@@ -0,0 +1,65 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Colorspace utilities.
+
+#ifndef WEBP_SHARPYUV_SHARPYUV_CSP_H_
+#define WEBP_SHARPYUV_SHARPYUV_CSP_H_
+
+#include "sharpyuv/sharpyuv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Range of YUV values.
+typedef enum {
+ kSharpYuvRangeFull, // YUV values between [0;255] (for 8 bit)
+ kSharpYuvRangeLimited // Y in [16;235], YUV in [16;240] (for 8 bit)
+} SharpYuvRange;
+
+// Constants that define a YUV color space.
+typedef struct {
+ // Kr and Kb are defined such that:
+ // Y = Kr * r + Kg * g + Kb * b where Kg = 1 - Kr - Kb.
+ float kr;
+ float kb;
+ int bit_depth; // 8, 10 or 12
+ SharpYuvRange range;
+} SharpYuvColorSpace;
+
+// Fills in 'matrix' for the given YUVColorSpace.
+SHARPYUV_EXTERN void SharpYuvComputeConversionMatrix(
+ const SharpYuvColorSpace* yuv_color_space,
+ SharpYuvConversionMatrix* matrix);
+
+// 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;
+
+// Returns a pointer to a matrix for one of the predefined colorspaces.
+SHARPYUV_EXTERN const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix(
+ SharpYuvMatrixType matrix_type);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_SHARPYUV_SHARPYUV_CSP_H_
diff --git a/c-lib/sharpyuv/sharpyuv_dsp.c b/c-lib/sharpyuv/sharpyuv_dsp.c
@@ -0,0 +1,105 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions for Sharp YUV.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "sharpyuv/sharpyuv_dsp.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "sharpyuv/sharpyuv_cpu.h"
+#include "src/dsp/cpu.h"
+#include "src/webp/types.h"
+
+//-----------------------------------------------------------------------------
+
+#if !WEBP_NEON_OMIT_C_CODE
+static uint16_t clip(int v, int max) {
+ return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
+}
+
+static uint64_t SharpYuvUpdateY_C(const uint16_t* ref, const uint16_t* src,
+ uint16_t* dst, int len, int bit_depth) {
+ uint64_t diff = 0;
+ int i;
+ const int max_y = (1 << bit_depth) - 1;
+ for (i = 0; i < len; ++i) {
+ const int diff_y = ref[i] - src[i];
+ const int new_y = (int)dst[i] + diff_y;
+ dst[i] = clip(new_y, max_y);
+ diff += (uint64_t)abs(diff_y);
+ }
+ return diff;
+}
+
+static void SharpYuvUpdateRGB_C(const int16_t* ref, const int16_t* src,
+ int16_t* dst, int len) {
+ int i;
+ for (i = 0; i < len; ++i) {
+ const int diff_uv = ref[i] - src[i];
+ dst[i] += diff_uv;
+ }
+}
+
+static void SharpYuvFilterRow_C(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out,
+ int bit_depth) {
+ int i;
+ const int max_y = (1 << bit_depth) - 1;
+ for (i = 0; i < len; ++i, ++A, ++B) {
+ const int v0 = (A[0] * 9 + A[1] * 3 + B[0] * 3 + B[1] + 8) >> 4;
+ const int v1 = (A[1] * 9 + A[0] * 3 + B[1] * 3 + B[0] + 8) >> 4;
+ out[2 * i + 0] = clip(best_y[2 * i + 0] + v0, max_y);
+ out[2 * i + 1] = clip(best_y[2 * i + 1] + v1, max_y);
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+//-----------------------------------------------------------------------------
+
+uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref,
+ uint16_t* dst, int len, int bit_depth);
+void (*SharpYuvUpdateRGB)(const int16_t* src, const int16_t* ref, int16_t* dst,
+ int len);
+void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out, int bit_depth);
+
+extern VP8CPUInfo SharpYuvGetCPUInfo;
+extern void InitSharpYuvSSE2(void);
+extern void InitSharpYuvNEON(void);
+
+void SharpYuvInitDsp(void) {
+#if !WEBP_NEON_OMIT_C_CODE
+ SharpYuvUpdateY = SharpYuvUpdateY_C;
+ SharpYuvUpdateRGB = SharpYuvUpdateRGB_C;
+ SharpYuvFilterRow = SharpYuvFilterRow_C;
+#endif
+
+ if (SharpYuvGetCPUInfo != NULL) {
+#if defined(WEBP_HAVE_SSE2)
+ if (SharpYuvGetCPUInfo(kSSE2)) {
+ InitSharpYuvSSE2();
+ }
+#endif // WEBP_HAVE_SSE2
+ }
+
+#if defined(WEBP_HAVE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (SharpYuvGetCPUInfo != NULL && SharpYuvGetCPUInfo(kNEON))) {
+ InitSharpYuvNEON();
+ }
+#endif // WEBP_HAVE_NEON
+
+ assert(SharpYuvUpdateY != NULL);
+ assert(SharpYuvUpdateRGB != NULL);
+ assert(SharpYuvFilterRow != NULL);
+}
diff --git a/c-lib/sharpyuv/sharpyuv_dsp.h b/c-lib/sharpyuv/sharpyuv_dsp.h
@@ -0,0 +1,28 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions for Sharp YUV.
+
+#ifndef WEBP_SHARPYUV_SHARPYUV_DSP_H_
+#define WEBP_SHARPYUV_SHARPYUV_DSP_H_
+
+#include "sharpyuv/sharpyuv_cpu.h"
+#include "src/webp/types.h"
+
+extern uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref,
+ uint16_t* dst, int len, int bit_depth);
+extern void (*SharpYuvUpdateRGB)(const int16_t* src, const int16_t* ref,
+ int16_t* dst, int len);
+extern void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out,
+ int bit_depth);
+
+void SharpYuvInitDsp(void);
+
+#endif // WEBP_SHARPYUV_SHARPYUV_DSP_H_
diff --git a/c-lib/sharpyuv/sharpyuv_gamma.c b/c-lib/sharpyuv/sharpyuv_gamma.c
@@ -0,0 +1,420 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Gamma correction utilities.
+
+#include "sharpyuv/sharpyuv_gamma.h"
+
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+
+#include "sharpyuv/sharpyuv.h"
+#include "src/webp/types.h"
+
+// Gamma correction compensates loss of resolution during chroma subsampling.
+// Size of pre-computed table for converting from gamma to linear.
+#define GAMMA_TO_LINEAR_TAB_BITS 10
+#define GAMMA_TO_LINEAR_TAB_SIZE (1 << GAMMA_TO_LINEAR_TAB_BITS)
+static uint32_t kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 2];
+#define LINEAR_TO_GAMMA_TAB_BITS 9
+#define LINEAR_TO_GAMMA_TAB_SIZE (1 << LINEAR_TO_GAMMA_TAB_BITS)
+static uint32_t kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 2];
+
+static const double kGammaF = 1. / 0.45;
+#define GAMMA_TO_LINEAR_BITS 16
+
+static volatile int kGammaTablesSOk = 0;
+void SharpYuvInitGammaTables(void) {
+ assert(GAMMA_TO_LINEAR_BITS <= 16);
+ if (!kGammaTablesSOk) {
+ int v;
+ const double a = 0.09929682680944;
+ const double thresh = 0.018053968510807;
+ const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
+ // Precompute gamma to linear table.
+ {
+ const double norm = 1. / GAMMA_TO_LINEAR_TAB_SIZE;
+ const double a_rec = 1. / (1. + a);
+ for (v = 0; v <= GAMMA_TO_LINEAR_TAB_SIZE; ++v) {
+ const double g = norm * v;
+ double value;
+ if (g <= thresh * 4.5) {
+ value = g / 4.5;
+ } else {
+ value = pow(a_rec * (g + a), kGammaF);
+ }
+ kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
+ }
+ // to prevent small rounding errors to cause read-overflow:
+ kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 1] =
+ kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE];
+ }
+ // Precompute linear to gamma table.
+ {
+ const double scale = 1. / LINEAR_TO_GAMMA_TAB_SIZE;
+ for (v = 0; v <= LINEAR_TO_GAMMA_TAB_SIZE; ++v) {
+ const double g = scale * v;
+ double value;
+ if (g <= thresh) {
+ value = 4.5 * g;
+ } else {
+ value = (1. + a) * pow(g, 1. / kGammaF) - a;
+ }
+ kLinearToGammaTabS[v] =
+ (uint32_t)(final_scale * value + 0.5);
+ }
+ // to prevent small rounding errors to cause read-overflow:
+ kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =
+ kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE];
+ }
+ kGammaTablesSOk = 1;
+ }
+}
+
+static WEBP_INLINE int Shift(int v, int shift) {
+ return (shift >= 0) ? (v << shift) : (v >> -shift);
+}
+
+static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,
+ int tab_pos_shift_right,
+ int tab_value_shift) {
+ const uint32_t tab_pos = Shift(v, -tab_pos_shift_right);
+ // fractional part, in 'tab_pos_shift' fixed-point precision
+ const uint32_t x = v - (tab_pos << tab_pos_shift_right); // fractional part
+ // v0 / v1 are in kGammaToLinearBits fixed-point precision (range [0..1])
+ const uint32_t v0 = Shift(tab[tab_pos + 0], tab_value_shift);
+ const uint32_t v1 = Shift(tab[tab_pos + 1], tab_value_shift);
+ // Final interpolation.
+ const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.
+ const int half =
+ (tab_pos_shift_right > 0) ? 1 << (tab_pos_shift_right - 1) : 0;
+ const uint32_t result = v0 + ((v2 + half) >> tab_pos_shift_right);
+ return result;
+}
+
+static uint32_t ToLinearSrgb(uint16_t v, int bit_depth) {
+ const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;
+ if (shift > 0) {
+ return kGammaToLinearTabS[v << shift];
+ }
+ return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);
+}
+
+static uint16_t FromLinearSrgb(uint32_t value, int bit_depth) {
+ return FixedPointInterpolation(
+ value, kLinearToGammaTabS,
+ (GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS),
+ bit_depth - GAMMA_TO_LINEAR_BITS);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define CLAMP(x, low, high) \
+ (((x) < (low)) ? (low) : (((high) < (x)) ? (high) : (x)))
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+static WEBP_INLINE float Roundf(float x) {
+ if (x < 0)
+ return (float)ceil((double)(x - 0.5f));
+ else
+ return (float)floor((double)(x + 0.5f));
+}
+
+static WEBP_INLINE float Powf(float base, float exp) {
+ return (float)pow((double)base, (double)exp);
+}
+
+static WEBP_INLINE float Log10f(float x) { return (float)log10((double)x); }
+
+static float ToLinear709(float gamma) {
+ if (gamma < 0.f) {
+ return 0.f;
+ } else if (gamma < 4.5f * 0.018053968510807f) {
+ return gamma / 4.5f;
+ } else if (gamma < 1.f) {
+ return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
+ }
+ return 1.f;
+}
+
+static float FromLinear709(float linear) {
+ if (linear < 0.f) {
+ return 0.f;
+ } else if (linear < 0.018053968510807f) {
+ return linear * 4.5f;
+ } else if (linear < 1.f) {
+ return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
+ }
+ return 1.f;
+}
+
+static float ToLinear470M(float gamma) {
+ return Powf(CLAMP(gamma, 0.f, 1.f), 2.2f);
+}
+
+static float FromLinear470M(float linear) {
+ return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.2f);
+}
+
+static float ToLinear470Bg(float gamma) {
+ return Powf(CLAMP(gamma, 0.f, 1.f), 2.8f);
+}
+
+static float FromLinear470Bg(float linear) {
+ return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.8f);
+}
+
+static float ToLinearSmpte240(float gamma) {
+ if (gamma < 0.f) {
+ return 0.f;
+ } else if (gamma < 4.f * 0.022821585529445f) {
+ return gamma / 4.f;
+ } else if (gamma < 1.f) {
+ return Powf((gamma + 0.111572195921731f) / 1.111572195921731f, 1.f / 0.45f);
+ }
+ return 1.f;
+}
+
+static float FromLinearSmpte240(float linear) {
+ if (linear < 0.f) {
+ return 0.f;
+ } else if (linear < 0.022821585529445f) {
+ return linear * 4.f;
+ } else if (linear < 1.f) {
+ return 1.111572195921731f * Powf(linear, 0.45f) - 0.111572195921731f;
+ }
+ return 1.f;
+}
+
+static float ToLinearLog100(float gamma) {
+ // The function is non-bijective so choose the middle of [0, 0.01].
+ const float mid_interval = 0.01f / 2.f;
+ return (gamma <= 0.0f) ? mid_interval
+ : Powf(10.0f, 2.f * (MIN(gamma, 1.f) - 1.0f));
+}
+
+static float FromLinearLog100(float linear) {
+ return (linear < 0.01f) ? 0.0f : 1.0f + Log10f(MIN(linear, 1.f)) / 2.0f;
+}
+
+static float ToLinearLog100Sqrt10(float gamma) {
+ // The function is non-bijective so choose the middle of [0, 0.00316227766f[.
+ const float mid_interval = 0.00316227766f / 2.f;
+ return (gamma <= 0.0f) ? mid_interval
+ : Powf(10.0f, 2.5f * (MIN(gamma, 1.f) - 1.0f));
+}
+
+static float FromLinearLog100Sqrt10(float linear) {
+ return (linear < 0.00316227766f) ? 0.0f
+ : 1.0f + Log10f(MIN(linear, 1.f)) / 2.5f;
+}
+
+static float ToLinearIec61966(float gamma) {
+ if (gamma <= -4.5f * 0.018053968510807f) {
+ return Powf((-gamma + 0.09929682680944f) / -1.09929682680944f, 1.f / 0.45f);
+ } else if (gamma < 4.5f * 0.018053968510807f) {
+ return gamma / 4.5f;
+ }
+ return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
+}
+
+static float FromLinearIec61966(float linear) {
+ if (linear <= -0.018053968510807f) {
+ return -1.09929682680944f * Powf(-linear, 0.45f) + 0.09929682680944f;
+ } else if (linear < 0.018053968510807f) {
+ return linear * 4.5f;
+ }
+ return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
+}
+
+static float ToLinearBt1361(float gamma) {
+ if (gamma < -0.25f) {
+ return -0.25f;
+ } else if (gamma < 0.f) {
+ return Powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.f / 0.45f) /
+ -4.f;
+ } else if (gamma < 4.5f * 0.018053968510807f) {
+ return gamma / 4.5f;
+ } else if (gamma < 1.f) {
+ return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
+ }
+ return 1.f;
+}
+
+static float FromLinearBt1361(float linear) {
+ if (linear < -0.25f) {
+ return -0.25f;
+ } else if (linear < 0.f) {
+ return -0.27482420670236f * Powf(-4.f * linear, 0.45f) + 0.02482420670236f;
+ } else if (linear < 0.018053968510807f) {
+ return linear * 4.5f;
+ } else if (linear < 1.f) {
+ return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
+ }
+ return 1.f;
+}
+
+static float ToLinearPq(float gamma) {
+ if (gamma > 0.f) {
+ const float pow_gamma = Powf(gamma, 32.f / 2523.f);
+ const float num = MAX(pow_gamma - 107.f / 128.f, 0.0f);
+ const float den = MAX(2413.f / 128.f - 2392.f / 128.f * pow_gamma, FLT_MIN);
+ return Powf(num / den, 4096.f / 653.f);
+ }
+ return 0.f;
+}
+
+static float FromLinearPq(float linear) {
+ if (linear > 0.f) {
+ const float pow_linear = Powf(linear, 653.f / 4096.f);
+ const float num = 107.f / 128.f + 2413.f / 128.f * pow_linear;
+ const float den = 1.0f + 2392.f / 128.f * pow_linear;
+ return Powf(num / den, 2523.f / 32.f);
+ }
+ return 0.f;
+}
+
+static float ToLinearSmpte428(float gamma) {
+ return Powf(MAX(gamma, 0.f), 2.6f) / 0.91655527974030934f;
+}
+
+static float FromLinearSmpte428(float linear) {
+ return Powf(0.91655527974030934f * MAX(linear, 0.f), 1.f / 2.6f);
+}
+
+// Conversion in BT.2100 requires RGB info. Simplify to gamma correction here.
+static float ToLinearHlg(float gamma) {
+ if (gamma < 0.f) {
+ return 0.f;
+ } else if (gamma <= 0.5f) {
+ return Powf((gamma * gamma) * (1.f / 3.f), 1.2f);
+ }
+ return Powf((expf((gamma - 0.55991073f) / 0.17883277f) + 0.28466892f) / 12.0f,
+ 1.2f);
+}
+
+static float FromLinearHlg(float linear) {
+ linear = Powf(linear, 1.f / 1.2f);
+ if (linear < 0.f) {
+ return 0.f;
+ } else if (linear <= (1.f / 12.f)) {
+ return sqrtf(3.f * linear);
+ }
+ return 0.17883277f * logf(12.f * linear - 0.28466892f) + 0.55991073f;
+}
+
+uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth,
+ SharpYuvTransferFunctionType transfer_type) {
+ float v_float, linear;
+ if (transfer_type == kSharpYuvTransferFunctionSrgb) {
+ return ToLinearSrgb(v, bit_depth);
+ }
+ v_float = (float)v / ((1 << bit_depth) - 1);
+ switch (transfer_type) {
+ case kSharpYuvTransferFunctionBt709:
+ case kSharpYuvTransferFunctionBt601:
+ case kSharpYuvTransferFunctionBt2020_10Bit:
+ case kSharpYuvTransferFunctionBt2020_12Bit:
+ linear = ToLinear709(v_float);
+ break;
+ case kSharpYuvTransferFunctionBt470M:
+ linear = ToLinear470M(v_float);
+ break;
+ case kSharpYuvTransferFunctionBt470Bg:
+ linear = ToLinear470Bg(v_float);
+ break;
+ case kSharpYuvTransferFunctionSmpte240:
+ linear = ToLinearSmpte240(v_float);
+ break;
+ case kSharpYuvTransferFunctionLinear:
+ return v;
+ case kSharpYuvTransferFunctionLog100:
+ linear = ToLinearLog100(v_float);
+ break;
+ case kSharpYuvTransferFunctionLog100_Sqrt10:
+ linear = ToLinearLog100Sqrt10(v_float);
+ break;
+ case kSharpYuvTransferFunctionIec61966:
+ linear = ToLinearIec61966(v_float);
+ break;
+ case kSharpYuvTransferFunctionBt1361:
+ linear = ToLinearBt1361(v_float);
+ break;
+ case kSharpYuvTransferFunctionSmpte2084:
+ linear = ToLinearPq(v_float);
+ break;
+ case kSharpYuvTransferFunctionSmpte428:
+ linear = ToLinearSmpte428(v_float);
+ break;
+ case kSharpYuvTransferFunctionHlg:
+ linear = ToLinearHlg(v_float);
+ break;
+ default:
+ assert(0);
+ linear = 0;
+ break;
+ }
+ return (uint32_t)Roundf(linear * ((1 << 16) - 1));
+}
+
+uint16_t SharpYuvLinearToGamma(uint32_t v, int bit_depth,
+ SharpYuvTransferFunctionType transfer_type) {
+ float v_float, linear;
+ if (transfer_type == kSharpYuvTransferFunctionSrgb) {
+ return FromLinearSrgb(v, bit_depth);
+ }
+ v_float = (float)v / ((1 << 16) - 1);
+ switch (transfer_type) {
+ case kSharpYuvTransferFunctionBt709:
+ case kSharpYuvTransferFunctionBt601:
+ case kSharpYuvTransferFunctionBt2020_10Bit:
+ case kSharpYuvTransferFunctionBt2020_12Bit:
+ linear = FromLinear709(v_float);
+ break;
+ case kSharpYuvTransferFunctionBt470M:
+ linear = FromLinear470M(v_float);
+ break;
+ case kSharpYuvTransferFunctionBt470Bg:
+ linear = FromLinear470Bg(v_float);
+ break;
+ case kSharpYuvTransferFunctionSmpte240:
+ linear = FromLinearSmpte240(v_float);
+ break;
+ case kSharpYuvTransferFunctionLinear:
+ return v;
+ case kSharpYuvTransferFunctionLog100:
+ linear = FromLinearLog100(v_float);
+ break;
+ case kSharpYuvTransferFunctionLog100_Sqrt10:
+ linear = FromLinearLog100Sqrt10(v_float);
+ break;
+ case kSharpYuvTransferFunctionIec61966:
+ linear = FromLinearIec61966(v_float);
+ break;
+ case kSharpYuvTransferFunctionBt1361:
+ linear = FromLinearBt1361(v_float);
+ break;
+ case kSharpYuvTransferFunctionSmpte2084:
+ linear = FromLinearPq(v_float);
+ break;
+ case kSharpYuvTransferFunctionSmpte428:
+ linear = FromLinearSmpte428(v_float);
+ break;
+ case kSharpYuvTransferFunctionHlg:
+ linear = FromLinearHlg(v_float);
+ break;
+ default:
+ assert(0);
+ linear = 0;
+ break;
+ }
+ return (uint16_t)Roundf(linear * ((1 << bit_depth) - 1));
+}
diff --git a/c-lib/sharpyuv/sharpyuv_gamma.h b/c-lib/sharpyuv/sharpyuv_gamma.h
@@ -0,0 +1,38 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Gamma correction utilities.
+
+#ifndef WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
+#define WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
+
+#include "sharpyuv/sharpyuv.h"
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Initializes precomputed tables. Must be called once before calling
+// SharpYuvGammaToLinear or SharpYuvLinearToGamma.
+void SharpYuvInitGammaTables(void);
+
+// Converts a 'bit_depth'-bit gamma color value to a 16-bit linear value.
+uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth,
+ SharpYuvTransferFunctionType transfer_type);
+
+// Converts a 16-bit linear color value to a 'bit_depth'-bit gamma value.
+uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth,
+ SharpYuvTransferFunctionType transfer_type);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
diff --git a/c-lib/sharpyuv/sharpyuv_neon.c b/c-lib/sharpyuv/sharpyuv_neon.c
@@ -0,0 +1,181 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions for Sharp YUV.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "sharpyuv/sharpyuv_dsp.h"
+
+#if defined(WEBP_USE_NEON)
+#include <assert.h>
+#include <stdlib.h>
+#include <arm_neon.h>
+
+static uint16_t clip_NEON(int v, int max) {
+ return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
+}
+
+static uint64_t SharpYuvUpdateY_NEON(const uint16_t* ref, const uint16_t* src,
+ uint16_t* dst, int len, int bit_depth) {
+ const int max_y = (1 << bit_depth) - 1;
+ int i;
+ const int16x8_t zero = vdupq_n_s16(0);
+ const int16x8_t max = vdupq_n_s16(max_y);
+ uint64x2_t sum = vdupq_n_u64(0);
+ uint64_t diff;
+
+ for (i = 0; i + 8 <= len; i += 8) {
+ const int16x8_t A = vreinterpretq_s16_u16(vld1q_u16(ref + i));
+ const int16x8_t B = vreinterpretq_s16_u16(vld1q_u16(src + i));
+ const int16x8_t C = vreinterpretq_s16_u16(vld1q_u16(dst + i));
+ const int16x8_t D = vsubq_s16(A, B); // diff_y
+ const int16x8_t F = vaddq_s16(C, D); // new_y
+ const uint16x8_t H =
+ vreinterpretq_u16_s16(vmaxq_s16(vminq_s16(F, max), zero));
+ const int16x8_t I = vabsq_s16(D); // abs(diff_y)
+ vst1q_u16(dst + i, H);
+ sum = vpadalq_u32(sum, vpaddlq_u16(vreinterpretq_u16_s16(I)));
+ }
+ diff = vgetq_lane_u64(sum, 0) + vgetq_lane_u64(sum, 1);
+ for (; i < len; ++i) {
+ const int diff_y = ref[i] - src[i];
+ const int new_y = (int)(dst[i]) + diff_y;
+ dst[i] = clip_NEON(new_y, max_y);
+ diff += (uint64_t)(abs(diff_y));
+ }
+ return diff;
+}
+
+static void SharpYuvUpdateRGB_NEON(const int16_t* ref, const int16_t* src,
+ int16_t* dst, int len) {
+ int i;
+ for (i = 0; i + 8 <= len; i += 8) {
+ const int16x8_t A = vld1q_s16(ref + i);
+ const int16x8_t B = vld1q_s16(src + i);
+ const int16x8_t C = vld1q_s16(dst + i);
+ const int16x8_t D = vsubq_s16(A, B); // diff_uv
+ const int16x8_t E = vaddq_s16(C, D); // new_uv
+ vst1q_s16(dst + i, E);
+ }
+ for (; i < len; ++i) {
+ const int diff_uv = ref[i] - src[i];
+ dst[i] += diff_uv;
+ }
+}
+
+static void SharpYuvFilterRow16_NEON(const int16_t* A, const int16_t* B,
+ int len, const uint16_t* best_y,
+ uint16_t* out, int bit_depth) {
+ const int max_y = (1 << bit_depth) - 1;
+ int i;
+ const int16x8_t max = vdupq_n_s16(max_y);
+ const int16x8_t zero = vdupq_n_s16(0);
+ for (i = 0; i + 8 <= len; i += 8) {
+ const int16x8_t a0 = vld1q_s16(A + i + 0);
+ const int16x8_t a1 = vld1q_s16(A + i + 1);
+ const int16x8_t b0 = vld1q_s16(B + i + 0);
+ const int16x8_t b1 = vld1q_s16(B + i + 1);
+ const int16x8_t a0b1 = vaddq_s16(a0, b1);
+ const int16x8_t a1b0 = vaddq_s16(a1, b0);
+ const int16x8_t a0a1b0b1 = vaddq_s16(a0b1, a1b0); // A0+A1+B0+B1
+ const int16x8_t a0b1_2 = vaddq_s16(a0b1, a0b1); // 2*(A0+B1)
+ const int16x8_t a1b0_2 = vaddq_s16(a1b0, a1b0); // 2*(A1+B0)
+ const int16x8_t c0 = vshrq_n_s16(vaddq_s16(a0b1_2, a0a1b0b1), 3);
+ const int16x8_t c1 = vshrq_n_s16(vaddq_s16(a1b0_2, a0a1b0b1), 3);
+ const int16x8_t e0 = vrhaddq_s16(c1, a0);
+ const int16x8_t e1 = vrhaddq_s16(c0, a1);
+ const int16x8x2_t f = vzipq_s16(e0, e1);
+ const int16x8_t g0 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 0));
+ const int16x8_t g1 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 8));
+ const int16x8_t h0 = vaddq_s16(g0, f.val[0]);
+ const int16x8_t h1 = vaddq_s16(g1, f.val[1]);
+ const int16x8_t i0 = vmaxq_s16(vminq_s16(h0, max), zero);
+ const int16x8_t i1 = vmaxq_s16(vminq_s16(h1, max), zero);
+ vst1q_u16(out + 2 * i + 0, vreinterpretq_u16_s16(i0));
+ vst1q_u16(out + 2 * i + 8, vreinterpretq_u16_s16(i1));
+ }
+ for (; i < len; ++i) {
+ const int a0b1 = A[i + 0] + B[i + 1];
+ const int a1b0 = A[i + 1] + B[i + 0];
+ const int a0a1b0b1 = a0b1 + a1b0 + 8;
+ const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+ const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+ out[2 * i + 0] = clip_NEON(best_y[2 * i + 0] + v0, max_y);
+ out[2 * i + 1] = clip_NEON(best_y[2 * i + 1] + v1, max_y);
+ }
+}
+
+static void SharpYuvFilterRow32_NEON(const int16_t* A, const int16_t* B,
+ int len, const uint16_t* best_y,
+ uint16_t* out, int bit_depth) {
+ const int max_y = (1 << bit_depth) - 1;
+ int i;
+ const uint16x8_t max = vdupq_n_u16(max_y);
+ for (i = 0; i + 4 <= len; i += 4) {
+ const int16x4_t a0 = vld1_s16(A + i + 0);
+ const int16x4_t a1 = vld1_s16(A + i + 1);
+ const int16x4_t b0 = vld1_s16(B + i + 0);
+ const int16x4_t b1 = vld1_s16(B + i + 1);
+ const int32x4_t a0b1 = vaddl_s16(a0, b1);
+ const int32x4_t a1b0 = vaddl_s16(a1, b0);
+ const int32x4_t a0a1b0b1 = vaddq_s32(a0b1, a1b0); // A0+A1+B0+B1
+ const int32x4_t a0b1_2 = vaddq_s32(a0b1, a0b1); // 2*(A0+B1)
+ const int32x4_t a1b0_2 = vaddq_s32(a1b0, a1b0); // 2*(A1+B0)
+ const int32x4_t c0 = vshrq_n_s32(vaddq_s32(a0b1_2, a0a1b0b1), 3);
+ const int32x4_t c1 = vshrq_n_s32(vaddq_s32(a1b0_2, a0a1b0b1), 3);
+ const int32x4_t e0 = vrhaddq_s32(c1, vmovl_s16(a0));
+ const int32x4_t e1 = vrhaddq_s32(c0, vmovl_s16(a1));
+ const int32x4x2_t f = vzipq_s32(e0, e1);
+
+ const int16x8_t g = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i));
+ const int32x4_t h0 = vaddw_s16(f.val[0], vget_low_s16(g));
+ const int32x4_t h1 = vaddw_s16(f.val[1], vget_high_s16(g));
+ const uint16x8_t i_16 = vcombine_u16(vqmovun_s32(h0), vqmovun_s32(h1));
+ const uint16x8_t i_clamped = vminq_u16(i_16, max);
+ vst1q_u16(out + 2 * i + 0, i_clamped);
+ }
+ for (; i < len; ++i) {
+ const int a0b1 = A[i + 0] + B[i + 1];
+ const int a1b0 = A[i + 1] + B[i + 0];
+ const int a0a1b0b1 = a0b1 + a1b0 + 8;
+ const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+ const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+ out[2 * i + 0] = clip_NEON(best_y[2 * i + 0] + v0, max_y);
+ out[2 * i + 1] = clip_NEON(best_y[2 * i + 1] + v1, max_y);
+ }
+}
+
+static void SharpYuvFilterRow_NEON(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out,
+ int bit_depth) {
+ if (bit_depth <= 10) {
+ SharpYuvFilterRow16_NEON(A, B, len, best_y, out, bit_depth);
+ } else {
+ SharpYuvFilterRow32_NEON(A, B, len, best_y, out, bit_depth);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+extern void InitSharpYuvNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void InitSharpYuvNEON(void) {
+ SharpYuvUpdateY = SharpYuvUpdateY_NEON;
+ SharpYuvUpdateRGB = SharpYuvUpdateRGB_NEON;
+ SharpYuvFilterRow = SharpYuvFilterRow_NEON;
+}
+
+#else // !WEBP_USE_NEON
+
+extern void InitSharpYuvNEON(void);
+
+void InitSharpYuvNEON(void) {}
+
+#endif // WEBP_USE_NEON
diff --git a/c-lib/sharpyuv/sharpyuv_sse2.c b/c-lib/sharpyuv/sharpyuv_sse2.c
@@ -0,0 +1,205 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions for Sharp YUV.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "sharpyuv/sharpyuv_dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
+
+#include <stdlib.h>
+
+#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;
+}
+
+static uint64_t SharpYuvUpdateY_SSE2(const uint16_t* ref, const uint16_t* src,
+ uint16_t* dst, int len, int bit_depth) {
+ const int max_y = (1 << bit_depth) - 1;
+ uint64_t diff = 0;
+ uint32_t tmp[4];
+ int i;
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i max = _mm_set1_epi16(max_y);
+ const __m128i one = _mm_set1_epi16(1);
+ __m128i sum = zero;
+
+ for (i = 0; i + 8 <= len; i += 8) {
+ const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
+ const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
+ const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
+ const __m128i D = _mm_sub_epi16(A, B); // diff_y
+ const __m128i E = _mm_cmpgt_epi16(zero, D); // sign (-1 or 0)
+ const __m128i F = _mm_add_epi16(C, D); // new_y
+ const __m128i G = _mm_or_si128(E, one); // -1 or 1
+ const __m128i H = _mm_max_epi16(_mm_min_epi16(F, max), zero);
+ const __m128i I = _mm_madd_epi16(D, G); // sum(abs(...))
+ _mm_storeu_si128((__m128i*)(dst + i), H);
+ sum = _mm_add_epi32(sum, I);
+ }
+ _mm_storeu_si128((__m128i*)tmp, sum);
+ diff = tmp[3] + tmp[2] + tmp[1] + tmp[0];
+ for (; i < len; ++i) {
+ const int diff_y = ref[i] - src[i];
+ const int new_y = (int)dst[i] + diff_y;
+ dst[i] = clip_SSE2(new_y, max_y);
+ diff += (uint64_t)abs(diff_y);
+ }
+ return diff;
+}
+
+static void SharpYuvUpdateRGB_SSE2(const int16_t* ref, const int16_t* src,
+ int16_t* dst, int len) {
+ int i = 0;
+ for (i = 0; i + 8 <= len; i += 8) {
+ const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
+ const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
+ const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
+ const __m128i D = _mm_sub_epi16(A, B); // diff_uv
+ const __m128i E = _mm_add_epi16(C, D); // new_uv
+ _mm_storeu_si128((__m128i*)(dst + i), E);
+ }
+ for (; i < len; ++i) {
+ const int diff_uv = ref[i] - src[i];
+ dst[i] += diff_uv;
+ }
+}
+
+static void SharpYuvFilterRow16_SSE2(const int16_t* A, const int16_t* B,
+ int len, const uint16_t* best_y,
+ uint16_t* out, int bit_depth) {
+ const int max_y = (1 << bit_depth) - 1;
+ int i;
+ const __m128i kCst8 = _mm_set1_epi16(8);
+ const __m128i max = _mm_set1_epi16(max_y);
+ const __m128i zero = _mm_setzero_si128();
+ for (i = 0; i + 8 <= len; i += 8) {
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)(A + i + 0));
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)(A + i + 1));
+ const __m128i b0 = _mm_loadu_si128((const __m128i*)(B + i + 0));
+ const __m128i b1 = _mm_loadu_si128((const __m128i*)(B + i + 1));
+ const __m128i a0b1 = _mm_add_epi16(a0, b1);
+ const __m128i a1b0 = _mm_add_epi16(a1, b0);
+ const __m128i a0a1b0b1 = _mm_add_epi16(a0b1, a1b0); // A0+A1+B0+B1
+ const __m128i a0a1b0b1_8 = _mm_add_epi16(a0a1b0b1, kCst8);
+ const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1); // 2*(A0+B1)
+ const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0); // 2*(A1+B0)
+ const __m128i c0 = _mm_srai_epi16(_mm_add_epi16(a0b1_2, a0a1b0b1_8), 3);
+ const __m128i c1 = _mm_srai_epi16(_mm_add_epi16(a1b0_2, a0a1b0b1_8), 3);
+ const __m128i d0 = _mm_add_epi16(c1, a0);
+ const __m128i d1 = _mm_add_epi16(c0, a1);
+ const __m128i e0 = _mm_srai_epi16(d0, 1);
+ const __m128i e1 = _mm_srai_epi16(d1, 1);
+ const __m128i f0 = _mm_unpacklo_epi16(e0, e1);
+ const __m128i f1 = _mm_unpackhi_epi16(e0, e1);
+ const __m128i g0 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
+ const __m128i g1 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 8));
+ const __m128i h0 = _mm_add_epi16(g0, f0);
+ const __m128i h1 = _mm_add_epi16(g1, f1);
+ const __m128i i0 = _mm_max_epi16(_mm_min_epi16(h0, max), zero);
+ const __m128i i1 = _mm_max_epi16(_mm_min_epi16(h1, max), zero);
+ _mm_storeu_si128((__m128i*)(out + 2 * i + 0), i0);
+ _mm_storeu_si128((__m128i*)(out + 2 * i + 8), i1);
+ }
+ for (; i < len; ++i) {
+ // (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
+ // = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
+ // We reuse the common sub-expressions.
+ const int a0b1 = A[i + 0] + B[i + 1];
+ const int a1b0 = A[i + 1] + B[i + 0];
+ const int a0a1b0b1 = a0b1 + a1b0 + 8;
+ const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+ const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+ out[2 * i + 0] = clip_SSE2(best_y[2 * i + 0] + v0, max_y);
+ out[2 * i + 1] = clip_SSE2(best_y[2 * i + 1] + v1, max_y);
+ }
+}
+
+static WEBP_INLINE __m128i s16_to_s32(__m128i in) {
+ return _mm_srai_epi32(_mm_unpacklo_epi16(in, in), 16);
+}
+
+static void SharpYuvFilterRow32_SSE2(const int16_t* A, const int16_t* B,
+ int len, const uint16_t* best_y,
+ uint16_t* out, int bit_depth) {
+ const int max_y = (1 << bit_depth) - 1;
+ int i;
+ const __m128i kCst8 = _mm_set1_epi32(8);
+ const __m128i max = _mm_set1_epi16(max_y);
+ const __m128i zero = _mm_setzero_si128();
+ for (i = 0; i + 4 <= len; i += 4) {
+ const __m128i a0 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(A + i + 0)));
+ const __m128i a1 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(A + i + 1)));
+ const __m128i b0 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(B + i + 0)));
+ const __m128i b1 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(B + i + 1)));
+ const __m128i a0b1 = _mm_add_epi32(a0, b1);
+ const __m128i a1b0 = _mm_add_epi32(a1, b0);
+ const __m128i a0a1b0b1 = _mm_add_epi32(a0b1, a1b0); // A0+A1+B0+B1
+ const __m128i a0a1b0b1_8 = _mm_add_epi32(a0a1b0b1, kCst8);
+ const __m128i a0b1_2 = _mm_add_epi32(a0b1, a0b1); // 2*(A0+B1)
+ const __m128i a1b0_2 = _mm_add_epi32(a1b0, a1b0); // 2*(A1+B0)
+ const __m128i c0 = _mm_srai_epi32(_mm_add_epi32(a0b1_2, a0a1b0b1_8), 3);
+ const __m128i c1 = _mm_srai_epi32(_mm_add_epi32(a1b0_2, a0a1b0b1_8), 3);
+ const __m128i d0 = _mm_add_epi32(c1, a0);
+ const __m128i d1 = _mm_add_epi32(c0, a1);
+ const __m128i e0 = _mm_srai_epi32(d0, 1);
+ const __m128i e1 = _mm_srai_epi32(d1, 1);
+ const __m128i f0 = _mm_unpacklo_epi32(e0, e1);
+ const __m128i f1 = _mm_unpackhi_epi32(e0, e1);
+ const __m128i g = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
+ const __m128i h_16 = _mm_add_epi16(g, _mm_packs_epi32(f0, f1));
+ const __m128i final = _mm_max_epi16(_mm_min_epi16(h_16, max), zero);
+ _mm_storeu_si128((__m128i*)(out + 2 * i + 0), final);
+ }
+ for (; i < len; ++i) {
+ // (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
+ // = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
+ // We reuse the common sub-expressions.
+ const int a0b1 = A[i + 0] + B[i + 1];
+ const int a1b0 = A[i + 1] + B[i + 0];
+ const int a0a1b0b1 = a0b1 + a1b0 + 8;
+ const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+ const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+ out[2 * i + 0] = clip_SSE2(best_y[2 * i + 0] + v0, max_y);
+ out[2 * i + 1] = clip_SSE2(best_y[2 * i + 1] + v1, max_y);
+ }
+}
+
+static void SharpYuvFilterRow_SSE2(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out,
+ int bit_depth) {
+ if (bit_depth <= 10) {
+ SharpYuvFilterRow16_SSE2(A, B, len, best_y, out, bit_depth);
+ } else {
+ SharpYuvFilterRow32_SSE2(A, B, len, best_y, out, bit_depth);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+extern void InitSharpYuvSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void InitSharpYuvSSE2(void) {
+ SharpYuvUpdateY = SharpYuvUpdateY_SSE2;
+ SharpYuvUpdateRGB = SharpYuvUpdateRGB_SSE2;
+ SharpYuvFilterRow = SharpYuvFilterRow_SSE2;
+}
+#else // !WEBP_USE_SSE2
+
+extern void InitSharpYuvSSE2(void);
+
+void InitSharpYuvSSE2(void) {}
+
+#endif // WEBP_USE_SSE2
diff --git a/c-lib/src/Makefile.am b/c-lib/src/Makefile.am
@@ -36,7 +36,7 @@ libwebp_la_LIBADD += utils/libwebputils.la
# other than the ones listed on the command line, i.e., after linking, it will
# not have unresolved symbols. Some platforms (Windows among them) require all
# symbols in shared libraries to be resolved at library creation.
-libwebp_la_LDFLAGS = -no-undefined -version-info 8:3:1
+libwebp_la_LDFLAGS = -no-undefined -version-info 9:0:2
libwebpincludedir = $(includedir)/webp
pkgconfig_DATA = libwebp.pc
@@ -48,7 +48,7 @@ if BUILD_LIBWEBPDECODER
libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
- libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 4:3:1
+ libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 5:0:2
pkgconfig_DATA += libwebpdecoder.pc
endif
diff --git a/c-lib/src/dec/alpha_dec.c b/c-lib/src/dec/alpha_dec.c
@@ -11,20 +11,26 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <assert.h>
#include <stdlib.h>
+
#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"
//------------------------------------------------------------------------------
// ALPHDecoder object.
// Allocates a new alpha decoder instance.
-static ALPHDecoder* ALPHNew(void) {
+WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) {
ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
return dec;
}
@@ -32,8 +38,8 @@ 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);
}
}
@@ -45,41 +51,43 @@ static void ALPHDelete(ALPHDecoder* const dec) {
// header for alpha data stored using lossless compression.
// Returns false in case of error in alpha header (data too short, invalid
// compression method or filter, error in lossless header data etc).
-static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
- size_t data_size, const VP8Io* const src_io,
- uint8_t* output) {
+WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
+ size_t data_size, const VP8Io* const src_io,
+ uint8_t* output) {
int ok = 0;
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
int rsrv;
- VP8Io* const io = &dec->io_;
+ 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;
}
// Copy the necessary parameters from src_io to io
- VP8InitIo(io);
+ if (!VP8InitIo(io)) {
+ return 0;
+ }
WebPInitCustomIo(NULL, io);
io->opaque = dec;
io->width = src_io->width;
@@ -92,11 +100,11 @@ 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);
}
@@ -107,76 +115,70 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
// starting from row number 'row'. It assumes that rows up to (row - 1) have
// already been decoded.
// Returns false in case of bitstream error.
-static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) {
- ALPHDecoder* const alph_dec = dec->alph_dec_;
- const int width = alph_dec->width_;
- const int height = alph_dec->io_.crop_bottom;
- if (alph_dec->method_ == ALPHA_NO_COMPRESSION) {
+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) {
int y;
- const uint8_t* prev_line = dec->alpha_prev_line_;
- const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width;
- uint8_t* dst = dec->alpha_plane_ + row * width;
- assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]);
- if (alph_dec->filter_ != WEBP_FILTER_NONE) {
- assert(WebPUnfilters[alph_dec->filter_] != NULL);
- for (y = 0; y < num_rows; ++y) {
- WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width);
- prev_line = dst;
- dst += width;
- deltas += width;
- }
- } else {
- for (y = 0; y < num_rows; ++y) {
- memcpy(dst, deltas, width * sizeof(*dst));
- prev_line = dst;
- dst += width;
- deltas += width;
- }
+ 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);
+ 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;
}
-static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) {
+WEBP_NODISCARD static int AllocateAlphaPlane(VP8Decoder* const dec,
+ const VP8Io* const io) {
const int stride = io->width;
const int height = io->crop_bottom;
const uint64_t alpha_size = (uint64_t)stride * height;
- assert(dec->alpha_plane_mem_ == NULL);
- dec->alpha_plane_mem_ =
- (uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_));
- if (dec->alpha_plane_mem_ == NULL) {
- return 0;
+ 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;
}
//------------------------------------------------------------------------------
// Main entry point.
-const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
- const VP8Io* const io,
- int row, int num_rows) {
+WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
+ const VP8Io* const io,
+ int row, int num_rows) {
const int width = io->width;
const int height = io->crop_bottom;
@@ -186,37 +188,46 @@ 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) return 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_)) {
+ 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,
+ "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;
}
}
@@ -224,7 +235,7 @@ 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/c-lib/src/dec/alphai_dec.h b/c-lib/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/c-lib/src/dec/buffer_dec.c b/c-lib/src/dec/buffer_dec.c
@@ -11,11 +11,16 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <assert.h>
#include <stdlib.h>
+#include <string.h>
#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);
}
@@ -75,7 +79,7 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
const int stride = abs(buf->stride);
const uint64_t size =
- MIN_BUFFER_SIZE(width * kModeBpp[mode], height, stride);
+ MIN_BUFFER_SIZE((uint64_t)width * kModeBpp[mode], height, stride);
ok &= (size <= buf->size);
ok &= (stride >= width * kModeBpp[mode]);
ok &= (buf->rgba != NULL);
diff --git a/c-lib/src/dec/common_dec.h b/c-lib/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/c-lib/src/dec/frame_dec.c b/c-lib/src/dec/frame_dec.c
@@ -11,9 +11,20 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <assert.h>
#include <stdlib.h>
+#include <string.h>
+
+#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/c-lib/src/dec/idec_dec.c b/c-lib/src/dec/idec_dec.c
@@ -12,13 +12,20 @@
// Author: somnath@google.com (Somnath Banerjee)
#include <assert.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#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.
@@ -52,133 +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()
-
- size_t part0_size_; // size of partition #0
- const uint8_t* part0_buf_; // buffer to store partition #0
+ MemBufferMode mode; // Operation mode
+ size_t start; // start location of the data to be decoded
+ size_t end; // end location
+ size_t buf_size; // size of the allocated buffer
+ uint8_t* buf; // We don't own this buffer in case WebPIUpdate()
+
+ size_t part0_size; // size of partition #0
+ const uint8_t* part0_buf; // buffer to store partition #0
} MemBuffer;
struct WebPIDecoder {
- DecState state_; // current decoding state
- WebPDecParams params_; // Params to store output info
- int is_lossless_; // for down-casting 'dec_'.
- void* dec_; // either a VP8Decoder or a VP8LDecoder instance
- VP8Io io_;
-
- MemBuffer mem_; // input memory buffer.
- WebPDecBuffer output_; // output buffer (when no external one is supplied,
- // or if the external one has slow-memory)
- WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually.
- size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
-
- int last_mb_y_; // last row reached for intra-mode decoding
+ DecState state; // current decoding state
+ WebPDecParams params; // Params to store output info
+ int is_lossless; // for down-casting 'dec'.
+ void* dec; // either a VP8Decoder or a VP8LDecoder instance
+ VP8Io io;
+
+ MemBuffer mem; // input memory buffer.
+ WebPDecBuffer output; // output buffer (when no external one is supplied,
+ // or if the external one has slow-memory)
+ WebPDecBuffer* final_output; // Slow-memory output to copy to eventually.
+ size_t chunk_size; // Compressed VP8/VP8L size extracted from Header.
+
+ int last_mb_y; // last row reached for intra-mode decoding
};
// MB context to restore in case VP8DecodeMB() fails
typedef struct {
- VP8MB left_;
- VP8MB info_;
- VP8BitReader token_br_;
+ 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.
-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_;
+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;
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;
@@ -187,81 +201,85 @@ 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;
}
-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_;
+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;
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);
}
}
-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) {
+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) {
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.
-static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
- const WebPDecoderOptions* const options = idec->params_.options;
- WebPDecBuffer* const output = idec->params_.output;
+WEBP_NODISCARD static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
+ 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) {
- WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy
- WebPFreeDecBuffer(&idec->output_);
- *output = *idec->final_output_;
- idec->final_output_ = NULL;
+ if (idec->final_output != NULL) {
+ const VP8StatusCode status = WebPCopyDecBufferPixels(
+ 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;
}
return VP8_STATUS_OK;
}
@@ -271,43 +289,43 @@ 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.
- 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;
@@ -322,31 +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;
}
- 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;
@@ -354,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
@@ -418,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);
}
}
@@ -488,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
@@ -502,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);
}
@@ -518,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;
@@ -601,34 +620,37 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
//------------------------------------------------------------------------------
// Internal constructor
-static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer,
- const WebPBitstreamFeatures* const features) {
+WEBP_NODISCARD static WebPIDecoder* NewDecoder(
+ WebPDecBuffer* const output_buffer,
+ const WebPBitstreamFeatures* const features) {
WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
if (idec == NULL) {
return NULL;
}
- idec->state_ = STATE_WEBP_HEADER;
- idec->chunk_size_ = 0;
+ idec->state = STATE_WEBP_HEADER;
+ idec->chunk_size = 0;
- idec->last_mb_y_ = -1;
+ idec->last_mb_y = -1;
- InitMemBuffer(&idec->mem_);
- 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;
}
@@ -663,26 +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.
- VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
+ // TODO(vrabaud) do we care about the return result?
+ (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);
}
@@ -705,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;
}
@@ -739,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;
}
@@ -769,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;
@@ -789,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
@@ -810,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
@@ -823,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,
@@ -843,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;
@@ -851,15 +874,15 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
return src;
}
-uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
- int* width, int* height, int* stride) {
+WEBP_NODISCARD uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
+ int* width, int* height, int* stride) {
const WebPDecBuffer* const src = GetOutputBuffer(idec);
if (src == NULL) return NULL;
if (src->colorspace >= MODE_YUV) {
return NULL;
}
- if (last_y != NULL) *last_y = idec->params_.last_y;
+ if (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;
@@ -867,17 +890,17 @@ uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
return src->u.RGBA.rgba;
}
-uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
- uint8_t** u, uint8_t** v, uint8_t** a,
- int* width, int* height,
- int* stride, int* uv_stride, int* a_stride) {
+WEBP_NODISCARD uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
+ uint8_t** u, uint8_t** v, uint8_t** a,
+ int* width, int* height, int* stride,
+ int* uv_stride, int* a_stride) {
const WebPDecBuffer* const src = GetOutputBuffer(idec);
if (src == NULL) return NULL;
if (src->colorspace < MODE_YUV) {
return NULL;
}
- if (last_y != NULL) *last_y = idec->params_.last_y;
+ if (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;
@@ -895,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/c-lib/src/dec/io_dec.c b/c-lib/src/dec/io_dec.c
@@ -12,12 +12,20 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <stddef.h>
#include <stdlib.h>
+#include <string.h>
+
+#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/c-lib/src/dec/quant_dec.c b/c-lib/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/c-lib/src/dec/tree_dec.c b/c-lib/src/dec/tree_dec.c
@@ -11,11 +11,19 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <string.h>
+
+#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) && !defined(__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
@@ -282,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];
@@ -352,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;
}
//------------------------------------------------------------------------------
@@ -512,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) {
@@ -522,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/c-lib/src/dec/vp8_dec.c b/c-lib/src/dec/vp8_dec.c
@@ -11,14 +11,25 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <assert.h>
#include <stdlib.h>
+#include <string.h>
#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) {
@@ -86,11 +97,13 @@ 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);
// 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;
}
@@ -149,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
@@ -161,46 +174,46 @@ 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
-// This function returns VP8_STATUS_SUSPENDED if we don't have all the
-// necessary data in 'buf'.
-// This case is not necessarily an error (for incremental decoding).
-// Still, no bitreader is ever initialized to make it possible to read
-// unavailable memory.
-// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
+// If we don't have all the necessary data in 'buf', this function returns
+// VP8_STATUS_SUSPENDED in incremental decoding, VP8_STATUS_NOT_ENOUGH_DATA
+// otherwise.
+// In incremental decoding, this case is not necessarily an error. Still, no
+// bitreader is ever initialized to make it possible to read unavailable memory.
+// If we don't even have the partitions' sizes, then VP8_STATUS_NOT_ENOUGH_DATA
// is returned, and this is an unrecoverable error.
// If the partitions were positioned ok, VP8_STATUS_OK is returned.
static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
const uint8_t* buf, size_t size) {
- VP8BitReader* const br = &dec->br_;
+ VP8BitReader* const br = &dec->br;
const uint8_t* sz = buf;
const uint8_t* buf_end = buf + size;
const uint8_t* part_start;
@@ -208,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;
@@ -219,40 +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);
- return (part_start < buf_end) ? VP8_STATUS_OK :
- VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
+ VP8InitBitReader(dec->parts + last_part, part_start, size_left);
+ if (part_start < buf_end) return VP8_STATUS_OK;
+ 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
@@ -282,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.");
}
@@ -299,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,
@@ -310,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;
@@ -338,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");
}
@@ -376,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;
}
@@ -439,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;
@@ -469,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;
@@ -494,6 +509,8 @@ static int GetCoeffsAlt(VP8BitReader* const br,
return 16;
}
+extern VP8CPUInfo VP8GetCPUInfo;
+
WEBP_DSP_INIT_FUNC(InitGetCoeffs) {
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
GetCoeffs = GetCoeffsAlt;
@@ -510,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;
@@ -524,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
@@ -543,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);
@@ -565,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);
@@ -585,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
}
@@ -603,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.");
@@ -659,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;
@@ -677,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);
@@ -702,7 +719,7 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
return 0;
}
- dec->ready_ = 0;
+ dec->ready = 0;
return ok;
}
@@ -710,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/c-lib/src/dec/vp8_dec.h b/c-lib/src/dec/vp8_dec.h
@@ -14,7 +14,10 @@
#ifndef WEBP_DEC_VP8_DEC_H_
#define WEBP_DEC_VP8_DEC_H_
+#include <stddef.h>
+
#include "src/webp/decode.h"
+#include "src/webp/types.h"
#ifdef __cplusplus
extern "C" {
@@ -108,16 +111,14 @@ struct VP8Io {
};
// Internal, version-checked, entry point
-int VP8InitIoInternal(VP8Io* const, int);
+WEBP_NODISCARD int VP8InitIoInternal(VP8Io* const, int);
// Set the custom IO function pointers and user-data. The setter for IO hooks
// should be called before initiating incremental decoding. Returns true if
// WebPIDecoder object is successfully modified, false otherwise.
-int WebPISetIOHooks(WebPIDecoder* const idec,
- VP8IoPutHook put,
- VP8IoSetupHook setup,
- VP8IoTeardownHook teardown,
- void* user_data);
+WEBP_NODISCARD int WebPISetIOHooks(WebPIDecoder* const idec, VP8IoPutHook put,
+ VP8IoSetupHook setup,
+ VP8IoTeardownHook teardown, void* user_data);
// Main decoding object. This is an opaque structure.
typedef struct VP8Decoder VP8Decoder;
@@ -128,17 +129,17 @@ VP8Decoder* VP8New(void);
// Must be called to make sure 'io' is initialized properly.
// Returns false in case of version mismatch. Upon such failure, no other
// decoding function should be called (VP8Decode, VP8GetHeaders, ...)
-static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
+WEBP_NODISCARD static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
}
// Decode the VP8 frame header. Returns true if ok.
// Note: 'io->data' must be pointing to the start of the VP8 frame header.
-int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
+WEBP_NODISCARD int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
// Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
// Returns false in case of error.
-int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
+WEBP_NODISCARD int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
// Return current status of the decoder:
VP8StatusCode VP8Status(VP8Decoder* const dec);
diff --git a/c-lib/src/dec/vp8i_dec.h b/c-lib/src/dec/vp8i_dec.h
@@ -15,12 +15,17 @@
#define WEBP_DEC_VP8I_DEC_H_
#include <string.h> // 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
extern "C" {
@@ -31,8 +36,8 @@ extern "C" {
// version numbers
#define DEC_MAJ_VERSION 1
-#define DEC_MIN_VERSION 2
-#define DEC_REV_VERSION 2
+#define DEC_MIN_VERSION 6
+#define DEC_REV_VERSION 0
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
// Constraints are: We need to store one 16x16 block of luma samples (y),
@@ -68,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
@@ -154,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.
@@ -180,88 +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_;
+ 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)
};
//------------------------------------------------------------------------------
@@ -281,7 +287,7 @@ int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec);
void VP8ParseQuant(VP8Decoder* const dec);
// in frame.c
-int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
+WEBP_NODISCARD int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
// Call io->setup() and finish setting up scan parameters.
// After this call returns, one must always call VP8ExitCritical() with the
// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
@@ -289,7 +295,7 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
// Must always be called in pair with VP8EnterCritical().
// Returns false in case of error.
-int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
+WEBP_NODISCARD int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
// Return the multi-threading method to use (0=off), depending
// on options and bitstream size. Only for lossy decoding.
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
@@ -299,11 +305,12 @@ int VP8GetThreadMethod(const WebPDecoderOptions* const options,
void VP8InitDithering(const WebPDecoderOptions* const options,
VP8Decoder* const dec);
// Process the last decoded row (filtering + output).
-int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
+WEBP_NODISCARD int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
// To be called at the start of a new scanline, to initialize predictors.
void VP8InitScanline(VP8Decoder* const dec);
// Decode one macroblock. Returns false if there is not enough data.
-int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
+WEBP_NODISCARD int VP8DecodeMB(VP8Decoder* const dec,
+ VP8BitReader* const token_br);
// in alpha.c
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
diff --git a/c-lib/src/dec/vp8l_dec.c b/c-lib/src/dec/vp8l_dec.c
@@ -12,17 +12,26 @@
// Authors: Vikas Arora (vikaas.arora@gmail.com)
// Jyrki Alakuijala (jyrki@google.com)
+#include <assert.h>
+#include <stddef.h>
#include <stdlib.h>
+#include <string.h>
#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
@@ -101,6 +110,14 @@ static const uint16_t kTableSize[12] = {
FIXED_TABLE_SIZE + 2704
};
+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;
+ }
+ return 0;
+}
+
static int DecodeImageStream(int xsize, int ysize,
int is_level0,
VP8LDecoder* const dec,
@@ -122,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,
@@ -178,7 +195,7 @@ static WEBP_INLINE int PlaneCodeToDistance(int xsize, int plane_code) {
//------------------------------------------------------------------------------
// Decodes the next Huffman code from bit-stream.
-// FillBitWindow(br) needs to be called at minimum every second call
+// VP8LFillBitWindow(br) needs to be called at minimum every second call
// to ReadSymbol, in order to pre-fetch enough bits.
static WEBP_INLINE int ReadSymbol(const HuffmanCode* table,
VP8LBitReader* const br) {
@@ -187,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;
}
@@ -206,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;
}
@@ -249,15 +266,15 @@ 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;
- HuffmanCode table[1 << LENGTHS_TABLE_BITS];
+ HuffmanTables tables;
- if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS,
- code_length_code_lengths,
- NUM_CODE_LENGTH_CODES)) {
+ if (!VP8LHuffmanTablesAllocate(1 << LENGTHS_TABLE_BITS, &tables) ||
+ !VP8LBuildHuffmanTable(&tables, LENGTHS_TABLE_BITS,
+ code_length_code_lengths, NUM_CODE_LENGTH_CODES)) {
goto End;
}
@@ -277,8 +294,8 @@ static int ReadHuffmanCodeLengths(
int code_len;
if (max_symbol-- == 0) break;
VP8LFillBitWindow(br);
- p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
- VP8LSetBitPos(br, br->bit_pos_ + p->bits);
+ p = &tables.curr_segment->start[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
+ VP8LSetBitPos(br, br->bit_pos + p->bits);
code_len = p->value;
if (code_len < kCodeLengthLiterals) {
code_lengths[symbol++] = code_len;
@@ -300,17 +317,19 @@ static int ReadHuffmanCodeLengths(
ok = 1;
End:
- if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ VP8LHuffmanTablesDeallocate(&tables);
+ if (!ok) return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
return ok;
}
// 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman
// tree.
static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
- int* const code_lengths, HuffmanCode* const table) {
+ int* const code_lengths,
+ 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));
@@ -321,7 +340,7 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
// The first code is either 1 bit or 8 bit code.
int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8);
code_lengths[symbol] = 1;
- // The second code (if present), is always 8 bit long.
+ // The second code (if present), is always 8 bits long.
if (num_symbols == 2) {
symbol = VP8LReadBits(br, 8);
code_lengths[symbol] = 1;
@@ -331,10 +350,7 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
int i;
int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 };
const int num_codes = VP8LReadBits(br, 4) + 4;
- if (num_codes > NUM_CODE_LENGTH_CODES) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- return 0;
- }
+ assert(num_codes <= NUM_CODE_LENGTH_CODES);
for (i = 0; i < num_codes; ++i) {
code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3);
@@ -343,46 +359,46 @@ 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);
}
if (!ok || size == 0) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- return 0;
+ return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
}
return size;
}
static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
int color_cache_bits, int allow_recursion) {
- int i, j;
- VP8LBitReader* const br = &dec->br_;
- VP8LMetadata* const hdr = &dec->hdr_;
+ int i;
+ VP8LBitReader* const br = &dec->br;
+ VP8LMetadata* const hdr = &dec->hdr;
uint32_t* huffman_image = NULL;
HTreeGroup* htree_groups = NULL;
- HuffmanCode* huffman_tables = NULL;
- HuffmanCode* huffman_table = NULL;
+ HuffmanTables* huffman_tables = &hdr->huffman_tables;
int num_htree_groups = 1;
int num_htree_groups_max = 1;
- int max_alphabet_size = 0;
- int* code_lengths = NULL;
- const int table_size = kTableSize[color_cache_bits];
int* mapping = NULL;
int ok = 0;
+ // Check the table has been 0 initialized (through InitMetadata).
+ assert(huffman_tables->root.start == NULL);
+ assert(huffman_tables->curr_segment == NULL);
+
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;
- if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec,
+ if (!DecodeImageStream(huffman_xsize, huffman_ysize, /*is_level0=*/0, dec,
&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;
@@ -402,7 +418,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
// values [0, num_htree_groups)
mapping = (int*)WebPSafeMalloc(num_htree_groups_max, sizeof(*mapping));
if (mapping == NULL) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
goto Error;
}
// -1 means a value is unmapped, and therefore unused in the Huffman
@@ -419,31 +435,57 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
}
}
- if (br->eos_) goto Error;
+ if (br->eos) goto Error;
- // Find maximum alphabet size for the htree group.
- for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
- int alphabet_size = kAlphabetSize[j];
- if (j == 0 && color_cache_bits > 0) {
- alphabet_size += 1 << color_cache_bits;
- }
- if (max_alphabet_size < alphabet_size) {
- max_alphabet_size = alphabet_size;
- }
+ if (!ReadHuffmanCodesHelper(color_cache_bits, num_htree_groups,
+ num_htree_groups_max, mapping, dec,
+ huffman_tables, &htree_groups)) {
+ goto Error;
}
+ ok = 1;
- code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size,
- sizeof(*code_lengths));
- huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size,
- sizeof(*huffman_tables));
- htree_groups = VP8LHtreeGroupsNew(num_htree_groups);
+ // All OK. Finalize pointers.
+ hdr->huffman_image = huffman_image;
+ hdr->num_htree_groups = num_htree_groups;
+ hdr->htree_groups = htree_groups;
- if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ Error:
+ WebPSafeFree(mapping);
+ if (!ok) {
+ WebPSafeFree(huffman_image);
+ VP8LHuffmanTablesDeallocate(huffman_tables);
+ VP8LHtreeGroupsFree(htree_groups);
+ }
+ return ok;
+}
+
+int ReadHuffmanCodesHelper(int color_cache_bits, int num_htree_groups,
+ int num_htree_groups_max, const int* const mapping,
+ VP8LDecoder* const dec,
+ HuffmanTables* const huffman_tables,
+ HTreeGroup** const htree_groups) {
+ int i, j, ok = 0;
+ const int max_alphabet_size =
+ kAlphabetSize[0] + ((color_cache_bits > 0) ? 1 << color_cache_bits : 0);
+ const int table_size = kTableSize[color_cache_bits];
+ int* code_lengths = NULL;
+
+ if ((mapping == NULL && num_htree_groups != num_htree_groups_max) ||
+ num_htree_groups > num_htree_groups_max) {
+ goto Error;
+ }
+
+ code_lengths =
+ (int*)WebPSafeCalloc((uint64_t)max_alphabet_size, sizeof(*code_lengths));
+ *htree_groups = VP8LHtreeGroupsNew(num_htree_groups);
+
+ if (*htree_groups == NULL || code_lengths == NULL ||
+ !VP8LHuffmanTablesAllocate(num_htree_groups * table_size,
+ huffman_tables)) {
+ VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
goto Error;
}
- huffman_table = huffman_tables;
for (i = 0; i < num_htree_groups_max; ++i) {
// If the index "i" is unused in the Huffman image, just make sure the
// coefficients are valid but do not store them.
@@ -460,7 +502,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
}
} else {
HTreeGroup* const htree_group =
- &htree_groups[(mapping == NULL) ? i : mapping[i]];
+ &(*htree_groups)[(mapping == NULL) ? i : mapping[i]];
HuffmanCode** const htrees = htree_group->htrees;
int size;
int total_size = 0;
@@ -468,19 +510,20 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
int max_bits = 0;
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
int alphabet_size = kAlphabetSize[j];
- htrees[j] = huffman_table;
if (j == 0 && color_cache_bits > 0) {
alphabet_size += (1 << color_cache_bits);
}
- size = ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_table);
+ size =
+ ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_tables);
+ htrees[j] = huffman_tables->curr_segment->curr_table;
if (size == 0) {
goto Error;
}
if (is_trivial_literal && kLiteralMap[j] == 1) {
- is_trivial_literal = (huffman_table->bits == 0);
+ is_trivial_literal = (htrees[j]->bits == 0);
}
- total_size += huffman_table->bits;
- huffman_table += size;
+ total_size += htrees[j]->bits;
+ huffman_tables->curr_segment->curr_table += size;
if (j <= ALPHA) {
int local_max_bits = code_lengths[0];
int k;
@@ -511,19 +554,12 @@ 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_tables_ = huffman_tables;
-
Error:
WebPSafeFree(code_lengths);
- WebPSafeFree(mapping);
if (!ok) {
- WebPSafeFree(huffman_image);
- WebPSafeFree(huffman_tables);
- VP8LHtreeGroupsFree(htree_groups);
+ VP8LHuffmanTablesDeallocate(huffman_tables);
+ VP8LHtreeGroupsFree(*htree_groups);
+ *htree_groups = NULL;
}
return ok;
}
@@ -547,8 +583,7 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
scaled_data_size * sizeof(*scaled_data);
uint8_t* memory = (uint8_t*)WebPSafeMalloc(memory_size, sizeof(*memory));
if (memory == NULL) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- return 0;
+ return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
}
assert(dec->rescaler_memory == NULL);
dec->rescaler_memory = memory;
@@ -594,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;
@@ -669,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;
}
@@ -679,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);
@@ -698,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;
}
@@ -747,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;
}
//------------------------------------------------------------------------------
@@ -761,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;
}
@@ -782,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 ?
@@ -810,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;
@@ -845,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;
}
//------------------------------------------------------------------------------
@@ -1009,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) {
@@ -1074,39 +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)) {
- ok = 0;
- dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED
- : VP8_STATUS_BITSTREAM_ERROR;
- } else {
- dec->last_pixel_ = pos;
+ br->eos = VP8LIsEndOfStream(br);
+ if (!ok || (br->eos && pos < end)) {
+ return VP8LSetError(
+ dec, br->eos ? VP8_STATUS_SUSPENDED : VP8_STATUS_BITSTREAM_ERROR);
}
+ 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);
}
}
@@ -1114,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) {
@@ -1236,59 +1269,69 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
}
}
- br->eos_ = VP8LIsEndOfStream(br);
- if (dec->incremental_ && br->eos_ && src < src_end) {
+ br->eos = VP8LIsEndOfStream(br);
+ // In incremental decoding:
+ // 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
+ // 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
+ // not matter.
+ assert(!dec->incremental || (br->eos && src < src_last) || src >= src_last);
+ if (dec->incremental && br->eos && src < src_last) {
RestoreState(dec);
- } else if (!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;
}
return 1;
Error:
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- return 0;
+ return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
}
// -----------------------------------------------------------------------------
// 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 AddPixelEq(), on a byte-basis.
+ // Equivalent to VP8LAddPixels(), on a byte-basis.
new_data[i] = (data[i] + new_data[i - 4]) & 0xff;
}
for (; i < 4 * final_num_colors; ++i) {
new_data[i] = 0; // black tail.
}
- WebPSafeFree(transform->data_);
- transform->data_ = new_color_map;
+ WebPSafeFree(transform->data);
+ transform->data = new_color_map;
}
return 1;
}
@@ -1296,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_),
- 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;
@@ -1330,13 +1374,16 @@ 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;
- ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_);
- ok = ok && ExpandColorMap(num_colors, transform);
+ *xsize = VP8LSubSampleSize(transform->xsize, bits);
+ transform->bits = bits;
+ ok = DecodeImageStream(num_colors, /*ysize=*/1, /*is_level0=*/0, dec,
+ &transform->data);
+ if (ok && !ExpandColorMap(num_colors, transform)) {
+ return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
+ }
break;
}
- case SUBTRACT_GREEN:
+ case SUBTRACT_GREEN_TRANSFORM:
break;
default:
assert(0); // can't happen
@@ -1357,11 +1404,11 @@ static void InitMetadata(VP8LMetadata* const hdr) {
static void ClearMetadata(VP8LMetadata* const hdr) {
assert(hdr != NULL);
- WebPSafeFree(hdr->huffman_image_);
- WebPSafeFree(hdr->huffman_tables_);
- VP8LHtreeGroupsFree(hdr->htree_groups_);
- VP8LColorCacheClear(&hdr->color_cache_);
- VP8LColorCacheClear(&hdr->saved_color_cache_);
+ WebPSafeFree(hdr->huffman_image);
+ VP8LHuffmanTablesDeallocate(&hdr->huffman_tables);
+ VP8LHtreeGroupsFree(hdr->htree_groups);
+ VP8LColorCacheClear(&hdr->color_cache);
+ VP8LColorCacheClear(&hdr->saved_color_cache);
InitMetadata(hdr);
}
@@ -1371,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) {
@@ -1406,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,
@@ -1422,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;
@@ -1439,7 +1488,7 @@ static int DecodeImageStream(int xsize, int ysize,
color_cache_bits = VP8LReadBits(br, 4);
ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS);
if (!ok) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
goto End;
}
}
@@ -1448,25 +1497,24 @@ static int DecodeImageStream(int xsize, int ysize,
ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize,
color_cache_bits, is_level0);
if (!ok) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
goto End;
}
// Finish setting up the color-cache
if (color_cache_bits > 0) {
- hdr->color_cache_size_ = 1 << color_cache_bits;
- if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- ok = 0;
+ 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;
}
@@ -1474,8 +1522,7 @@ static int DecodeImageStream(int xsize, int ysize,
const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize;
data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data));
if (data == NULL) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- ok = 0;
+ ok = VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
goto End;
}
}
@@ -1483,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) {
@@ -1498,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;
@@ -1516,24 +1563,22 @@ 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
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- return 0;
+ 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) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- return 0;
+ 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;
}
@@ -1542,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,
@@ -1578,38 +1623,39 @@ 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_, 1, dec, NULL)) {
+ if (!DecodeImageStream(alph_dec->width, alph_dec->height, /*is_level0=*/1,
+ dec, /*decoded_data=*/NULL)) {
goto Err;
}
// Special case: if alpha data uses only the color indexing transform and
// doesn't use color cache (a frequent case), we will use DecodeAlphaData()
// method that only needs allocation of 1 byte per pixel (alpha channel).
- if (dec->next_transform_ == 1 &&
- dec->transforms_[0].type_ == COLOR_INDEXING_TRANSFORM &&
- Is8bOptimizable(&dec->hdr_)) {
- alph_dec->use_8b_decode_ = 1;
+ 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:
@@ -1618,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);
}
@@ -1643,27 +1689,29 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) {
if (dec == NULL) return 0;
if (io == NULL) {
- dec->status_ = VP8_STATUS_INVALID_PARAM;
- return 0;
+ 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->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ 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;
- if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error;
+ if (!DecodeImageStream(width, height, /*is_level0=*/1, dec,
+ /*decoded_data=*/NULL)) {
+ goto Error;
+ }
return 1;
Error:
VP8LClear(dec);
- assert(dec->status_ != VP8_STATUS_OK);
+ assert(dec->status != VP8_STATUS_OK);
return 0;
}
@@ -1673,22 +1721,22 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
if (dec == NULL) return 0;
- assert(dec->hdr_.huffman_tables_ != 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)) {
- dec->status_ = VP8_STATUS_INVALID_PARAM;
+ VP8LSetError(dec, VP8_STATUS_INVALID_PARAM);
goto Err;
}
@@ -1698,44 +1746,44 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err;
#else
if (io->use_scaling) {
- dec->status_ = VP8_STATUS_INVALID_PARAM;
+ VP8LSetError(dec, VP8_STATUS_INVALID_PARAM);
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_)) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ 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/c-lib/src/dec/vp8li_dec.h b/c-lib/src/dec/vp8li_dec.h
@@ -16,10 +16,16 @@
#define WEBP_DEC_VP8LI_DEC_H_
#include <string.h> // 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
extern "C" {
@@ -33,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 huffman_mask_;
- int huffman_subsample_bits_;
- int huffman_xsize_;
- uint32_t* huffman_image_;
- int num_htree_groups_;
- HTreeGroup* htree_groups_;
- HuffmanCode* huffman_tables_;
+ 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;
} 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.
@@ -99,33 +105,42 @@ struct ALPHDecoder; // Defined in dec/alphai.h.
// Decodes image header for alpha data stored using lossless compression.
// Returns false in case of error.
-int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
- const uint8_t* const data, size_t data_size);
+WEBP_NODISCARD int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
+ const uint8_t* const data,
+ size_t data_size);
// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are
// already decoded in previous call(s), it will resume decoding from where it
// was paused.
// Returns false in case of bitstream error.
-int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec,
- int last_row);
+WEBP_NODISCARD int VP8LDecodeAlphaImageStream(
+ struct ALPHDecoder* const alph_dec, int last_row);
// Allocates and initialize a new lossless decoder instance.
-VP8LDecoder* VP8LNew(void);
+WEBP_NODISCARD VP8LDecoder* VP8LNew(void);
// Decodes the image header. Returns false in case of error.
-int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io);
+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_.
-int VP8LDecodeImage(VP8LDecoder* const dec);
-
-// Resets the decoder in its initial state, reclaiming memory.
-// Preserves the dec->status_ value.
-void VP8LClear(VP8LDecoder* const dec);
+// this function. Returns false in case of error, with updated dec->status.
+WEBP_NODISCARD int VP8LDecodeImage(VP8LDecoder* const dec);
// Clears and deallocate a lossless decoder instance.
void VP8LDelete(VP8LDecoder* const dec);
+// Helper function for reading the different Huffman codes and storing them in
+// 'huffman_tables' and 'htree_groups'.
+// If mapping is NULL 'num_htree_groups_max' must equal 'num_htree_groups'.
+// If it is not NULL, it maps 'num_htree_groups_max' indices to the
+// 'num_htree_groups' groups. If 'num_htree_groups_max' > 'num_htree_groups',
+// some of those indices map to -1. This is used for non-balanced codes to
+// limit memory usage.
+WEBP_NODISCARD int ReadHuffmanCodesHelper(
+ int color_cache_bits, int num_htree_groups, int num_htree_groups_max,
+ const int* const mapping, VP8LDecoder* const dec,
+ HuffmanTables* const huffman_tables, HTreeGroup** const htree_groups);
+
//------------------------------------------------------------------------------
#ifdef __cplusplus
diff --git a/c-lib/src/dec/webp_dec.c b/c-lib/src/dec/webp_dec.c
@@ -11,13 +11,21 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <assert.h>
#include <stdlib.h>
+#include <string.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/utils/rescaler_utils.h"
#include "src/utils/utils.h"
+#include "src/webp/decode.h"
+#include "src/webp/format_constants.h"
#include "src/webp/mux_types.h" // ALPHA_FLAG
+#include "src/webp/types.h"
//------------------------------------------------------------------------------
// RIFF layout is:
@@ -179,7 +187,7 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
}
// For odd-sized chunk-payload, there's one byte padding at the end.
- disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
+ disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1u;
total_size += disk_chunk_size;
// Check that total bytes skipped so far does not exceed riff_size.
@@ -444,8 +452,9 @@ void WebPResetDecParams(WebPDecParams* const params) {
// "Into" decoding variants
// Main flow
-static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
- WebPDecParams* const params) {
+WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data,
+ size_t data_size,
+ WebPDecParams* const params) {
VP8StatusCode status;
VP8Io io;
WebPHeaderStructure headers;
@@ -459,7 +468,9 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
}
assert(params != NULL);
- VP8InitIo(&io);
+ if (!VP8InitIo(&io)) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
io.data = headers.data + headers.offset;
io.data_size = headers.data_size - headers.offset;
WebPInitCustomIo(params, &io); // Plug the I/O functions.
@@ -469,23 +480,23 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
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;
}
}
}
@@ -496,14 +507,14 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
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;
}
}
}
@@ -523,17 +534,16 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
}
// Helpers
-static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
- const uint8_t* const data,
- size_t data_size,
- uint8_t* const rgba,
- int stride, size_t size) {
+WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
+ const uint8_t* const data,
+ size_t data_size,
+ uint8_t* const rgba,
+ int stride, size_t size) {
WebPDecParams params;
WebPDecBuffer buf;
- if (rgba == NULL) {
+ if (rgba == NULL || !WebPInitDecBuffer(&buf)) {
return NULL;
}
- WebPInitDecBuffer(&buf);
WebPResetDecParams(¶ms);
params.output = &buf;
buf.colorspace = colorspace;
@@ -578,8 +588,7 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
uint8_t* v, size_t v_size, int v_stride) {
WebPDecParams params;
WebPDecBuffer output;
- if (luma == NULL) return NULL;
- WebPInitDecBuffer(&output);
+ if (luma == NULL || !WebPInitDecBuffer(&output)) return NULL;
WebPResetDecParams(¶ms);
params.output = &output;
output.colorspace = MODE_YUV;
@@ -601,13 +610,17 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
//------------------------------------------------------------------------------
-static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
- size_t data_size, int* const width, int* const height,
- WebPDecBuffer* const keep_info) {
+WEBP_NODISCARD static uint8_t* Decode(WEBP_CSP_MODE mode,
+ const uint8_t* const data,
+ size_t data_size, int* const width,
+ int* const height,
+ WebPDecBuffer* const keep_info) {
WebPDecParams params;
WebPDecBuffer output;
- WebPInitDecBuffer(&output);
+ if (!WebPInitDecBuffer(&output)) {
+ return NULL;
+ }
WebPResetDecParams(¶ms);
params.output = &output;
output.colorspace = mode;
@@ -658,19 +671,26 @@ uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
int* width, int* height, uint8_t** u, uint8_t** v,
int* stride, int* uv_stride) {
- WebPDecBuffer output; // only to preserve the side-infos
- uint8_t* const out = Decode(MODE_YUV, data, data_size,
- width, height, &output);
+ // data, width and height are checked by Decode().
+ if (u == NULL || v == NULL || stride == NULL || uv_stride == NULL) {
+ return NULL;
+ }
+
+ {
+ WebPDecBuffer output; // only to preserve the side-infos
+ uint8_t* const out = Decode(MODE_YUV, data, data_size,
+ width, height, &output);
- if (out != NULL) {
- const WebPYUVABuffer* const buf = &output.u.YUVA;
- *u = buf->u;
- *v = buf->v;
- *stride = buf->y_stride;
- *uv_stride = buf->u_stride;
- assert(buf->u_stride == buf->v_stride);
+ if (out != NULL) {
+ const WebPYUVABuffer* const buf = &output.u.YUVA;
+ *u = buf->u;
+ *v = buf->v;
+ *stride = buf->y_stride;
+ *uv_stride = buf->u_stride;
+ assert(buf->u_stride == buf->v_stride);
+ }
+ return out;
}
- return out;
}
static void DefaultFeatures(WebPBitstreamFeatures* const features) {
@@ -726,7 +746,64 @@ int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
}
memset(config, 0, sizeof(*config));
DefaultFeatures(&config->input);
- WebPInitDecBuffer(&config->output);
+ if (!WebPInitDecBuffer(&config->output)) {
+ return 0;
+ }
+ 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;
}
@@ -765,7 +842,9 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
if (WebPAvoidSlowMemory(params.output, &config->input)) {
// decoding to slow memory: use a temporary in-mem buffer to decode into.
WebPDecBuffer in_mem_buffer;
- WebPInitDecBuffer(&in_mem_buffer);
+ if (!WebPInitDecBuffer(&in_mem_buffer)) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
in_mem_buffer.colorspace = config->output.colorspace;
in_mem_buffer.width = config->input.width;
in_mem_buffer.height = config->input.height;
@@ -787,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/c-lib/src/dec/webpi_dec.h b/c-lib/src/dec/webpi_dec.h
@@ -18,8 +18,12 @@
extern "C" {
#endif
-#include "src/utils/rescaler_utils.h"
+#include <stddef.h>
+
#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.
@@ -87,8 +91,9 @@ void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io);
// Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers
// to the *compressed* format, not the output one.
-int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
- VP8Io* const io, WEBP_CSP_MODE src_colorspace);
+WEBP_NODISCARD int WebPIoInitFromOptions(
+ const WebPDecoderOptions* const options, VP8Io* const io,
+ WEBP_CSP_MODE src_colorspace);
//------------------------------------------------------------------------------
// Internal functions regarding WebPDecBuffer memory (in buffer.c).
diff --git a/c-lib/src/demux/Makefile.am b/c-lib/src/demux/Makefile.am
@@ -13,6 +13,6 @@ noinst_HEADERS =
noinst_HEADERS += ../webp/format_constants.h
libwebpdemux_la_LIBADD = ../libwebp.la
-libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:9:0
+libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:17:0
libwebpdemuxincludedir = $(includedir)/webp
pkgconfig_DATA = libwebpdemux.pc
diff --git a/c-lib/src/demux/anim_decode.c b/c-lib/src/demux/anim_decode.c
@@ -20,6 +20,9 @@
#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
@@ -38,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).
};
@@ -68,10 +71,11 @@ int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options,
return 1;
}
-static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options,
- WebPAnimDecoder* const dec) {
+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;
@@ -79,10 +83,12 @@ static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options,
mode != MODE_rgbA && mode != MODE_bgrA) {
return 0;
}
- dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA)
- ? &BlendPixelRowNonPremult
- : &BlendPixelRowPremult;
- WebPInitDecoderConfig(config);
+ dec->blend_func = (mode == MODE_RGBA || mode == MODE_BGRA)
+ ? &BlendPixelRowNonPremult
+ : &BlendPixelRowPremult;
+ if (!WebPInitDecoderConfig(config)) {
+ return 0;
+ }
config->output.colorspace = mode;
config->output.is_external_memory = 1;
config->options.use_threads = dec_options->use_threads;
@@ -119,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;
@@ -146,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;
}
@@ -157,8 +163,8 @@ static int IsFullFrame(int width, int height, int canvas_width,
}
// Clear the canvas to transparent.
-static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
- uint32_t canvas_height) {
+WEBP_NODISCARD static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
+ uint32_t canvas_height) {
const uint64_t size =
(uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf);
if (!CheckSizeOverflow(size)) return 0;
@@ -179,8 +185,8 @@ static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset,
}
// Copy width * height pixels from 'src' to 'dst'.
-static int CopyCanvas(const uint8_t* src, uint8_t* dst,
- uint32_t width, uint32_t height) {
+WEBP_NODISCARD static int CopyCanvas(const uint8_t* src, uint8_t* dst,
+ uint32_t width, uint32_t height) {
const uint64_t size = (uint64_t)width * height * NUM_CHANNELS;
if (!CheckSizeOverflow(size)) return 0;
assert(src != NULL && dst != NULL);
@@ -334,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;
}
@@ -366,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;
@@ -384,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.
@@ -403,37 +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;
- CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height);
- if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
- ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS,
- dec->prev_iter_.x_offset, dec->prev_iter_.y_offset,
- dec->prev_iter_.width, dec->prev_iter_.height);
+ dec->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);
}
- ++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;
@@ -444,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/c-lib/src/demux/demux.c b/c-lib/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 2
-#define DMUX_REV_VERSION 2
+#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,70 +590,67 @@ 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_;
- int frame_count = 0;
+ 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;
}
-
- ++frame_count;
}
}
return 1;
@@ -660,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
@@ -686,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;
@@ -737,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));
@@ -746,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);
@@ -769,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);
@@ -788,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;
}
@@ -803,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;
}
@@ -814,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;
}
@@ -837,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;
@@ -863,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;
@@ -899,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;
@@ -911,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;
}
@@ -933,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;
@@ -975,4 +975,3 @@ int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
(void)iter;
}
-
diff --git a/c-lib/src/demux/libwebpdemux.pc.in b/c-lib/src/demux/libwebpdemux.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: libwebpdemux
Description: Library for parsing the WebP graphics format container
Version: @PACKAGE_VERSION@
-Requires: libwebp >= 0.2.0
+Requires.private: libwebp >= 0.2.0
Cflags: -I${includedir}
-Libs: -L${libdir} -lwebpdemux
+Libs: -L${libdir} -l@webp_libname_prefix@webpdemux
diff --git a/c-lib/src/demux/libwebpdemux.rc b/c-lib/src/demux/libwebpdemux.rc
@@ -6,8 +6,8 @@
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,0,2,2
- PRODUCTVERSION 1,0,2,2
+ FILEVERSION 1,0,6,0
+ PRODUCTVERSION 1,0,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -24,12 +24,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Google, Inc."
VALUE "FileDescription", "libwebpdemux DLL"
- VALUE "FileVersion", "1.2.2"
+ VALUE "FileVersion", "1.6.0"
VALUE "InternalName", "libwebpdemux.dll"
- VALUE "LegalCopyright", "Copyright (C) 2021"
+ VALUE "LegalCopyright", "Copyright (C) 2025"
VALUE "OriginalFilename", "libwebpdemux.dll"
VALUE "ProductName", "WebP Image Demuxer"
- VALUE "ProductVersion", "1.2.2"
+ VALUE "ProductVersion", "1.6.0"
END
END
BLOCK "VarFileInfo"
diff --git a/c-lib/src/dsp/Makefile.am b/c-lib/src/dsp/Makefile.am
@@ -5,6 +5,8 @@ noinst_LTLIBRARIES += libwebpdsp_sse2.la
noinst_LTLIBRARIES += libwebpdspdecode_sse2.la
noinst_LTLIBRARIES += libwebpdsp_sse41.la
noinst_LTLIBRARIES += libwebpdspdecode_sse41.la
+noinst_LTLIBRARIES += libwebpdsp_avx2.la
+noinst_LTLIBRARIES += libwebpdspdecode_avx2.la
noinst_LTLIBRARIES += libwebpdsp_neon.la
noinst_LTLIBRARIES += libwebpdspdecode_neon.la
noinst_LTLIBRARIES += libwebpdsp_msa.la
@@ -24,6 +26,7 @@ commondir = $(includedir)/webp
COMMON_SOURCES =
COMMON_SOURCES += alpha_processing.c
COMMON_SOURCES += cpu.c
+COMMON_SOURCES += cpu.h
COMMON_SOURCES += dec.c
COMMON_SOURCES += dec_clip_tables.c
COMMON_SOURCES += dsp.h
@@ -43,6 +46,11 @@ ENC_SOURCES += lossless_enc.c
ENC_SOURCES += quant.h
ENC_SOURCES += ssim.c
+libwebpdspdecode_avx2_la_SOURCES =
+libwebpdspdecode_avx2_la_SOURCES += lossless_avx2.c
+libwebpdspdecode_avx2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdspdecode_avx2_la_CFLAGS = $(AM_CFLAGS) $(AVX2_FLAGS)
+
libwebpdspdecode_sse41_la_SOURCES =
libwebpdspdecode_sse41_la_SOURCES += alpha_processing_sse41.c
libwebpdspdecode_sse41_la_SOURCES += dec_sse41.c
@@ -122,6 +130,12 @@ libwebpdsp_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
libwebpdsp_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
libwebpdsp_sse41_la_LIBADD = libwebpdspdecode_sse41.la
+libwebpdsp_avx2_la_SOURCES =
+libwebpdsp_avx2_la_SOURCES += lossless_enc_avx2.c
+libwebpdsp_avx2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_avx2_la_CFLAGS = $(AM_CFLAGS) $(AVX2_FLAGS)
+libwebpdsp_avx2_la_LIBADD = libwebpdspdecode_avx2.la
+
libwebpdsp_neon_la_SOURCES =
libwebpdsp_neon_la_SOURCES += cost_neon.c
libwebpdsp_neon_la_SOURCES += enc_neon.c
@@ -166,6 +180,7 @@ libwebpdsp_la_LDFLAGS = -lm
libwebpdsp_la_LIBADD =
libwebpdsp_la_LIBADD += libwebpdsp_sse2.la
libwebpdsp_la_LIBADD += libwebpdsp_sse41.la
+libwebpdsp_la_LIBADD += libwebpdsp_avx2.la
libwebpdsp_la_LIBADD += libwebpdsp_neon.la
libwebpdsp_la_LIBADD += libwebpdsp_msa.la
libwebpdsp_la_LIBADD += libwebpdsp_mips32.la
@@ -179,6 +194,7 @@ if BUILD_LIBWEBPDECODER
libwebpdspdecode_la_LIBADD =
libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse2.la
libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse41.la
+ libwebpdspdecode_la_LIBADD += libwebpdspdecode_avx2.la
libwebpdspdecode_la_LIBADD += libwebpdspdecode_neon.la
libwebpdspdecode_la_LIBADD += libwebpdspdecode_msa.la
libwebpdspdecode_la_LIBADD += libwebpdspdecode_mips32.la
diff --git a/c-lib/src/dsp/alpha_processing.c b/c-lib/src/dsp/alpha_processing.c
@@ -12,7 +12,11 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <stddef.h>
+
+#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)
@@ -425,6 +429,7 @@ void (*WebPAlphaReplace)(uint32_t* src, int length, uint32_t color);
//------------------------------------------------------------------------------
// Init function
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitAlphaProcessingMIPSdspR2(void);
extern void WebPInitAlphaProcessingSSE2(void);
extern void WebPInitAlphaProcessingSSE41(void);
diff --git a/c-lib/src/dsp/alpha_processing_neon.c b/c-lib/src/dsp/alpha_processing_neon.c
@@ -83,7 +83,7 @@ static void ApplyAlphaMultiply_NEON(uint8_t* rgba, int alpha_first,
static int DispatchAlpha_NEON(const uint8_t* WEBP_RESTRICT alpha,
int alpha_stride, int width, int height,
uint8_t* WEBP_RESTRICT dst, int dst_stride) {
- uint32_t alpha_mask = 0xffffffffu;
+ uint32_t alpha_mask = 0xffu;
uint8x8_t mask8 = vdup_n_u8(0xff);
uint32_t tmp[2];
int i, j;
@@ -107,6 +107,7 @@ static int DispatchAlpha_NEON(const uint8_t* WEBP_RESTRICT alpha,
dst += dst_stride;
}
vst1_u8((uint8_t*)tmp, mask8);
+ alpha_mask *= 0x01010101;
alpha_mask &= tmp[0];
alpha_mask &= tmp[1];
return (alpha_mask != 0xffffffffu);
@@ -135,7 +136,7 @@ static void DispatchAlphaToGreen_NEON(const uint8_t* WEBP_RESTRICT alpha,
static int ExtractAlpha_NEON(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
int width, int height,
uint8_t* WEBP_RESTRICT alpha, int alpha_stride) {
- uint32_t alpha_mask = 0xffffffffu;
+ uint32_t alpha_mask = 0xffu;
uint8x8_t mask8 = vdup_n_u8(0xff);
uint32_t tmp[2];
int i, j;
@@ -157,6 +158,7 @@ static int ExtractAlpha_NEON(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
alpha += alpha_stride;
}
vst1_u8((uint8_t*)tmp, mask8);
+ alpha_mask *= 0x01010101;
alpha_mask &= tmp[0];
alpha_mask &= tmp[1];
return (alpha_mask == 0xffffffffu);
diff --git a/c-lib/src/dsp/alpha_processing_sse2.c b/c-lib/src/dsp/alpha_processing_sse2.c
@@ -16,6 +16,9 @@
#if defined(WEBP_USE_SSE2)
#include <emmintrin.h>
+#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(0xffffff00u); // to preserve RGB
- const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);
- __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,
@@ -106,8 +116,8 @@ static int ExtractAlpha_SSE2(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
// value is not 0xff if any of the alpha[] is not equal to 0xff.
uint32_t alpha_and = 0xff;
int i, j;
- const __m128i a_mask = _mm_set1_epi32(0xffu); // to preserve alpha
- const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);
+ const __m128i a_mask = _mm_set1_epi32(0xff); // to preserve alpha
+ const __m128i all_0xff = _mm_set_epi32(0, 0, ~0, ~0);
__m128i all_alphas = all_0xff;
// We must be able to access 3 extra bytes after the last written byte
@@ -144,6 +154,46 @@ static int ExtractAlpha_SSE2(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
return (alpha_and == 0xff);
}
+static void ExtractGreen_SSE2(const uint32_t* WEBP_RESTRICT argb,
+ uint8_t* WEBP_RESTRICT alpha, int size) {
+ int i;
+ const __m128i mask = _mm_set1_epi32(0xff);
+ const __m128i* src = (const __m128i*)argb;
+
+ for (i = 0; i + 16 <= size; i += 16, src += 4) {
+ const __m128i a0 = _mm_loadu_si128(src + 0);
+ const __m128i a1 = _mm_loadu_si128(src + 1);
+ const __m128i a2 = _mm_loadu_si128(src + 2);
+ const __m128i a3 = _mm_loadu_si128(src + 3);
+ const __m128i b0 = _mm_srli_epi32(a0, 8);
+ const __m128i b1 = _mm_srli_epi32(a1, 8);
+ const __m128i b2 = _mm_srli_epi32(a2, 8);
+ const __m128i b3 = _mm_srli_epi32(a3, 8);
+ const __m128i c0 = _mm_and_si128(b0, mask);
+ const __m128i c1 = _mm_and_si128(b1, mask);
+ const __m128i c2 = _mm_and_si128(b2, mask);
+ const __m128i c3 = _mm_and_si128(b3, mask);
+ const __m128i d0 = _mm_packs_epi32(c0, c1);
+ const __m128i d1 = _mm_packs_epi32(c2, c3);
+ const __m128i e = _mm_packus_epi16(d0, d1);
+ // store
+ _mm_storeu_si128((__m128i*)&alpha[i], e);
+ }
+ if (i + 8 <= size) {
+ const __m128i a0 = _mm_loadu_si128(src + 0);
+ const __m128i a1 = _mm_loadu_si128(src + 1);
+ const __m128i b0 = _mm_srli_epi32(a0, 8);
+ const __m128i b1 = _mm_srli_epi32(a1, 8);
+ const __m128i c0 = _mm_and_si128(b0, mask);
+ const __m128i c1 = _mm_and_si128(b1, mask);
+ const __m128i d = _mm_packs_epi32(c0, c1);
+ const __m128i e = _mm_packus_epi16(d, d);
+ _mm_storel_epi64((__m128i*)&alpha[i], e);
+ i += 8;
+ }
+ for (; i < size; ++i) alpha[i] = argb[i] >> 8;
+}
+
//------------------------------------------------------------------------------
// Non-dither premultiplied modes
@@ -178,7 +228,7 @@ static int ExtractAlpha_SSE2(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first,
int w, int h, int stride) {
const __m128i zero = _mm_setzero_si128();
- const __m128i kMult = _mm_set1_epi16(0x8081u);
+ const __m128i kMult = _mm_set1_epi16((short)0x8081);
const __m128i kMask = _mm_set_epi16(0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0);
const int kSpan = 4;
while (h-- > 0) {
@@ -267,7 +317,7 @@ static int HasAlpha32b_SSE2(const uint8_t* src, int length) {
}
static void AlphaReplace_SSE2(uint32_t* src, int length, uint32_t color) {
- const __m128i m_color = _mm_set1_epi32(color);
+ const __m128i m_color = _mm_set1_epi32((int)color);
const __m128i zero = _mm_setzero_si128();
int i = 0;
for (; i + 8 <= length; i += 8) {
@@ -354,6 +404,7 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) {
WebPDispatchAlpha = DispatchAlpha_SSE2;
WebPDispatchAlphaToGreen = DispatchAlphaToGreen_SSE2;
WebPExtractAlpha = ExtractAlpha_SSE2;
+ WebPExtractGreen = ExtractGreen_SSE2;
WebPHasAlpha8b = HasAlpha8b_SSE2;
WebPHasAlpha32b = HasAlpha32b_SSE2;
diff --git a/c-lib/src/dsp/alpha_processing_sse41.c b/c-lib/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 <emmintrin.h>
#include <smmintrin.h>
//------------------------------------------------------------------------------
@@ -26,7 +28,7 @@ static int ExtractAlpha_SSE41(const uint8_t* WEBP_RESTRICT argb,
// value is not 0xff if any of the alpha[] is not equal to 0xff.
uint32_t alpha_and = 0xff;
int i, j;
- const __m128i all_0xff = _mm_set1_epi32(~0u);
+ const __m128i all_0xff = _mm_set1_epi32(~0);
__m128i all_alphas = all_0xff;
// We must be able to access 3 extra bytes after the last written byte
diff --git a/c-lib/src/dsp/cost.c b/c-lib/src/dsp/cost.c
@@ -9,8 +9,15 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#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);
@@ -374,6 +381,7 @@ static void SetResidualCoeffs_C(const int16_t* const coeffs,
VP8GetResidualCostFunc VP8GetResidualCost;
VP8SetResidualCoeffsFunc VP8SetResidualCoeffs;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8EncDspCostInitMIPS32(void);
extern void VP8EncDspCostInitMIPSdspR2(void);
extern void VP8EncDspCostInitSSE2(void);
diff --git a/c-lib/src/dsp/cost_mips32.c b/c-lib/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/c-lib/src/dsp/cost_neon.c b/c-lib/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);
@@ -29,7 +29,7 @@ static void SetResidualCoeffs_NEON(const int16_t* const coeffs,
const uint8x16_t eob = vcombine_u8(vqmovn_u16(eob_0), vqmovn_u16(eob_1));
const uint8x16_t masked = vandq_u8(eob, vld1q_u8(position));
-#ifdef __aarch64__
+#if WEBP_AARCH64
res->last = vmaxvq_u8(masked) - 1;
#else
const uint8x8_t eob_8x8 = vmax_u8(vget_low_u8(masked), vget_high_u8(masked));
@@ -43,7 +43,7 @@ static void SetResidualCoeffs_NEON(const int16_t* const coeffs,
vst1_lane_s32(&res->last, vreinterpret_s32_u32(eob_32x2), 0);
--res->last;
-#endif // __aarch64__
+#endif // WEBP_AARCH64
res->coeffs = coeffs;
}
diff --git a/c-lib/src/dsp/cost_sse2.c b/c-lib/src/dsp/cost_sse2.c
@@ -16,14 +16,18 @@
#if defined(WEBP_USE_SSE2)
#include <emmintrin.h>
+#include <assert.h>
+
+#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/c-lib/src/dsp/cpu.c b/c-lib/src/dsp/cpu.c
@@ -11,7 +11,7 @@
//
// Author: Christian Duvivier (cduvivier@google.com)
-#include "src/dsp/dsp.h"
+#include "src/dsp/cpu.h"
#if defined(WEBP_HAVE_NEON_RTCD)
#include <stdio.h>
@@ -22,6 +22,10 @@
#include <cpu-features.h>
#endif
+#include <stddef.h>
+
+#include "src/webp/types.h"
+
//------------------------------------------------------------------------------
// SSE2 detection.
//
@@ -36,18 +40,6 @@ static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
: "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
: "a"(info_type), "c"(0));
}
-#elif defined(__x86_64__) && \
- (defined(__code_model_medium__) || defined(__code_model_large__)) && \
- defined(__PIC__)
-static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
- __asm__ volatile (
- "xchg{q}\t{%%rbx}, %q1\n"
- "cpuid\n"
- "xchg{q}\t{%%rbx}, %q1\n"
- : "=a"(cpu_info[0]), "=&r"(cpu_info[1]), "=c"(cpu_info[2]),
- "=d"(cpu_info[3])
- : "a"(info_type), "c"(0));
-}
#elif defined(__i386__) || defined(__x86_64__)
static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
__asm__ volatile (
@@ -173,6 +165,7 @@ static int x86CPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
#elif defined(WEBP_ANDROID_NEON) // NB: needs to be before generic NEON test.
static int AndroidCPUInfo(CPUFeature feature) {
@@ -184,6 +177,7 @@ static int AndroidCPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
#elif defined(EMSCRIPTEN) // also needs to be before generic NEON test
// Use compile flags as an indicator of SIMD support instead of a runtime check.
@@ -208,11 +202,12 @@ static int wasmCPUInfo(CPUFeature feature) {
}
return 0;
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = wasmCPUInfo;
#elif defined(WEBP_HAVE_NEON)
// In most cases this function doesn't check for NEON support (it's assumed by
// the configuration), but enables turning off NEON at runtime, for testing
-// purposes, by setting VP8DecGetCPUInfo = NULL.
+// purposes, by setting VP8GetCPUInfo = NULL.
static int armCPUInfo(CPUFeature feature) {
if (feature != kNEON) return 0;
#if defined(__linux__) && defined(WEBP_HAVE_NEON_RTCD)
@@ -236,6 +231,7 @@ static int armCPUInfo(CPUFeature feature) {
return 1;
#endif
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
#elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) || \
defined(WEBP_USE_MSA)
@@ -247,7 +243,9 @@ static int mipsCPUInfo(CPUFeature feature) {
}
}
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo;
#else
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
VP8CPUInfo VP8GetCPUInfo = NULL;
#endif
diff --git a/c-lib/src/dsp/cpu.h b/c-lib/src/dsp/cpu.h
@@ -0,0 +1,281 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// CPU detection functions and macros.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_DSP_CPU_H_
+#define WEBP_DSP_CPU_H_
+
+#include <stddef.h>
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#include "src/webp/types.h"
+
+#if defined(__GNUC__)
+#define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__)
+#define LOCAL_GCC_PREREQ(maj, min) (LOCAL_GCC_VERSION >= (((maj) << 8) | (min)))
+#else
+#define LOCAL_GCC_VERSION 0
+#define LOCAL_GCC_PREREQ(maj, min) 0
+#endif
+
+#if defined(__clang__)
+#define LOCAL_CLANG_VERSION ((__clang_major__ << 8) | __clang_minor__)
+#define LOCAL_CLANG_PREREQ(maj, min) \
+ (LOCAL_CLANG_VERSION >= (((maj) << 8) | (min)))
+#else
+#define LOCAL_CLANG_VERSION 0
+#define LOCAL_CLANG_PREREQ(maj, min) 0
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+//------------------------------------------------------------------------------
+// x86 defines.
+
+#if !defined(HAVE_CONFIG_H)
+#if defined(_MSC_VER) && _MSC_VER > 1310 && \
+ (defined(_M_X64) || defined(_M_IX86))
+#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1500 && \
+ (defined(_M_X64) || defined(_M_IX86))
+#define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets
+#endif
+
+#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
+// files without intrinsics, allowing the corresponding Init() to be called.
+// Files containing intrinsics will need to be built targeting the instruction
+// set so should succeed on one of the earlier tests.
+#if (defined(__SSE2__) || defined(WEBP_MSC_SSE2)) && \
+ (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE2))
+#define WEBP_USE_SSE2
+#endif
+
+#if defined(WEBP_USE_SSE2) && !defined(WEBP_HAVE_SSE2)
+#define WEBP_HAVE_SSE2
+#endif
+
+#if (defined(__SSE4_1__) || defined(WEBP_MSC_SSE41)) && \
+ (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE41))
+#define WEBP_USE_SSE41
+#endif
+
+#if defined(WEBP_USE_SSE41) && !defined(WEBP_HAVE_SSE41)
+#define WEBP_HAVE_SSE41
+#endif
+
+#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
+
+#undef WEBP_MSC_AVX2
+#undef WEBP_MSC_SSE41
+#undef WEBP_MSC_SSE2
+
+//------------------------------------------------------------------------------
+// Arm defines.
+
+// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
+// inline assembly would need to be modified for use with Native Client.
+#if ((defined(__ARM_NEON__) || defined(__aarch64__)) && \
+ (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_NEON))) && \
+ !defined(__native_client__)
+#define WEBP_USE_NEON
+#endif
+
+#if !defined(WEBP_USE_NEON) && defined(__ANDROID__) && \
+ defined(__ARM_ARCH_7A__) && defined(HAVE_CPU_FEATURES_H)
+#define WEBP_ANDROID_NEON // Android targets that may have NEON
+#define WEBP_USE_NEON
+#endif
+
+// Note: ARM64 is supported in Visual Studio 2017, but requires the direct
+// inclusion of arm64_neon.h; Visual Studio 2019 includes this file in
+// arm_neon.h. Compile errors were seen with Visual Studio 2019 16.4 with
+// vtbl4_u8(); a fix was made in 16.6.
+#if defined(_MSC_VER) && \
+ ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
+ (_MSC_VER >= 1926 && (defined(_M_ARM64) || defined(_M_ARM64EC))))
+#define WEBP_USE_NEON
+#define WEBP_USE_INTRINSICS
+#endif
+
+#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
+#define WEBP_AARCH64 1
+#else
+#define WEBP_AARCH64 0
+#endif
+
+#if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON)
+#define WEBP_HAVE_NEON
+#endif
+
+//------------------------------------------------------------------------------
+// MIPS defines.
+
+#if defined(__mips__) && !defined(__mips64) && defined(__mips_isa_rev) && \
+ (__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
+#define WEBP_USE_MIPS32
+#if (__mips_isa_rev >= 2)
+#define WEBP_USE_MIPS32_R2
+#if defined(__mips_dspr2) || (defined(__mips_dsp_rev) && __mips_dsp_rev >= 2)
+#define WEBP_USE_MIPS_DSP_R2
+#endif
+#endif
+#endif
+
+#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
+#define WEBP_USE_MSA
+#endif
+
+//------------------------------------------------------------------------------
+
+#ifndef WEBP_DSP_OMIT_C_CODE
+#define WEBP_DSP_OMIT_C_CODE 1
+#endif
+
+#if defined(WEBP_USE_NEON) && WEBP_DSP_OMIT_C_CODE
+#define WEBP_NEON_OMIT_C_CODE 1
+#else
+#define WEBP_NEON_OMIT_C_CODE 0
+#endif
+
+#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || WEBP_AARCH64)
+#define WEBP_NEON_WORK_AROUND_GCC 1
+#else
+#define WEBP_NEON_WORK_AROUND_GCC 0
+#endif
+
+//------------------------------------------------------------------------------
+
+// This macro prevents thread_sanitizer from reporting known concurrent writes.
+#define WEBP_TSAN_IGNORE_FUNCTION
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#undef WEBP_TSAN_IGNORE_FUNCTION
+#define WEBP_TSAN_IGNORE_FUNCTION __attribute__((no_sanitize_thread))
+#endif
+#endif
+
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer)
+#define WEBP_MSAN
+#endif
+#endif
+
+#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
+#include <pthread.h> // NOLINT
+
+#define WEBP_DSP_INIT(func) \
+ do { \
+ static volatile VP8CPUInfo func##_last_cpuinfo_used = \
+ (VP8CPUInfo)&func##_last_cpuinfo_used; \
+ static pthread_mutex_t func##_lock = PTHREAD_MUTEX_INITIALIZER; \
+ if (pthread_mutex_lock(&func##_lock)) break; \
+ if (func##_last_cpuinfo_used != VP8GetCPUInfo) func(); \
+ func##_last_cpuinfo_used = VP8GetCPUInfo; \
+ (void)pthread_mutex_unlock(&func##_lock); \
+ } while (0)
+#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
+#define WEBP_DSP_INIT(func) \
+ do { \
+ static volatile VP8CPUInfo func##_last_cpuinfo_used = \
+ (VP8CPUInfo)&func##_last_cpuinfo_used; \
+ if (func##_last_cpuinfo_used == VP8GetCPUInfo) break; \
+ func(); \
+ func##_last_cpuinfo_used = VP8GetCPUInfo; \
+ } while (0)
+#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32)
+
+// Defines an Init + helper function that control multiple initialization of
+// function pointers / tables.
+/* Usage:
+ WEBP_DSP_INIT_FUNC(InitFunc) {
+ ...function body
+ }
+*/
+#define WEBP_DSP_INIT_FUNC(name) \
+ static WEBP_TSAN_IGNORE_FUNCTION void name##_body(void); \
+ WEBP_TSAN_IGNORE_FUNCTION void name(void) { WEBP_DSP_INIT(name##_body); } \
+ static WEBP_TSAN_IGNORE_FUNCTION void name##_body(void)
+
+#define WEBP_UBSAN_IGNORE_UNDEF
+#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
+#if defined(__clang__) && defined(__has_attribute)
+#if __has_attribute(no_sanitize)
+// This macro prevents the undefined behavior sanitizer from reporting
+// failures. This is only meant to silence unaligned loads on platforms that
+// are known to support them.
+#undef WEBP_UBSAN_IGNORE_UNDEF
+#define WEBP_UBSAN_IGNORE_UNDEF __attribute__((no_sanitize("undefined")))
+
+// This macro prevents the undefined behavior sanitizer from reporting
+// failures related to unsigned integer overflows. This is only meant to
+// silence cases where this well defined behavior is expected.
+#undef WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
+#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW \
+ __attribute__((no_sanitize("unsigned-integer-overflow")))
+#endif
+#endif
+
+// If 'ptr' is NULL, returns NULL. Otherwise returns 'ptr + off'.
+// Prevents undefined behavior sanitizer nullptr-with-nonzero-offset warning.
+#if !defined(WEBP_OFFSET_PTR)
+#define WEBP_OFFSET_PTR(ptr, off) (((ptr) == NULL) ? NULL : ((ptr) + (off)))
+#endif
+
+// Regularize the definition of WEBP_SWAP_16BIT_CSP (backward compatibility)
+#if !defined(WEBP_SWAP_16BIT_CSP)
+#define WEBP_SWAP_16BIT_CSP 0
+#endif
+
+// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__)
+#if !defined(WORDS_BIGENDIAN) && \
+ (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \
+ (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
+#define WORDS_BIGENDIAN
+#endif
+
+typedef enum {
+ kSSE2,
+ kSSE3,
+ kSlowSSSE3, // special feature for slow SSSE3 architectures
+ kSSE4_1,
+ kAVX,
+ kAVX2,
+ kNEON,
+ kMIPS32,
+ kMIPSdspR2,
+ kMSA
+} CPUFeature;
+
+// returns true if the CPU supports the feature.
+typedef int (*VP8CPUInfo)(CPUFeature feature);
+
+#endif // WEBP_DSP_CPU_H_
diff --git a/c-lib/src/dsp/dec.c b/c-lib/src/dsp/dec.c
@@ -12,10 +12,15 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <stddef.h>
+#include <string.h>
-#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"
//------------------------------------------------------------------------------
@@ -37,19 +42,19 @@ static WEBP_INLINE uint8_t clip_8b(int v) {
STORE(3, y, DC - (d)); \
} while (0)
-#define MUL1(a) ((((a) * 20091) >> 16) + (a))
-#define MUL2(a) (((a) * 35468) >> 16)
-
#if !WEBP_NEON_OMIT_C_CODE
-static void TransformOne_C(const int16_t* in, uint8_t* dst) {
+static void TransformOne_C(const int16_t* WEBP_RESTRICT in,
+ uint8_t* WEBP_RESTRICT dst) {
int C[4 * 4], *tmp;
int i;
tmp = C;
for (i = 0; i < 4; ++i) { // vertical pass
const int a = in[0] + in[8]; // [-4096, 4094]
const int b = in[0] - in[8]; // [-4095, 4095]
- const int c = MUL2(in[4]) - MUL1(in[12]); // [-3783, 3783]
- const int d = MUL1(in[4]) + MUL2(in[12]); // [-3785, 3781]
+ const int c = WEBP_TRANSFORM_AC3_MUL2(in[4]) -
+ WEBP_TRANSFORM_AC3_MUL1(in[12]); // [-3783, 3783]
+ const int d = WEBP_TRANSFORM_AC3_MUL1(in[4]) +
+ WEBP_TRANSFORM_AC3_MUL2(in[12]); // [-3785, 3781]
tmp[0] = a + d; // [-7881, 7875]
tmp[1] = b + c; // [-7878, 7878]
tmp[2] = b - c; // [-7878, 7878]
@@ -69,8 +74,10 @@ static void TransformOne_C(const int16_t* in, uint8_t* dst) {
const int dc = tmp[0] + 4;
const int a = dc + tmp[8];
const int b = dc - tmp[8];
- const int c = MUL2(tmp[4]) - MUL1(tmp[12]);
- const int d = MUL1(tmp[4]) + MUL2(tmp[12]);
+ const int c =
+ WEBP_TRANSFORM_AC3_MUL2(tmp[4]) - WEBP_TRANSFORM_AC3_MUL1(tmp[12]);
+ const int d =
+ WEBP_TRANSFORM_AC3_MUL1(tmp[4]) + WEBP_TRANSFORM_AC3_MUL2(tmp[12]);
STORE(0, 0, a + d);
STORE(1, 0, b + c);
STORE(2, 0, b - c);
@@ -81,22 +88,22 @@ 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 = MUL2(in[4]);
- const int d4 = MUL1(in[4]);
- const int c1 = MUL2(in[1]);
- const int d1 = MUL1(in[1]);
+ const int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]);
+ const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]);
+ const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
+ const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
STORE2(0, a + d4, d1, c1);
STORE2(1, a + c4, d1, c1);
STORE2(2, a - c4, d1, c1);
STORE2(3, a - d4, d1, c1);
}
-#undef MUL1
-#undef MUL2
#undef STORE2
-static void TransformTwo_C(const int16_t* in, uint8_t* dst, int do_two) {
+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);
@@ -104,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) {
@@ -121,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);
@@ -134,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) {
@@ -162,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
@@ -662,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);
}
@@ -695,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) {
@@ -731,9 +742,10 @@ 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);
extern void VP8DspInitSSE41(void);
extern void VP8DspInitNEON(void);
diff --git a/c-lib/src/dsp/dec_clip_tables.c b/c-lib/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/c-lib/src/dsp/dec_mips32.c b/c-lib/src/dsp/dec_mips32.c
@@ -18,8 +18,8 @@
#include "src/dsp/mips_macro.h"
-static const int kC1 = 20091 + (1 << 16);
-static const int kC2 = 35468;
+static const int kC1 = WEBP_TRANSFORM_AC3_C1;
+static const int kC2 = WEBP_TRANSFORM_AC3_C2;
static WEBP_INLINE int abs_mips32(int x) {
const int sign = x >> 31;
@@ -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,11 +215,12 @@ 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;
- int temp15, temp16, temp17, temp18;
+ int temp15, temp16, temp17, temp18, temp19;
int16_t* p_in = (int16_t*)in;
// loops unrolled and merged to avoid usage of tmp buffer
@@ -233,16 +234,14 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
"addu %[temp16], %[temp0], %[temp8] \n\t"
"subu %[temp0], %[temp0], %[temp8] \n\t"
"mul %[temp8], %[temp4], %[kC2] \n\t"
- "mul %[temp17], %[temp12], %[kC1] \n\t"
- "mul %[temp4], %[temp4], %[kC1] \n\t"
+ MUL_SHIFT_C1(temp17, temp12)
+ MUL_SHIFT_C1_IO(temp4, temp19)
"mul %[temp12], %[temp12], %[kC2] \n\t"
"lh %[temp1], 2(%[in]) \n\t"
"lh %[temp5], 10(%[in]) \n\t"
"lh %[temp9], 18(%[in]) \n\t"
"lh %[temp13], 26(%[in]) \n\t"
"sra %[temp8], %[temp8], 16 \n\t"
- "sra %[temp17], %[temp17], 16 \n\t"
- "sra %[temp4], %[temp4], 16 \n\t"
"sra %[temp12], %[temp12], 16 \n\t"
"lh %[temp2], 4(%[in]) \n\t"
"lh %[temp6], 12(%[in]) \n\t"
@@ -261,49 +260,43 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
"addu %[temp12], %[temp0], %[temp17] \n\t"
"subu %[temp0], %[temp0], %[temp17] \n\t"
"mul %[temp9], %[temp5], %[kC2] \n\t"
- "mul %[temp17], %[temp13], %[kC1] \n\t"
- "mul %[temp5], %[temp5], %[kC1] \n\t"
+ MUL_SHIFT_C1(temp17, temp13)
+ MUL_SHIFT_C1_IO(temp5, temp19)
"mul %[temp13], %[temp13], %[kC2] \n\t"
"sra %[temp9], %[temp9], 16 \n\t"
- "sra %[temp17], %[temp17], 16 \n\t"
"subu %[temp17], %[temp9], %[temp17] \n\t"
- "sra %[temp5], %[temp5], 16 \n\t"
"sra %[temp13], %[temp13], 16 \n\t"
"addu %[temp5], %[temp5], %[temp13] \n\t"
"addu %[temp13], %[temp1], %[temp17] \n\t"
"subu %[temp1], %[temp1], %[temp17] \n\t"
- "mul %[temp17], %[temp14], %[kC1] \n\t"
+ MUL_SHIFT_C1(temp17, temp14)
"mul %[temp14], %[temp14], %[kC2] \n\t"
"addu %[temp9], %[temp16], %[temp5] \n\t"
"subu %[temp5], %[temp16], %[temp5] \n\t"
"addu %[temp16], %[temp2], %[temp10] \n\t"
"subu %[temp2], %[temp2], %[temp10] \n\t"
"mul %[temp10], %[temp6], %[kC2] \n\t"
- "mul %[temp6], %[temp6], %[kC1] \n\t"
- "sra %[temp17], %[temp17], 16 \n\t"
+ MUL_SHIFT_C1_IO(temp6, temp19)
"sra %[temp14], %[temp14], 16 \n\t"
"sra %[temp10], %[temp10], 16 \n\t"
- "sra %[temp6], %[temp6], 16 \n\t"
"subu %[temp17], %[temp10], %[temp17] \n\t"
"addu %[temp6], %[temp6], %[temp14] \n\t"
"addu %[temp10], %[temp16], %[temp6] \n\t"
"subu %[temp6], %[temp16], %[temp6] \n\t"
"addu %[temp14], %[temp2], %[temp17] \n\t"
"subu %[temp2], %[temp2], %[temp17] \n\t"
- "mul %[temp17], %[temp15], %[kC1] \n\t"
+ MUL_SHIFT_C1(temp17, temp15)
"mul %[temp15], %[temp15], %[kC2] \n\t"
"addu %[temp16], %[temp3], %[temp11] \n\t"
"subu %[temp3], %[temp3], %[temp11] \n\t"
"mul %[temp11], %[temp7], %[kC2] \n\t"
- "mul %[temp7], %[temp7], %[kC1] \n\t"
+ MUL_SHIFT_C1_IO(temp7, temp19)
"addiu %[temp8], %[temp8], 4 \n\t"
"addiu %[temp12], %[temp12], 4 \n\t"
"addiu %[temp0], %[temp0], 4 \n\t"
"addiu %[temp4], %[temp4], 4 \n\t"
- "sra %[temp17], %[temp17], 16 \n\t"
"sra %[temp15], %[temp15], 16 \n\t"
"sra %[temp11], %[temp11], 16 \n\t"
- "sra %[temp7], %[temp7], 16 \n\t"
"subu %[temp17], %[temp11], %[temp17] \n\t"
"addu %[temp7], %[temp7], %[temp15] \n\t"
"addu %[temp15], %[temp3], %[temp17] \n\t"
@@ -313,48 +306,40 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
"addu %[temp16], %[temp8], %[temp10] \n\t"
"subu %[temp8], %[temp8], %[temp10] \n\t"
"mul %[temp10], %[temp9], %[kC2] \n\t"
- "mul %[temp17], %[temp11], %[kC1] \n\t"
- "mul %[temp9], %[temp9], %[kC1] \n\t"
+ MUL_SHIFT_C1(temp17, temp11)
+ MUL_SHIFT_C1_IO(temp9, temp19)
"mul %[temp11], %[temp11], %[kC2] \n\t"
"sra %[temp10], %[temp10], 16 \n\t"
- "sra %[temp17], %[temp17], 16 \n\t"
- "sra %[temp9], %[temp9], 16 \n\t"
"sra %[temp11], %[temp11], 16 \n\t"
"subu %[temp17], %[temp10], %[temp17] \n\t"
"addu %[temp11], %[temp9], %[temp11] \n\t"
"addu %[temp10], %[temp12], %[temp14] \n\t"
"subu %[temp12], %[temp12], %[temp14] \n\t"
"mul %[temp14], %[temp13], %[kC2] \n\t"
- "mul %[temp9], %[temp15], %[kC1] \n\t"
- "mul %[temp13], %[temp13], %[kC1] \n\t"
+ MUL_SHIFT_C1(temp9, temp15)
+ MUL_SHIFT_C1_IO(temp13, temp19)
"mul %[temp15], %[temp15], %[kC2] \n\t"
"sra %[temp14], %[temp14], 16 \n\t"
- "sra %[temp9], %[temp9], 16 \n\t"
- "sra %[temp13], %[temp13], 16 \n\t"
"sra %[temp15], %[temp15], 16 \n\t"
"subu %[temp9], %[temp14], %[temp9] \n\t"
"addu %[temp15], %[temp13], %[temp15] \n\t"
"addu %[temp14], %[temp0], %[temp2] \n\t"
"subu %[temp0], %[temp0], %[temp2] \n\t"
"mul %[temp2], %[temp1], %[kC2] \n\t"
- "mul %[temp13], %[temp3], %[kC1] \n\t"
- "mul %[temp1], %[temp1], %[kC1] \n\t"
+ MUL_SHIFT_C1(temp13, temp3)
+ MUL_SHIFT_C1_IO(temp1, temp19)
"mul %[temp3], %[temp3], %[kC2] \n\t"
"sra %[temp2], %[temp2], 16 \n\t"
- "sra %[temp13], %[temp13], 16 \n\t"
- "sra %[temp1], %[temp1], 16 \n\t"
"sra %[temp3], %[temp3], 16 \n\t"
"subu %[temp13], %[temp2], %[temp13] \n\t"
"addu %[temp3], %[temp1], %[temp3] \n\t"
"addu %[temp2], %[temp4], %[temp6] \n\t"
"subu %[temp4], %[temp4], %[temp6] \n\t"
"mul %[temp6], %[temp5], %[kC2] \n\t"
- "mul %[temp1], %[temp7], %[kC1] \n\t"
- "mul %[temp5], %[temp5], %[kC1] \n\t"
+ MUL_SHIFT_C1(temp1, temp7)
+ MUL_SHIFT_C1_IO(temp5, temp19)
"mul %[temp7], %[temp7], %[kC2] \n\t"
"sra %[temp6], %[temp6], 16 \n\t"
- "sra %[temp1], %[temp1], 16 \n\t"
- "sra %[temp5], %[temp5], 16 \n\t"
"sra %[temp7], %[temp7], 16 \n\t"
"subu %[temp1], %[temp6], %[temp1] \n\t"
"addu %[temp7], %[temp5], %[temp7] \n\t"
@@ -542,13 +527,14 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
[temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11),
[temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14),
[temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17),
- [temp18]"=&r"(temp18)
+ [temp18]"=&r"(temp18), [temp19]"=&r"(temp19)
: [in]"r"(p_in), [kC1]"r"(kC1), [kC2]"r"(kC2), [dst]"r"(dst)
: "memory", "hi", "lo"
);
}
-static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) {
+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/c-lib/src/dsp/dec_mips_dsp_r2.c b/c-lib/src/dsp/dec_mips_dsp_r2.c
@@ -18,12 +18,11 @@
#include "src/dsp/mips_macro.h"
-static const int kC1 = 20091 + (1 << 16);
-static const int kC2 = 35468;
+static const int kC1 = WEBP_TRANSFORM_AC3_C1;
+static const int kC2 = WEBP_TRANSFORM_AC3_C2;
-#define MUL(a, b) (((a) * (b)) >> 16)
-
-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 (
@@ -47,12 +46,13 @@ 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 = MUL(in[4], kC2);
- const int d4 = MUL(in[4], kC1);
- const int c1 = MUL(in[1], kC2);
- const int d1 = MUL(in[1], kC1);
+ int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]);
+ const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]);
+ const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
+ const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9;
int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18;
@@ -83,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;
@@ -150,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);
@@ -436,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);
}
@@ -467,20 +469,18 @@ 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);
}
-#undef MUL
-
//------------------------------------------------------------------------------
// Simple In-loop filtering (Paragraph 15.2)
diff --git a/c-lib/src/dsp/dec_msa.c b/c-lib/src/dsp/dec_msa.c
@@ -37,10 +37,9 @@
d1_m = d_tmp1_m + d_tmp2_m; \
BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \
}
-#define MULT1(a) ((((a) * 20091) >> 16) + (a))
-#define MULT2(a) (((a) * 35468) >> 16)
-static void TransformOne(const int16_t* in, uint8_t* dst) {
+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;
@@ -67,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 };
@@ -116,18 +117,20 @@ 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 = MULT2(in[4]);
- const int d4 = MULT1(in[4]);
- const int in2 = MULT2(in[1]);
- const int in3 = MULT1(in[1]);
+ const int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]);
+ const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]);
+ const int in2 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
+ const int in3 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
v4i32 tmp0 = { 0 };
v4i32 out0 = __msa_fill_w(a + d4);
v4i32 out1 = __msa_fill_w(a + c4);
@@ -477,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;
@@ -522,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;
@@ -558,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;
@@ -589,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/c-lib/src/dsp/dec_neon.c b/c-lib/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;
@@ -1000,8 +1002,9 @@ static void HFilter8i_NEON(uint8_t* u, uint8_t* v, int stride,
// libwebp adds 1 << 16 to cospi8sqrt2minus1 (kC1). However, this causes the
// same issue with kC1 and vqdmulh that we work around by down shifting kC2
-static const int16_t kC1 = 20091;
-static const int16_t kC2 = 17734; // half of kC2, actually. See comment above.
+static const int16_t kC1 = WEBP_TRANSFORM_AC3_C1;
+static const int16_t kC2 =
+ WEBP_TRANSFORM_AC3_C2 / 2; // half of kC2, actually. See comment above.
#if defined(WEBP_USE_INTRINSICS)
static WEBP_INLINE void Transpose8x2_NEON(const int16x8_t in0,
@@ -1040,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);
@@ -1050,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 };
@@ -1183,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);
}
@@ -1204,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;
{
@@ -1255,15 +1263,13 @@ static void TransformWHT_NEON(const int16_t* in, int16_t* out) {
//------------------------------------------------------------------------------
-#define MUL(a, b) (((a) * (b)) >> 16)
-static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) {
- static const int kC1_full = 20091 + (1 << 16);
- static const int kC2_full = 35468;
+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(MUL(in[4], kC2_full));
- const int16x4_t d4 = vdup_n_s16(MUL(in[4], kC1_full));
- const int c1 = MUL(in[1], kC2_full);
- const int d1 = MUL(in[1], kC1_full);
+ const 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]));
+ const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
+ const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
const uint64_t cd = (uint64_t)( d1 & 0xffff) << 0 |
(uint64_t)( c1 & 0xffff) << 16 |
(uint64_t)(-c1 & 0xffff) << 32 |
@@ -1274,7 +1280,6 @@ static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) {
const int16x8_t m2_m3 = vcombine_s16(vqsub_s16(B, c4), vqsub_s16(B, d4));
Add4x4_NEON(m0_m1, m2_m3, dst);
}
-#undef MUL
//------------------------------------------------------------------------------
// 4x4
@@ -1303,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));
@@ -1428,7 +1434,7 @@ static WEBP_INLINE void DC8_NEON(uint8_t* dst, int do_top, int do_left) {
if (do_top) {
const uint8x8_t A = vld1_u8(dst - BPS); // top row
-#if defined(__aarch64__)
+#if WEBP_AARCH64
const uint16_t p2 = vaddlv_u8(A);
sum_top = vdupq_n_u16(p2);
#else
@@ -1511,7 +1517,7 @@ static WEBP_INLINE void DC16_NEON(uint8_t* dst, int do_top, int do_left) {
if (do_top) {
const uint8x16_t A = vld1q_u8(dst - BPS); // top row
-#if defined(__aarch64__)
+#if WEBP_AARCH64
const uint16_t p3 = vaddlvq_u8(A);
sum_top = vdupq_n_u16(p3);
#else
@@ -1575,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/c-lib/src/dsp/dec_sse2.c b/c-lib/src/dsp/dec_sse2.c
@@ -23,14 +23,18 @@
#endif
#include <emmintrin.h>
-#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
@@ -158,10 +162,10 @@ static void Transform_SSE2(const int16_t* in, uint8_t* dst, int do_two) {
dst3 = _mm_loadl_epi64((__m128i*)(dst + 3 * BPS));
} else {
// Load four bytes/pixels per line.
- dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS));
- dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS));
- dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS));
- dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS));
+ dst0 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 0 * BPS));
+ dst1 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 1 * BPS));
+ dst2 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 2 * BPS));
+ dst3 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 3 * BPS));
}
// Convert to 16b.
dst0 = _mm_unpacklo_epi8(dst0, zero);
@@ -187,24 +191,23 @@ static void Transform_SSE2(const int16_t* in, uint8_t* dst, int do_two) {
_mm_storel_epi64((__m128i*)(dst + 3 * BPS), dst3);
} else {
// Store four bytes/pixels per line.
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
}
}
}
#if (USE_TRANSFORM_AC3 == 1)
-#define MUL(a, b) (((a) * (b)) >> 16)
-static void TransformAC3(const int16_t* in, uint8_t* dst) {
- static const int kC1 = 20091 + (1 << 16);
- static const int kC2 = 35468;
+
+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(MUL(in[4], kC2));
- const __m128i d4 = _mm_set1_epi16(MUL(in[4], kC1));
- const int c1 = MUL(in[1], kC2);
- const int d1 = MUL(in[1], kC1);
+ const __m128i c4 = _mm_set1_epi16(WEBP_TRANSFORM_AC3_MUL2(in[4]));
+ const __m128i d4 = _mm_set1_epi16(WEBP_TRANSFORM_AC3_MUL1(in[4]));
+ const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]);
+ const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]);
const __m128i CD = _mm_set_epi16(0, 0, 0, 0, -d1, -c1, c1, d1);
const __m128i B = _mm_adds_epi16(A, CD);
const __m128i m0 = _mm_adds_epi16(B, d4);
@@ -213,10 +216,10 @@ static void TransformAC3(const int16_t* in, uint8_t* dst) {
const __m128i m3 = _mm_subs_epi16(B, d4);
const __m128i zero = _mm_setzero_si128();
// Load the source pixels.
- __m128i dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS));
- __m128i dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS));
- __m128i dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS));
- __m128i dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS));
+ __m128i dst0 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 0 * BPS));
+ __m128i dst1 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 1 * BPS));
+ __m128i dst2 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 2 * BPS));
+ __m128i dst3 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 3 * BPS));
// Convert to 16b.
dst0 = _mm_unpacklo_epi8(dst0, zero);
dst1 = _mm_unpacklo_epi8(dst1, zero);
@@ -233,12 +236,12 @@ static void TransformAC3(const int16_t* in, uint8_t* dst) {
dst2 = _mm_packus_epi16(dst2, dst2);
dst3 = _mm_packus_epi16(dst3, dst3);
// Store the results.
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
}
-#undef MUL
+
#endif // USE_TRANSFORM_AC3
//------------------------------------------------------------------------------
@@ -259,15 +262,15 @@ static WEBP_INLINE void SignedShift8b_SSE2(__m128i* const x) {
*x = _mm_packs_epi16(lo_1, hi_1);
}
-#define FLIP_SIGN_BIT2(a, b) { \
+#define FLIP_SIGN_BIT2(a, b) do { \
(a) = _mm_xor_si128(a, sign_bit); \
(b) = _mm_xor_si128(b, sign_bit); \
-}
+} while (0)
-#define FLIP_SIGN_BIT4(a, b, c, d) { \
+#define FLIP_SIGN_BIT4(a, b, c, d) do { \
FLIP_SIGN_BIT2(a, b); \
FLIP_SIGN_BIT2(c, d); \
-}
+} while (0)
// input/output is uint8_t
static WEBP_INLINE void GetNotHEV_SSE2(const __m128i* const p1,
@@ -477,11 +480,11 @@ static WEBP_INLINE void Load8x4_SSE2(const uint8_t* const b, int stride,
// A0 = 63 62 61 60 23 22 21 20 43 42 41 40 03 02 01 00
// A1 = 73 72 71 70 33 32 31 30 53 52 51 50 13 12 11 10
const __m128i A0 = _mm_set_epi32(
- WebPMemToUint32(&b[6 * stride]), WebPMemToUint32(&b[2 * stride]),
- WebPMemToUint32(&b[4 * stride]), WebPMemToUint32(&b[0 * stride]));
+ WebPMemToInt32(&b[6 * stride]), WebPMemToInt32(&b[2 * stride]),
+ WebPMemToInt32(&b[4 * stride]), WebPMemToInt32(&b[0 * stride]));
const __m128i A1 = _mm_set_epi32(
- WebPMemToUint32(&b[7 * stride]), WebPMemToUint32(&b[3 * stride]),
- WebPMemToUint32(&b[5 * stride]), WebPMemToUint32(&b[1 * stride]));
+ WebPMemToInt32(&b[7 * stride]), WebPMemToInt32(&b[3 * stride]),
+ WebPMemToInt32(&b[5 * stride]), WebPMemToInt32(&b[1 * stride]));
// B0 = 53 43 52 42 51 41 50 40 13 03 12 02 11 01 10 00
// B1 = 73 63 72 62 71 61 70 60 33 23 32 22 31 21 30 20
@@ -540,7 +543,7 @@ static WEBP_INLINE void Store4x4_SSE2(__m128i* const x,
uint8_t* dst, int stride) {
int i;
for (i = 0; i < 4; ++i, dst += stride) {
- WebPUint32ToMem(dst, _mm_cvtsi128_si32(*x));
+ WebPInt32ToMem(dst, _mm_cvtsi128_si32(*x));
*x = _mm_srli_si128(*x, 4);
}
}
@@ -645,12 +648,12 @@ static void SimpleHFilter16i_SSE2(uint8_t* p, int stride, int thresh) {
(m) = _mm_max_epu8(m, MM_ABS(p2, p1)); \
} while (0)
-#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) { \
+#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) do { \
(e1) = _mm_loadu_si128((__m128i*)&(p)[0 * (stride)]); \
(e2) = _mm_loadu_si128((__m128i*)&(p)[1 * (stride)]); \
(e3) = _mm_loadu_si128((__m128i*)&(p)[2 * (stride)]); \
(e4) = _mm_loadu_si128((__m128i*)&(p)[3 * (stride)]); \
-}
+} while (0)
#define LOADUV_H_EDGE(p, u, v, stride) do { \
const __m128i U = _mm_loadl_epi64((__m128i*)&(u)[(stride)]); \
@@ -658,18 +661,18 @@ static void SimpleHFilter16i_SSE2(uint8_t* p, int stride, int thresh) {
(p) = _mm_unpacklo_epi64(U, V); \
} while (0)
-#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) { \
+#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) do { \
LOADUV_H_EDGE(e1, u, v, 0 * (stride)); \
LOADUV_H_EDGE(e2, u, v, 1 * (stride)); \
LOADUV_H_EDGE(e3, u, v, 2 * (stride)); \
LOADUV_H_EDGE(e4, u, v, 3 * (stride)); \
-}
+} while (0)
-#define STOREUV(p, u, v, stride) { \
+#define STOREUV(p, u, v, stride) do { \
_mm_storel_epi64((__m128i*)&(u)[(stride)], p); \
(p) = _mm_srli_si128(p, 8); \
_mm_storel_epi64((__m128i*)&(v)[(stride)], p); \
-}
+} while (0)
static WEBP_INLINE void ComplexMask_SSE2(const __m128i* const p1,
const __m128i* const p0,
@@ -794,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;
@@ -819,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;
@@ -839,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;
@@ -865,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;
@@ -908,10 +913,10 @@ static void VE4_SSE2(uint8_t* dst) { // vertical
const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGH00), one);
const __m128i b = _mm_subs_epu8(a, lsb);
const __m128i avg = _mm_avg_epu8(b, BCDEFGH0);
- const uint32_t vals = _mm_cvtsi128_si32(avg);
+ const int vals = _mm_cvtsi128_si32(avg);
int i;
for (i = 0; i < 4; ++i) {
- WebPUint32ToMem(dst + i * BPS, vals);
+ WebPInt32ToMem(dst + i * BPS, vals);
}
}
@@ -925,10 +930,10 @@ static void LD4_SSE2(uint8_t* dst) { // Down-Left
const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one);
const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0);
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg ));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg ));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
}
static void VR4_SSE2(uint8_t* dst) { // Vertical-Right
@@ -946,10 +951,10 @@ static void VR4_SSE2(uint8_t* dst) { // Vertical-Right
const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
const __m128i efgh = _mm_avg_epu8(avg2, XABCD);
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd ));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh ));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd ));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh ));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
// these two are hard to implement in SSE2, so we keep the C-version:
DST(0, 2) = AVG3(J, I, X);
@@ -970,11 +975,12 @@ static void VL4_SSE2(uint8_t* dst) { // Vertical-Left
const __m128i abbc = _mm_or_si128(ab, bc);
const __m128i lsb2 = _mm_and_si128(abbc, lsb1);
const __m128i avg4 = _mm_subs_epu8(avg3, lsb2);
- const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 ));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 ));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
+ const uint32_t extra_out =
+ (uint32_t)_mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 ));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 ));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
// these two are hard to get and irregular
DST(3, 2) = (extra_out >> 0) & 0xff;
@@ -990,7 +996,7 @@ static void RD4_SSE2(uint8_t* dst) { // Down-right
const uint32_t K = dst[-1 + 2 * BPS];
const uint32_t L = dst[-1 + 3 * BPS];
const __m128i LKJI_____ =
- _mm_cvtsi32_si128(L | (K << 8) | (J << 16) | (I << 24));
+ _mm_cvtsi32_si128((int)(L | (K << 8) | (J << 16) | (I << 24)));
const __m128i LKJIXABCD = _mm_or_si128(LKJI_____, ____XABCD);
const __m128i KJIXABCD_ = _mm_srli_si128(LKJIXABCD, 1);
const __m128i JIXABCD__ = _mm_srli_si128(LKJIXABCD, 2);
@@ -998,10 +1004,10 @@ static void RD4_SSE2(uint8_t* dst) { // Down-right
const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one);
const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_);
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg ));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg ));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
}
#undef DST
@@ -1015,13 +1021,13 @@ static WEBP_INLINE void TrueMotion_SSE2(uint8_t* dst, int size) {
const __m128i zero = _mm_setzero_si128();
int y;
if (size == 4) {
- const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top));
+ const __m128i top_values = _mm_cvtsi32_si128(WebPMemToInt32(top));
const __m128i top_base = _mm_unpacklo_epi8(top_values, zero);
for (y = 0; y < 4; ++y, dst += BPS) {
const int val = dst[-1] - top[-1];
const __m128i base = _mm_set1_epi16(val);
const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero);
- WebPUint32ToMem(dst, _mm_cvtsi128_si32(out));
+ WebPInt32ToMem(dst, _mm_cvtsi128_si32(out));
}
} else if (size == 8) {
const __m128i top_values = _mm_loadl_epi64((const __m128i*)top);
@@ -1062,7 +1068,7 @@ static void VE16_SSE2(uint8_t* dst) {
static void HE16_SSE2(uint8_t* dst) { // horizontal
int j;
for (j = 16; j > 0; --j) {
- const __m128i values = _mm_set1_epi8(dst[-1]);
+ const __m128i values = _mm_set1_epi8((char)dst[-1]);
_mm_storeu_si128((__m128i*)dst, values);
dst += BPS;
}
@@ -1070,7 +1076,7 @@ static void HE16_SSE2(uint8_t* dst) { // horizontal
static WEBP_INLINE void Put16_SSE2(uint8_t v, uint8_t* dst) {
int j;
- const __m128i values = _mm_set1_epi8(v);
+ const __m128i values = _mm_set1_epi8((char)v);
for (j = 0; j < 16; ++j) {
_mm_storeu_si128((__m128i*)(dst + j * BPS), values);
}
@@ -1130,7 +1136,7 @@ static void VE8uv_SSE2(uint8_t* dst) { // vertical
// helper for chroma-DC predictions
static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) {
int j;
- const __m128i values = _mm_set1_epi8(v);
+ const __m128i values = _mm_set1_epi8((char)v);
for (j = 0; j < 8; ++j) {
_mm_storel_epi64((__m128i*)(dst + j * BPS), values);
}
diff --git a/c-lib/src/dsp/dec_sse41.c b/c-lib/src/dsp/dec_sse41.c
@@ -14,16 +14,19 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE41)
-
+#include <emmintrin.h>
#include <smmintrin.h>
+
+#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
int j;
const __m128i kShuffle3 = _mm_set1_epi8(3);
for (j = 16; j > 0; --j) {
- const __m128i in = _mm_cvtsi32_si128(WebPMemToUint32(dst - 4));
+ const __m128i in = _mm_cvtsi32_si128(WebPMemToInt32(dst - 4));
const __m128i values = _mm_shuffle_epi8(in, kShuffle3);
_mm_storeu_si128((__m128i*)dst, values);
dst += BPS;
diff --git a/c-lib/src/dsp/dsp.h b/c-lib/src/dsp/dsp.h
@@ -18,6 +18,7 @@
#include "src/webp/config.h"
#endif
+#include "src/dsp/cpu.h"
#include "src/webp/types.h"
#ifdef __cplusplus
@@ -43,226 +44,6 @@ extern "C" {
#define WEBP_RESTRICT
#endif
-//------------------------------------------------------------------------------
-// CPU detection
-
-#if defined(__GNUC__)
-# define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__)
-# define LOCAL_GCC_PREREQ(maj, min) \
- (LOCAL_GCC_VERSION >= (((maj) << 8) | (min)))
-#else
-# define LOCAL_GCC_VERSION 0
-# define LOCAL_GCC_PREREQ(maj, min) 0
-#endif
-
-#if defined(__clang__)
-# define LOCAL_CLANG_VERSION ((__clang_major__ << 8) | __clang_minor__)
-# define LOCAL_CLANG_PREREQ(maj, min) \
- (LOCAL_CLANG_VERSION >= (((maj) << 8) | (min)))
-#else
-# define LOCAL_CLANG_VERSION 0
-# define LOCAL_CLANG_PREREQ(maj, min) 0
-#endif
-
-#ifndef __has_builtin
-# define __has_builtin(x) 0
-#endif
-
-#if !defined(HAVE_CONFIG_H)
-#if defined(_MSC_VER) && _MSC_VER > 1310 && \
- (defined(_M_X64) || defined(_M_IX86))
-#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER >= 1500 && \
- (defined(_M_X64) || defined(_M_IX86))
-#define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets
-#endif
-#endif
-
-// WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp
-// files without intrinsics, allowing the corresponding Init() to be called.
-// Files containing intrinsics will need to be built targeting the instruction
-// set so should succeed on one of the earlier tests.
-#if (defined(__SSE2__) || defined(WEBP_MSC_SSE2)) && \
- (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE2))
-#define WEBP_USE_SSE2
-#endif
-
-#if defined(WEBP_USE_SSE2) && !defined(WEBP_HAVE_SSE2)
-#define WEBP_HAVE_SSE2
-#endif
-
-#if (defined(__SSE4_1__) || defined(WEBP_MSC_SSE41)) && \
- (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE41))
-#define WEBP_USE_SSE41
-#endif
-
-#if defined(WEBP_USE_SSE41) && !defined(WEBP_HAVE_SSE41)
-#define WEBP_HAVE_SSE41
-#endif
-
-#undef WEBP_MSC_SSE41
-#undef WEBP_MSC_SSE2
-
-// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
-// inline assembly would need to be modified for use with Native Client.
-#if ((defined(__ARM_NEON__) || defined(__aarch64__)) && \
- (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_NEON))) && \
- !defined(__native_client__)
-#define WEBP_USE_NEON
-#endif
-
-#if !defined(WEBP_USE_NEON) && defined(__ANDROID__) && \
- defined(__ARM_ARCH_7A__) && defined(HAVE_CPU_FEATURES_H)
-#define WEBP_ANDROID_NEON // Android targets that may have NEON
-#define WEBP_USE_NEON
-#endif
-
-// Note: ARM64 is supported in Visual Studio 2017, but requires the direct
-// inclusion of arm64_neon.h; Visual Studio 2019 includes this file in
-// arm_neon.h. Compile errors were seen with Visual Studio 2019 16.4 with
-// vtbl4_u8(); a fix was made in 16.6.
-#if defined(_MSC_VER) && \
- ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
- (_MSC_VER >= 1926 && defined(_M_ARM64)))
-#define WEBP_USE_NEON
-#define WEBP_USE_INTRINSICS
-#endif
-
-#if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON)
-#define WEBP_HAVE_NEON
-#endif
-
-#if defined(__mips__) && !defined(__mips64) && \
- defined(__mips_isa_rev) && (__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
-#define WEBP_USE_MIPS32
-#if (__mips_isa_rev >= 2)
-#define WEBP_USE_MIPS32_R2
-#if defined(__mips_dspr2) || (defined(__mips_dsp_rev) && __mips_dsp_rev >= 2)
-#define WEBP_USE_MIPS_DSP_R2
-#endif
-#endif
-#endif
-
-#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
-#define WEBP_USE_MSA
-#endif
-
-#ifndef WEBP_DSP_OMIT_C_CODE
-#define WEBP_DSP_OMIT_C_CODE 1
-#endif
-
-#if defined(WEBP_USE_NEON) && WEBP_DSP_OMIT_C_CODE
-#define WEBP_NEON_OMIT_C_CODE 1
-#else
-#define WEBP_NEON_OMIT_C_CODE 0
-#endif
-
-#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__))
-#define WEBP_NEON_WORK_AROUND_GCC 1
-#else
-#define WEBP_NEON_WORK_AROUND_GCC 0
-#endif
-
-// This macro prevents thread_sanitizer from reporting known concurrent writes.
-#define WEBP_TSAN_IGNORE_FUNCTION
-#if defined(__has_feature)
-#if __has_feature(thread_sanitizer)
-#undef WEBP_TSAN_IGNORE_FUNCTION
-#define WEBP_TSAN_IGNORE_FUNCTION __attribute__((no_sanitize_thread))
-#endif
-#endif
-
-#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
-#include <pthread.h> // NOLINT
-
-#define WEBP_DSP_INIT(func) do { \
- static volatile VP8CPUInfo func ## _last_cpuinfo_used = \
- (VP8CPUInfo)&func ## _last_cpuinfo_used; \
- static pthread_mutex_t func ## _lock = PTHREAD_MUTEX_INITIALIZER; \
- if (pthread_mutex_lock(&func ## _lock)) break; \
- if (func ## _last_cpuinfo_used != VP8GetCPUInfo) func(); \
- func ## _last_cpuinfo_used = VP8GetCPUInfo; \
- (void)pthread_mutex_unlock(&func ## _lock); \
-} while (0)
-#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
-#define WEBP_DSP_INIT(func) do { \
- static volatile VP8CPUInfo func ## _last_cpuinfo_used = \
- (VP8CPUInfo)&func ## _last_cpuinfo_used; \
- if (func ## _last_cpuinfo_used == VP8GetCPUInfo) break; \
- func(); \
- func ## _last_cpuinfo_used = VP8GetCPUInfo; \
-} while (0)
-#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32)
-
-// Defines an Init + helper function that control multiple initialization of
-// function pointers / tables.
-/* Usage:
- WEBP_DSP_INIT_FUNC(InitFunc) {
- ...function body
- }
-*/
-#define WEBP_DSP_INIT_FUNC(name) \
- static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void); \
- WEBP_TSAN_IGNORE_FUNCTION void name(void) { \
- WEBP_DSP_INIT(name ## _body); \
- } \
- static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void)
-
-#define WEBP_UBSAN_IGNORE_UNDEF
-#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
-#if defined(__clang__) && defined(__has_attribute)
-#if __has_attribute(no_sanitize)
-// This macro prevents the undefined behavior sanitizer from reporting
-// failures. This is only meant to silence unaligned loads on platforms that
-// are known to support them.
-#undef WEBP_UBSAN_IGNORE_UNDEF
-#define WEBP_UBSAN_IGNORE_UNDEF \
- __attribute__((no_sanitize("undefined")))
-
-// This macro prevents the undefined behavior sanitizer from reporting
-// failures related to unsigned integer overflows. This is only meant to
-// silence cases where this well defined behavior is expected.
-#undef WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
-#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW \
- __attribute__((no_sanitize("unsigned-integer-overflow")))
-#endif
-#endif
-
-// If 'ptr' is NULL, returns NULL. Otherwise returns 'ptr + off'.
-// Prevents undefined behavior sanitizer nullptr-with-nonzero-offset warning.
-#if !defined(WEBP_OFFSET_PTR)
-#define WEBP_OFFSET_PTR(ptr, off) (((ptr) == NULL) ? NULL : ((ptr) + (off)))
-#endif
-
-// Regularize the definition of WEBP_SWAP_16BIT_CSP (backward compatibility)
-#if !defined(WEBP_SWAP_16BIT_CSP)
-#define WEBP_SWAP_16BIT_CSP 0
-#endif
-
-// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__)
-#if !defined(WORDS_BIGENDIAN) && \
- (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \
- (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
-#define WORDS_BIGENDIAN
-#endif
-
-typedef enum {
- kSSE2,
- kSSE3,
- kSlowSSSE3, // special feature for slow SSSE3 architectures
- kSSE4_1,
- kAVX,
- kAVX2,
- kNEON,
- kMIPS32,
- kMIPSdspR2,
- kMSA
-} CPUFeature;
-// returns true if the CPU supports the feature.
-typedef int (*VP8CPUInfo)(CPUFeature feature);
-WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
//------------------------------------------------------------------------------
// Init stub generator
@@ -279,53 +60,66 @@ WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
// 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];
@@ -337,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],
@@ -357,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.
@@ -412,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;
@@ -422,6 +220,11 @@ extern VP8DecIdct VP8TransformDC;
extern VP8DecIdct VP8TransformDCUV;
extern VP8WHT VP8TransformWHT;
+#define WEBP_TRANSFORM_AC3_C1 20091
+#define WEBP_TRANSFORM_AC3_C2 35468
+#define WEBP_TRANSFORM_AC3_MUL1(a) ((((a) * WEBP_TRANSFORM_AC3_C1) >> 16) + (a))
+#define WEBP_TRANSFORM_AC3_MUL2(a) (((a) * WEBP_TRANSFORM_AC3_C2) >> 16)
+
// *dst is the destination block, with stride BPS. Boundary samples are
// assumed accessible when needed.
typedef void (*VP8PredFunc)(uint8_t* dst);
@@ -447,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;
@@ -467,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);
@@ -481,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
@@ -494,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)
@@ -512,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 */];
@@ -530,35 +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);
-
-// utilities for accurate RGB->YUV conversion
-extern uint64_t (*WebPSharpYUVUpdateY)(const uint16_t* src, const uint16_t* ref,
- uint16_t* dst, int len);
-extern void (*WebPSharpYUVUpdateRGB)(const int16_t* src, const int16_t* ref,
- int16_t* dst, int len);
-extern void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B,
- int len,
- const uint16_t* best_y, uint16_t* out);
+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);
@@ -571,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;
@@ -585,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);
@@ -703,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/c-lib/src/dsp/enc.c b/c-lib/src/dsp/enc.c
@@ -13,9 +13,13 @@
#include <assert.h>
#include <stdlib.h> // for abs()
+#include <string.h>
+#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,20 +114,19 @@ 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 const int kC1 = 20091 + (1 << 16);
-static const int kC2 = 35468;
-#define MUL(a, b) (((a) * (b)) >> 16)
-
-static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
- uint8_t* dst) {
+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;
for (i = 0; i < 4; ++i) { // vertical pass
const int a = in[0] + in[8];
const int b = in[0] - in[8];
- const int c = MUL(in[4], kC2) - MUL(in[12], kC1);
- const int d = MUL(in[4], kC1) + MUL(in[12], kC2);
+ const int c =
+ WEBP_TRANSFORM_AC3_MUL2(in[4]) - WEBP_TRANSFORM_AC3_MUL1(in[12]);
+ const int d =
+ WEBP_TRANSFORM_AC3_MUL1(in[4]) + WEBP_TRANSFORM_AC3_MUL2(in[12]);
tmp[0] = a + d;
tmp[1] = b + c;
tmp[2] = b - c;
@@ -134,10 +138,12 @@ static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
tmp = C;
for (i = 0; i < 4; ++i) { // horizontal pass
const int dc = tmp[0] + 4;
- const int a = dc + tmp[8];
- const int b = dc - tmp[8];
- const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
- const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
+ const int a = dc + tmp[8];
+ const int b = dc - tmp[8];
+ const int c =
+ WEBP_TRANSFORM_AC3_MUL2(tmp[4]) - WEBP_TRANSFORM_AC3_MUL1(tmp[12]);
+ const int d =
+ WEBP_TRANSFORM_AC3_MUL1(tmp[4]) + WEBP_TRANSFORM_AC3_MUL2(tmp[12]);
STORE(0, i, a + d);
STORE(1, i, b + c);
STORE(2, i, b - 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;
@@ -222,7 +234,6 @@ static void FTransformWHT_C(const int16_t* in, int16_t* out) {
}
#endif // !WEBP_NEON_OMIT_C_CODE
-#undef MUL
#undef STORE
//------------------------------------------------------------------------------
@@ -235,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);
@@ -245,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) {
@@ -257,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) {
@@ -287,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;
@@ -313,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);
@@ -333,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]),
@@ -361,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];
@@ -373,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];
@@ -399,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];
@@ -417,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];
@@ -439,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];
@@ -461,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];
@@ -476,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];
@@ -499,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) {
@@ -517,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);
@@ -530,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;
@@ -549,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;
@@ -587,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;
@@ -621,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) {
@@ -645,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;
@@ -676,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;
@@ -689,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);
@@ -698,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);
}
@@ -732,6 +769,7 @@ VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT;
VP8BlockCopy VP8Copy4x4;
VP8BlockCopy VP8Copy16x8;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8EncDspInitSSE2(void);
extern void VP8EncDspInitSSE41(void);
extern void VP8EncDspInitNEON(void);
@@ -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
- VP8FTransform2 = FTransform2_C;
+#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;
VP8EncPredChroma8 = IntraChromaPreds_C;
VP8Mean16x4 = Mean16x4_C;
- VP8EncQuantizeBlockWHT = QuantizeBlock_C;
VP8Copy4x4 = Copy4x4_C;
VP8Copy16x8 = Copy16x8_C;
diff --git a/c-lib/src/dsp/enc_mips32.c b/c-lib/src/dsp/enc_mips32.c
@@ -21,8 +21,8 @@
#include "src/enc/vp8i_enc.h"
#include "src/enc/cost_enc.h"
-static const int kC1 = 20091 + (1 << 16);
-static const int kC2 = 35468;
+static const int kC1 = WEBP_TRANSFORM_AC3_C1;
+static const int kC2 = WEBP_TRANSFORM_AC3_C2;
// macro for one vertical pass in ITransformOne
// MUL macro inlined
@@ -30,7 +30,7 @@ static const int kC2 = 35468;
// A..D - offsets in bytes to load from in buffer
// TEMP0..TEMP3 - registers for corresponding tmp elements
// TEMP4..TEMP5 - temporary registers
-#define VERTICAL_PASS(A, B, C, D, TEMP4, TEMP0, TEMP1, TEMP2, TEMP3) \
+#define VERTICAL_PASS(A, B, C, D, TEMP4, TEMP0, TEMP1, TEMP2, TEMP3) \
"lh %[temp16], " #A "(%[temp20]) \n\t" \
"lh %[temp18], " #B "(%[temp20]) \n\t" \
"lh %[temp17], " #C "(%[temp20]) \n\t" \
@@ -38,12 +38,10 @@ static const int kC2 = 35468;
"addu %[" #TEMP4 "], %[temp16], %[temp18] \n\t" \
"subu %[temp16], %[temp16], %[temp18] \n\t" \
"mul %[" #TEMP0 "], %[temp17], %[kC2] \n\t" \
- "mul %[temp18], %[temp19], %[kC1] \n\t" \
- "mul %[temp17], %[temp17], %[kC1] \n\t" \
+ MUL_SHIFT_C1_IO(temp17, temp18) \
+ MUL_SHIFT_C1(temp18, temp19) \
"mul %[temp19], %[temp19], %[kC2] \n\t" \
"sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\n" \
- "sra %[temp18], %[temp18], 16 \n\n" \
- "sra %[temp17], %[temp17], 16 \n\n" \
"sra %[temp19], %[temp19], 16 \n\n" \
"subu %[" #TEMP2 "], %[" #TEMP0 "], %[temp18] \n\t" \
"addu %[" #TEMP3 "], %[temp17], %[temp19] \n\t" \
@@ -58,17 +56,15 @@ static const int kC2 = 35468;
// temp0..temp15 holds tmp[0]..tmp[15]
// A - offset in bytes to load from ref and store to dst buffer
// TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements
-#define HORIZONTAL_PASS(A, TEMP0, TEMP4, TEMP8, TEMP12) \
+#define HORIZONTAL_PASS(A, TEMP0, TEMP4, TEMP8, TEMP12) \
"addiu %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \
"addu %[temp16], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
"subu %[temp17], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
"mul %[" #TEMP0 "], %[" #TEMP4 "], %[kC2] \n\t" \
- "mul %[" #TEMP8 "], %[" #TEMP12 "], %[kC1] \n\t" \
- "mul %[" #TEMP4 "], %[" #TEMP4 "], %[kC1] \n\t" \
+ MUL_SHIFT_C1_IO(TEMP4, TEMP8) \
+ MUL_SHIFT_C1(TEMP8, TEMP12) \
"mul %[" #TEMP12 "], %[" #TEMP12 "], %[kC2] \n\t" \
"sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\t" \
- "sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \
- "sra %[" #TEMP4 "], %[" #TEMP4 "], 16 \n\t" \
"sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \
"subu %[temp18], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
"addu %[temp19], %[" #TEMP4 "], %[" #TEMP12 "] \n\t" \
@@ -113,9 +109,9 @@ static const int kC2 = 35468;
"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;
@@ -145,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);
@@ -196,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)
@@ -240,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;
@@ -362,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;
@@ -397,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) {
@@ -479,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;
@@ -541,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;
@@ -575,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;
@@ -601,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;
@@ -623,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/c-lib/src/dsp/enc_mips_dsp_r2.c b/c-lib/src/dsp/enc_mips_dsp_r2.c
@@ -20,8 +20,8 @@
#include "src/enc/cost_enc.h"
#include "src/enc/vp8i_enc.h"
-static const int kC1 = 20091 + (1 << 16);
-static const int kC2 = 35468;
+static const int kC1 = WEBP_TRANSFORM_AC3_C1;
+static const int kC2 = WEBP_TRANSFORM_AC3_C2;
// O - output
// I - input (macro doesn't change it)
@@ -141,8 +141,9 @@ static const int kC2 = 35468;
"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/c-lib/src/dsp/enc_msa.c b/c-lib/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/c-lib/src/dsp/enc_neon.c b/c-lib/src/dsp/enc_neon.c
@@ -27,8 +27,9 @@
// This code is pretty much the same as TransformOne in the dec_neon.c, except
// for subtraction to *ref. See the comments there for algorithmic explanations.
-static const int16_t kC1 = 20091;
-static const int16_t kC2 = 17734; // half of kC2, actually. See comment above.
+static const int16_t kC1 = WEBP_TRANSFORM_AC3_C1;
+static const int16_t kC2 =
+ WEBP_TRANSFORM_AC3_C2 / 2; // half of kC2, actually. See comment above.
// This code works but is *slower* than the inlined-asm version below
// (with gcc-4.6). So we disable it for now. Later, it'll be conditional to
@@ -59,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);
@@ -119,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);
@@ -130,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 };
@@ -246,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);
@@ -293,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);
@@ -363,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;
@@ -483,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;
@@ -658,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);
@@ -700,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) {
@@ -714,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 };
@@ -746,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);
@@ -764,12 +774,18 @@ static WEBP_INLINE void AccumulateSSE16_NEON(const uint8_t* const a,
// Horizontal sum of all four uint32_t values in 'sum'.
static int SumToInt_NEON(uint32x4_t sum) {
+#if WEBP_AARCH64
+ return (int)vaddvq_u32(sum);
+#else
const uint64x2_t sum2 = vpaddlq_u32(sum);
- const uint64_t sum3 = vgetq_lane_u64(sum2, 0) + vgetq_lane_u64(sum2, 1);
- return (int)sum3;
+ const uint32x2_t sum3 = vadd_u32(vreinterpret_u32_u64(vget_low_u64(sum2)),
+ vreinterpret_u32_u64(vget_high_u64(sum2)));
+ return (int)vget_lane_u32(sum3, 0);
+#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) {
@@ -778,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) {
@@ -787,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) {
@@ -800,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);
@@ -819,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)
@@ -854,13 +874,13 @@ 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;
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
uint8x16x2_t all_out;
INIT_VECTOR2(all_out, vreinterpretq_u8_s16(out0), vreinterpretq_u8_s16(out1));
@@ -896,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;
@@ -905,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
@@ -925,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/c-lib/src/dsp/enc_sse2.c b/c-lib/src/dsp/enc_sse2.c
@@ -14,20 +14,178 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
+
#include <assert.h>
#include <stdlib.h> // for abs()
-#include <emmintrin.h>
+#include <string.h>
#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 or two inverse transforms.
-static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
- int do_two) {
+// Does one inverse transform.
+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
+ // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
+ //
+ // To be able to use signed 16-bit integers, we use the following trick to
+ // have constants within range:
+ // - Associated constants are obtained by subtracting the 16-bit fixed point
+ // version of one:
+ // k = K - (1 << 16) => K = k + (1 << 16)
+ // K1 = 85267 => k1 = 20091
+ // K2 = 35468 => k2 = -30068
+ // - The multiplication of a variable by a constant become the sum of the
+ // variable and the multiplication of that variable by the associated
+ // constant:
+ // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
+ const __m128i k1k2 = _mm_set_epi16(-30068, -30068, -30068, -30068,
+ 20091, 20091, 20091, 20091);
+ const __m128i k2k1 = _mm_set_epi16(20091, 20091, 20091, 20091,
+ -30068, -30068, -30068, -30068);
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i zero_four = _mm_set_epi16(0, 0, 0, 0, 4, 4, 4, 4);
+ __m128i T01, T23;
+
+ // Load and concatenate the transform coefficients.
+ const __m128i in01 = _mm_loadu_si128((const __m128i*)&in[0]);
+ const __m128i in23 = _mm_loadu_si128((const __m128i*)&in[8]);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+
+ // Vertical pass and subsequent transpose.
+ {
+ const __m128i in1 = _mm_unpackhi_epi64(in01, in01);
+ const __m128i in3 = _mm_unpackhi_epi64(in23, in23);
+
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
+ // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
+ const __m128i a_d3 = _mm_add_epi16(in01, in23);
+ const __m128i b_c3 = _mm_sub_epi16(in01, in23);
+ const __m128i c1d1 = _mm_mulhi_epi16(in1, k2k1);
+ const __m128i c2d2 = _mm_mulhi_epi16(in3, k1k2);
+ const __m128i c3 = _mm_unpackhi_epi64(b_c3, b_c3);
+ const __m128i c4 = _mm_sub_epi16(c1d1, c2d2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ const __m128i d4u = _mm_add_epi16(c1d1, c2d2);
+ const __m128i du = _mm_add_epi16(a_d3, d4u);
+ const __m128i d = _mm_unpackhi_epi64(du, du);
+
+ // Second pass.
+ const __m128i comb_ab = _mm_unpacklo_epi64(a_d3, b_c3);
+ const __m128i comb_dc = _mm_unpacklo_epi64(d, c);
+
+ const __m128i tmp01 = _mm_add_epi16(comb_ab, comb_dc);
+ const __m128i tmp32 = _mm_sub_epi16(comb_ab, comb_dc);
+ const __m128i tmp23 = _mm_shuffle_epi32(tmp32, _MM_SHUFFLE(1, 0, 3, 2));
+
+ const __m128i transpose_0 = _mm_unpacklo_epi16(tmp01, tmp23);
+ const __m128i transpose_1 = _mm_unpackhi_epi16(tmp01, tmp23);
+ // a00 a20 a01 a21 a02 a22 a03 a23
+ // a10 a30 a11 a31 a12 a32 a13 a33
+
+ T01 = _mm_unpacklo_epi16(transpose_0, transpose_1);
+ T23 = _mm_unpackhi_epi16(transpose_0, transpose_1);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+ }
+
+ // Horizontal pass and subsequent transpose.
+ {
+ const __m128i T1 = _mm_unpackhi_epi64(T01, T01);
+ const __m128i T3 = _mm_unpackhi_epi64(T23, T23);
+
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ const __m128i dc = _mm_add_epi16(T01, zero_four);
+
+ // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
+ // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
+ const __m128i a_d3 = _mm_add_epi16(dc, T23);
+ const __m128i b_c3 = _mm_sub_epi16(dc, T23);
+ const __m128i c1d1 = _mm_mulhi_epi16(T1, k2k1);
+ const __m128i c2d2 = _mm_mulhi_epi16(T3, k1k2);
+ const __m128i c3 = _mm_unpackhi_epi64(b_c3, b_c3);
+ const __m128i c4 = _mm_sub_epi16(c1d1, c2d2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ const __m128i d4u = _mm_add_epi16(c1d1, c2d2);
+ const __m128i du = _mm_add_epi16(a_d3, d4u);
+ const __m128i d = _mm_unpackhi_epi64(du, du);
+
+ // Second pass.
+ const __m128i comb_ab = _mm_unpacklo_epi64(a_d3, b_c3);
+ const __m128i comb_dc = _mm_unpacklo_epi64(d, c);
+
+ const __m128i tmp01 = _mm_add_epi16(comb_ab, comb_dc);
+ const __m128i tmp32 = _mm_sub_epi16(comb_ab, comb_dc);
+ const __m128i tmp23 = _mm_shuffle_epi32(tmp32, _MM_SHUFFLE(1, 0, 3, 2));
+
+ const __m128i shifted01 = _mm_srai_epi16(tmp01, 3);
+ const __m128i shifted23 = _mm_srai_epi16(tmp23, 3);
+ // a00 a01 a02 a03 a10 a11 a12 a13
+ // a20 a21 a22 a23 a30 a31 a32 a33
+
+ const __m128i transpose_0 = _mm_unpacklo_epi16(shifted01, shifted23);
+ const __m128i transpose_1 = _mm_unpackhi_epi16(shifted01, shifted23);
+ // a00 a20 a01 a21 a02 a22 a03 a23
+ // a10 a30 a11 a31 a12 a32 a13 a33
+
+ T01 = _mm_unpacklo_epi16(transpose_0, transpose_1);
+ T23 = _mm_unpackhi_epi16(transpose_0, transpose_1);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+ }
+
+ // Add inverse transform to 'ref' and store.
+ {
+ // Load the reference(s).
+ __m128i ref01, ref23, ref0123;
+ int32_t buf[4];
+
+ // Load four bytes/pixels per line.
+ const __m128i ref0 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[0 * BPS]));
+ const __m128i ref1 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[1 * BPS]));
+ const __m128i ref2 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[2 * BPS]));
+ const __m128i ref3 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[3 * BPS]));
+ ref01 = _mm_unpacklo_epi32(ref0, ref1);
+ ref23 = _mm_unpacklo_epi32(ref2, ref3);
+
+ // Convert to 16b.
+ ref01 = _mm_unpacklo_epi8(ref01, zero);
+ ref23 = _mm_unpacklo_epi8(ref23, zero);
+ // Add the inverse transform(s).
+ ref01 = _mm_add_epi16(ref01, T01);
+ ref23 = _mm_add_epi16(ref23, T23);
+ // Unsigned saturate to 8b.
+ ref0123 = _mm_packus_epi16(ref01, ref23);
+
+ _mm_storeu_si128((__m128i *)buf, ref0123);
+
+ // Store four bytes/pixels per line.
+ WebPInt32ToMem(&dst[0 * BPS], buf[0]);
+ WebPInt32ToMem(&dst[1 * BPS], buf[1]);
+ WebPInt32ToMem(&dst[2 * BPS], buf[2]);
+ WebPInt32ToMem(&dst[3 * BPS], buf[3]);
+ }
+}
+
+// Does two inverse transforms.
+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
@@ -49,33 +207,21 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
__m128i T0, T1, T2, T3;
// Load and concatenate the transform coefficients (we'll do two inverse
- // transforms in parallel). In the case of only one inverse transform, the
- // second half of the vectors will just contain random value we'll never
- // use nor store.
+ // transforms in parallel).
__m128i in0, in1, in2, in3;
{
- in0 = _mm_loadl_epi64((const __m128i*)&in[0]);
- in1 = _mm_loadl_epi64((const __m128i*)&in[4]);
- in2 = _mm_loadl_epi64((const __m128i*)&in[8]);
- in3 = _mm_loadl_epi64((const __m128i*)&in[12]);
- // a00 a10 a20 a30 x x x x
- // a01 a11 a21 a31 x x x x
- // a02 a12 a22 a32 x x x x
- // a03 a13 a23 a33 x x x x
- if (do_two) {
- const __m128i inB0 = _mm_loadl_epi64((const __m128i*)&in[16]);
- const __m128i inB1 = _mm_loadl_epi64((const __m128i*)&in[20]);
- const __m128i inB2 = _mm_loadl_epi64((const __m128i*)&in[24]);
- const __m128i inB3 = _mm_loadl_epi64((const __m128i*)&in[28]);
- in0 = _mm_unpacklo_epi64(in0, inB0);
- in1 = _mm_unpacklo_epi64(in1, inB1);
- in2 = _mm_unpacklo_epi64(in2, inB2);
- in3 = _mm_unpacklo_epi64(in3, inB3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
+ const __m128i tmp0 = _mm_loadu_si128((const __m128i*)&in[0]);
+ const __m128i tmp1 = _mm_loadu_si128((const __m128i*)&in[8]);
+ const __m128i tmp2 = _mm_loadu_si128((const __m128i*)&in[16]);
+ const __m128i tmp3 = _mm_loadu_si128((const __m128i*)&in[24]);
+ in0 = _mm_unpacklo_epi64(tmp0, tmp2);
+ in1 = _mm_unpackhi_epi64(tmp0, tmp2);
+ in2 = _mm_unpacklo_epi64(tmp1, tmp3);
+ in3 = _mm_unpackhi_epi64(tmp1, tmp3);
+ // a00 a10 a20 a30 b00 b10 b20 b30
+ // a01 a11 a21 a31 b01 b11 b21 b31
+ // a02 a12 a22 a32 b02 b12 b22 b32
+ // a03 a13 a23 a33 b03 b13 b23 b33
}
// Vertical pass and subsequent transpose.
@@ -148,19 +294,11 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
const __m128i zero = _mm_setzero_si128();
// Load the reference(s).
__m128i ref0, ref1, ref2, ref3;
- if (do_two) {
- // Load eight bytes/pixels per line.
- ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
- ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
- ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
- ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
- } else {
- // Load four bytes/pixels per line.
- ref0 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[0 * BPS]));
- ref1 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[1 * BPS]));
- ref2 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[2 * BPS]));
- ref3 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[3 * BPS]));
- }
+ // Load eight bytes/pixels per line.
+ ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
+ ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
+ ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
+ ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
// Convert to 16b.
ref0 = _mm_unpacklo_epi8(ref0, zero);
ref1 = _mm_unpacklo_epi8(ref1, zero);
@@ -176,20 +314,23 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
ref1 = _mm_packus_epi16(ref1, ref1);
ref2 = _mm_packus_epi16(ref2, ref2);
ref3 = _mm_packus_epi16(ref3, ref3);
- // Store the results.
- if (do_two) {
- // Store eight bytes/pixels per line.
- _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
- _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
- _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
- _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
- } else {
- // Store four bytes/pixels per line.
- WebPUint32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0));
- WebPUint32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1));
- WebPUint32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2));
- WebPUint32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3));
- }
+ // Store eight bytes/pixels per line.
+ _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
+ _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
+ _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
+ _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
+ }
+}
+
+// Does one or two inverse transforms.
+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);
+ } else {
+ ITransform_One_SSE2(ref, in, dst);
}
}
@@ -241,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,
@@ -292,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]);
@@ -336,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.
@@ -385,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]);
@@ -401,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.
@@ -434,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;
@@ -481,7 +627,7 @@ static void CollectHistogram_SSE2(const uint8_t* ref, const uint8_t* pred,
// helper for chroma-DC predictions
static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) {
int j;
- const __m128i values = _mm_set1_epi8(v);
+ const __m128i values = _mm_set1_epi8((char)v);
for (j = 0; j < 8; ++j) {
_mm_storel_epi64((__m128i*)(dst + j * BPS), values);
}
@@ -489,7 +635,7 @@ static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) {
static WEBP_INLINE void Put16_SSE2(uint8_t v, uint8_t* dst) {
int j;
- const __m128i values = _mm_set1_epi8(v);
+ const __m128i values = _mm_set1_epi8((char)v);
for (j = 0; j < 16; ++j) {
_mm_store_si128((__m128i*)(dst + j * BPS), values);
}
@@ -508,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) {
@@ -516,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) {
@@ -524,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);
@@ -537,26 +686,29 @@ 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(left[j]);
+ const __m128i values = _mm_set1_epi8((char)left[j]);
_mm_storel_epi64((__m128i*)dst, values);
dst += BPS;
}
}
-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(left[j]);
+ const __m128i values = _mm_set1_epi8((char)left[j]);
_mm_store_si128((__m128i*)dst, values);
dst += BPS;
}
}
-static WEBP_INLINE void HorizontalPred_SSE2(uint8_t* dst,
- const uint8_t* left, int size) {
+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);
@@ -568,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) {
@@ -596,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);
@@ -617,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);
@@ -626,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);
@@ -634,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);
}
@@ -643,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);
@@ -658,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 =
@@ -667,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);
}
@@ -682,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);
@@ -712,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);
@@ -722,15 +886,16 @@ static WEBP_INLINE void VE4_SSE2(uint8_t* dst,
const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGH00), one);
const __m128i b = _mm_subs_epu8(a, lsb);
const __m128i avg = _mm_avg_epu8(b, BCDEFGH0);
- const uint32_t vals = _mm_cvtsi128_si32(avg);
+ const int vals = _mm_cvtsi128_si32(avg);
int i;
for (i = 0; i < 4; ++i) {
- WebPUint32ToMem(dst + i * BPS, vals);
+ WebPInt32ToMem(dst + i * BPS, vals);
}
}
-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];
@@ -742,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);
@@ -760,14 +927,15 @@ static WEBP_INLINE void LD4_SSE2(uint8_t* dst,
const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one);
const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0);
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg ));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg ));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+ 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];
@@ -782,18 +950,19 @@ static WEBP_INLINE void VR4_SSE2(uint8_t* dst,
const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
const __m128i efgh = _mm_avg_epu8(avg2, XABCD);
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd ));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh ));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd ));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh ));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
// these two are hard to implement in SSE2, so we keep the C-version:
DST(0, 2) = AVG3(J, I, X);
DST(0, 3) = AVG3(K, J, I);
}
-static WEBP_INLINE void VL4_SSE2(uint8_t* dst,
- const uint8_t* top) { // Vertical-Left
+// 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);
@@ -807,19 +976,21 @@ static WEBP_INLINE void VL4_SSE2(uint8_t* dst,
const __m128i abbc = _mm_or_si128(ab, bc);
const __m128i lsb2 = _mm_and_si128(abbc, lsb1);
const __m128i avg4 = _mm_subs_epu8(avg3, lsb2);
- const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 ));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 ));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
+ const uint32_t extra_out =
+ (uint32_t)_mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
+ WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 ));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 ));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
// these two are hard to get and irregular
DST(3, 2) = (extra_out >> 0) & 0xff;
DST(3, 3) = (extra_out >> 8) & 0xff;
}
-static WEBP_INLINE void RD4_SSE2(uint8_t* dst,
- const uint8_t* top) { // Down-right
+// 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);
@@ -829,13 +1000,14 @@ static WEBP_INLINE void RD4_SSE2(uint8_t* dst,
const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one);
const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_);
- WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg ));
- WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
- WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
- WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+ WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg ));
+ WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+ WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+ 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];
@@ -850,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];
@@ -873,16 +1046,17 @@ 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(WebPMemToUint32(top));
+ const __m128i top_values = _mm_cvtsi32_si128(WebPMemToInt32(top));
const __m128i top_base = _mm_unpacklo_epi8(top_values, zero);
int y;
for (y = 0; y < 4; ++y, dst += BPS) {
const int val = top[-2 - y] - top[-1];
const __m128i base = _mm_set1_epi16(val);
const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero);
- WebPUint32ToMem(dst, _mm_cvtsi128_si32(out));
+ WebPInt32ToMem(dst, _mm_cvtsi128_si32(out));
}
}
@@ -895,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);
@@ -911,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);
@@ -931,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);
@@ -959,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];
@@ -981,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;
@@ -1019,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,
@@ -1056,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]);
@@ -1094,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();
@@ -1195,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) {
@@ -1217,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;
@@ -1229,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);
@@ -1265,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);
@@ -1330,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/c-lib/src/dsp/enc_sse41.c b/c-lib/src/dsp/enc_sse41.c
@@ -14,18 +14,23 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE41)
+#include <emmintrin.h>
#include <smmintrin.h>
+
#include <stdlib.h> // 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/c-lib/src/dsp/filters.c b/c-lib/src/dsp/filters.c
@@ -11,65 +11,57 @@
//
// Author: Urvang (urvang@google.com)
-#include "src/dsp/dsp.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include "src/dsp/cpu.h"
+#include "src/dsp/dsp.h"
+#include "src/webp/types.h"
+
//------------------------------------------------------------------------------
// Helpful macro.
-# define SANITY_CHECK(in, out) \
- assert((in) != NULL); \
- assert((out) != NULL); \
- assert(width > 0); \
- assert(height > 0); \
- assert(stride >= width); \
- assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
- (void)height; // Silence unused warning.
+#define DCHECK(in, out) \
+ do { \
+ assert((in) != NULL); \
+ assert((out) != NULL); \
+ assert((in) != (out)); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ } 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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
- preds = inverse ? out : in;
-
- if (row == 0) {
- // Leftmost pixel is the same as input for topmost scanline.
- out[0] = in[0];
- PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
- row = 1;
- preds += stride;
- in += stride;
- out += stride;
- }
+ uint8_t* WEBP_RESTRICT out) {
+ const uint8_t* preds = in;
+ int row;
+ DCHECK(in, out);
+
+ // 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;
@@ -79,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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
- preds = inverse ? out : in;
-
- if (row == 0) {
- // Very first top-left pixel is copied.
- out[0] = in[0];
- // Rest of top scan-line is left-predicted.
- PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
- row = 1;
- in += stride;
- out += stride;
- } else {
- // We are starting from in-between. Make sure 'preds' points to prev row.
- preds -= stride;
- }
+ uint8_t* WEBP_RESTRICT out) {
+ const uint8_t* preds = in;
+ int row;
+ DCHECK(in, out);
+
+ // 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;
@@ -124,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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
- preds = inverse ? out : in;
+ uint8_t* WEBP_RESTRICT out) {
+ const uint8_t* preds = in;
+ int row;
+ DCHECK(in, out);
// 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;
@@ -165,30 +136,38 @@ static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in,
}
#endif // !WEBP_NEON_OMIT_C_CODE
-#undef SANITY_CHECK
+#undef DCHECK
//------------------------------------------------------------------------------
#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
//------------------------------------------------------------------------------
+static void NoneUnfilter_C(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ (void)prev;
+ if (out != in) memcpy(out, in, width * sizeof(*out));
+}
+
static void HorizontalUnfilter_C(const uint8_t* prev, const uint8_t* in,
uint8_t* out, int width) {
uint8_t pred = (prev == NULL) ? 0 : prev[0];
@@ -233,13 +212,14 @@ static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in,
WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8FiltersInitMIPSdspR2(void);
extern void VP8FiltersInitMSA(void);
extern void VP8FiltersInitNEON(void);
extern void VP8FiltersInitSSE2(void);
WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
- WebPUnfilters[WEBP_FILTER_NONE] = NULL;
+ WebPUnfilters[WEBP_FILTER_NONE] = NoneUnfilter_C;
#if !WEBP_NEON_OMIT_C_CODE
WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_C;
WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_C;
@@ -278,6 +258,7 @@ WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
}
#endif
+ assert(WebPUnfilters[WEBP_FILTER_NONE] != NULL);
assert(WebPUnfilters[WEBP_FILTER_HORIZONTAL] != NULL);
assert(WebPUnfilters[WEBP_FILTER_VERTICAL] != NULL);
assert(WebPUnfilters[WEBP_FILTER_GRADIENT] != NULL);
diff --git a/c-lib/src/dsp/filters_mips_dsp_r2.c b/c-lib/src/dsp/filters_mips_dsp_r2.c
@@ -24,14 +24,15 @@
//------------------------------------------------------------------------------
// Helpful macro.
-# define SANITY_CHECK(in, out) \
- assert(in != NULL); \
- assert(out != NULL); \
- assert(width > 0); \
- assert(height > 0); \
- assert(stride >= width); \
- assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
- (void)height; // Silence unused warning.
+#define DCHECK(in, out) \
+ do { \
+ assert((in) != NULL); \
+ assert((out) != NULL); \
+ assert((in) != (out)); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ } while (0)
#define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \
const uint8_t* psrc = (uint8_t*)(SRC); \
@@ -101,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);
}
@@ -182,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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
- preds = in;
-
- if (row == 0) {
- // Leftmost pixel is the same as input for topmost scanline.
- out[0] = in[0];
- PredictLine_MIPSdspR2(in + 1, out + 1, width - 1);
- row = 1;
- preds += stride;
- in += stride;
- out += stride;
- }
+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);
+
+ // 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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
- preds = in;
-
- if (row == 0) {
- // Very first top-left pixel is copied.
- out[0] = in[0];
- // Rest of top scan-line is left-predicted.
- PredictLine_MIPSdspR2(in + 1, out + 1, width - 1);
- row = 1;
- in += stride;
- out += stride;
- } else {
- // We are starting from in-between. Make sure 'preds' points to prev row.
- preds -= stride;
- }
+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);
+
+ // 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);
}
//------------------------------------------------------------------------------
@@ -295,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) { \
@@ -304,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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
- preds = in;
+ uint8_t* WEBP_RESTRICT out) {
+ const uint8_t* preds = in;
+ int row;
+ DCHECK(in, out);
// 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);
}
//------------------------------------------------------------------------------
@@ -378,7 +348,7 @@ static void GradientUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in,
#undef DO_PREDICT_LINE_VERTICAL
#undef PREDICT_LINE_ONE_PASS
#undef DO_PREDICT_LINE
-#undef SANITY_CHECK
+#undef DCHECK
//------------------------------------------------------------------------------
// Entry point
diff --git a/c-lib/src/dsp/filters_msa.c b/c-lib/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) {
@@ -56,23 +57,27 @@ static WEBP_INLINE void PredictLineInverse0(const uint8_t* src,
//------------------------------------------------------------------------------
// Helpful macro.
-#define SANITY_CHECK(in, out) \
- assert(in != NULL); \
- assert(out != NULL); \
- assert(width > 0); \
- assert(height > 0); \
- assert(stride >= width);
+#define DCHECK(in, out) \
+ do { \
+ assert((in) != NULL); \
+ assert((out) != NULL); \
+ assert((in) != (out)); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ } while (0)
//------------------------------------------------------------------------------
// 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;
int row = 1;
- SANITY_CHECK(in, out);
+ DCHECK(in, out);
// Leftmost pixel is the same as input for topmost scanline.
out[0] = in[0];
@@ -97,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) {
@@ -129,13 +134,14 @@ 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;
int row = 1;
- SANITY_CHECK(in, out);
+ DCHECK(in, out);
// left prediction for top scan-line
out[0] = in[0];
@@ -157,13 +163,14 @@ 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;
int row = 1;
- SANITY_CHECK(in, out);
+ DCHECK(in, out);
// Very first top-left pixel is copied.
out[0] = in[0];
@@ -182,7 +189,7 @@ static void VerticalFilter_MSA(const uint8_t* data, int width, int height,
}
}
-#undef SANITY_CHECK
+#undef DCHECK
//------------------------------------------------------------------------------
// Entry point
diff --git a/c-lib/src/dsp/filters_neon.c b/c-lib/src/dsp/filters_neon.c
@@ -21,14 +21,15 @@
//------------------------------------------------------------------------------
// Helpful macros.
-# define SANITY_CHECK(in, out) \
- assert(in != NULL); \
- assert(out != NULL); \
- assert(width > 0); \
- assert(height > 0); \
- assert(stride >= width); \
- assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
- (void)height; // Silence unused warning.
+#define DCHECK(in, out) \
+ do { \
+ assert((in) != NULL); \
+ assert((out) != NULL); \
+ assert((in) != (out)); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ } while (0)
// load eight u8 and widen to s16
#define U8_TO_S16(A) vreinterpretq_s16_u16(vmovl_u8(A))
@@ -44,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) {
@@ -57,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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
-
- if (row == 0) {
- // Leftmost pixel is the same as input for topmost scanline.
- out[0] = in[0];
- PredictLineLeft_NEON(in + 1, out + 1, width - 1);
- row = 1;
- in += stride;
- out += stride;
- }
+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);
+
+ // 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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
-
- if (row == 0) {
- // Very first top-left pixel is copied.
- out[0] = in[0];
- // Rest of top scan-line is left-predicted.
- PredictLineLeft_NEON(in + 1, out + 1, width - 1);
- row = 1;
- in += stride;
- out += stride;
- }
+ uint8_t* WEBP_RESTRICT out) {
+ int row;
+ DCHECK(in, out);
+
+ // 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);
}
//------------------------------------------------------------------------------
@@ -149,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]);
@@ -165,43 +151,34 @@ 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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
+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);
// 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 SANITY_CHECK
+#undef DCHECK
//------------------------------------------------------------------------------
// Inverse transforms
diff --git a/c-lib/src/dsp/filters_sse2.c b/c-lib/src/dsp/filters_sse2.c
@@ -20,20 +20,25 @@
#include <stdlib.h>
#include <string.h>
+#include "src/dsp/cpu.h"
+#include "src/webp/types.h"
+
//------------------------------------------------------------------------------
// Helpful macro.
-# define SANITY_CHECK(in, out) \
- assert((in) != NULL); \
- assert((out) != NULL); \
- assert(width > 0); \
- assert(height > 0); \
- assert(stride >= width); \
- assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
- (void)height; // Silence unused warning.
-
-static void PredictLineTop_SSE2(const uint8_t* src, const uint8_t* pred,
- uint8_t* dst, int length) {
+#define DCHECK(in, out) \
+ do { \
+ assert((in) != NULL); \
+ assert((out) != NULL); \
+ assert((in) != (out)); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ } while (0)
+
+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);
@@ -51,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);
@@ -71,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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
-
- if (row == 0) {
- // Leftmost pixel is the same as input for topmost scanline.
- out[0] = in[0];
- PredictLineLeft_SSE2(in + 1, out + 1, width - 1);
- row = 1;
- in += stride;
- out += stride;
- }
+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);
+
+ // 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;
}
@@ -105,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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
-
- if (row == 0) {
- // Very first top-left pixel is copied.
- out[0] = in[0];
- // Rest of top scan-line is left-predicted.
- PredictLineLeft_SSE2(in + 1, out + 1, width - 1);
- row = 1;
- in += stride;
- out += stride;
- }
+ uint8_t* WEBP_RESTRICT out) {
+ int row;
+ DCHECK(in, out);
+
+ // 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;
}
@@ -144,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();
@@ -168,53 +158,47 @@ 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;
- SANITY_CHECK(in, out);
- in += start_offset;
- out += start_offset;
+ uint8_t* WEBP_RESTRICT out) {
+ int row;
+ DCHECK(in, out);
// 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;
}
}
-#undef SANITY_CHECK
+#undef DCHECK
//------------------------------------------------------------------------------
-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/c-lib/src/dsp/lossless.c b/c-lib/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 <assert.h>
-#include <math.h>
#include <stdlib.h>
+#include <string.h>
+
#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.
@@ -49,7 +55,7 @@ static WEBP_INLINE uint32_t Clip255(uint32_t a) {
}
static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) {
- return Clip255(a + b - c);
+ return Clip255((uint32_t)(a + b - c));
}
static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
@@ -66,7 +72,7 @@ static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
}
static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) {
- return Clip255(a + (a - b) / 2);
+ return Clip255((uint32_t)(a + (a - b) / 2));
}
static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1,
@@ -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(m->green_to_red_, green);
+ new_red += ColorTransformDelta((int8_t)m->green_to_red, green);
new_red &= 0xff;
- new_blue += ColorTransformDelta(m->green_to_blue_, green);
- new_blue += ColorTransformDelta(m->red_to_blue_, (int8_t)new_red);
+ new_blue += 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_) {
- case SUBTRACT_GREEN:
+ 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;
@@ -588,8 +599,10 @@ VP8LConvertFunc VP8LConvertBGRAToBGR;
VP8LMapARGBFunc VP8LMapColor32b;
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);
@@ -642,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/c-lib/src/dsp/lossless.h b/c-lib/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 double (*VP8LCostFunc)(const uint32_t* population, int length);
-typedef double (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y,
- int length);
-typedef float (*VP8LCombinedShannonEntropyFunc)(const int X[256],
- const int Y[256]);
+typedef uint32_t (*VP8LCostFunc)(const uint32_t* population, int length);
+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
- double 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/c-lib/src/dsp/lossless_avx2.c b/c-lib/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 <stddef.h>
+#include <immintrin.h>
+
+#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/c-lib/src/dsp/lossless_common.h b/c-lib/src/dsp/lossless_common.h
@@ -16,9 +16,12 @@
#ifndef WEBP_DSP_LOSSLESS_COMMON_H_
#define WEBP_DSP_LOSSLESS_COMMON_H_
-#include "src/webp/types.h"
+#include <assert.h>
+#include <stddef.h>
+#include "src/dsp/cpu.h"
#include "src/utils/utils.h"
+#include "src/webp/types.h"
#ifdef __cplusplus
extern "C" {
@@ -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);
@@ -166,22 +190,22 @@ uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
}
//------------------------------------------------------------------------------
-// Transform-related functions use din both encoding and decoding.
+// Transform-related functions used in both encoding and decoding.
// Macros used to create a batch predictor that iteratively uses a
// one-pixel predictor.
// The predictor is added to the output pixel (which
// is therefore considered as a residual) to get the final prediction.
-#define GENERATE_PREDICTOR_ADD(PREDICTOR, PREDICTOR_ADD) \
-static void PREDICTOR_ADD(const uint32_t* in, const uint32_t* upper, \
- int num_pixels, uint32_t* out) { \
- int x; \
- assert(upper != NULL); \
- for (x = 0; x < num_pixels; ++x) { \
- const uint32_t pred = (PREDICTOR)(&out[x - 1], upper + x); \
- out[x] = VP8LAddPixels(in[x], pred); \
- } \
+#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/c-lib/src/dsp/lossless_enc.c b/c-lib/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 <assert.h>
#include <math.h>
#include <stdlib.h>
-#include "src/dec/vp8li_dec.h"
-#include "src/utils/endian_inl_utils.h"
+#include <string.h>
+
+#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"
-
-// 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
+#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) * (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_2_PRECISION_BITS)*math.log2(i)*i) if i
+// else 0 for i in range(256)]]
+// print(',\n '.join([','.join(v) for v in batched([i.rjust(15)
+// for i in a],4)]))
+const uint64_t kSLog2Table[LOG_LOOKUP_IDX_MAX] = {
+ 0, 0, 16777216, 39886887,
+ 67108864, 97388723, 130105423, 164848600,
+ 201326592, 239321324, 278663526, 319217973,
+ 360874141, 403539997, 447137711, 491600606,
+ 536870912, 582898099, 629637592, 677049776,
+ 725099212, 773754010, 822985323, 872766924,
+ 923074875, 973887230, 1025183802, 1076945958,
+ 1129156447, 1181799249, 1234859451, 1288323135,
+ 1342177280, 1396409681, 1451008871, 1505964059,
+ 1561265072, 1616902301, 1672866655, 1729149526,
+ 1785742744, 1842638548, 1899829557, 1957308741,
+ 2015069397, 2073105127, 2131409817, 2189977618ull,
+ 2248802933ull, 2307880396ull, 2367204859ull, 2426771383ull,
+ 2486575220ull, 2546611805ull, 2606876748ull, 2667365819ull,
+ 2728074942ull, 2789000187ull, 2850137762ull, 2911484006ull,
+ 2973035382ull, 3034788471ull, 3096739966ull, 3158886666ull,
+ 3221225472ull, 3283753383ull, 3346467489ull, 3409364969ull,
+ 3472443085ull, 3535699182ull, 3599130679ull, 3662735070ull,
+ 3726509920ull, 3790452862ull, 3854561593ull, 3918833872ull,
+ 3983267519ull, 4047860410ull, 4112610476ull, 4177515704ull,
+ 4242574127ull, 4307783833ull, 4373142952ull, 4438649662ull,
+ 4504302186ull, 4570098787ull, 4636037770ull, 4702117480ull,
+ 4768336298ull, 4834692645ull, 4901184974ull, 4967811774ull,
+ 5034571569ull, 5101462912ull, 5168484389ull, 5235634615ull,
+ 5302912235ull, 5370315922ull, 5437844376ull, 5505496324ull,
+ 5573270518ull, 5641165737ull, 5709180782ull, 5777314477ull,
+ 5845565671ull, 5913933235ull, 5982416059ull, 6051013057ull,
+ 6119723161ull, 6188545324ull, 6257478518ull, 6326521733ull,
+ 6395673979ull, 6464934282ull, 6534301685ull, 6603775250ull,
+ 6673354052ull, 6743037185ull, 6812823756ull, 6882712890ull,
+ 6952703725ull, 7022795412ull, 7092987118ull, 7163278025ull,
+ 7233667324ull, 7304154222ull, 7374737939ull, 7445417707ull,
+ 7516192768ull, 7587062379ull, 7658025806ull, 7729082328ull,
+ 7800231234ull, 7871471825ull, 7942803410ull, 8014225311ull,
+ 8085736859ull, 8157337394ull, 8229026267ull, 8300802839ull,
+ 8372666477ull, 8444616560ull, 8516652476ull, 8588773618ull,
+ 8660979393ull, 8733269211ull, 8805642493ull, 8878098667ull,
+ 8950637170ull, 9023257446ull, 9095958945ull, 9168741125ull,
+ 9241603454ull, 9314545403ull, 9387566451ull, 9460666086ull,
+ 9533843800ull, 9607099093ull, 9680431471ull, 9753840445ull,
+ 9827325535ull, 9900886263ull, 9974522161ull, 10048232765ull,
+ 10122017615ull, 10195876260ull, 10269808253ull, 10343813150ull,
+ 10417890516ull, 10492039919ull, 10566260934ull, 10640553138ull,
+ 10714916116ull, 10789349456ull, 10863852751ull, 10938425600ull,
+ 11013067604ull, 11087778372ull, 11162557513ull, 11237404645ull,
+ 11312319387ull, 11387301364ull, 11462350205ull, 11537465541ull,
+ 11612647010ull, 11687894253ull, 11763206912ull, 11838584638ull,
+ 11914027082ull, 11989533899ull, 12065104750ull, 12140739296ull,
+ 12216437206ull, 12292198148ull, 12368021795ull, 12443907826ull,
+ 12519855920ull, 12595865759ull, 12671937032ull, 12748069427ull,
+ 12824262637ull, 12900516358ull, 12976830290ull, 13053204134ull,
+ 13129637595ull, 13206130381ull, 13282682202ull, 13359292772ull,
+ 13435961806ull, 13512689025ull, 13589474149ull, 13666316903ull,
+ 13743217014ull, 13820174211ull, 13897188225ull, 13974258793ull,
+ 14051385649ull, 14128568535ull, 14205807192ull, 14283101363ull,
+ 14360450796ull, 14437855239ull, 14515314443ull, 14592828162ull,
+ 14670396151ull, 14748018167ull, 14825693972ull, 14903423326ull,
+ 14981205995ull, 15059041743ull, 15136930339ull, 15214871554ull,
+ 15292865160ull, 15370910930ull, 15449008641ull, 15527158071ull,
+ 15605359001ull, 15683611210ull, 15761914485ull, 15840268608ull,
+ 15918673369ull, 15997128556ull, 16075633960ull, 16154189373ull,
+ 16232794589ull, 16311449405ull, 16390153617ull, 16468907026ull,
+ 16547709431ull, 16626560636ull, 16705460444ull, 16784408661ull,
+ 16863405094ull, 16942449552ull, 17021541845ull, 17100681785ull
};
const VP8LPrefixCode kPrefixEncodeCode[PREFIX_LOOKUP_IDX_MAX] = {
@@ -326,23 +249,19 @@ const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX] = {
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126
};
-static float FastSLog2Slow_C(uint32_t v) {
+static uint64_t FastSLog2Slow_C(uint32_t v) {
assert(v >= 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;
- double retval = 0.;
- 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);
- return (float)retval;
+ 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;
}
//------------------------------------------------------------------------------
@@ -522,11 +457,11 @@ static void GetCombinedEntropyUnrefined_C(const uint32_t X[],
void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels) {
int i;
for (i = 0; i < num_pixels; ++i) {
- const int argb = argb_data[i];
+ const int argb = (int)argb_data[i];
const int green = (argb >> 8) & 0xff;
const uint32_t new_r = (((argb >> 16) & 0xff) - green) & 0xff;
const uint32_t new_b = (((argb >> 0) & 0xff) - green) & 0xff;
- argb_data[i] = (argb & 0xff00ff00u) | (new_r << 16) | new_b;
+ argb_data[i] = ((uint32_t)argb & 0xff00ff00u) | (new_r << 16) | new_b;
}
}
@@ -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(m->green_to_red_, green);
+ new_red -= ColorTransformDelta((int8_t)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((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);
}
@@ -560,7 +495,7 @@ static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red,
uint32_t argb) {
const int8_t green = U32ToS8(argb >> 8);
int new_red = argb >> 16;
- new_red -= ColorTransformDelta(green_to_red, green);
+ new_red -= ColorTransformDelta((int8_t)green_to_red, green);
return (new_red & 0xff);
}
@@ -569,15 +504,16 @@ static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue,
uint32_t argb) {
const int8_t green = U32ToS8(argb >> 8);
const int8_t red = U32ToS8(argb >> 16);
- uint8_t new_blue = argb & 0xff;
- new_blue -= ColorTransformDelta(green_to_blue, green);
- new_blue -= ColorTransformDelta(red_to_blue, red);
+ int new_blue = argb & 0xff;
+ new_blue -= ColorTransformDelta((int8_t)green_to_blue, green);
+ new_blue -= ColorTransformDelta((int8_t)red_to_blue, red);
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);
@@ -636,100 +573,43 @@ void VP8LBundleColorMap_C(const uint8_t* const row, int width, int xbits,
//------------------------------------------------------------------------------
-static double ExtraCost_C(const uint32_t* population, int length) {
- int i;
- double cost = 0.;
- for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2];
- return cost;
-}
-
-static double ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y,
- int length) {
+static uint32_t ExtraCost_C(const uint32_t* population, int length) {
int i;
- double cost = 0.;
- for (i = 2; i < length - 2; ++i) {
- const int xy = X[i + 2] + Y[i + 2];
- cost += (i >> 1) * xy;
+ uint32_t cost = population[4] + population[5];
+ assert(length % 2 == 0);
+ for (i = 2; i < length / 2 - 1; ++i) {
+ cost += i * (population[2 * i + 2] + population[2 * i + 3]);
}
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;
@@ -740,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) { \
@@ -766,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;
@@ -787,12 +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);
@@ -814,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;
@@ -868,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
}
@@ -903,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/c-lib/src/dsp/lossless_enc_avx2.c b/c-lib/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 <emmintrin.h>
+#include <immintrin.h>
+
+#include <assert.h>
+#include <stddef.h>
+
+#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/c-lib/src/dsp/lossless_enc_mips32.c b/c-lib/src/dsp/lossless_enc_mips32.c
@@ -23,12 +23,12 @@
#include <stdlib.h>
#include <string.h>
-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);
}
}
@@ -103,8 +103,8 @@ static float FastLog2Slow_MIPS32(uint32_t v) {
// cost += i * *(pop + 1);
// pop += 2;
// }
-// return (double)cost;
-static double ExtraCost_MIPS32(const uint32_t* const population, int length) {
+// return cost;
+static uint32_t ExtraCost_MIPS32(const uint32_t* const population, int length) {
int i, temp0, temp1;
const uint32_t* pop = &population[4];
const uint32_t* const LoopEnd = &population[length];
@@ -130,60 +130,7 @@ static double ExtraCost_MIPS32(const uint32_t* const population, int length) {
: "memory", "hi", "lo"
);
- return (double)((int64_t)temp0 << 32 | temp1);
-}
-
-// C version of this function:
-// int i = 0;
-// int64_t cost = 0;
-// const uint32_t* pX = &X[4];
-// const uint32_t* pY = &Y[4];
-// const uint32_t* LoopEnd = &X[length];
-// while (pX != LoopEnd) {
-// const uint32_t xy0 = *pX + *pY;
-// const uint32_t xy1 = *(pX + 1) + *(pY + 1);
-// ++i;
-// cost += i * xy0;
-// cost += i * xy1;
-// pX += 2;
-// pY += 2;
-// }
-// return (double)cost;
-static double ExtraCostCombined_MIPS32(const uint32_t* const X,
- const uint32_t* const Y, int length) {
- int i, temp0, temp1, temp2, temp3;
- const uint32_t* pX = &X[4];
- const uint32_t* pY = &Y[4];
- const uint32_t* const LoopEnd = &X[length];
-
- __asm__ volatile(
- "mult $zero, $zero \n\t"
- "xor %[i], %[i], %[i] \n\t"
- "beq %[pX], %[LoopEnd], 2f \n\t"
- "1: \n\t"
- "lw %[temp0], 0(%[pX]) \n\t"
- "lw %[temp1], 0(%[pY]) \n\t"
- "lw %[temp2], 4(%[pX]) \n\t"
- "lw %[temp3], 4(%[pY]) \n\t"
- "addiu %[i], %[i], 1 \n\t"
- "addu %[temp0], %[temp0], %[temp1] \n\t"
- "addu %[temp2], %[temp2], %[temp3] \n\t"
- "addiu %[pX], %[pX], 8 \n\t"
- "addiu %[pY], %[pY], 8 \n\t"
- "madd %[i], %[temp0] \n\t"
- "madd %[i], %[temp2] \n\t"
- "bne %[pX], %[LoopEnd], 1b \n\t"
- "2: \n\t"
- "mfhi %[temp0] \n\t"
- "mflo %[temp1] \n\t"
- : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
- [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),
- [i]"=&r"(i), [pX]"+r"(pX), [pY]"+r"(pY)
- : [LoopEnd]"r"(LoopEnd)
- : "memory", "hi", "lo"
- );
-
- return (double)((int64_t)temp0 << 32 | temp1);
+ return ((int64_t)temp0 << 32 | temp1);
}
#define HUFFMAN_COST_PASS \
@@ -215,8 +162,10 @@ static double 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,27 +293,29 @@ 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 uint32_t end = ((size) / 4) * 4;
+ const int end = ((size) / 4) * 4;
const uint32_t* const LoopEnd = pa + end;
int i;
ASM_START
ADD_TO_OUT(0, 4, 8, 12, 1, pa, pb, pout)
ASM_END_0
- for (i = end; i < size; ++i) pout[i] = pa[i] + pb[i];
+ 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 uint32_t end = ((size) / 4) * 4;
+ const int end = ((size) / 4) * 4;
const uint32_t* const LoopEnd = pa + end;
int i;
ASM_START
ADD_TO_OUT(0, 4, 8, 12, 0, pa, pout, pout)
ASM_END_1
- for (i = end; i < size; ++i) pout[i] += pa[i];
+ for (i = 0; i < size - end; ++i) pout[i] += pa[i];
}
#undef ASM_END_1
@@ -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/c-lib/src/dsp/lossless_enc_mips_dsp_r2.c b/c-lib/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/c-lib/src/dsp/lossless_enc_msa.c b/c-lib/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/c-lib/src/dsp/lossless_enc_neon.c b/c-lib/src/dsp/lossless_enc_neon.c
@@ -25,7 +25,7 @@
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
#define USE_VTBLQ
#endif
@@ -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/c-lib/src/dsp/lossless_enc_sse2.c b/c-lib/src/dsp/lossless_enc_sse2.c
@@ -14,11 +14,17 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE2)
-#include <assert.h>
#include <emmintrin.h>
+
+#include <assert.h>
+#include <string.h>
+
+#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,13 +55,14 @@ 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);
- const __m128i mask_ag = _mm_set1_epi32(0xff00ff00); // alpha-green masks
- const __m128i mask_rb = _mm_set1_epi32(0x00ff00ff); // red-blue masks
+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;
for (i = 0; i + 4 <= num_pixels; i += 4) {
const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]); // argb
@@ -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;
- double retval = 0.;
- 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,20 +307,20 @@ 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);
- return (float)retval;
+ retval = VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY) - retval;
+ return retval;
}
#else
@@ -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);
@@ -376,7 +424,7 @@ static void BundleColorMap_SSE2(const uint8_t* const row, int width, int xbits,
break;
}
case 2: {
- const __m128i mask_or = _mm_set1_epi32(0xff000000);
+ const __m128i mask_or = _mm_set1_epi32((int)0xff000000);
const __m128i mul_cst = _mm_set1_epi16(0x0104);
const __m128i mask_mul = _mm_set1_epi16(0x0f00);
for (x = 0; x + 16 <= width; x += 16, dst += 4) {
@@ -425,9 +473,9 @@ 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(ARGB_BLACK);
+ const __m128i black = _mm_set1_epi32((int)ARGB_BLACK);
for (i = 0; i + 4 <= num_pixels; i += 4) {
const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
const __m128i res = _mm_sub_epi8(src, black);
@@ -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/c-lib/src/dsp/lossless_enc_sse41.c b/c-lib/src/dsp/lossless_enc_sse41.c
@@ -14,12 +14,40 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE41)
-#include <assert.h>
+#include <emmintrin.h>
#include <smmintrin.h>
+
+#include <assert.h>
+
+#include "src/dsp/cpu.h"
#include "src/dsp/lossless.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)
+//------------------------------------------------------------------------------
+// Cost operations.
+
+static WEBP_INLINE uint32_t HorizontalSum_SSE41(__m128i cost) {
+ cost = _mm_add_epi32(cost, _mm_srli_si128(cost, 8));
+ cost = _mm_add_epi32(cost, _mm_srli_si128(cost, 4));
+ return _mm_cvtsi128_si32(cost);
+}
+
+static uint32_t ExtraCost_SSE41(const uint32_t* const a, int length) {
+ int i;
+ __m128i cost = _mm_set_epi32(2 * a[7], 2 * a[6], a[5], a[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 w = _mm_set_epi32(j + 3, j + 2, j + 1, j);
+ const __m128i a2 = _mm_hadd_epi32(a0, a1);
+ const __m128i mul = _mm_mullo_epi32(a2, w);
+ cost = _mm_add_epi32(mul, cost);
+ }
+ return HorizontalSum_SSE41(cost);
+}
//------------------------------------------------------------------------------
// Subtract-Green Transform
@@ -44,13 +72,17 @@ static void SubtractGreenFromBlueAndRed_SSE41(uint32_t* argb_data,
//------------------------------------------------------------------------------
// 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) \
_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 =
@@ -93,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) {
@@ -143,9 +176,15 @@ static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride,
extern void VP8LEncDspInitSSE41(void);
WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE41(void) {
+ VP8LExtraCost = ExtraCost_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/c-lib/src/dsp/lossless_mips_dsp_r2.c b/c-lib/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/c-lib/src/dsp/lossless_msa.c b/c-lib/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/c-lib/src/dsp/lossless_neon.c b/c-lib/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]);
@@ -146,9 +147,9 @@ static void ConvertBGRAToRGB_NEON(const uint32_t* src,
#define LOAD_U32P_AS_U8(IN) vreinterpret_u8_u32(vld1_u32((IN)))
#define LOADQ_U32_AS_U8(IN) vreinterpretq_u8_u32(vdupq_n_u32((IN)))
#define LOADQ_U32P_AS_U8(IN) vreinterpretq_u8_u32(vld1q_u32((IN)))
-#define GET_U8_AS_U32(IN) vget_lane_u32(vreinterpret_u32_u8((IN)), 0);
-#define GETQ_U8_AS_U32(IN) vgetq_lane_u32(vreinterpretq_u32_u8((IN)), 0);
-#define STOREQ_U8_AS_U32P(OUT, IN) vst1q_u32((OUT), vreinterpretq_u32_u8((IN)));
+#define GET_U8_AS_U32(IN) vget_lane_u32(vreinterpret_u32_u8((IN)), 0)
+#define GETQ_U8_AS_U32(IN) vgetq_lane_u32(vreinterpretq_u32_u8((IN)), 0)
+#define STOREQ_U8_AS_U32P(OUT, IN) vst1q_u32((OUT), vreinterpretq_u32_u8((IN)))
#define ROTATE32_LEFT(L) vextq_u8((L), (L), 12) // D|C|B|A -> C|B|A|D
static WEBP_INLINE uint8x8_t Average2_u8_NEON(uint32_t a0, uint32_t a1) {
@@ -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) {
@@ -498,7 +499,7 @@ static void PredictorAdd13_NEON(const uint32_t* in, const uint32_t* upper,
// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
// non-standard versions there.
-#if defined(__APPLE__) && defined(__aarch64__) && \
+#if defined(__APPLE__) && WEBP_AARCH64 && \
defined(__apple_build_version__) && (__apple_build_version__< 6020037)
#define USE_VTBLQ
#endif
@@ -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/c-lib/src/dsp/lossless_sse2.c b/c-lib/src/dsp/lossless_sse2.c
@@ -15,10 +15,15 @@
#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
+#include <string.h>
+
#include "src/dsp/common_sse2.h"
+#include "src/dsp/cpu.h"
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
-#include <emmintrin.h>
+#include "src/webp/format_constants.h"
+#include "src/webp/types.h"
//------------------------------------------------------------------------------
// Predictor Transform
@@ -27,23 +32,22 @@ static WEBP_INLINE uint32_t ClampedAddSubtractFull_SSE2(uint32_t c0,
uint32_t c1,
uint32_t c2) {
const __m128i zero = _mm_setzero_si128();
- const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero);
- const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero);
- const __m128i C2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero);
+ const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c0), zero);
+ const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c1), zero);
+ const __m128i C2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c2), zero);
const __m128i V1 = _mm_add_epi16(C0, C1);
const __m128i V2 = _mm_sub_epi16(V1, C2);
const __m128i b = _mm_packus_epi16(V2, V2);
- const uint32_t output = _mm_cvtsi128_si32(b);
- return output;
+ return (uint32_t)_mm_cvtsi128_si32(b);
}
static WEBP_INLINE uint32_t ClampedAddSubtractHalf_SSE2(uint32_t c0,
uint32_t c1,
uint32_t c2) {
const __m128i zero = _mm_setzero_si128();
- const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero);
- const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero);
- const __m128i B0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero);
+ const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c0), zero);
+ const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c1), zero);
+ const __m128i B0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c2), zero);
const __m128i avg = _mm_add_epi16(C1, C0);
const __m128i A0 = _mm_srli_epi16(avg, 1);
const __m128i A1 = _mm_sub_epi16(A0, B0);
@@ -52,16 +56,15 @@ static WEBP_INLINE uint32_t ClampedAddSubtractHalf_SSE2(uint32_t c0,
const __m128i A3 = _mm_srai_epi16(A2, 1);
const __m128i A4 = _mm_add_epi16(A0, A3);
const __m128i A5 = _mm_packus_epi16(A4, A4);
- const uint32_t output = _mm_cvtsi128_si32(A5);
- return output;
+ return (uint32_t)_mm_cvtsi128_si32(A5);
}
static WEBP_INLINE uint32_t Select_SSE2(uint32_t a, uint32_t b, uint32_t c) {
int pa_minus_pb;
const __m128i zero = _mm_setzero_si128();
- const __m128i A0 = _mm_cvtsi32_si128(a);
- const __m128i B0 = _mm_cvtsi32_si128(b);
- const __m128i C0 = _mm_cvtsi32_si128(c);
+ const __m128i A0 = _mm_cvtsi32_si128((int)a);
+ const __m128i B0 = _mm_cvtsi32_si128((int)b);
+ const __m128i C0 = _mm_cvtsi32_si128((int)c);
const __m128i AC0 = _mm_subs_epu8(A0, C0);
const __m128i CA0 = _mm_subs_epu8(C0, A0);
const __m128i BC0 = _mm_subs_epu8(B0, C0);
@@ -94,8 +97,8 @@ static WEBP_INLINE void Average2_uint32_SSE2(const uint32_t a0,
__m128i* const avg) {
// (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1)
const __m128i ones = _mm_set1_epi8(1);
- const __m128i A0 = _mm_cvtsi32_si128(a0);
- const __m128i A1 = _mm_cvtsi32_si128(a1);
+ const __m128i A0 = _mm_cvtsi32_si128((int)a0);
+ const __m128i A1 = _mm_cvtsi32_si128((int)a1);
const __m128i avg1 = _mm_avg_epu8(A0, A1);
const __m128i one = _mm_and_si128(_mm_xor_si128(A0, A1), ones);
*avg = _mm_sub_epi8(avg1, one);
@@ -103,8 +106,8 @@ static WEBP_INLINE void Average2_uint32_SSE2(const uint32_t a0,
static WEBP_INLINE __m128i Average2_uint32_16_SSE2(uint32_t a0, uint32_t a1) {
const __m128i zero = _mm_setzero_si128();
- const __m128i A0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a0), zero);
- const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a1), zero);
+ const __m128i A0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)a0), zero);
+ const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)a1), zero);
const __m128i sum = _mm_add_epi16(A1, A0);
return _mm_srli_epi16(sum, 1);
}
@@ -112,19 +115,18 @@ static WEBP_INLINE __m128i Average2_uint32_16_SSE2(uint32_t a0, uint32_t a1) {
static WEBP_INLINE uint32_t Average2_SSE2(uint32_t a0, uint32_t a1) {
__m128i output;
Average2_uint32_SSE2(a0, a1, &output);
- return _mm_cvtsi128_si32(output);
+ return (uint32_t)_mm_cvtsi128_si32(output);
}
static WEBP_INLINE uint32_t Average3_SSE2(uint32_t a0, uint32_t a1,
uint32_t a2) {
const __m128i zero = _mm_setzero_si128();
const __m128i avg1 = Average2_uint32_16_SSE2(a0, a2);
- const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a1), zero);
+ const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)a1), zero);
const __m128i sum = _mm_add_epi16(avg1, A1);
const __m128i avg2 = _mm_srli_epi16(sum, 1);
const __m128i A2 = _mm_packus_epi16(avg2, avg2);
- const uint32_t output = _mm_cvtsi128_si32(A2);
- return output;
+ return (uint32_t)_mm_cvtsi128_si32(A2);
}
static WEBP_INLINE uint32_t Average4_SSE2(uint32_t a0, uint32_t a1,
@@ -134,8 +136,7 @@ static WEBP_INLINE uint32_t Average4_SSE2(uint32_t a0, uint32_t a1,
const __m128i sum = _mm_add_epi16(avg2, avg1);
const __m128i avg3 = _mm_srli_epi16(sum, 1);
const __m128i A0 = _mm_packus_epi16(avg3, avg3);
- const uint32_t output = _mm_cvtsi128_si32(A0);
- return output;
+ return (uint32_t)_mm_cvtsi128_si32(A0);
}
static uint32_t Predictor5_SSE2(const uint32_t* const left,
@@ -190,9 +191,9 @@ 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(ARGB_BLACK);
+ const __m128i black = _mm_set1_epi32((int)ARGB_BLACK);
for (i = 0; i + 4 <= num_pixels; i += 4) {
const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
const __m128i res = _mm_add_epi8(src, black);
@@ -206,9 +207,9 @@ 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(out[-1]);
+ __m128i prev = _mm_set1_epi32((int)out[-1]);
for (i = 0; i + 4 <= num_pixels; i += 4) {
// a | b | c | d
const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
@@ -234,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]); \
@@ -263,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)); \
@@ -285,12 +288,12 @@ GENERATE_PREDICTOR_2(9, upper[i + 1])
#undef GENERATE_PREDICTOR_2
// Predictor10: average of (average of (L,TL), average of (T, TR)).
-#define DO_PRED10(OUT) do { \
- __m128i avgLTL, avg; \
- Average2_m128i(&L, &TL, &avgLTL); \
- Average2_m128i(&avgTTR, &avgLTL, &avg); \
- L = _mm_add_epi8(avg, src); \
- out[i + (OUT)] = _mm_cvtsi128_si32(L); \
+#define DO_PRED10(OUT) do { \
+ __m128i avgLTL, avg; \
+ Average2_m128i(&L, &TL, &avgLTL); \
+ Average2_m128i(&avgTTR, &avgLTL, &avg); \
+ L = _mm_add_epi8(avg, src); \
+ out[i + (OUT)] = (uint32_t)_mm_cvtsi128_si32(L); \
} while (0)
#define DO_PRED10_SHIFT do { \
@@ -301,9 +304,9 @@ 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(out[-1]);
+ __m128i L = _mm_cvtsi32_si128((int)out[-1]);
for (i = 0; i + 4 <= num_pixels; i += 4) {
__m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
__m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
@@ -336,7 +339,7 @@ static void PredictorAdd10_SSE2(const uint32_t* in, const uint32_t* upper,
const __m128i B = _mm_andnot_si128(mask, T); \
const __m128i pred = _mm_or_si128(A, B); /* pred = (pa > b)? L : T*/ \
L = _mm_add_epi8(src, pred); \
- out[i + (OUT)] = _mm_cvtsi128_si32(L); \
+ out[i + (OUT)] = (uint32_t)_mm_cvtsi128_si32(L); \
} while (0)
#define DO_PRED11_SHIFT do { \
@@ -348,10 +351,10 @@ 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(out[-1]);
+ __m128i L = _mm_cvtsi32_si128((int)out[-1]);
for (i = 0; i + 4 <= num_pixels; i += 4) {
__m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
__m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
@@ -384,12 +387,12 @@ static void PredictorAdd11_SSE2(const uint32_t* in, const uint32_t* upper,
#undef DO_PRED11_SHIFT
// Predictor12: ClampedAddSubtractFull.
-#define DO_PRED12(DIFF, LANE, OUT) do { \
- const __m128i all = _mm_add_epi16(L, (DIFF)); \
- const __m128i alls = _mm_packus_epi16(all, all); \
- const __m128i res = _mm_add_epi8(src, alls); \
- out[i + (OUT)] = _mm_cvtsi128_si32(res); \
- L = _mm_unpacklo_epi8(res, zero); \
+#define DO_PRED12(DIFF, LANE, OUT) do { \
+ const __m128i all = _mm_add_epi16(L, (DIFF)); \
+ const __m128i alls = _mm_packus_epi16(all, all); \
+ const __m128i res = _mm_add_epi8(src, alls); \
+ out[i + (OUT)] = (uint32_t)_mm_cvtsi128_si32(res); \
+ L = _mm_unpacklo_epi8(res, zero); \
} while (0)
#define DO_PRED12_SHIFT(DIFF, LANE) do { \
@@ -399,10 +402,10 @@ 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(out[-1]);
+ const __m128i L8 = _mm_cvtsi32_si128((int)out[-1]);
__m128i L = _mm_unpacklo_epi8(L8, zero);
for (i = 0; i + 4 <= num_pixels; i += 4) {
// Load 4 pixels at a time.
@@ -464,11 +467,11 @@ 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(0xff00ff00); // alpha-green masks
+ const __m128i mask_ag = _mm_set1_epi32((int)0xff00ff00); // alpha-green masks
int i;
for (i = 0; i + 4 <= num_pixels; i += 4) {
const __m128i in = _mm_loadu_si128((const __m128i*)&src[i]); // argb
@@ -494,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;
@@ -530,9 +533,9 @@ 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) {
- const __m128i red_blue_mask = _mm_set1_epi32(0x00ff00ffu);
+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;
while (num_pixels >= 8) {
@@ -558,10 +561,11 @@ 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(0xf0);
+ const __m128i mask_0xf0 = _mm_set1_epi8((char)0xf0);
const __m128i* in = (const __m128i*)src;
__m128i* out = (__m128i*)dst;
while (num_pixels >= 8) {
@@ -594,10 +598,11 @@ static void ConvertBGRAToRGBA4444_SSE2(const uint32_t* src,
}
}
-static void ConvertBGRAToRGB565_SSE2(const uint32_t* src,
- int num_pixels, uint8_t* dst) {
- const __m128i mask_0xe0 = _mm_set1_epi8(0xe0);
- const __m128i mask_0xf8 = _mm_set1_epi8(0xf8);
+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);
const __m128i* in = (const __m128i*)src;
__m128i* out = (__m128i*)dst;
@@ -635,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;
@@ -707,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/c-lib/src/dsp/lossless_sse41.c b/c-lib/src/dsp/lossless_sse41.c
@@ -12,10 +12,12 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE41)
+#include <emmintrin.h>
+#include <smmintrin.h>
-#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
@@ -25,11 +27,12 @@ static void TransformColorInverse_SSE41(const VP8LMultipliers* const m,
int num_pixels, uint32_t* dst) {
// sign-extended multiplying constants, pre-shifted by 5.
#define CST(X) (((int16_t)(m->X << 8)) >> 5) // sign-extend
- const __m128i mults_rb = _mm_set1_epi32((uint32_t)CST(green_to_red_) << 16 |
- (CST(green_to_blue_) & 0xffff));
- const __m128i mults_b2 = _mm_set1_epi32(CST(red_to_blue_));
+ 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));
#undef CST
- const __m128i mask_ag = _mm_set1_epi32(0xff00ff00);
+ const __m128i mask_ag = _mm_set1_epi32((int)0xff00ff00);
const __m128i perm1 = _mm_setr_epi8(-1, 1, -1, 1, -1, 5, -1, 5,
-1, 9, -1, 9, -1, 13, -1, 13);
const __m128i perm2 = _mm_setr_epi8(-1, 2, -1, -1, -1, 6, -1, -1,
@@ -76,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,
@@ -94,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,
@@ -123,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/c-lib/src/dsp/mips_macro.h b/c-lib/src/dsp/mips_macro.h
@@ -45,28 +45,38 @@
"ulw %[" #O2 "], " #I3 "+" XSTR(I9) "*" #I7 "(%[" #I0 "]) \n\t" \
"ulw %[" #O3 "], " #I4 "+" XSTR(I9) "*" #I8 "(%[" #I0 "]) \n\t"
+
+// O - output
+// I - input (macro doesn't change it so it should be different from I)
+#define MUL_SHIFT_C1(O, I) \
+ "mul %[" #O "], %[" #I "], %[kC1] \n\t" \
+ "sra %[" #O "], %[" #O "], 16 \n\t" \
+ "addu %[" #O "], %[" #O "], %[" #I "] \n\t"
+#define MUL_SHIFT_C2(O, I) \
+ "mul %[" #O "], %[" #I "], %[kC2] \n\t" \
+ "sra %[" #O "], %[" #O "], 16 \n\t"
+
+// Same as #define MUL_SHIFT_C1 but I and O are the same. It stores the
+// intermediary result in TMP.
+#define MUL_SHIFT_C1_IO(IO, TMP) \
+ "mul %[" #TMP "], %[" #IO "], %[kC1] \n\t" \
+ "sra %[" #TMP "], %[" #TMP "], 16 \n\t" \
+ "addu %[" #IO "], %[" #TMP "], %[" #IO "] \n\t"
+
// O - output
// IO - input/output
// I - input (macro doesn't change it)
#define MUL_SHIFT_SUM(O0, O1, O2, O3, O4, O5, O6, O7, \
IO0, IO1, IO2, IO3, \
I0, I1, I2, I3, I4, I5, I6, I7) \
- "mul %[" #O0 "], %[" #I0 "], %[kC2] \n\t" \
- "mul %[" #O1 "], %[" #I0 "], %[kC1] \n\t" \
- "mul %[" #O2 "], %[" #I1 "], %[kC2] \n\t" \
- "mul %[" #O3 "], %[" #I1 "], %[kC1] \n\t" \
- "mul %[" #O4 "], %[" #I2 "], %[kC2] \n\t" \
- "mul %[" #O5 "], %[" #I2 "], %[kC1] \n\t" \
- "mul %[" #O6 "], %[" #I3 "], %[kC2] \n\t" \
- "mul %[" #O7 "], %[" #I3 "], %[kC1] \n\t" \
- "sra %[" #O0 "], %[" #O0 "], 16 \n\t" \
- "sra %[" #O1 "], %[" #O1 "], 16 \n\t" \
- "sra %[" #O2 "], %[" #O2 "], 16 \n\t" \
- "sra %[" #O3 "], %[" #O3 "], 16 \n\t" \
- "sra %[" #O4 "], %[" #O4 "], 16 \n\t" \
- "sra %[" #O5 "], %[" #O5 "], 16 \n\t" \
- "sra %[" #O6 "], %[" #O6 "], 16 \n\t" \
- "sra %[" #O7 "], %[" #O7 "], 16 \n\t" \
+ MUL_SHIFT_C2(O0, I0) \
+ MUL_SHIFT_C1(O1, I0) \
+ MUL_SHIFT_C2(O2, I1) \
+ MUL_SHIFT_C1(O3, I1) \
+ MUL_SHIFT_C2(O4, I2) \
+ MUL_SHIFT_C1(O5, I2) \
+ MUL_SHIFT_C2(O6, I3) \
+ MUL_SHIFT_C1(O7, I3) \
"addu %[" #IO0 "], %[" #IO0 "], %[" #I4 "] \n\t" \
"addu %[" #IO1 "], %[" #IO1 "], %[" #I5 "] \n\t" \
"subu %[" #IO2 "], %[" #IO2 "], %[" #I6 "] \n\t" \
diff --git a/c-lib/src/dsp/msa_macro.h b/c-lib/src/dsp/msa_macro.h
@@ -73,27 +73,25 @@
#define ST_UW(...) ST_W(v4u32, __VA_ARGS__)
#define ST_SW(...) ST_W(v4i32, __VA_ARGS__)
-#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \
- static inline TYPE FUNC_NAME(const void* const psrc) { \
- const uint8_t* const psrc_m = (const uint8_t*)psrc; \
- TYPE val_m; \
- asm volatile ( \
- "" #INSTR " %[val_m], %[psrc_m] \n\t" \
- : [val_m] "=r" (val_m) \
- : [psrc_m] "m" (*psrc_m)); \
- return val_m; \
+#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \
+ static inline TYPE FUNC_NAME(const void* const psrc) { \
+ const uint8_t* const psrc_m = (const uint8_t*)psrc; \
+ TYPE val_m; \
+ __asm__ volatile("" #INSTR " %[val_m], %[psrc_m] \n\t" \
+ : [val_m] "=r"(val_m) \
+ : [psrc_m] "m"(*psrc_m)); \
+ return val_m; \
}
#define MSA_LOAD(psrc, FUNC_NAME) FUNC_NAME(psrc)
-#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \
- static inline void FUNC_NAME(TYPE val, void* const pdst) { \
- uint8_t* const pdst_m = (uint8_t*)pdst; \
- TYPE val_m = val; \
- asm volatile ( \
- " " #INSTR " %[val_m], %[pdst_m] \n\t" \
- : [pdst_m] "=m" (*pdst_m) \
- : [val_m] "r" (val_m)); \
+#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \
+ static inline void FUNC_NAME(TYPE val, void* const pdst) { \
+ uint8_t* const pdst_m = (uint8_t*)pdst; \
+ TYPE val_m = val; \
+ __asm__ volatile(" " #INSTR " %[val_m], %[pdst_m] \n\t" \
+ : [pdst_m] "=m"(*pdst_m) \
+ : [val_m] "r"(val_m)); \
}
#define MSA_STORE(val, pdst, FUNC_NAME) FUNC_NAME(val, pdst)
diff --git a/c-lib/src/dsp/neon.h b/c-lib/src/dsp/neon.h
@@ -21,7 +21,7 @@
// Right now, some intrinsics functions seem slower, so we disable them
// everywhere except newer clang/gcc or aarch64 where the inline assembly is
// incompatible.
-#if LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,9) || defined(__aarch64__)
+#if LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 9) || WEBP_AARCH64
#define WEBP_USE_INTRINSICS // use intrinsics when possible
#endif
@@ -46,7 +46,7 @@
// if using intrinsics, this flag avoids some functions that make gcc-4.6.3
// crash ("internal compiler error: in immed_double_const, at emit-rtl.").
// (probably similar to gcc.gnu.org/bugzilla/show_bug.cgi?id=48183)
-#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__))
+#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || WEBP_AARCH64)
#define WORK_AROUND_GCC
#endif
diff --git a/c-lib/src/dsp/quant.h b/c-lib/src/dsp/quant.h
@@ -21,18 +21,24 @@
#define IsFlat IsFlat_NEON
-static uint32x2_t horizontal_add_uint32x4(const uint32x4_t a) {
+static uint32_t horizontal_add_uint32x4(const uint32x4_t a) {
+#if WEBP_AARCH64
+ return vaddvq_u32(a);
+#else
const uint64x2_t b = vpaddlq_u32(a);
- return vadd_u32(vreinterpret_u32_u64(vget_low_u64(b)),
- vreinterpret_u32_u64(vget_high_u64(b)));
+ const uint32x2_t c = vadd_u32(vreinterpret_u32_u64(vget_low_u64(b)),
+ vreinterpret_u32_u64(vget_high_u64(b)));
+ return vget_lane_u32(c, 0);
+#endif
}
static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks,
int thresh) {
const int16x8_t tst_ones = vdupq_n_s16(-1);
uint32x4_t sum = vdupq_n_u32(0);
+ int i;
- for (int i = 0; i < num_blocks; ++i) {
+ for (i = 0; i < num_blocks; ++i) {
// Set DC to zero.
const int16x8_t a_0 = vsetq_lane_s16(0, vld1q_s16(levels), 0);
const int16x8_t a_1 = vld1q_s16(levels + 8);
@@ -45,7 +51,7 @@ static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks,
levels += 16;
}
- return thresh >= (int32_t)vget_lane_u32(horizontal_add_uint32x4(sum), 0);
+ return thresh >= (int)horizontal_add_uint32x4(sum);
}
#else
diff --git a/c-lib/src/dsp/rescaler.c b/c-lib/src/dsp/rescaler.c
@@ -12,7 +12,10 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <stddef.h>
+#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);
@@ -197,6 +201,7 @@ WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPRescalerDspInitSSE2(void);
extern void WebPRescalerDspInitMIPS32(void);
extern void WebPRescalerDspInitMIPSdspR2(void);
diff --git a/c-lib/src/dsp/rescaler_mips32.c b/c-lib/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/c-lib/src/dsp/rescaler_msa.c b/c-lib/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/c-lib/src/dsp/rescaler_neon.c b/c-lib/src/dsp/rescaler_neon.c
@@ -32,7 +32,7 @@
#define STORE_32x8(SRC0, SRC1, DST) do { \
vst1q_u32((DST) + 0, SRC0); \
vst1q_u32((DST) + 4, SRC1); \
-} while (0);
+} while (0)
#if (WEBP_RESCALER_RFIX == 32)
#define MAKE_HALF_CST(C) vdupq_n_s32((int32_t)((C) >> 1))
@@ -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/c-lib/src/dsp/rescaler_sse2.c b/c-lib/src/dsp/rescaler_sse2.c
@@ -17,8 +17,12 @@
#include <emmintrin.h>
#include <assert.h>
+#include <stddef.h>
+
+#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;
@@ -85,7 +89,7 @@ static void RescalerImportRowExpand_SSE2(WebPRescaler* const wrk,
const __m128i mult = _mm_cvtsi32_si128(((x_add - accum) << 16) | accum);
const __m128i out = _mm_madd_epi16(cur_pixels, mult);
assert(sizeof(*frow) == sizeof(uint32_t));
- WebPUint32ToMem((uint8_t*)frow, _mm_cvtsi128_si32(out));
+ WebPInt32ToMem((uint8_t*)frow, _mm_cvtsi128_si32(out));
frow += 1;
if (frow >= frow_end) break;
accum -= wrk->x_sub;
@@ -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();
@@ -132,7 +136,7 @@ static void RescalerImportRowShrink_SSE2(WebPRescaler* const wrk,
__m128i base = zero;
accum += wrk->x_add;
while (accum > 0) {
- const __m128i A = _mm_cvtsi32_si128(WebPMemToUint32(src));
+ const __m128i A = _mm_cvtsi32_si128(WebPMemToInt32(src));
src += 4;
base = _mm_unpacklo_epi8(A, zero);
// To avoid overflow, we need: base * x_add / x_sub < 32768
@@ -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);
@@ -198,7 +200,7 @@ static WEBP_INLINE void ProcessRow_SSE2(const __m128i* const A0,
const __m128i* const mult,
uint8_t* const dst) {
const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER);
- const __m128i mask = _mm_set_epi32(0xffffffffu, 0, 0xffffffffu, 0);
+ const __m128i mask = _mm_set_epi32(~0, 0, ~0, 0);
const __m128i B0 = _mm_mul_epu32(*A0, *mult);
const __m128i B1 = _mm_mul_epu32(*A1, *mult);
const __m128i B2 = _mm_mul_epu32(*A2, *mult);
diff --git a/c-lib/src/dsp/ssim.c b/c-lib/src/dsp/ssim.c
@@ -14,7 +14,9 @@
#include <assert.h>
#include <stdlib.h> // for abs()
+#include "src/dsp/cpu.h"
#include "src/dsp/dsp.h"
+#include "src/webp/types.h"
#if !defined(WEBP_REDUCE_SIZE)
@@ -137,6 +139,7 @@ VP8SSIMGetClippedFunc VP8SSIMGetClipped;
VP8AccumulateSSEFunc VP8AccumulateSSE;
#endif
+extern VP8CPUInfo VP8GetCPUInfo;
extern void VP8SSIMDspInitSSE2(void);
WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) {
diff --git a/c-lib/src/dsp/ssim_sse2.c b/c-lib/src/dsp/ssim_sse2.c
@@ -14,11 +14,13 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
#include <assert.h>
-#include <emmintrin.h>
#include "src/dsp/common_sse2.h"
+#include "src/dsp/cpu.h"
+#include "src/webp/types.h"
#if !defined(WEBP_DISABLE_STATS)
diff --git a/c-lib/src/dsp/upsampling.c b/c-lib/src/dsp/upsampling.c
@@ -11,10 +11,14 @@
//
// Author: somnath@google.com (Somnath Banerjee)
+#include <assert.h>
+#include <stddef.h>
+
+#include "src/dsp/cpu.h"
+#include "src/webp/types.h"
#include "src/dsp/dsp.h"
#include "src/dsp/yuv.h"
-
-#include <assert.h>
+#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)]); \
}
@@ -215,6 +231,7 @@ static void EmptyYuv444Func(const uint8_t* y,
WebPYUV444Converter WebPYUV444Converters[MODE_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitYUV444ConvertersMIPSdspR2(void);
extern void WebPInitYUV444ConvertersSSE2(void);
extern void WebPInitYUV444ConvertersSSE41(void);
diff --git a/c-lib/src/dsp/upsampling_mips_dsp_r2.c b/c-lib/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/c-lib/src/dsp/upsampling_msa.c b/c-lib/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/c-lib/src/dsp/upsampling_neon.c b/c-lib/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);
}
@@ -111,7 +112,7 @@ static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 };
vst4_u8(out, v255_r_g_b); \
} while (0)
-#if !defined(WEBP_SWAP_16BIT_CSP)
+#if (WEBP_SWAP_16BIT_CSP == 0)
#define ZIP_U8(lo, hi) vzip_u8((lo), (hi))
#else
#define ZIP_U8(lo, hi) vzip_u8((hi), (lo))
@@ -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/c-lib/src/dsp/upsampling_sse2.c b/c-lib/src/dsp/upsampling_sse2.c
@@ -14,11 +14,15 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
#include <assert.h>
-#include <emmintrin.h>
#include <string.h>
+
+#include "src/webp/types.h"
+#include "src/dsp/cpu.h"
#include "src/dsp/yuv.h"
+#include "src/webp/decode.h"
#ifdef FANCY_UPSAMPLING
@@ -58,7 +62,7 @@
} while (0)
// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
-#define UPSAMPLE_32PIXELS(r1, r2, out) { \
+#define UPSAMPLE_32PIXELS(r1, r2, out) do { \
const __m128i one = _mm_set1_epi8(1); \
const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \
const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \
@@ -85,11 +89,12 @@
/* pack the alternate pixels */ \
PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \
PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \
-}
+} 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,14 +119,18 @@ 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 }; \
- 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); \
@@ -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) { \
@@ -229,11 +242,11 @@ static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
} \
}
-YUV444_FUNC(Yuv444ToRgba_SSE2, VP8YuvToRgba32_SSE2, WebPYuv444ToRgba_C, 4);
-YUV444_FUNC(Yuv444ToBgra_SSE2, VP8YuvToBgra32_SSE2, WebPYuv444ToBgra_C, 4);
+YUV444_FUNC(Yuv444ToRgba_SSE2, VP8YuvToRgba32_SSE2, WebPYuv444ToRgba_C, 4)
+YUV444_FUNC(Yuv444ToBgra_SSE2, VP8YuvToBgra32_SSE2, WebPYuv444ToBgra_C, 4)
#if !defined(WEBP_REDUCE_CSP)
-YUV444_FUNC(Yuv444ToRgb_SSE2, VP8YuvToRgb32_SSE2, WebPYuv444ToRgb_C, 3);
-YUV444_FUNC(Yuv444ToBgr_SSE2, VP8YuvToBgr32_SSE2, WebPYuv444ToBgr_C, 3);
+YUV444_FUNC(Yuv444ToRgb_SSE2, VP8YuvToRgb32_SSE2, WebPYuv444ToRgb_C, 3)
+YUV444_FUNC(Yuv444ToBgr_SSE2, VP8YuvToBgr32_SSE2, WebPYuv444ToBgr_C, 3)
YUV444_FUNC(Yuv444ToArgb_SSE2, VP8YuvToArgb32_SSE2, WebPYuv444ToArgb_C, 4)
YUV444_FUNC(Yuv444ToRgba4444_SSE2, VP8YuvToRgba444432_SSE2, \
WebPYuv444ToRgba4444_C, 2)
diff --git a/c-lib/src/dsp/upsampling_sse41.c b/c-lib/src/dsp/upsampling_sse41.c
@@ -14,11 +14,15 @@
#include "src/dsp/dsp.h"
#if defined(WEBP_USE_SSE41)
+#include <smmintrin.h>
#include <assert.h>
-#include <smmintrin.h>
#include <string.h>
+
+#include "src/webp/types.h"
+#include "src/dsp/cpu.h"
#include "src/dsp/yuv.h"
+#include "src/webp/decode.h"
#ifdef FANCY_UPSAMPLING
@@ -60,7 +64,7 @@
} while (0)
// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
-#define UPSAMPLE_32PIXELS(r1, r2, out) { \
+#define UPSAMPLE_32PIXELS(r1, r2, out) do { \
const __m128i one = _mm_set1_epi8(1); \
const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \
const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \
@@ -87,11 +91,12 @@
/* pack the alternate pixels */ \
PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \
PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \
-}
+} 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) { \
@@ -217,8 +230,8 @@ static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
}
#if !defined(WEBP_REDUCE_CSP)
-YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3);
-YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3);
+YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3)
+YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3)
#endif // WEBP_REDUCE_CSP
WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE41(void) {
diff --git a/c-lib/src/dsp/yuv.c b/c-lib/src/dsp/yuv.c
@@ -11,18 +11,23 @@
//
// Author: Skal (pascal.massimino@gmail.com)
-#include "src/dsp/yuv.h"
-
#include <assert.h>
#include <stdlib.h>
+#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) {
@@ -70,6 +76,7 @@ void WebPSamplerProcessPlane(const uint8_t* y, int y_stride,
WebPSamplerRowFunc WebPSamplers[MODE_LAST];
+extern VP8CPUInfo VP8GetCPUInfo;
extern void WebPInitSamplersSSE2(void);
extern void WebPInitSamplersSSE41(void);
extern void WebPInitSamplersMIPS32(void);
@@ -116,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];
@@ -125,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;
@@ -168,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];
@@ -194,71 +206,23 @@ void WebPConvertRGBA32ToUV_C(const uint16_t* rgb,
//-----------------------------------------------------------------------------
-#if !WEBP_NEON_OMIT_C_CODE
-#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic
-static uint16_t clip_y(int v) {
- return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
-}
-
-static uint64_t SharpYUVUpdateY_C(const uint16_t* ref, const uint16_t* src,
- uint16_t* dst, int len) {
- uint64_t diff = 0;
- int i;
- for (i = 0; i < len; ++i) {
- const int diff_y = ref[i] - src[i];
- const int new_y = (int)dst[i] + diff_y;
- dst[i] = clip_y(new_y);
- diff += (uint64_t)abs(diff_y);
- }
- return diff;
-}
-
-static void SharpYUVUpdateRGB_C(const int16_t* ref, const int16_t* src,
- int16_t* dst, int len) {
- int i;
- for (i = 0; i < len; ++i) {
- const int diff_uv = ref[i] - src[i];
- dst[i] += diff_uv;
- }
-}
-
-static void SharpYUVFilterRow_C(const int16_t* A, const int16_t* B, int len,
- const uint16_t* best_y, uint16_t* out) {
- int i;
- for (i = 0; i < len; ++i, ++A, ++B) {
- const int v0 = (A[0] * 9 + A[1] * 3 + B[0] * 3 + B[1] + 8) >> 4;
- const int v1 = (A[1] * 9 + A[0] * 3 + B[1] * 3 + B[0] + 8) >> 4;
- out[2 * i + 0] = clip_y(best_y[2 * i + 0] + v0);
- out[2 * i + 1] = clip_y(best_y[2 * i + 1] + v1);
- }
-}
-#endif // !WEBP_NEON_OMIT_C_CODE
-
-#undef MAX_Y
-
-//-----------------------------------------------------------------------------
-
-void (*WebPConvertRGB24ToY)(const uint8_t* rgb, uint8_t* y, int width);
-void (*WebPConvertBGR24ToY)(const uint8_t* bgr, uint8_t* y, int width);
-void (*WebPConvertRGBA32ToUV)(const uint16_t* rgb,
- uint8_t* u, uint8_t* v, int width);
-
-void (*WebPConvertARGBToY)(const uint32_t* argb, uint8_t* y, int width);
-void (*WebPConvertARGBToUV)(const uint32_t* argb, uint8_t* u, uint8_t* v,
+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* 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);
-uint64_t (*WebPSharpYUVUpdateY)(const uint16_t* ref, const uint16_t* src,
- uint16_t* dst, int len);
-void (*WebPSharpYUVUpdateRGB)(const int16_t* ref, const int16_t* src,
- int16_t* dst, int len);
-void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B, int len,
- const uint16_t* best_y, uint16_t* out);
-
extern void WebPInitConvertARGBToYUVSSE2(void);
extern void WebPInitConvertARGBToYUVSSE41(void);
extern void WebPInitConvertARGBToYUVNEON(void);
-extern void WebPInitSharpYUVSSE2(void);
-extern void WebPInitSharpYUVNEON(void);
WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) {
WebPConvertARGBToY = ConvertARGBToY_C;
@@ -269,17 +233,10 @@ WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) {
WebPConvertRGBA32ToUV = WebPConvertRGBA32ToUV_C;
-#if !WEBP_NEON_OMIT_C_CODE
- WebPSharpYUVUpdateY = SharpYUVUpdateY_C;
- WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_C;
- WebPSharpYUVFilterRow = SharpYUVFilterRow_C;
-#endif
-
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_HAVE_SSE2)
if (VP8GetCPUInfo(kSSE2)) {
WebPInitConvertARGBToYUVSSE2();
- WebPInitSharpYUVSSE2();
}
#endif // WEBP_HAVE_SSE2
#if defined(WEBP_HAVE_SSE41)
@@ -293,7 +250,6 @@ WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) {
if (WEBP_NEON_OMIT_C_CODE ||
(VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
WebPInitConvertARGBToYUVNEON();
- WebPInitSharpYUVNEON();
}
#endif // WEBP_HAVE_NEON
@@ -302,7 +258,4 @@ WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) {
assert(WebPConvertRGB24ToY != NULL);
assert(WebPConvertBGR24ToY != NULL);
assert(WebPConvertRGBA32ToUV != NULL);
- assert(WebPSharpYUVUpdateY != NULL);
- assert(WebPSharpYUVUpdateRGB != NULL);
- assert(WebPSharpYUVFilterRow != NULL);
}
diff --git a/c-lib/src/dsp/yuv.h b/c-lib/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/c-lib/src/dsp/yuv_mips32.c b/c-lib/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/c-lib/src/dsp/yuv_mips_dsp_r2.c b/c-lib/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/c-lib/src/dsp/yuv_neon.c b/c-lib/src/dsp/yuv_neon.c
@@ -18,6 +18,7 @@
#include <assert.h>
#include <stdlib.h>
+#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) {
@@ -173,116 +180,8 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVNEON(void) {
WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_NEON;
}
-//------------------------------------------------------------------------------
-
-#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic
-static uint16_t clip_y_NEON(int v) {
- return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
-}
-
-static uint64_t SharpYUVUpdateY_NEON(const uint16_t* ref, const uint16_t* src,
- uint16_t* dst, int len) {
- int i;
- const int16x8_t zero = vdupq_n_s16(0);
- const int16x8_t max = vdupq_n_s16(MAX_Y);
- uint64x2_t sum = vdupq_n_u64(0);
- uint64_t diff;
-
- for (i = 0; i + 8 <= len; i += 8) {
- const int16x8_t A = vreinterpretq_s16_u16(vld1q_u16(ref + i));
- const int16x8_t B = vreinterpretq_s16_u16(vld1q_u16(src + i));
- const int16x8_t C = vreinterpretq_s16_u16(vld1q_u16(dst + i));
- const int16x8_t D = vsubq_s16(A, B); // diff_y
- const int16x8_t F = vaddq_s16(C, D); // new_y
- const uint16x8_t H =
- vreinterpretq_u16_s16(vmaxq_s16(vminq_s16(F, max), zero));
- const int16x8_t I = vabsq_s16(D); // abs(diff_y)
- vst1q_u16(dst + i, H);
- sum = vpadalq_u32(sum, vpaddlq_u16(vreinterpretq_u16_s16(I)));
- }
- diff = vgetq_lane_u64(sum, 0) + vgetq_lane_u64(sum, 1);
- for (; i < len; ++i) {
- const int diff_y = ref[i] - src[i];
- const int new_y = (int)(dst[i]) + diff_y;
- dst[i] = clip_y_NEON(new_y);
- diff += (uint64_t)(abs(diff_y));
- }
- return diff;
-}
-
-static void SharpYUVUpdateRGB_NEON(const int16_t* ref, const int16_t* src,
- int16_t* dst, int len) {
- int i;
- for (i = 0; i + 8 <= len; i += 8) {
- const int16x8_t A = vld1q_s16(ref + i);
- const int16x8_t B = vld1q_s16(src + i);
- const int16x8_t C = vld1q_s16(dst + i);
- const int16x8_t D = vsubq_s16(A, B); // diff_uv
- const int16x8_t E = vaddq_s16(C, D); // new_uv
- vst1q_s16(dst + i, E);
- }
- for (; i < len; ++i) {
- const int diff_uv = ref[i] - src[i];
- dst[i] += diff_uv;
- }
-}
-
-static void SharpYUVFilterRow_NEON(const int16_t* A, const int16_t* B, int len,
- const uint16_t* best_y, uint16_t* out) {
- int i;
- const int16x8_t max = vdupq_n_s16(MAX_Y);
- const int16x8_t zero = vdupq_n_s16(0);
- for (i = 0; i + 8 <= len; i += 8) {
- const int16x8_t a0 = vld1q_s16(A + i + 0);
- const int16x8_t a1 = vld1q_s16(A + i + 1);
- const int16x8_t b0 = vld1q_s16(B + i + 0);
- const int16x8_t b1 = vld1q_s16(B + i + 1);
- const int16x8_t a0b1 = vaddq_s16(a0, b1);
- const int16x8_t a1b0 = vaddq_s16(a1, b0);
- const int16x8_t a0a1b0b1 = vaddq_s16(a0b1, a1b0); // A0+A1+B0+B1
- const int16x8_t a0b1_2 = vaddq_s16(a0b1, a0b1); // 2*(A0+B1)
- const int16x8_t a1b0_2 = vaddq_s16(a1b0, a1b0); // 2*(A1+B0)
- const int16x8_t c0 = vshrq_n_s16(vaddq_s16(a0b1_2, a0a1b0b1), 3);
- const int16x8_t c1 = vshrq_n_s16(vaddq_s16(a1b0_2, a0a1b0b1), 3);
- const int16x8_t d0 = vaddq_s16(c1, a0);
- const int16x8_t d1 = vaddq_s16(c0, a1);
- const int16x8_t e0 = vrshrq_n_s16(d0, 1);
- const int16x8_t e1 = vrshrq_n_s16(d1, 1);
- const int16x8x2_t f = vzipq_s16(e0, e1);
- const int16x8_t g0 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 0));
- const int16x8_t g1 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 8));
- const int16x8_t h0 = vaddq_s16(g0, f.val[0]);
- const int16x8_t h1 = vaddq_s16(g1, f.val[1]);
- const int16x8_t i0 = vmaxq_s16(vminq_s16(h0, max), zero);
- const int16x8_t i1 = vmaxq_s16(vminq_s16(h1, max), zero);
- vst1q_u16(out + 2 * i + 0, vreinterpretq_u16_s16(i0));
- vst1q_u16(out + 2 * i + 8, vreinterpretq_u16_s16(i1));
- }
- for (; i < len; ++i) {
- const int a0b1 = A[i + 0] + B[i + 1];
- const int a1b0 = A[i + 1] + B[i + 0];
- const int a0a1b0b1 = a0b1 + a1b0 + 8;
- const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
- const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
- out[2 * i + 0] = clip_y_NEON(best_y[2 * i + 0] + v0);
- out[2 * i + 1] = clip_y_NEON(best_y[2 * i + 1] + v1);
- }
-}
-#undef MAX_Y
-
-//------------------------------------------------------------------------------
-
-extern void WebPInitSharpYUVNEON(void);
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitSharpYUVNEON(void) {
- WebPSharpYUVUpdateY = SharpYUVUpdateY_NEON;
- WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_NEON;
- WebPSharpYUVFilterRow = SharpYUVFilterRow_NEON;
-}
-
#else // !WEBP_USE_NEON
WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVNEON)
-WEBP_DSP_INIT_STUB(WebPInitSharpYUVNEON)
#endif // WEBP_USE_NEON
diff --git a/c-lib/src/dsp/yuv_sse2.c b/c-lib/src/dsp/yuv_sse2.c
@@ -14,10 +14,16 @@
#include "src/dsp/yuv.h"
#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
-#include "src/dsp/common_sse2.h"
#include <stdlib.h>
-#include <emmintrin.h>
+
+#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.
@@ -74,15 +80,15 @@ static WEBP_INLINE __m128i Load_HI_16_SSE2(const uint8_t* src) {
// Load and replicate the U/V samples
static WEBP_INLINE __m128i Load_UV_HI_8_SSE2(const uint8_t* src) {
const __m128i zero = _mm_setzero_si128();
- const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src);
+ const __m128i tmp0 = _mm_cvtsi32_si128(WebPMemToInt32(src));
const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0);
return _mm_unpacklo_epi16(tmp1, tmp1); // replicate samples
}
// Convert 32 samples of YUV444 to R/G/B
-static void YUV444ToRGB_SSE2(const uint8_t* const y,
- const uint8_t* const u,
- const uint8_t* const v,
+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),
@@ -91,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),
@@ -106,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);
@@ -118,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);
@@ -130,7 +134,7 @@ static WEBP_INLINE void PackAndStore4444_SSE2(const __m128i* const R,
const __m128i rg0 = _mm_packus_epi16(*B, *A);
const __m128i ba0 = _mm_packus_epi16(*R, *G);
#endif
- const __m128i mask_0xf0 = _mm_set1_epi8(0xf0);
+ const __m128i mask_0xf0 = _mm_set1_epi8((char)0xf0);
const __m128i rb1 = _mm_unpacklo_epi8(rg0, ba0); // rbrbrbrbrb...
const __m128i ga1 = _mm_unpackhi_epi8(rg0, ba0); // gagagagaga...
const __m128i rb2 = _mm_and_si128(rb1, mask_0xf0);
@@ -143,13 +147,14 @@ 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);
- const __m128i r1 = _mm_and_si128(r0, _mm_set1_epi8(0xf8));
+ const __m128i r1 = _mm_and_si128(r0, _mm_set1_epi8((char)0xf8));
const __m128i b1 = _mm_and_si128(_mm_srli_epi16(b0, 3), _mm_set1_epi8(0x1f));
- const __m128i g1 = _mm_srli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0xe0)), 5);
+ const __m128i g1 =
+ _mm_srli_epi16(_mm_and_si128(g0, _mm_set1_epi8((char)0xe0)), 5);
const __m128i g2 = _mm_slli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0x1c)), 3);
const __m128i rg = _mm_or_si128(r1, g1);
const __m128i gb = _mm_or_si128(g2, b1);
@@ -167,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
@@ -190,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) {
@@ -201,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) {
@@ -212,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) {
@@ -223,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) {
@@ -234,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;
@@ -244,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;
@@ -266,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;
@@ -291,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) {
@@ -313,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) {
@@ -335,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) {
@@ -357,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;
@@ -394,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;
@@ -468,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));
@@ -485,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);
@@ -559,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) {
@@ -593,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) {
@@ -627,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) {
@@ -655,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;
@@ -692,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
@@ -712,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) {
@@ -747,128 +776,9 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVSSE2(void) {
WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_SSE2;
}
-//------------------------------------------------------------------------------
-
-#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic
-static uint16_t clip_y(int v) {
- return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
-}
-
-static uint64_t SharpYUVUpdateY_SSE2(const uint16_t* ref, const uint16_t* src,
- uint16_t* dst, int len) {
- uint64_t diff = 0;
- uint32_t tmp[4];
- int i;
- const __m128i zero = _mm_setzero_si128();
- const __m128i max = _mm_set1_epi16(MAX_Y);
- const __m128i one = _mm_set1_epi16(1);
- __m128i sum = zero;
-
- for (i = 0; i + 8 <= len; i += 8) {
- const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
- const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
- const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
- const __m128i D = _mm_sub_epi16(A, B); // diff_y
- const __m128i E = _mm_cmpgt_epi16(zero, D); // sign (-1 or 0)
- const __m128i F = _mm_add_epi16(C, D); // new_y
- const __m128i G = _mm_or_si128(E, one); // -1 or 1
- const __m128i H = _mm_max_epi16(_mm_min_epi16(F, max), zero);
- const __m128i I = _mm_madd_epi16(D, G); // sum(abs(...))
- _mm_storeu_si128((__m128i*)(dst + i), H);
- sum = _mm_add_epi32(sum, I);
- }
- _mm_storeu_si128((__m128i*)tmp, sum);
- diff = tmp[3] + tmp[2] + tmp[1] + tmp[0];
- for (; i < len; ++i) {
- const int diff_y = ref[i] - src[i];
- const int new_y = (int)dst[i] + diff_y;
- dst[i] = clip_y(new_y);
- diff += (uint64_t)abs(diff_y);
- }
- return diff;
-}
-
-static void SharpYUVUpdateRGB_SSE2(const int16_t* ref, const int16_t* src,
- int16_t* dst, int len) {
- int i = 0;
- for (i = 0; i + 8 <= len; i += 8) {
- const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
- const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
- const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
- const __m128i D = _mm_sub_epi16(A, B); // diff_uv
- const __m128i E = _mm_add_epi16(C, D); // new_uv
- _mm_storeu_si128((__m128i*)(dst + i), E);
- }
- for (; i < len; ++i) {
- const int diff_uv = ref[i] - src[i];
- dst[i] += diff_uv;
- }
-}
-
-static void SharpYUVFilterRow_SSE2(const int16_t* A, const int16_t* B, int len,
- const uint16_t* best_y, uint16_t* out) {
- int i;
- const __m128i kCst8 = _mm_set1_epi16(8);
- const __m128i max = _mm_set1_epi16(MAX_Y);
- const __m128i zero = _mm_setzero_si128();
- for (i = 0; i + 8 <= len; i += 8) {
- const __m128i a0 = _mm_loadu_si128((const __m128i*)(A + i + 0));
- const __m128i a1 = _mm_loadu_si128((const __m128i*)(A + i + 1));
- const __m128i b0 = _mm_loadu_si128((const __m128i*)(B + i + 0));
- const __m128i b1 = _mm_loadu_si128((const __m128i*)(B + i + 1));
- const __m128i a0b1 = _mm_add_epi16(a0, b1);
- const __m128i a1b0 = _mm_add_epi16(a1, b0);
- const __m128i a0a1b0b1 = _mm_add_epi16(a0b1, a1b0); // A0+A1+B0+B1
- const __m128i a0a1b0b1_8 = _mm_add_epi16(a0a1b0b1, kCst8);
- const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1); // 2*(A0+B1)
- const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0); // 2*(A1+B0)
- const __m128i c0 = _mm_srai_epi16(_mm_add_epi16(a0b1_2, a0a1b0b1_8), 3);
- const __m128i c1 = _mm_srai_epi16(_mm_add_epi16(a1b0_2, a0a1b0b1_8), 3);
- const __m128i d0 = _mm_add_epi16(c1, a0);
- const __m128i d1 = _mm_add_epi16(c0, a1);
- const __m128i e0 = _mm_srai_epi16(d0, 1);
- const __m128i e1 = _mm_srai_epi16(d1, 1);
- const __m128i f0 = _mm_unpacklo_epi16(e0, e1);
- const __m128i f1 = _mm_unpackhi_epi16(e0, e1);
- const __m128i g0 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
- const __m128i g1 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 8));
- const __m128i h0 = _mm_add_epi16(g0, f0);
- const __m128i h1 = _mm_add_epi16(g1, f1);
- const __m128i i0 = _mm_max_epi16(_mm_min_epi16(h0, max), zero);
- const __m128i i1 = _mm_max_epi16(_mm_min_epi16(h1, max), zero);
- _mm_storeu_si128((__m128i*)(out + 2 * i + 0), i0);
- _mm_storeu_si128((__m128i*)(out + 2 * i + 8), i1);
- }
- for (; i < len; ++i) {
- // (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
- // = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
- // We reuse the common sub-expressions.
- const int a0b1 = A[i + 0] + B[i + 1];
- const int a1b0 = A[i + 1] + B[i + 0];
- const int a0a1b0b1 = a0b1 + a1b0 + 8;
- const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
- const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
- out[2 * i + 0] = clip_y(best_y[2 * i + 0] + v0);
- out[2 * i + 1] = clip_y(best_y[2 * i + 1] + v1);
- }
-}
-
-#undef MAX_Y
-
-//------------------------------------------------------------------------------
-
-extern void WebPInitSharpYUVSSE2(void);
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitSharpYUVSSE2(void) {
- WebPSharpYUVUpdateY = SharpYUVUpdateY_SSE2;
- WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_SSE2;
- WebPSharpYUVFilterRow = SharpYUVFilterRow_SSE2;
-}
-
#else // !WEBP_USE_SSE2
WEBP_DSP_INIT_STUB(WebPInitSamplersSSE2)
WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVSSE2)
-WEBP_DSP_INIT_STUB(WebPInitSharpYUVSSE2)
#endif // WEBP_USE_SSE2
diff --git a/c-lib/src/dsp/yuv_sse41.c b/c-lib/src/dsp/yuv_sse41.c
@@ -14,10 +14,17 @@
#include "src/dsp/yuv.h"
#if defined(WEBP_USE_SSE41)
+#include <emmintrin.h>
+#include <smmintrin.h>
-#include "src/dsp/common_sse41.h"
#include <stdlib.h>
-#include <smmintrin.h>
+
+#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.
@@ -74,15 +81,15 @@ static WEBP_INLINE __m128i Load_HI_16_SSE41(const uint8_t* src) {
// Load and replicate the U/V samples
static WEBP_INLINE __m128i Load_UV_HI_8_SSE41(const uint8_t* src) {
const __m128i zero = _mm_setzero_si128();
- const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src);
+ const __m128i tmp0 = _mm_cvtsi32_si128(WebPMemToInt32(src));
const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0);
return _mm_unpacklo_epi16(tmp1, tmp1); // replicate samples
}
// Convert 32 samples of YUV444 to R/G/B
-static void YUV444ToRGB_SSE41(const uint8_t* const y,
- const uint8_t* const u,
- const uint8_t* const v,
+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),
@@ -91,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),
@@ -107,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
@@ -130,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;
@@ -152,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;
@@ -177,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;
@@ -214,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;
@@ -288,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));
@@ -332,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);
@@ -405,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) {
@@ -439,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) {
@@ -473,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) {
@@ -501,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;
@@ -538,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
@@ -568,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/c-lib/src/enc/Makefile.am b/c-lib/src/enc/Makefile.am
@@ -37,6 +37,7 @@ libwebpencodeinclude_HEADERS += ../webp/types.h
noinst_HEADERS =
noinst_HEADERS += ../webp/format_constants.h
+libwebpencode_la_LIBADD = ../../sharpyuv/libsharpyuv.la
libwebpencode_la_LDFLAGS = -lm
libwebpencode_la_CPPFLAGS = $(AM_CPPFLAGS)
libwebpencodeincludedir = $(includedir)/webp
diff --git a/c-lib/src/enc/alpha_enc.c b/c-lib/src/enc/alpha_enc.c
@@ -13,12 +13,17 @@
#include <assert.h>
#include <stdlib.h>
+#include <string.h>
-#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"
// -----------------------------------------------------------------------------
@@ -54,7 +59,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height,
WebPConfig config;
WebPPicture picture;
- WebPPictureInit(&picture);
+ if (!WebPPictureInit(&picture)) return 0;
picture.width = width;
picture.height = height;
picture.use_argb = 1;
@@ -65,7 +70,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height,
WebPDispatchAlphaToGreen(data, width, picture.width, picture.height,
picture.argb, picture.argb_stride);
- WebPConfigInit(&config);
+ if (!WebPConfigInit(&config)) return 0;
config.lossless = 1;
// Enable exact, or it would alter RGB values of transparent alpha, which is
// normally OK but not here since we are not encoding the input image but an
@@ -82,13 +87,9 @@ static int EncodeLossless(const uint8_t* const data, int width, int height,
(use_quality_100 && effort_level == 6) ? 100 : 8.f * effort_level;
assert(config.quality >= 0 && config.quality <= 100.f);
- // TODO(urvang): Temporary fix to avoid generating images that trigger
- // a decoder bug related to alpha with color cache.
- // See: https://code.google.com/p/webp/issues/detail?id=239
- // Need to re-enable this later.
- ok = (VP8LEncodeStream(&config, &picture, bw, 0 /*use_cache*/) == VP8_ENC_OK);
+ ok = VP8LEncodeStream(&config, &picture, bw);
WebPPictureFree(&picture);
- ok = ok && !bw->error_;
+ ok = ok && !bw->error;
if (!ok) {
VP8LBitWriterWipeOut(bw);
return 0;
@@ -140,6 +141,11 @@ 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) {
+ VP8LBitWriterWipeOut(&tmp_bw);
+ memset(&result->bw, 0, sizeof(result->bw));
+ return 0;
+ }
output_size = VP8LBitWriterNumBytes(&tmp_bw);
if (output_size > data_size) {
// compressed size is larger than source! Revert to uncompressed mode.
@@ -148,6 +154,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
}
} else {
VP8LBitWriterWipeOut(&tmp_bw);
+ memset(&result->bw, 0, sizeof(result->bw));
return 0;
}
}
@@ -162,14 +169,14 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
header = method | (filter << 2);
if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
- VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size);
+ if (!VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size)) ok = 0;
ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
ok = ok && VP8BitWriterAppend(&result->bw, output, output_size);
if (method != ALPHA_NO_COMPRESSION) {
VP8LBitWriterWipeOut(&tmp_bw);
}
- ok = ok && !result->bw.error_;
+ ok = ok && !result->bw.error;
result->score = VP8BitWriterSize(&result->bw);
return ok;
}
@@ -272,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;
@@ -293,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;
@@ -312,11 +320,11 @@ static int EncodeAlpha(VP8Encoder* const enc,
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
if (quality < 0 || quality > 100) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
}
if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
}
if (method == ALPHA_NO_COMPRESSION) {
@@ -326,7 +334,7 @@ static int EncodeAlpha(VP8Encoder* const enc,
quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
if (quant_alpha == NULL) {
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// Extract alpha data (width x height) from raw_data (stride x height).
@@ -346,10 +354,13 @@ static int EncodeAlpha(VP8Encoder* const enc,
ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
filter, reduce_levels, effort_level, output,
output_size, pic->stats);
+ if (!ok) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise
+ }
#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
}
@@ -363,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]
@@ -379,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;
@@ -400,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 0;
+ return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
WebPGetWorkerInterface()->Launch(worker);
return 1;
@@ -417,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/c-lib/src/enc/analysis_enc.c b/c-lib/src/enc/analysis_enc.c
@@ -11,13 +11,17 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
+#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
@@ -391,12 +395,14 @@ static int DoSegmentsJob(void* arg1, void* arg2) {
return ok;
}
+#ifdef WEBP_USE_THREAD
static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) {
int i;
for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i];
dst->alpha += src->alpha;
dst->uv_alpha += src->uv_alpha;
}
+#endif
// initialize the job struct with some tasks to perform
static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job,
@@ -407,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;
@@ -420,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 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 total_mb = last_row * enc->mb_w_;
-#ifdef WEBP_USE_THREAD
const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it
- const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow);
+ const int do_mt = (enc->thread_level > 0) && (split_row >= kMinSplitRow);
#else
const int do_mt = 0;
#endif
@@ -438,6 +444,7 @@ int VP8EncAnalyze(VP8Encoder* const enc) {
WebPGetWorkerInterface();
SegmentJob main_job;
if (do_mt) {
+#ifdef WEBP_USE_THREAD
SegmentJob side_job;
// Note the use of '&' instead of '&&' because we must call the functions
// no matter what.
@@ -455,6 +462,7 @@ int VP8EncAnalyze(VP8Encoder* const enc) {
}
worker_interface->End(&side_job.worker);
if (ok) MergeJobs(&side_job, &main_job); // merge results together
+#endif // WEBP_USE_THREAD
} else {
// Even for single-thread case, we use the generic Worker tools.
InitSegmentJob(enc, &main_job, 0, last_row);
@@ -463,13 +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,
+ VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise
+ }
return ok;
}
-
diff --git a/c-lib/src/enc/backward_references_cost_enc.c b/c-lib/src/enc/backward_references_cost_enc.c
@@ -15,12 +15,15 @@
//
#include <assert.h>
+#include <string.h>
+#include "src/dsp/lossless_common.h"
#include "src/enc/backward_references_enc.h"
#include "src/enc/histogram_enc.h"
-#include "src/dsp/lossless_common.h"
#include "src/utils/color_cache_utils.h"
#include "src/utils/utils.h"
+#include "src/webp/format_constants.h"
+#include "src/webp/types.h"
#define VALUES_IN_BYTE 256
@@ -30,15 +33,15 @@ extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
const PixOrCopy v);
typedef struct {
- double alpha_[VALUES_IN_BYTE];
- double red_[VALUES_IN_BYTE];
- double blue_[VALUES_IN_BYTE];
- double distance_[NUM_DISTANCE_CODES];
- double* literal_;
+ 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[], double output[]) {
+ int num_symbols, const uint32_t population_counts[], uint32_t output[]) {
uint32_t sum = 0;
int nonzeros = 0;
int i;
@@ -51,7 +54,7 @@ static void ConvertPopulationCountTableToBitEstimates(
if (nonzeros <= 1) {
memset(output, 0, num_symbols * sizeof(*output));
} else {
- const double logsum = VP8LFastLog2(sum);
+ const uint32_t logsum = VP8LFastLog2(sum);
for (i = 0; i < num_symbols; ++i) {
output[i] = logsum - VP8LFastLog2(population_counts[i]);
}
@@ -61,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:
@@ -92,50 +90,50 @@ static int CostModelBuild(CostModel* const m, int xsize, int cache_bits,
return ok;
}
-static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
- return m->alpha_[v >> 24] +
- m->red_[(v >> 16) & 0xff] +
- m->literal_[(v >> 8) & 0xff] +
- m->blue_[v & 0xff];
+static WEBP_INLINE 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 double 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 double 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 double 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) {
- double 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 double mul0 = 0.68;
- cost_val += GetCacheCost(cost_model, ix) * mul0;
+ cost_val += DivRound(GetCacheCost(cost_model, ix) * 68, 100);
} else {
- const double mul1 = 0.82;
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] = (float)cost_val;
+ cost[idx] = cost_val;
dist_array[idx] = 1; // only one is inserted.
}
}
@@ -146,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 {
- double 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_;
- double 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
@@ -233,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);
}
@@ -253,22 +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_.
- manager->cache_intervals_size_ = 1;
- manager->cost_cache_[0] = GetLengthCost(cost_model, 0);
+ // 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->cache_intervals_size = 1;
for (i = 1; i < cost_cache_size; ++i) {
- manager->cost_cache_[i] = GetLengthCost(cost_model, i);
// Get the number of bound intervals.
- if (manager->cost_cache_[i] != manager->cost_cache_[i - 1]) {
- ++manager->cache_intervals_size_;
+ if (manager->cost_cache[i] != manager->cost_cache[i - 1]) {
+ ++manager->cache_intervals_size;
}
}
@@ -276,42 +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 double 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);
}
- 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] = 1e38f;
+ // 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;
}
@@ -319,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;
}
}
@@ -333,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);
}
@@ -343,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.
@@ -356,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
@@ -373,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;
}
@@ -391,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) {
@@ -443,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
@@ -457,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,
- double 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;
@@ -472,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 = (float)(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 = (float)(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;
}
}
}
@@ -570,22 +576,21 @@ static int BackwardReferencesHashChainDistanceOnly(
const int pix_count = xsize * ysize;
const int use_color_cache = (cache_bits > 0);
const size_t literal_array_size =
- sizeof(double) * (NUM_LITERAL_CODES + NUM_LENGTH_CODES +
- ((cache_bits > 0) ? (1 << cache_bits) : 0));
+ 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);
VP8LColorCache hashers;
CostManager* cost_manager =
- (CostManager*)WebPSafeMalloc(1ULL, sizeof(*cost_manager));
+ (CostManager*)WebPSafeCalloc(1ULL, sizeof(*cost_manager));
int offset_prev = -1, len_prev = -1;
- double offset_cost = -1;
+ 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_ = (double*)(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;
@@ -603,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) {
@@ -662,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;
}
@@ -674,8 +680,8 @@ static int BackwardReferencesHashChainDistanceOnly(
len_prev = len;
}
- ok = !refs->error_;
-Error:
+ ok = !refs->error;
+ Error:
if (cc_init) VP8LColorCacheClear(&hashers);
CostManagerClear(cost_manager);
WebPSafeFree(cost_model);
@@ -747,7 +753,7 @@ static int BackwardReferencesHashChainFollowChosenPath(
++i;
}
}
- ok = !refs->error_;
+ ok = !refs->error;
Error:
if (cc_init) VP8LColorCacheClear(&hashers);
return ok;
diff --git a/c-lib/src/enc/backward_references_enc.c b/c-lib/src/enc/backward_references_enc.c
@@ -10,22 +10,24 @@
// Author: Jyrki Alakuijala (jyrki@google.com)
//
+#include "src/enc/backward_references_enc.h"
+
#include <assert.h>
-#include <float.h>
-#include <math.h>
+#include <string.h>
-#include "src/dsp/dsp.h"
+#include "src/dsp/cpu.h"
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
-#include "src/enc/backward_references_enc.h"
#include "src/enc/histogram_enc.h"
+#include "src/enc/vp8i_enc.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"
#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)
@@ -77,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;
}
}
@@ -108,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;
}
@@ -189,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;
}
// -----------------------------------------------------------------------------
@@ -255,28 +257,36 @@ static WEBP_INLINE int MaxFindCopyLength(int len) {
int VP8LHashChainFill(VP8LHashChain* const p, int quality,
const uint32_t* const argb, int xsize, int ysize,
- int low_effort) {
+ int low_effort, const WebPPicture* const pic,
+ int percent_range, int* const percent) {
const int size = xsize * ysize;
const int iter_max = GetMaxItersForQuality(quality);
const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);
+ int remaining_percent = percent_range;
+ int percent_start = *percent;
int pos;
int argb_comp;
uint32_t base_position;
int32_t* hash_to_first_index;
- // Temporarily use the p->offset_length_ as a hash chain.
- int32_t* chain = (int32_t*)p->offset_length_;
+ // 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;
}
hash_to_first_index =
(int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
- if (hash_to_first_index == NULL) return 0;
+ if (hash_to_first_index == NULL) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+
+ percent_range = remaining_percent / 2;
+ remaining_percent -= percent_range;
// Set the int32_t array to -1.
memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));
@@ -323,18 +333,28 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality,
hash_to_first_index[hash_code] = pos++;
argb_comp = argb_comp_next;
}
+
+ if (!WebPReportProgress(
+ pic, percent_start + percent_range * pos / (size - 2), percent)) {
+ WebPSafeFree(hash_to_first_index);
+ return 0;
+ }
}
// Process the penultimate pixel.
chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];
WebPSafeFree(hash_to_first_index);
+ percent_start += percent_range;
+ if (!WebPReportProgress(pic, percent_start, percent)) return 0;
+ percent_range = remaining_percent;
+
// Find the best match interval at each pixel, defined by an offset to the
// pixel and a length. The right-most pixel cannot match anything to the right
// (hence a best length of 0) and the left-most pixel nothing to the left
// (hence an offset of 0).
assert(size > 2);
- p->offset_length_[0] = p->offset_length_[size - 1] = 0;
+ 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;
@@ -394,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.
@@ -417,8 +437,17 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality,
max_base_position = base_position;
}
}
+
+ if (!WebPReportProgress(pic,
+ percent_start + percent_range *
+ (size - 2 - base_position) /
+ (size - 2),
+ percent)) {
+ return 0;
+ }
}
- return 1;
+
+ return WebPReportProgress(pic, percent_start + percent_range, percent);
}
static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,
@@ -479,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,
@@ -544,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;
@@ -619,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);
@@ -686,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,
@@ -728,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;
- double 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);
@@ -767,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 {
@@ -789,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 {
@@ -802,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;
}
@@ -813,14 +842,14 @@ static int CalculateBestCacheSize(const uint32_t* argb, int quality,
}
for (i = 0; i <= cache_bits_max; ++i) {
- const double entropy = VP8LHistogramEstimateBits(histos[i]);
+ const uint64_t entropy = VP8LHistogramEstimateBits(histos[i]);
if (i == 0 || entropy < entropy_min) {
entropy_min = entropy;
*best_cache_bits = i;
}
}
ok = 1;
-Error:
+ Error:
for (i = 0; i <= cache_bits_max; ++i) {
if (cc_init[i]) VP8LColorCacheClear(&hashers[i]);
VP8LFreeHistogram(histos[i]);
@@ -890,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};
- double bit_costs_best[2] = {DBL_MAX, DBL_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;
@@ -902,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;
- double bit_cost = 0.;
+ uint64_t bit_cost = 0u;
if ((lz77_types_to_try & lz77_type) == 0) continue;
switch (lz77_type) {
case kLZ77RLE:
@@ -976,15 +1005,16 @@ 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;
- if (VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits,
- hash_chain_tmp, &refs[i],
- refs_tmp)) {
- double bit_cost_trace;
- VP8LHistogramCreate(histo, refs_tmp, cache_bits);
- bit_cost_trace = VP8LHistogramEstimateBits(histo);
- if (bit_cost_trace < bit_costs_best[i]) {
- BackwardRefsSwap(refs_tmp, &refs[i]);
- }
+ uint64_t bit_cost_trace;
+ if (!VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits,
+ hash_chain_tmp, &refs[i],
+ refs_tmp)) {
+ goto Error;
+ }
+ VP8LHistogramCreate(histo, refs_tmp, cache_bits);
+ bit_cost_trace = VP8LHistogramEstimateBits(histo);
+ if (bit_cost_trace < bit_costs_best[i]) {
+ BackwardRefsSwap(refs_tmp, &refs[i]);
}
}
@@ -1000,31 +1030,35 @@ static int GetBackwardReferences(int width, int height,
}
status = 1;
-Error:
+ Error:
VP8LHashChainClear(&hash_chain_box);
VP8LFreeHistogram(histo);
return status;
}
-WebPEncodingError VP8LGetBackwardReferences(
+int VP8LGetBackwardReferences(
int width, int height, const uint32_t* const argb, int quality,
int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,
- int* const cache_bits_best) {
+ int* const cache_bits_best, const WebPPicture* const pic, int percent_range,
+ int* const percent) {
if (low_effort) {
VP8LBackwardRefs* refs_best;
*cache_bits_best = cache_bits_max;
refs_best = GetBackwardReferencesLowEffort(
width, height, argb, cache_bits_best, hash_chain, refs);
- if (refs_best == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+ if (refs_best == NULL) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
// Set it in first position.
BackwardRefsSwap(refs_best, &refs[0]);
} else {
if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try,
cache_bits_max, do_no_cache, hash_chain, refs,
cache_bits_best)) {
- return VP8_ENC_ERROR_OUT_OF_MEMORY;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
}
- return VP8_ENC_OK;
+
+ return WebPReportProgress(pic, *percent + percent_range, percent);
}
diff --git a/c-lib/src/enc/backward_references_enc.h b/c-lib/src/enc/backward_references_enc.h
@@ -15,9 +15,10 @@
#include <assert.h>
#include <stdlib.h>
-#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,28 +127,29 @@ 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.
int VP8LHashChainInit(VP8LHashChain* const p, int size);
-// Pre-compute the best matches for argb.
+// Pre-compute the best matches for argb. pic and percent are for progress.
int VP8LHashChainFill(VP8LHashChain* const p, int quality,
const uint32_t* const argb, int xsize, int ysize,
- int low_effort);
+ int low_effort, const WebPPicture* const pic,
+ int percent_range, int* const percent);
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,
@@ -169,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
@@ -188,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.
@@ -204,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);
}
// -----------------------------------------------------------------------------
@@ -227,11 +229,14 @@ enum VP8LLZ77Type {
// VP8LBackwardRefs is put in the first element, the best value with no-cache in
// the second element.
// In both cases, the last element is used as temporary internally.
-WebPEncodingError VP8LGetBackwardReferences(
+// pic and percent are for progress.
+// Returns false in case of error (stored in pic->error_code).
+int VP8LGetBackwardReferences(
int width, int height, const uint32_t* const argb, int quality,
int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,
- int* const cache_bits_best);
+ int* const cache_bits_best, const WebPPicture* const pic, int percent_range,
+ int* const percent);
#ifdef __cplusplus
}
diff --git a/c-lib/src/enc/config_enc.c b/c-lib/src/enc/config_enc.c
@@ -15,7 +15,10 @@
#include "src/webp/config.h"
#endif
+#include <stddef.h>
+
#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/c-lib/src/enc/cost_enc.c b/c-lib/src/enc/cost_enc.c
@@ -11,7 +11,13 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <stdlib.h>
+
+#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/c-lib/src/enc/cost_enc.h b/c-lib/src/enc/cost_enc.h
@@ -16,7 +16,11 @@
#include <assert.h>
#include <stdlib.h>
+
+#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/c-lib/src/enc/filter_enc.c b/c-lib/src/enc/filter_enc.c
@@ -12,8 +12,13 @@
// Author: somnath@google.com (Somnath Banerjee)
#include <assert.h>
-#include "src/enc/vp8i_enc.h"
+#include <stddef.h>
+#include <string.h>
+
+#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/c-lib/src/enc/frame_enc.c b/c-lib/src/enc/frame_enc.c
@@ -11,12 +11,17 @@
//
// Author: Skal (pascal.massimino@gmail.com)
-#include <string.h>
+#include <assert.h>
#include <math.h>
+#include <string.h>
+#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));
}
}
}
@@ -578,7 +583,7 @@ static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt,
uint64_t size = 0;
uint64_t size_p0 = 0;
uint64_t distortion = 0;
- const uint64_t pixel_count = nb_mbs * 384;
+ const uint64_t pixel_count = (uint64_t)nb_mbs * 384;
VP8IteratorInit(enc, &it);
SetLoopParams(enc, s->q);
@@ -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
- 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,6 +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 ok;
}
@@ -727,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
}
}
@@ -746,14 +752,19 @@ 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);
+ CodeResiduals(it.bw, &it, &info);
+ if (it.bw->error) {
+ // enc->pic->error_code is set in PostLoopFinalize().
+ ok = 0;
+ break;
+ }
} else { // reset predictors after a skip
ResetAfterSkip(&it);
}
@@ -776,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 = 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;
@@ -793,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;
@@ -815,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);
@@ -825,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;
@@ -842,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;
@@ -860,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);
}
@@ -877,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/c-lib/src/enc/histogram_enc.c b/c-lib/src/enc/histogram_enc.c
@@ -13,15 +13,19 @@
#include "src/webp/config.h"
#endif
-#include <math.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
-#include "src/enc/backward_references_enc.h"
-#include "src/enc/histogram_enc.h"
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
+#include "src/enc/backward_references_enc.h"
+#include "src/enc/histogram_enc.h"
+#include "src/enc/vp8i_enc.h"
#include "src/utils/utils.h"
-
-#define MAX_COST 1.e38
+#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.
@@ -31,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;
+
+// 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;
+}
+
+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));
+}
+
+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 A, VP8LHistogram** const B) {
- VP8LHistogram* const tmp = *A;
- *A = *B;
- *B = tmp;
+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_;
+ 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);
+ 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_));
+ dst->literal = dst_literal;
+ memcpy(dst->literal, src->literal, literal_size * sizeof(*dst->literal));
}
-int VP8LGetHistogramSize(int cache_bits) {
- const int literal_size = VP8LHistogramNumCodes(cache_bits);
- const size_t total_size = sizeof(VP8LHistogram) + sizeof(int) * literal_size;
- assert(total_size <= (size_t)0x7fffffff);
- return (int)total_size;
-}
-
-void VP8LFreeHistogram(VP8LHistogram* const histo) {
- WebPSafeFree(histo);
-}
-
-void VP8LFreeHistogramSet(VP8LHistogramSet* const histo) {
- WebPSafeFree(histo);
-}
+void VP8LFreeHistogram(VP8LHistogram* const h) { WebPSafeFree(h); }
-void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
- VP8LHistogram* const histo) {
- VP8LRefsCursor c = VP8LRefsCursorInit(refs);
- while (VP8LRefsCursorOk(&c)) {
- VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, NULL, 0);
- VP8LRefsCursorNext(&c);
- }
+void VP8LFreeHistogramSet(VP8LHistogramSet* const histograms) {
+ WebPSafeFree(histograms);
}
-void VP8LHistogramCreate(VP8LHistogram* const p,
+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;
}
@@ -124,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));
}
@@ -164,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;
@@ -176,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 {
@@ -221,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 double BitsEntropyRefine(const VP8LBitEntropy* entropy) {
- double mix;
+static WEBP_INLINE uint64_t BitsEntropyRefine(const VP8LBitEntropy* entropy) {
+ uint64_t mix;
if (entropy->nonzeros < 5) {
if (entropy->nonzeros <= 1) {
return 0;
@@ -238,67 +253,72 @@ static WEBP_INLINE double 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.99 * entropy->sum + 0.01 * 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.95;
+ mix = 950;
} else {
- mix = 0.7; // nonzeros == 4.
+ mix = 700; // nonzeros == 4.
}
} else {
- mix = 0.627;
+ mix = 627;
}
{
- double min_limit = 2 * entropy->sum - entropy->max_val;
- min_limit = mix * min_limit + (1.0 - 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;
}
}
-double 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 double 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 double kSmallBias = 9.1;
- 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 double 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.
- double 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.5625 + 0.234375 * 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.578125 + 0.703125 * 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.796875 * stats->streaks[0][0];
+ retval_extra += 1840 * stats->streaks[0][0];
// Originally 26/8.
- retval += 3.28125 * 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 double 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);
@@ -306,131 +326,175 @@ static double 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);
}
+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.
-static WEBP_INLINE double GetCombinedEntropy(const uint32_t* const X,
- const uint32_t* const Y,
- int length, int is_X_used,
- int is_Y_used,
- int trivial_at_end) {
+// '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;
- 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);
+ 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.
-double VP8LHistogramEstimateBits(VP8LHistogram* const p) {
- return
- PopulationCost(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_),
- NULL, &p->is_used_[0])
- + PopulationCost(p->red_, NUM_LITERAL_CODES, NULL, &p->is_used_[1])
- + PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL, &p->is_used_[2])
- + PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL, &p->is_used_[3])
- + PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL, &p->is_used_[4])
- + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES)
- + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES);
+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,
- double cost_threshold,
- double* cost) {
- const int palette_code_bits = a->palette_code_bits_;
- int trivial_at_end = 0;
- assert(a->palette_code_bits_ == b->palette_code_bits_);
- *cost += GetCombinedEntropy(a->literal_, b->literal_,
- VP8LHistogramNumCodes(palette_code_bits),
- a->is_used_[0], b->is_used_[0], 0);
- *cost += VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES,
- b->literal_ + NUM_LITERAL_CODES,
- NUM_LENGTH_CODES);
- if (*cost > cost_threshold) return 0;
-
- if (a->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM &&
- a->trivial_symbol_ == b->trivial_symbol_) {
- // A, R and B are all 0 or 0xff.
- const uint32_t color_a = (a->trivial_symbol_ >> 24) & 0xff;
- const uint32_t color_r = (a->trivial_symbol_ >> 16) & 0xff;
- const uint32_t color_b = (a->trivial_symbol_ >> 0) & 0xff;
- if ((color_a == 0 || color_a == 0xff) &&
- (color_r == 0 || color_r == 0xff) &&
- (color_b == 0 || color_b == 0xff)) {
- trivial_at_end = 1;
- }
+// 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;
-
- *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;
+// 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;
+
+ // 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;
+ }
- *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;
+ return 1;
+}
- *cost +=
- GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES,
- a->is_used_[4], b->is_used_[4], 0);
- *cost +=
- VP8LExtraCostCombined(a->distance_, b->distance_, NUM_DISTANCE_CODES);
- if (*cost > cost_threshold) return 0;
+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]));
+ }
+ }
+ }
- return 1;
+ 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 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 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
@@ -439,100 +503,95 @@ 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 double HistogramAddEval(const VP8LHistogram* const a,
- const VP8LHistogram* const b,
- VP8LHistogram* const out,
- double cost_threshold) {
- double cost = 0;
- const double sum_cost = a->bit_cost_ + b->bit_cost_;
- cost_threshold += sum_cost;
-
- if (GetCombinedHistogramEntropy(a, b, cost_threshold, &cost)) {
- HistogramAdd(a, b, out);
- out->bit_cost_ = cost;
- out->palette_code_bits_ = a->palette_code_bits_;
+// 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 double HistogramAddThresh(const VP8LHistogram* const a,
- const VP8LHistogram* const b,
- double cost_threshold) {
- double 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;
}
// -----------------------------------------------------------------------------
// The structure to keep track of cost range for the three dominant entropy
// symbols.
-// TODO(skal): Evaluate if float can be used here instead of double for
-// representing the entropy costs.
typedef struct {
- double literal_max_;
- double literal_min_;
- double red_max_;
- double red_min_;
- double blue_max_;
- double blue_min_;
+ 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_COST;
- c->red_max_ = 0.;
- c->red_min_ = MAX_COST;
- c->blue_max_ = 0.;
- c->blue_min_ = MAX_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 double alpha_cost =
- PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym,
- &h->is_used_[3]);
- const double distance_cost =
- PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL, &h->is_used_[4]) +
- VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES);
- const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_);
- h->literal_cost_ =
- PopulationCost(h->literal_, num_codes, NULL, &h->is_used_[0]) +
- VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES);
- h->red_cost_ =
- PopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym, &h->is_used_[1]);
- h->blue_cost_ =
- PopulationCost(h->blue_, NUM_LITERAL_CODES, &blue_sym, &h->is_used_[2]);
- h->bit_cost_ = h->literal_cost_ + h->red_cost_ + h->blue_cost_ +
- alpha_cost + distance_cost;
- if ((alpha_sym | red_sym | blue_sym) == VP8L_NON_TRIVIAL_SYM) {
- h->trivial_symbol_ = VP8L_NON_TRIVIAL_SYM;
- } else {
- h->trivial_symbol_ =
- ((uint32_t)alpha_sym << 24) | (red_sym << 16) | (blue_sym << 0);
+static 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(double min, double max, double val) {
- const double range = max - min;
- if (range > 0.) {
- const double 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;
@@ -541,14 +600,14 @@ static int GetBinIdForEntropy(double min, double max, double 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;
@@ -567,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;
@@ -578,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;
}
}
}
@@ -615,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;
@@ -625,30 +676,23 @@ 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.
+// 'combine_cost_factor' has to be divided by 100.
static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
- int* num_used,
- const uint16_t* const clusters,
- uint16_t* const cluster_mappings,
- VP8LHistogram* cur_combo,
- const uint16_t* const bin_map,
- int num_bins,
- double combine_cost_factor,
+ VP8LHistogram* cur_combo, int num_bins,
+ int32_t combine_cost_factor,
int low_effort) {
VP8LHistogram** const histograms = image_histo->histograms;
int idx;
@@ -664,54 +708,60 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
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 double bit_cost = histograms[idx]->bit_cost_;
- const double bit_cost_thresh = -bit_cost * combine_cost_factor;
- const double curr_cost_diff =
- HistogramAddEval(histograms[first], histograms[idx],
- cur_combo, bit_cost_thresh);
- if (curr_cost_diff < bit_cost_thresh) {
+ 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]);
}
}
}
@@ -731,8 +781,9 @@ static uint32_t MyRand(uint32_t* const seed) {
typedef struct {
int idx1;
int idx2;
- double cost_diff;
- double cost_combo;
+ int64_t cost_diff;
+ uint64_t cost_combo;
+ uint64_t costs[5];
} HistogramPair;
typedef struct {
@@ -772,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);
@@ -784,31 +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,
- double threshold,
- HistogramPair* const pair) {
- const double sum_cost = h1->bit_cost_ + h2->bit_cost_;
- pair->cost_combo = 0.;
- GetCombinedHistogramEntropy(h1, h2, sum_cost + threshold, &pair->cost_combo);
- pair->cost_diff = pair->cost_combo - sum_cost;
+// 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 double HistoQueuePush(HistoQueue* const histo_queue,
- VP8LHistogram** const histograms, int idx1,
- int idx2, double 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;
@@ -819,10 +888,8 @@ static double 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]);
@@ -834,8 +901,7 @@ static double 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;
@@ -854,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);
}
}
@@ -867,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;) {
@@ -879,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;
}
@@ -886,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);
}
}
@@ -901,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'
@@ -920,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;
- double best_cost =
- (histo_queue.size == 0) ? 0. : 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) {
- double 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 =
@@ -979,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) {
@@ -1005,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:
+ End:
HistoQueueClear(&histo_queue);
- WebPSafeFree(mappings);
return ok;
}
@@ -1045,10 +1074,10 @@ End:
// 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;
@@ -1057,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;
- double best_bits = MAX_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.
@@ -1065,9 +1094,9 @@ static void HistogramRemap(const VP8LHistogramSet* const in,
continue;
}
for (k = 0; k < out_size; ++k) {
- double cur_bits;
- cur_bits = HistogramAddThresh(out_histo[k], in_histo[i], best_bits);
- if (k == 0 || cur_bits < best_bits) {
+ int64_t cur_bits;
+ if (HistogramAddThresh(out_histo[k], in_histo[i], best_bits,
+ &cur_bits)) {
best_bits = cur_bits;
best_out = k;
}
@@ -1093,89 +1122,25 @@ static void HistogramRemap(const VP8LHistogramSet* const in,
}
}
-static double GetCombineCostFactor(int histo_size, int quality) {
- double combine_cost_factor = 0.16;
+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.;
- if (histo_size > 512) combine_cost_factor /= 2.;
- if (histo_size > 1024) combine_cost_factor /= 2.;
- if (quality <= 50) combine_cost_factor /= 2.;
+ 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,
+ const VP8LBackwardRefs* const refs, int quality,
+ int low_effort, int histogram_bits, int cache_bits,
VP8LHistogramSet* const image_histo,
VP8LHistogram* const tmp_histo,
- uint16_t* const histogram_symbols) {
- int ok = 0;
+ uint32_t* const histogram_symbols,
+ const WebPPicture* const pic, int percent_range,
+ int* const percent) {
const int histo_xsize =
histogram_bits ? VP8LSubSampleSize(xsize, histogram_bits) : 1;
const int histo_ysize =
@@ -1188,65 +1153,55 @@ 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) goto Error;
+ 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 double 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;
}
}
}
// Find the optimal map from original histograms to the final ones.
- RemoveEmptyHistograms(image_histo);
HistogramRemap(orig_histo, image_histo, histogram_symbols);
- ok = 1;
+ if (!WebPReportProgress(pic, *percent + percent_range, percent)) {
+ goto Error;
+ }
Error:
VP8LFreeHistogramSet(orig_histo);
- WebPSafeFree(map_tmp);
- return ok;
+ return (pic->error_code == VP8_ENC_OK);
}
diff --git a/c-lib/src/enc/histogram_enc.h b/c-lib/src/enc/histogram_enc.h
@@ -14,9 +14,8 @@
#ifndef WEBP_ENC_HISTOGRAM_ENC_H_
#define WEBP_ENC_HISTOGRAM_ENC_H_
-#include <string.h>
-
#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.
- double bit_cost_; // cached value of bit cost.
- double literal_cost_; // Cached values of dominant entropy costs:
- double red_cost_; // literal, red & blue.
- double blue_cost_;
- uint8_t is_used_[5]; // 5 for literal, red, blue, alpha, distance
+ 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,32 +97,28 @@ 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);
}
-// Builds the histogram image.
+// Builds the histogram image. pic and percent are for progress.
+// Returns false in case of error (stored in pic->error_code).
int VP8LGetHistoImageSymbols(int xsize, int ysize,
- const VP8LBackwardRefs* const refs,
- int quality, int low_effort,
- int histogram_bits, int cache_bits,
+ 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);
// Returns the entropy for the symbols in the input array.
-double 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.
-double VP8LHistogramEstimateBits(VP8LHistogram* const p);
+uint64_t VP8LHistogramEstimateBits(const VP8LHistogram* const h);
#ifdef __cplusplus
}
diff --git a/c-lib/src/enc/iterator_enc.c b/c-lib/src/enc/iterator_enc.c
@@ -13,87 +13,92 @@
#include <string.h>
+#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/c-lib/src/enc/near_lossless_enc.c b/c-lib/src/enc/near_lossless_enc.c
@@ -16,10 +16,13 @@
#include <assert.h>
#include <stdlib.h>
+#include <string.h>
#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/c-lib/src/enc/picture_csp_enc.c b/c-lib/src/enc/picture_csp_enc.c
@@ -12,15 +12,25 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
-#include <stdlib.h>
#include <math.h>
+#include <stdlib.h>
+#include <string.h>
-#include "src/enc/vp8i_enc.h"
-#include "src/utils/random_utils.h"
-#include "src/utils/utils.h"
+#include "sharpyuv/sharpyuv.h"
+#include "sharpyuv/sharpyuv_csp.h"
+#include "src/dsp/cpu.h"
#include "src/dsp/dsp.h"
#include "src/dsp/lossless.h"
#include "src/dsp/yuv.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 <pthread.h>
+#endif
// Uncomment to disable gamma-compression during RGB->U/V averaging
#define USE_GAMMA_COMPRESSION
@@ -62,10 +72,12 @@ static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
int WebPPictureHasTransparency(const WebPPicture* picture) {
if (picture == NULL) return 0;
if (picture->use_argb) {
- const int alpha_offset = ALPHA_OFFSET;
- return CheckNonOpaque((const uint8_t*)picture->argb + alpha_offset,
- picture->width, picture->height,
- 4, picture->argb_stride * sizeof(*picture->argb));
+ if (picture->argb != NULL) {
+ return CheckNonOpaque((const uint8_t*)picture->argb + ALPHA_OFFSET,
+ picture->width, picture->height,
+ 4, picture->argb_stride * sizeof(*picture->argb));
+ }
+ return 0;
}
return CheckNonOpaque(picture->a, picture->width, picture->height,
1, picture->a_stride);
@@ -76,30 +88,31 @@ int WebPPictureHasTransparency(const WebPPicture* picture) {
#if defined(USE_GAMMA_COMPRESSION)
-// gamma-compensates loss of resolution during chroma subsampling
-#define kGamma 0.80 // for now we use a different gamma value than kGammaF
-#define kGammaFix 12 // fixed-point precision for linear values
-#define kGammaScale ((1 << kGammaFix) - 1)
-#define kGammaTabFix 7 // fixed-point fractional bits precision
-#define kGammaTabScale (1 << kGammaTabFix)
-#define kGammaTabRounder (kGammaTabScale >> 1)
-#define kGammaTabSize (1 << (kGammaFix - kGammaTabFix))
+// Gamma correction compensates loss of resolution during chroma subsampling.
+#define GAMMA_FIX 12 // fixed-point precision for linear values
+#define GAMMA_TAB_FIX 7 // fixed-point fractional bits precision
+#define GAMMA_TAB_SIZE (1 << (GAMMA_FIX - GAMMA_TAB_FIX))
+static const double kGamma = 0.80;
+static const int kGammaScale = ((1 << GAMMA_FIX) - 1);
+static const int kGammaTabScale = (1 << GAMMA_TAB_FIX);
+static const int kGammaTabRounder = (1 << GAMMA_TAB_FIX >> 1);
-static int kLinearToGammaTab[kGammaTabSize + 1];
+static int kLinearToGammaTab[GAMMA_TAB_SIZE + 1];
static uint16_t kGammaToLinearTab[256];
static volatile int kGammaTablesOk = 0;
static void InitGammaTables(void);
+extern VP8CPUInfo VP8GetCPUInfo;
WEBP_DSP_INIT_FUNC(InitGammaTables) {
if (!kGammaTablesOk) {
int v;
- const double scale = (double)(1 << kGammaTabFix) / kGammaScale;
+ const double scale = (double)(1 << GAMMA_TAB_FIX) / kGammaScale;
const double norm = 1. / 255.;
for (v = 0; v <= 255; ++v) {
kGammaToLinearTab[v] =
(uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5);
}
- for (v = 0; v <= kGammaTabSize; ++v) {
+ for (v = 0; v <= GAMMA_TAB_SIZE; ++v) {
kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5);
}
kGammaTablesOk = 1;
@@ -111,12 +124,12 @@ static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) {
}
static WEBP_INLINE int Interpolate(int v) {
- const int tab_pos = v >> (kGammaTabFix + 2); // integer part
+ const int tab_pos = v >> (GAMMA_TAB_FIX + 2); // integer part
const int x = v & ((kGammaTabScale << 2) - 1); // fractional part
const int v0 = kLinearToGammaTab[tab_pos];
const int v1 = kLinearToGammaTab[tab_pos + 1];
const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x); // interpolate
- assert(tab_pos + 1 < kGammaTabSize + 1);
+ assert(tab_pos + 1 < GAMMA_TAB_SIZE + 1);
return y;
}
@@ -124,7 +137,7 @@ static WEBP_INLINE int Interpolate(int v) {
// U/V value, suitable for RGBToU/V calls.
static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
const int y = Interpolate(base_value << shift); // final uplifted value
- return (y + kGammaTabRounder) >> kGammaTabFix; // descale
+ return (y + kGammaTabRounder) >> GAMMA_TAB_FIX; // descale
}
#else
@@ -158,415 +171,26 @@ static int RGBToV(int r, int g, int b, VP8Random* const rg) {
//------------------------------------------------------------------------------
// Sharp RGB->YUV conversion
-static const int kNumIterations = 4;
static const int kMinDimensionIterativeConversion = 4;
-// We could use SFIX=0 and only uint8_t for fixed_y_t, but it produces some
-// banding sometimes. Better use extra precision.
-#define SFIX 2 // fixed-point precision of RGB and Y/W
-typedef int16_t fixed_t; // signed type with extra SFIX precision for UV
-typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W
-
-#define SHALF (1 << SFIX >> 1)
-#define MAX_Y_T ((256 << SFIX) - 1)
-#define SROUNDER (1 << (YUV_FIX + SFIX - 1))
-
-#if defined(USE_GAMMA_COMPRESSION)
-
-// We use tables of different size and precision for the Rec709 / BT2020
-// transfer function.
-#define kGammaF (1./0.45)
-static uint32_t kLinearToGammaTabS[kGammaTabSize + 2];
-#define GAMMA_TO_LINEAR_BITS 14
-static uint32_t kGammaToLinearTabS[MAX_Y_T + 1]; // size scales with Y_FIX
-static volatile int kGammaTablesSOk = 0;
-static void InitGammaTablesS(void);
-
-WEBP_DSP_INIT_FUNC(InitGammaTablesS) {
- assert(2 * GAMMA_TO_LINEAR_BITS < 32); // we use uint32_t intermediate values
- if (!kGammaTablesSOk) {
- int v;
- const double norm = 1. / MAX_Y_T;
- const double scale = 1. / kGammaTabSize;
- const double a = 0.09929682680944;
- const double thresh = 0.018053968510807;
- const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
- for (v = 0; v <= MAX_Y_T; ++v) {
- const double g = norm * v;
- double value;
- if (g <= thresh * 4.5) {
- value = g / 4.5;
- } else {
- const double a_rec = 1. / (1. + a);
- value = pow(a_rec * (g + a), kGammaF);
- }
- kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
- }
- for (v = 0; v <= kGammaTabSize; ++v) {
- const double g = scale * v;
- double value;
- if (g <= thresh) {
- value = 4.5 * g;
- } else {
- value = (1. + a) * pow(g, 1. / kGammaF) - a;
- }
- // we already incorporate the 1/2 rounding constant here
- kLinearToGammaTabS[v] =
- (uint32_t)(MAX_Y_T * value) + (1 << GAMMA_TO_LINEAR_BITS >> 1);
- }
- // to prevent small rounding errors to cause read-overflow:
- kLinearToGammaTabS[kGammaTabSize + 1] = kLinearToGammaTabS[kGammaTabSize];
- kGammaTablesSOk = 1;
- }
-}
-
-// return value has a fixed-point precision of GAMMA_TO_LINEAR_BITS
-static WEBP_INLINE uint32_t GammaToLinearS(int v) {
- return kGammaToLinearTabS[v];
-}
-
-static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
- // 'value' is in GAMMA_TO_LINEAR_BITS fractional precision
- const uint32_t v = value * kGammaTabSize;
- const uint32_t tab_pos = v >> GAMMA_TO_LINEAR_BITS;
- // fractional part, in GAMMA_TO_LINEAR_BITS fixed-point precision
- const uint32_t x = v - (tab_pos << GAMMA_TO_LINEAR_BITS); // fractional part
- // v0 / v1 are in GAMMA_TO_LINEAR_BITS fixed-point precision (range [0..1])
- const uint32_t v0 = kLinearToGammaTabS[tab_pos + 0];
- const uint32_t v1 = kLinearToGammaTabS[tab_pos + 1];
- // Final interpolation. Note that rounding is already included.
- const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.
- const uint32_t result = v0 + (v2 >> GAMMA_TO_LINEAR_BITS);
- return result;
-}
-
-#else
-
-static void InitGammaTablesS(void) {}
-static WEBP_INLINE uint32_t GammaToLinearS(int v) {
- return (v << GAMMA_TO_LINEAR_BITS) / MAX_Y_T;
-}
-static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
- return (MAX_Y_T * value) >> GAMMA_TO_LINEAR_BITS;
-}
-
-#endif // USE_GAMMA_COMPRESSION
-
-//------------------------------------------------------------------------------
-
-static uint8_t clip_8b(fixed_t v) {
- return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
-}
-
-static fixed_y_t clip_y(int y) {
- return (!(y & ~MAX_Y_T)) ? (fixed_y_t)y : (y < 0) ? 0 : MAX_Y_T;
-}
-
-//------------------------------------------------------------------------------
-
-static int RGBToGray(int r, int g, int b) {
- const int luma = 13933 * r + 46871 * g + 4732 * b + YUV_HALF;
- return (luma >> YUV_FIX);
-}
-
-static uint32_t ScaleDown(int a, int b, int c, int d) {
- const uint32_t A = GammaToLinearS(a);
- const uint32_t B = GammaToLinearS(b);
- const uint32_t C = GammaToLinearS(c);
- const uint32_t D = GammaToLinearS(d);
- return LinearToGammaS((A + B + C + D + 2) >> 2);
-}
-
-static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w) {
- int i;
- for (i = 0; i < w; ++i) {
- const uint32_t R = GammaToLinearS(src[0 * w + i]);
- const uint32_t G = GammaToLinearS(src[1 * w + i]);
- const uint32_t B = GammaToLinearS(src[2 * w + i]);
- const uint32_t Y = RGBToGray(R, G, B);
- dst[i] = (fixed_y_t)LinearToGammaS(Y);
- }
-}
-
-static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
- fixed_t* dst, int uv_w) {
- int i;
- for (i = 0; i < uv_w; ++i) {
- const int r = ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1],
- src2[0 * uv_w + 0], src2[0 * uv_w + 1]);
- const int g = ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1],
- src2[2 * uv_w + 0], src2[2 * uv_w + 1]);
- const int b = ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1],
- src2[4 * uv_w + 0], src2[4 * uv_w + 1]);
- const int W = RGBToGray(r, g, b);
- dst[0 * uv_w] = (fixed_t)(r - W);
- dst[1 * uv_w] = (fixed_t)(g - W);
- dst[2 * uv_w] = (fixed_t)(b - W);
- dst += 1;
- src1 += 2;
- src2 += 2;
- }
-}
-
-static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
- int i;
- for (i = 0; i < w; ++i) {
- y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
- }
-}
-
-//------------------------------------------------------------------------------
-
-static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0) {
- const int v0 = (A * 3 + B + 2) >> 2;
- return clip_y(v0 + W0);
-}
-
-//------------------------------------------------------------------------------
-
-static WEBP_INLINE fixed_y_t UpLift(uint8_t a) { // 8bit -> SFIX
- return ((fixed_y_t)a << SFIX) | SHALF;
-}
-
-static void ImportOneRow(const uint8_t* const r_ptr,
- const uint8_t* const g_ptr,
- const uint8_t* const b_ptr,
- int step,
- int pic_width,
- fixed_y_t* const dst) {
- int i;
- const int w = (pic_width + 1) & ~1;
- for (i = 0; i < pic_width; ++i) {
- const int off = i * step;
- dst[i + 0 * w] = UpLift(r_ptr[off]);
- dst[i + 1 * w] = UpLift(g_ptr[off]);
- dst[i + 2 * w] = UpLift(b_ptr[off]);
- }
- if (pic_width & 1) { // replicate rightmost pixel
- dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
- dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
- dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];
- }
-}
-
-static void InterpolateTwoRows(const fixed_y_t* const best_y,
- const fixed_t* prev_uv,
- const fixed_t* cur_uv,
- const fixed_t* next_uv,
- int w,
- fixed_y_t* out1,
- fixed_y_t* out2) {
- const int uv_w = w >> 1;
- const int len = (w - 1) >> 1; // length to filter
- int k = 3;
- while (k-- > 0) { // process each R/G/B segments in turn
- // special boundary case for i==0
- out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0]);
- out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w]);
-
- WebPSharpYUVFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1);
- WebPSharpYUVFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1);
-
- // special boundary case for i == w - 1 when w is even
- if (!(w & 1)) {
- out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
- best_y[w - 1 + 0]);
- out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
- best_y[w - 1 + w]);
- }
- out1 += w;
- out2 += w;
- prev_uv += uv_w;
- cur_uv += uv_w;
- next_uv += uv_w;
- }
-}
-
-static WEBP_INLINE uint8_t ConvertRGBToY(int r, int g, int b) {
- const int luma = 16839 * r + 33059 * g + 6420 * b + SROUNDER;
- return clip_8b(16 + (luma >> (YUV_FIX + SFIX)));
-}
-
-static WEBP_INLINE uint8_t ConvertRGBToU(int r, int g, int b) {
- const int u = -9719 * r - 19081 * g + 28800 * b + SROUNDER;
- return clip_8b(128 + (u >> (YUV_FIX + SFIX)));
-}
-
-static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) {
- const int v = +28800 * r - 24116 * g - 4684 * b + SROUNDER;
- return clip_8b(128 + (v >> (YUV_FIX + SFIX)));
-}
-
-static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
- WebPPicture* const picture) {
- int i, j;
- uint8_t* dst_y = picture->y;
- uint8_t* dst_u = picture->u;
- uint8_t* dst_v = picture->v;
- const fixed_t* const best_uv_base = best_uv;
- const int w = (picture->width + 1) & ~1;
- const int h = (picture->height + 1) & ~1;
- const int uv_w = w >> 1;
- const int uv_h = h >> 1;
- for (best_uv = best_uv_base, j = 0; j < picture->height; ++j) {
- for (i = 0; i < picture->width; ++i) {
- const int off = (i >> 1);
- const int W = best_y[i];
- const int r = best_uv[off + 0 * uv_w] + W;
- const int g = best_uv[off + 1 * uv_w] + W;
- const int b = best_uv[off + 2 * uv_w] + W;
- dst_y[i] = ConvertRGBToY(r, g, b);
- }
- best_y += w;
- best_uv += (j & 1) * 3 * uv_w;
- dst_y += picture->y_stride;
- }
- for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
- for (i = 0; i < uv_w; ++i) {
- const int off = i;
- const int r = best_uv[off + 0 * uv_w];
- const int g = best_uv[off + 1 * uv_w];
- const int b = best_uv[off + 2 * uv_w];
- dst_u[i] = ConvertRGBToU(r, g, b);
- dst_v[i] = ConvertRGBToV(r, g, b);
- }
- best_uv += 3 * uv_w;
- dst_u += picture->uv_stride;
- dst_v += picture->uv_stride;
- }
- return 1;
-}
-
//------------------------------------------------------------------------------
// Main function
-#define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((W) * (H), sizeof(T)))
-
static int PreprocessARGB(const uint8_t* r_ptr,
const uint8_t* g_ptr,
const uint8_t* b_ptr,
int step, int rgb_stride,
WebPPicture* const picture) {
- // we expand the right/bottom border if needed
- const int w = (picture->width + 1) & ~1;
- const int h = (picture->height + 1) & ~1;
- const int uv_w = w >> 1;
- const int uv_h = h >> 1;
- uint64_t prev_diff_y_sum = ~0;
- int j, iter;
-
- // TODO(skal): allocate one big memory chunk. But for now, it's easier
- // for valgrind debugging to have several chunks.
- fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch
- fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);
- fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);
- fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
- fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
- fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
- fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
- fixed_y_t* best_y = best_y_base;
- fixed_y_t* target_y = target_y_base;
- fixed_t* best_uv = best_uv_base;
- fixed_t* target_uv = target_uv_base;
- const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
- int ok;
-
- if (best_y_base == NULL || best_uv_base == NULL ||
- target_y_base == NULL || target_uv_base == NULL ||
- best_rgb_y == NULL || best_rgb_uv == NULL ||
- tmp_buffer == NULL) {
- ok = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
- goto End;
- }
- assert(picture->width >= kMinDimensionIterativeConversion);
- assert(picture->height >= kMinDimensionIterativeConversion);
-
- WebPInitConvertARGBToYUV();
-
- // Import RGB samples to W/RGB representation.
- for (j = 0; j < picture->height; j += 2) {
- const int is_last_row = (j == picture->height - 1);
- fixed_y_t* const src1 = tmp_buffer + 0 * w;
- fixed_y_t* const src2 = tmp_buffer + 3 * w;
-
- // prepare two rows of input
- ImportOneRow(r_ptr, g_ptr, b_ptr, step, picture->width, src1);
- if (!is_last_row) {
- ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
- step, picture->width, src2);
- } else {
- memcpy(src2, src1, 3 * w * sizeof(*src2));
- }
- StoreGray(src1, best_y + 0, w);
- StoreGray(src2, best_y + w, w);
-
- UpdateW(src1, target_y, w);
- UpdateW(src2, target_y + w, w);
- UpdateChroma(src1, src2, target_uv, uv_w);
- memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
- best_y += 2 * w;
- best_uv += 3 * uv_w;
- target_y += 2 * w;
- target_uv += 3 * uv_w;
- r_ptr += 2 * rgb_stride;
- g_ptr += 2 * rgb_stride;
- b_ptr += 2 * rgb_stride;
+ const int ok = SharpYuvConvert(
+ r_ptr, g_ptr, b_ptr, step, rgb_stride, /*rgb_bit_depth=*/8,
+ picture->y, picture->y_stride, picture->u, picture->uv_stride, picture->v,
+ picture->uv_stride, /*yuv_bit_depth=*/8, picture->width,
+ picture->height, SharpYuvGetConversionMatrix(kSharpYuvMatrixWebp));
+ if (!ok) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
-
- // Iterate and resolve clipping conflicts.
- for (iter = 0; iter < kNumIterations; ++iter) {
- const fixed_t* cur_uv = best_uv_base;
- const fixed_t* prev_uv = best_uv_base;
- uint64_t diff_y_sum = 0;
-
- best_y = best_y_base;
- best_uv = best_uv_base;
- target_y = target_y_base;
- target_uv = target_uv_base;
- for (j = 0; j < h; j += 2) {
- fixed_y_t* const src1 = tmp_buffer + 0 * w;
- fixed_y_t* const src2 = tmp_buffer + 3 * w;
- {
- const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
- InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w, src1, src2);
- prev_uv = cur_uv;
- cur_uv = next_uv;
- }
-
- UpdateW(src1, best_rgb_y + 0 * w, w);
- UpdateW(src2, best_rgb_y + 1 * w, w);
- UpdateChroma(src1, src2, best_rgb_uv, uv_w);
-
- // update two rows of Y and one row of RGB
- diff_y_sum += WebPSharpYUVUpdateY(target_y, best_rgb_y, best_y, 2 * w);
- WebPSharpYUVUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
-
- best_y += 2 * w;
- best_uv += 3 * uv_w;
- target_y += 2 * w;
- target_uv += 3 * uv_w;
- }
- // test exit condition
- if (iter > 0) {
- if (diff_y_sum < diff_y_threshold) break;
- if (diff_y_sum > prev_diff_y_sum) break;
- }
- prev_diff_y_sum = diff_y_sum;
- }
- // final reconstruction
- ok = ConvertWRGBToYUV(best_y_base, best_uv_base, picture);
-
- End:
- WebPSafeFree(best_y_base);
- WebPSafeFree(best_uv_base);
- WebPSafeFree(target_y_base);
- WebPSafeFree(target_uv_base);
- WebPSafeFree(best_rgb_y);
- WebPSafeFree(best_rgb_uv);
- WebPSafeFree(tmp_buffer);
return ok;
}
-#undef SAFE_ALLOC
//------------------------------------------------------------------------------
// "Fast" regular RGB->YUV
@@ -591,8 +215,8 @@ static const int kAlphaFix = 19;
// and constant are adjusted very tightly to fit 32b arithmetic.
// In particular, they use the fact that the operands for 'v / a' are actually
// derived as v = (a0.p0 + a1.p1 + a2.p2 + a3.p3) and a = a0 + a1 + a2 + a3
-// with ai in [0..255] and pi in [0..1<<kGammaFix). The constraint to avoid
-// overflow is: kGammaFix + kAlphaFix <= 31.
+// with ai in [0..255] and pi in [0..1<<GAMMA_FIX). The constraint to avoid
+// overflow is: GAMMA_FIX + kAlphaFix <= 31.
static const uint32_t kInvAlpha[4 * 0xff + 1] = {
0, /* alpha = 0 */
524288, 262144, 174762, 131072, 104857, 87381, 74898, 65536,
@@ -818,11 +442,20 @@ static WEBP_INLINE void AccumulateRGB(const uint8_t* const r_ptr,
dst[0] = SUM4(r_ptr + j, step);
dst[1] = SUM4(g_ptr + j, step);
dst[2] = SUM4(b_ptr + j, step);
+ // MemorySanitizer may raise false positives with data that passes through
+ // RGBA32PackedToPlanar_16b_SSE41() due to incorrect modeling of shuffles.
+ // See https://crbug.com/webp/573.
+#ifdef WEBP_MSAN
+ dst[3] = 0;
+#endif
}
if (width & 1) {
dst[0] = SUM2(r_ptr + j);
dst[1] = SUM2(g_ptr + j);
dst[2] = SUM2(b_ptr + j);
+#ifdef WEBP_MSAN
+ dst[3] = 0;
+#endif
}
}
@@ -839,6 +472,8 @@ static WEBP_INLINE void ConvertRowsToUV(const uint16_t* rgb,
}
}
+extern void SharpYuvInit(VP8CPUInfo cpu_info_func);
+
static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
const uint8_t* g_ptr,
const uint8_t* b_ptr,
@@ -863,18 +498,18 @@ static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
use_iterative_conversion = 0;
}
- if (!WebPPictureAllocYUVA(picture, width, height)) {
+ if (!WebPPictureAllocYUVA(picture)) {
return 0;
}
if (has_alpha) {
assert(step == 4);
#if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE)
- assert(kAlphaFix + kGammaFix <= 31);
+ assert(kAlphaFix + GAMMA_FIX <= 31);
#endif
}
if (use_iterative_conversion) {
- InitGammaTablesS();
+ SharpYuvInit(VP8GetCPUInfo);
if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) {
return 0;
}
@@ -903,7 +538,9 @@ static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
WebPInitConvertARGBToYUV();
InitGammaTables();
- if (tmp_rgb == NULL) return 0; // malloc error
+ if (tmp_rgb == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
// Downsample Y/U/V planes, two rows at a time
for (y = 0; y < (height >> 1); ++y) {
@@ -1044,7 +681,7 @@ int WebPPictureYUVAToARGB(WebPPicture* picture) {
return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
}
// Allocate a new argb buffer (discarding the previous one).
- if (!WebPPictureAllocARGB(picture, picture->width, picture->height)) return 0;
+ if (!WebPPictureAllocARGB(picture)) return 0;
picture->use_argb = 1;
// Convert
@@ -1106,6 +743,8 @@ static int Import(WebPPicture* const picture,
const int width = picture->width;
const int height = picture->height;
+ if (abs(rgb_stride) < (import_alpha ? 4 : 3) * width) return 0;
+
if (!picture->use_argb) {
const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL;
return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
@@ -1163,24 +802,24 @@ static int Import(WebPPicture* const picture,
#if !defined(WEBP_REDUCE_CSP)
int WebPPictureImportBGR(WebPPicture* picture,
- const uint8_t* rgb, int rgb_stride) {
- return (picture != NULL && rgb != NULL)
- ? Import(picture, rgb, rgb_stride, 3, 1, 0)
+ const uint8_t* bgr, int bgr_stride) {
+ return (picture != NULL && bgr != NULL)
+ ? Import(picture, bgr, bgr_stride, 3, 1, 0)
: 0;
}
int WebPPictureImportBGRA(WebPPicture* picture,
- const uint8_t* rgba, int rgba_stride) {
- return (picture != NULL && rgba != NULL)
- ? Import(picture, rgba, rgba_stride, 4, 1, 1)
+ const uint8_t* bgra, int bgra_stride) {
+ return (picture != NULL && bgra != NULL)
+ ? Import(picture, bgra, bgra_stride, 4, 1, 1)
: 0;
}
int WebPPictureImportBGRX(WebPPicture* picture,
- const uint8_t* rgba, int rgba_stride) {
- return (picture != NULL && rgba != NULL)
- ? Import(picture, rgba, rgba_stride, 4, 1, 0)
+ const uint8_t* bgrx, int bgrx_stride) {
+ return (picture != NULL && bgrx != NULL)
+ ? Import(picture, bgrx, bgrx_stride, 4, 1, 0)
: 0;
}
@@ -1201,9 +840,9 @@ int WebPPictureImportRGBA(WebPPicture* picture,
}
int WebPPictureImportRGBX(WebPPicture* picture,
- const uint8_t* rgba, int rgba_stride) {
- return (picture != NULL && rgba != NULL)
- ? Import(picture, rgba, rgba_stride, 4, 0, 0)
+ const uint8_t* rgbx, int rgbx_stride) {
+ return (picture != NULL && rgbx != NULL)
+ ? Import(picture, rgbx, rgbx_stride, 4, 0, 0)
: 0;
}
diff --git a/c-lib/src/enc/picture_enc.c b/c-lib/src/enc/picture_enc.c
@@ -12,11 +12,14 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <limits.h>
#include <stdlib.h>
+#include <string.h>
#include "src/enc/vp8i_enc.h"
-#include "src/dsp/dsp.h"
#include "src/utils/utils.h"
+#include "src/webp/encode.h"
+#include "src/webp/types.h"
//------------------------------------------------------------------------------
// WebPPicture
@@ -45,6 +48,22 @@ int WebPPictureInitInternal(WebPPicture* picture, int version) {
//------------------------------------------------------------------------------
+int WebPValidatePicture(const WebPPicture* const picture) {
+ if (picture == NULL) return 0;
+ if (picture->width <= 0 || picture->height <= 0) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
+ }
+ if (picture->width <= 0 || picture->width / 4 > INT_MAX / 4 ||
+ picture->height <= 0 || picture->height / 4 > INT_MAX / 4) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
+ }
+ if (picture->colorspace != WEBP_YUV420 &&
+ picture->colorspace != WEBP_YUV420A) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
+ }
+ return 1;
+}
+
static void WebPPictureResetBufferARGB(WebPPicture* const picture) {
picture->memory_argb_ = NULL;
picture->argb = NULL;
@@ -63,18 +82,17 @@ void WebPPictureResetBuffers(WebPPicture* const picture) {
WebPPictureResetBufferYUVA(picture);
}
-int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height) {
+int WebPPictureAllocARGB(WebPPicture* const picture) {
void* memory;
+ const int width = picture->width;
+ const int height = picture->height;
const uint64_t argb_size = (uint64_t)width * height;
- assert(picture != NULL);
+ if (!WebPValidatePicture(picture)) return 0;
WebPSafeFree(picture->memory_argb_);
WebPPictureResetBufferARGB(picture);
- if (width <= 0 || height <= 0) {
- return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
- }
// allocate a new buffer.
memory = WebPSafeMalloc(argb_size + WEBP_ALIGN_CST, sizeof(*picture->argb));
if (memory == NULL) {
@@ -86,10 +104,10 @@ int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height) {
return 1;
}
-int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height) {
- const WebPEncCSP uv_csp =
- (WebPEncCSP)((int)picture->colorspace & WEBP_CSP_UV_MASK);
+int WebPPictureAllocYUVA(WebPPicture* const picture) {
const int has_alpha = (int)picture->colorspace & WEBP_CSP_ALPHA_BIT;
+ const int width = picture->width;
+ const int height = picture->height;
const int y_stride = width;
const int uv_width = (int)(((int64_t)width + 1) >> 1);
const int uv_height = (int)(((int64_t)height + 1) >> 1);
@@ -98,15 +116,11 @@ int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height) {
uint64_t y_size, uv_size, a_size, total_size;
uint8_t* mem;
- assert(picture != NULL);
+ if (!WebPValidatePicture(picture)) return 0;
WebPSafeFree(picture->memory_);
WebPPictureResetBufferYUVA(picture);
- if (uv_csp != WEBP_YUV420) {
- return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
- }
-
// alpha
a_width = has_alpha ? width : 0;
a_stride = a_width;
@@ -152,15 +166,12 @@ int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height) {
int WebPPictureAlloc(WebPPicture* picture) {
if (picture != NULL) {
- const int width = picture->width;
- const int height = picture->height;
-
WebPPictureFree(picture); // erase previous buffer
if (!picture->use_argb) {
- return WebPPictureAllocYUVA(picture, width, height);
+ return WebPPictureAllocYUVA(picture);
} else {
- return WebPPictureAllocARGB(picture, width, height);
+ return WebPPictureAllocARGB(picture);
}
}
return 1;
@@ -218,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/c-lib/src/enc/picture_psnr_enc.c b/c-lib/src/enc/picture_psnr_enc.c
@@ -18,6 +18,7 @@
#include <math.h>
#include <stdlib.h>
+#include "src/webp/types.h"
#include "src/dsp/dsp.h"
#include "src/enc/vp8i_enc.h"
#include "src/utils/utils.h"
diff --git a/c-lib/src/enc/picture_rescale_enc.c b/c-lib/src/enc/picture_rescale_enc.c
@@ -13,14 +13,17 @@
#include "src/webp/encode.h"
-#if !defined(WEBP_REDUCE_SIZE)
-
#include <assert.h>
#include <stdlib.h>
+#include "src/webp/types.h"
+#include "src/dsp/dsp.h"
#include "src/enc/vp8i_enc.h"
+
+#if !defined(WEBP_REDUCE_SIZE)
#include "src/utils/rescaler_utils.h"
#include "src/utils/utils.h"
+#endif // !defined(WEBP_REDUCE_SIZE)
#define HALVE(x) (((x) + 1) >> 1)
@@ -56,6 +59,7 @@ static int AdjustAndCheckRectangle(const WebPPicture* const pic,
return 1;
}
+#if !defined(WEBP_REDUCE_SIZE)
int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
if (src == NULL || dst == NULL) return 0;
if (src == dst) return 1;
@@ -81,6 +85,7 @@ int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
}
return 1;
}
+#endif // !defined(WEBP_REDUCE_SIZE)
int WebPPictureIsView(const WebPPicture* picture) {
if (picture == NULL) return 0;
@@ -120,6 +125,7 @@ int WebPPictureView(const WebPPicture* src,
return 1;
}
+#if !defined(WEBP_REDUCE_SIZE)
//------------------------------------------------------------------------------
// Picture cropping
@@ -133,7 +139,9 @@ int WebPPictureCrop(WebPPicture* pic,
PictureGrabSpecs(pic, &tmp);
tmp.width = width;
tmp.height = height;
- if (!WebPPictureAlloc(&tmp)) return 0;
+ if (!WebPPictureAlloc(&tmp)) {
+ return WebPEncodingSetError(pic, tmp.error_code);
+ }
if (!pic->use_argb) {
const int y_offset = top * pic->y_stride + left;
@@ -198,77 +206,76 @@ static void AlphaMultiplyY(WebPPicture* const pic, int inverse) {
}
}
-int WebPPictureRescale(WebPPicture* pic, int width, int height) {
+int WebPPictureRescale(WebPPicture* picture, int width, int height) {
WebPPicture tmp;
int prev_width, prev_height;
rescaler_t* work;
- if (pic == NULL) return 0;
- prev_width = pic->width;
- prev_height = pic->height;
+ if (picture == NULL) return 0;
+ prev_width = picture->width;
+ prev_height = picture->height;
if (!WebPRescalerGetScaledDimensions(
prev_width, prev_height, &width, &height)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
- PictureGrabSpecs(pic, &tmp);
+ PictureGrabSpecs(picture, &tmp);
tmp.width = width;
tmp.height = height;
- if (!WebPPictureAlloc(&tmp)) return 0;
+ if (!WebPPictureAlloc(&tmp)) {
+ return WebPEncodingSetError(picture, tmp.error_code);
+ }
- if (!pic->use_argb) {
+ if (!picture->use_argb) {
work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
if (work == NULL) {
WebPPictureFree(&tmp);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// If present, we need to rescale alpha first (for AlphaMultiplyY).
- if (pic->a != NULL) {
+ if (picture->a != NULL) {
WebPInitAlphaProcessing();
- if (!RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
+ if (!RescalePlane(picture->a, prev_width, prev_height, picture->a_stride,
tmp.a, width, height, tmp.a_stride, work, 1)) {
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
}
// We take transparency into account on the luma plane only. That's not
// totally exact blending, but still is a good approximation.
- AlphaMultiplyY(pic, 0);
- if (!RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
+ AlphaMultiplyY(picture, 0);
+ if (!RescalePlane(picture->y, prev_width, prev_height, picture->y_stride,
tmp.y, width, height, tmp.y_stride, work, 1) ||
- !RescalePlane(pic->u,
- HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
- tmp.u,
- HALVE(width), HALVE(height), tmp.uv_stride, work, 1) ||
- !RescalePlane(pic->v,
- HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
- tmp.v,
- HALVE(width), HALVE(height), tmp.uv_stride, work, 1)) {
- return 0;
+ !RescalePlane(picture->u, HALVE(prev_width), HALVE(prev_height),
+ picture->uv_stride, tmp.u, HALVE(width), HALVE(height),
+ tmp.uv_stride, work, 1) ||
+ !RescalePlane(picture->v, HALVE(prev_width), HALVE(prev_height),
+ picture->uv_stride, tmp.v, HALVE(width), HALVE(height),
+ tmp.uv_stride, work, 1)) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
AlphaMultiplyY(&tmp, 1);
} else {
work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
if (work == NULL) {
WebPPictureFree(&tmp);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
// In order to correctly interpolate colors, we need to apply the alpha
// weighting first (black-matting), scale the RGB values, and remove
// the premultiplication afterward (while preserving the alpha channel).
WebPInitAlphaProcessing();
- AlphaMultiplyARGB(pic, 0);
- if (!RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
- pic->argb_stride * 4,
- (uint8_t*)tmp.argb, width, height,
- tmp.argb_stride * 4, work, 4)) {
- return 0;
+ AlphaMultiplyARGB(picture, 0);
+ if (!RescalePlane((const uint8_t*)picture->argb, prev_width, prev_height,
+ picture->argb_stride * 4, (uint8_t*)tmp.argb, width,
+ height, tmp.argb_stride * 4, work, 4)) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
}
AlphaMultiplyARGB(&tmp, 1);
}
- WebPPictureFree(pic);
+ WebPPictureFree(picture);
WebPSafeFree(work);
- *pic = tmp;
+ *picture = tmp;
return 1;
}
@@ -280,23 +287,6 @@ int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
return 0;
}
-int WebPPictureIsView(const WebPPicture* picture) {
- (void)picture;
- return 0;
-}
-
-int WebPPictureView(const WebPPicture* src,
- int left, int top, int width, int height,
- WebPPicture* dst) {
- (void)src;
- (void)left;
- (void)top;
- (void)width;
- (void)height;
- (void)dst;
- return 0;
-}
-
int WebPPictureCrop(WebPPicture* pic,
int left, int top, int width, int height) {
(void)pic;
diff --git a/c-lib/src/enc/picture_tools_enc.c b/c-lib/src/enc/picture_tools_enc.c
@@ -12,9 +12,14 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <stddef.h>
+#include <string.h>
-#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.
@@ -190,27 +195,28 @@ static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) {
return (0xff000000u | (r << 16) | (g << 8) | b);
}
-void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
+void WebPBlendAlpha(WebPPicture* picture, uint32_t background_rgb) {
const int red = (background_rgb >> 16) & 0xff;
const int green = (background_rgb >> 8) & 0xff;
const int blue = (background_rgb >> 0) & 0xff;
int x, y;
- if (pic == NULL) return;
- if (!pic->use_argb) {
- const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop
+ if (picture == NULL) return;
+ if (!picture->use_argb) {
+ // omit last pixel during u/v loop
+ const int uv_width = (picture->width >> 1);
const int Y0 = VP8RGBToY(red, green, blue, YUV_HALF);
// VP8RGBToU/V expects the u/v values summed over four pixels
const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
- const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
- uint8_t* y_ptr = pic->y;
- uint8_t* u_ptr = pic->u;
- uint8_t* v_ptr = pic->v;
- uint8_t* a_ptr = pic->a;
+ const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT;
+ uint8_t* y_ptr = picture->y;
+ uint8_t* u_ptr = picture->u;
+ uint8_t* v_ptr = picture->v;
+ uint8_t* a_ptr = picture->a;
if (!has_alpha || a_ptr == NULL) return; // nothing to do
- for (y = 0; y < pic->height; ++y) {
+ for (y = 0; y < picture->height; ++y) {
// Luma blending
- for (x = 0; x < pic->width; ++x) {
+ for (x = 0; x < picture->width; ++x) {
const uint8_t alpha = a_ptr[x];
if (alpha < 0xff) {
y_ptr[x] = BLEND(Y0, y_ptr[x], alpha);
@@ -219,7 +225,7 @@ void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
// Chroma blending every even line
if ((y & 1) == 0) {
uint8_t* const a_ptr2 =
- (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
+ (y + 1 == picture->height) ? a_ptr : a_ptr + picture->a_stride;
for (x = 0; x < uv_width; ++x) {
// Average four alpha values into a single blending weight.
// TODO(skal): might lead to visible contouring. Can we do better?
@@ -229,24 +235,24 @@ void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
u_ptr[x] = BLEND_10BIT(U0, u_ptr[x], alpha);
v_ptr[x] = BLEND_10BIT(V0, v_ptr[x], alpha);
}
- if (pic->width & 1) { // rightmost pixel
+ if (picture->width & 1) { // rightmost pixel
const uint32_t alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
u_ptr[x] = BLEND_10BIT(U0, u_ptr[x], alpha);
v_ptr[x] = BLEND_10BIT(V0, v_ptr[x], alpha);
}
} else {
- u_ptr += pic->uv_stride;
- v_ptr += pic->uv_stride;
+ u_ptr += picture->uv_stride;
+ v_ptr += picture->uv_stride;
}
- memset(a_ptr, 0xff, pic->width); // reset alpha value to opaque
- a_ptr += pic->a_stride;
- y_ptr += pic->y_stride;
+ memset(a_ptr, 0xff, picture->width); // reset alpha value to opaque
+ a_ptr += picture->a_stride;
+ y_ptr += picture->y_stride;
}
} else {
- uint32_t* argb = pic->argb;
+ uint32_t* argb = picture->argb;
const uint32_t background = MakeARGB32(red, green, blue);
- for (y = 0; y < pic->height; ++y) {
- for (x = 0; x < pic->width; ++x) {
+ for (y = 0; y < picture->height; ++y) {
+ for (x = 0; x < picture->width; ++x) {
const int alpha = (argb[x] >> 24) & 0xff;
if (alpha != 0xff) {
if (alpha > 0) {
@@ -262,7 +268,7 @@ void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
}
}
}
- argb += pic->argb_stride;
+ argb += picture->argb_stride;
}
}
}
diff --git a/c-lib/src/enc/predictor_enc.c b/c-lib/src/enc/predictor_enc.c
@@ -14,52 +14,75 @@
// Urvang Joshi (urvang@google.com)
// Vincent Rabaud (vrabaud@google.com)
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
#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,
- double 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 double exp_decay_factor = 0.6;
- double bits = 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;
- double retval = 0;
+ int64_t retval = 0;
for (i = 0; i < 4; ++i) {
- const double kExpValue = 0.94;
- retval += PredictionCostSpatial(tile[i], 1, kExpValue);
- retval += VP8LCombinedShannonEntropy(tile[i], accumulated[i]);
+ 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)];
}
//------------------------------------------------------------------------------
@@ -90,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));
@@ -290,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.
@@ -317,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
@@ -381,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;
- }
- }
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 256; j++) {
- accumulated[i][j] += best_histo[i][j];
+ 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]);
+ }
+ }
+ }
}
}
-
- 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
@@ -468,108 +521,381 @@ 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<<n)^2 increment operations (4 for the number of channels, and
+ // (1<<n)^2 for the number of pixels in the tile)
+ // - use the histogram of the tile to update the histogram of the super-tile,
+ // with a cost of HISTO_SIZE (1024)
+ // The first method is therefore faster until n==4. 'update_up_to_index'
+ // defines the maximum subsampling_index for which the residuals should be
+ // individually added to the super-tile histogram.
+ const uint32_t update_up_to_index =
+ GetMax(GetMin(4, max_bits), min_bits) - min_bits;
+ // Coordinates in the max-tile in tile units.
+ uint32_t local_tile_x = 0, local_tile_y = 0;
+ uint32_t max_tile_x = 0, max_tile_y = 0;
+ uint32_t tile_x = 0, tile_y = 0;
+
+ *best_bits = 0;
+ *best_mode = NULL;
+ if (raw_data == NULL) return;
+
+ while (tile_y < tiles_per_col) {
+ ComputeResidualsForTile(width, height, tile_x, tile_y, min_bits,
+ update_up_to_index, all_argb, argb_scratch, argb,
+ max_quantization, exact, used_subtract_green);
+
+ // Update all the super-tiles that are complete.
+ subsampling_index = 0;
+ while (1) {
+ const uint32_t super_tile_x = tile_x >> 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.
-void VP8LResidualImage(int width, int height, int bits, int low_effort,
- uint32_t* const argb, uint32_t* const argb_scratch,
- uint32_t* const image, int near_lossless_quality,
- int exact, int used_subtract_green) {
- const int tiles_per_row = VP8LSubSampleSize(width, bits);
- const int tiles_per_col = VP8LSubSampleSize(height, bits);
- int tile_y;
- int histo[4][256];
+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;
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);
- }
+ // 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);
}
//------------------------------------------------------------------------------
// 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 double kExpValue = 2.4;
- 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
@@ -578,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) {
@@ -587,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 };
@@ -638,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;
@@ -648,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) {
@@ -666,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;
@@ -714,13 +1042,16 @@ static void CopyTileWithColorTransform(int xsize, int ysize,
}
}
-void VP8LColorSpaceTransform(int width, int height, int bits, int quality,
- uint32_t* const argb, uint32_t* image) {
+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 best_bits) {
const int max_tile_size = 1 << bits;
const int tile_xsize = VP8LSubSampleSize(width, bits);
const int tile_ysize = VP8LSubSampleSize(height, bits);
- int accumulated_red_histo[256] = { 0 };
- int accumulated_blue_histo[256] = { 0 };
+ int percent_start = *percent;
+ 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);
@@ -768,5 +1099,13 @@ void VP8LColorSpaceTransform(int width, int height, int bits, int quality,
}
}
}
+ if (!WebPReportProgress(
+ pic, percent_start + percent_range * tile_y / tile_ysize,
+ percent)) {
+ return 0;
+ }
}
+ VP8LOptimizeSampling(image, width, height, bits, MAX_TRANSFORM_BITS,
+ best_bits);
+ return 1;
}
diff --git a/c-lib/src/enc/quant_enc.c b/c-lib/src/enc/quant_enc.c
@@ -14,10 +14,14 @@
#include <assert.h>
#include <math.h>
#include <stdlib.h> // for abs()
+#include <string.h>
+#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_);
-
- m->min_disto_ = 20 * m->y1_.q_[0]; // quantization-aware min disto
- m->max_edge_ = 0;
-
- m->i4_penalty_ = 1000 * q_i4 * q_i4;
+ CheckLambdaValue(&m->lambda_i4);
+ CheckLambdaValue(&m->lambda_i16);
+ CheckLambdaValue(&m->lambda_uv);
+ CheckLambdaValue(&m->lambda_mode);
+ CheckLambdaValue(&m->lambda_trellis_i4);
+ CheckLambdaValue(&m->lambda_trellis_i16);
+ CheckLambdaValue(&m->lambda_trellis_uv);
+ CheckLambdaValue(&m->tlambda);
+
+ m->min_disto = 20 * m->y1.q[0]; // quantization-aware min disto
+ m->max_edge = 0;
+
+ m->i4_penalty = 1000 * q_i4 * q_i4;
}
}
@@ -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);
}
//------------------------------------------------------------------------------
@@ -533,7 +539,8 @@ static void InitScore(VP8ModeScore* const rd) {
rd->score = MAX_COST;
}
-static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
+static void CopyScore(VP8ModeScore* WEBP_RESTRICT const dst,
+ const VP8ModeScore* WEBP_RESTRICT const src) {
dst->D = src->D;
dst->SD = src->SD;
dst->R = src->R;
@@ -542,7 +549,8 @@ static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
dst->score = src->score;
}
-static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
+static void AddScore(VP8ModeScore* WEBP_RESTRICT const dst,
+ const VP8ModeScore* WEBP_RESTRICT const src) {
dst->D += src->D;
dst->SD += src->SD;
dst->R += src->R;
@@ -588,14 +596,14 @@ static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate,
// Coefficient type.
enum { TYPE_I16_AC = 0, TYPE_I16_DC = 1, TYPE_CHROMA_A = 2, TYPE_I4_AC = 3 };
-static int TrellisQuantizeBlock(const VP8Encoder* const enc,
+static int TrellisQuantizeBlock(const VP8Encoder* WEBP_RESTRICT const enc,
int16_t in[16], int16_t out[16],
int ctx0, int coeff_type,
- const VP8Matrix* const mtx,
+ 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];
@@ -607,7 +615,7 @@ static int TrellisQuantizeBlock(const VP8Encoder* 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
@@ -639,13 +647,13 @@ static int TrellisQuantizeBlock(const VP8Encoder* 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;
@@ -753,7 +761,7 @@ static int TrellisQuantizeBlock(const VP8Encoder* 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);
@@ -767,14 +775,14 @@ static int TrellisQuantizeBlock(const VP8Encoder* const enc,
// all at once. Output is the reconstructed block in *yuv_out, and the
// quantized levels in *levels.
-static int ReconstructIntra16(VP8EncIterator* const it,
- VP8ModeScore* const rd,
- uint8_t* const yuv_out,
+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];
@@ -783,18 +791,18 @@ static int ReconstructIntra16(VP8EncIterator* 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;
}
@@ -804,7 +812,7 @@ static int ReconstructIntra16(VP8EncIterator* 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);
}
@@ -819,25 +827,25 @@ static int ReconstructIntra16(VP8EncIterator* const it,
return nz;
}
-static int ReconstructIntra4(VP8EncIterator* const it,
+static int ReconstructIntra4(VP8EncIterator* WEBP_RESTRICT const it,
int16_t levels[16],
- const uint8_t* const src,
- uint8_t* const yuv_out,
+ 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;
@@ -855,12 +863,13 @@ static int ReconstructIntra4(VP8EncIterator* const it,
// Quantize as usual, but also compute and return the quantization error.
// Error is already divided by DSHIFT.
-static int QuantizeSingle(int16_t* const v, const VP8Matrix* const mtx) {
+static int QuantizeSingle(int16_t* WEBP_RESTRICT const v,
+ const VP8Matrix* WEBP_RESTRICT const mtx) {
int V = *v;
const int sign = (V < 0);
if (sign) V = -V;
- if (V > (int)mtx->zthresh_[0]) {
- const int qV = QUANTDIV(V, mtx->iq_[0], mtx->bias_[0]) * mtx->q_[0];
+ 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;
@@ -869,9 +878,10 @@ static int QuantizeSingle(int16_t* const v, const VP8Matrix* const mtx) {
return (sign ? -V : V) >> DSCALE;
}
-static void CorrectDCValues(const VP8EncIterator* const it,
- const VP8Matrix* const mtx,
- int16_t tmp[][16], VP8ModeScore* const rd) {
+static void CorrectDCValues(const VP8EncIterator* WEBP_RESTRICT const it,
+ const VP8Matrix* WEBP_RESTRICT const mtx,
+ int16_t tmp[][16],
+ VP8ModeScore* WEBP_RESTRICT const rd) {
// | top[0] | top[1]
// --------+--------+---------
// left[0] | tmp[0] tmp[1] <-> err0 err1
@@ -881,8 +891,8 @@ static void CorrectDCValues(const VP8EncIterator* 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);
@@ -893,7 +903,7 @@ static void CorrectDCValues(const VP8EncIterator* 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;
@@ -902,12 +912,12 @@ static void CorrectDCValues(const VP8EncIterator* const it,
}
}
-static void StoreDiffusionErrors(VP8EncIterator* const it,
- const VP8ModeScore* const rd) {
+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
@@ -922,12 +932,13 @@ static void StoreDiffusionErrors(VP8EncIterator* const it,
//------------------------------------------------------------------------------
-static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
- uint8_t* const yuv_out, int mode) {
- const VP8Encoder* const enc = it->enc_;
- const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode];
- const uint8_t* const src = it->yuv_in_ + U_OFF_ENC;
- const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+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];
int nz = 0;
int n;
int16_t tmp[8][16];
@@ -935,25 +946,25 @@ static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
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;
}
}
@@ -975,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) {
@@ -991,24 +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* const it, VP8ModeScore* rd) {
+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
@@ -1040,13 +1052,13 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
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);
}
}
@@ -1054,52 +1066,53 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
//------------------------------------------------------------------------------
// return the cost array corresponding to the surrounding prediction modes.
-static const uint16_t* GetCostModeI4(VP8EncIterator* const it,
+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* const it, VP8ModeScore* const rd) {
- const VP8Encoder* const enc = it->enc_;
- const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
- const int lambda = dqm->lambda_i4_;
- const int tlambda = dqm->tlambda_;
- const uint8_t* const src0 = it->yuv_in_ + Y_OFF_ENC;
- uint8_t* const best_blocks = it->yuv_out2_ + Y_OFF_ENC;
+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;
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);
@@ -1128,25 +1141,25 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
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
@@ -1159,13 +1172,14 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
//------------------------------------------------------------------------------
-static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
+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;
@@ -1192,7 +1206,7 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
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);
@@ -1203,7 +1217,7 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
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);
}
}
@@ -1211,54 +1225,55 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
//------------------------------------------------------------------------------
// Final reconstruction and quantization.
-static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) {
- const VP8Encoder* const enc = it->enc_;
- const int is_i16 = (it->mb_->type_ == 1);
+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);
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;
}
// Refine intra16/intra4 sub-modes based on distortion only (not rate).
-static void RefineUsingDistortion(VP8EncIterator* const it,
+static void RefineUsingDistortion(VP8EncIterator* WEBP_RESTRICT const it,
int try_both_modes, int refine_uv_mode,
- VP8ModeScore* const rd) {
+ VP8ModeScore* WEBP_RESTRICT const rd) {
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) {
@@ -1270,10 +1285,10 @@ static void RefineUsingDistortion(VP8EncIterator* 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
}
}
@@ -1290,12 +1305,12 @@ static void RefineUsingDistortion(VP8EncIterator* 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) {
@@ -1304,18 +1319,18 @@ static void RefineUsingDistortion(VP8EncIterator* 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.
@@ -1324,16 +1339,16 @@ static void RefineUsingDistortion(VP8EncIterator* 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) {
@@ -1343,7 +1358,7 @@ static void RefineUsingDistortion(VP8EncIterator* 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;
@@ -1352,10 +1367,11 @@ static void RefineUsingDistortion(VP8EncIterator* const it,
//------------------------------------------------------------------------------
// Entry point
-int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
+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);
@@ -1365,14 +1381,14 @@ int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
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/c-lib/src/enc/syntax_enc.c b/c-lib/src/enc/syntax_enc.c
@@ -12,18 +12,23 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <stddef.h>
+#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);
}
@@ -258,31 +263,34 @@ static int EmitPartitionsSize(const VP8Encoder* const enc,
buf[3 * p + 1] = (part_size >> 8) & 0xff;
buf[3 * p + 2] = (part_size >> 16) & 0xff;
}
- return p ? pic->writer(buf, 3 * p, pic) : 1;
+ if (p && !pic->writer(buf, 3 * p, pic)) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
+ }
+ return 1;
}
//------------------------------------------------------------------------------
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);
@@ -290,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;
@@ -331,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;
@@ -344,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.
@@ -365,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
@@ -379,10 +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/c-lib/src/enc/token_enc.c b/c-lib/src/enc/token_enc.c
@@ -20,9 +20,13 @@
#include <stdlib.h>
#include <string.h>
+#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/c-lib/src/enc/tree_enc.c b/c-lib/src/enc/tree_enc.c
@@ -11,7 +11,12 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include <string.h>
+
+#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/c-lib/src/enc/vp8i_enc.h b/c-lib/src/enc/vp8i_enc.h
@@ -15,12 +15,15 @@
#define WEBP_ENC_VP8I_ENC_H_
#include <string.h> // 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,8 +34,8 @@ extern "C" {
// version numbers
#define ENC_MAJ_VERSION 1
-#define ENC_MIN_VERSION 2
-#define ENC_REV_VERSION 2
+#define ENC_MIN_VERSION 6
+#define ENC_REV_VERSION 0
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
@@ -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
-
- DError left_derr_; // left error diffusion (u/v)
- DError* top_derr_; // top diffusion error - NULL if disabled
-
- uint8_t* y_left_; // left luma samples (addressable from index -1 to 15).
- uint8_t* u_left_; // left u samples (addressable from index -1 to 7)
- uint8_t* v_left_; // left v samples (addressable from index -1 to 7)
-
- uint8_t* y_top_; // top luma samples at position 'x_'
- uint8_t* uv_top_; // top u/v samples at position 'x_', packed as 16 bytes
-
- // memory for storing y/u/v_left_
- uint8_t yuv_left_mem_[17 + 16 + 16 + 8 + WEBP_ALIGN_CST];
- // memory for yuv_*
- uint8_t yuv_mem_[3 * YUV_SIZE_ENC + PRED_SIZE_ENC + WEBP_ALIGN_CST];
+ 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
+
+ uint8_t* y_left; // left luma samples (addressable from index -1 to 15).
+ uint8_t* u_left; // left u samples (addressable from index -1 to 7)
+ uint8_t* v_left; // left v samples (addressable from index -1 to 7)
+
+ uint8_t* y_top; // top luma samples at position 'x'
+ uint8_t* uv_top; // top u/v samples at position 'x', packed as 16 bytes
+
+ // memory for storing y/u/v_left
+ uint8_t yuv_left_mem[17 + 16 + 16 + 8 + WEBP_ALIGN_CST];
+ // memory for yuv*
+ uint8_t yuv_mem[3 * YUV_SIZE_ENC + PRED_SIZE_ENC + WEBP_ALIGN_CST];
} VP8EncIterator;
// in iterator.c
// must be called first
void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it);
-// restart a scan
-void VP8IteratorReset(VP8EncIterator* const it);
// reset iterator position to row 'y'
void VP8IteratorSetRow(VP8EncIterator* const it, int y);
// set count down (=number of iterations to go)
@@ -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,14 +465,15 @@ 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* const it, VP8ModeScore* const rd,
+int VP8Decimate(VP8EncIterator* WEBP_RESTRICT const it,
+ VP8ModeScore* WEBP_RESTRICT const rd,
VP8RDLevel rd_opt);
// in alpha.c
@@ -490,19 +493,24 @@ int VP8FilterStrengthFromDelta(int sharpness, int delta);
// misc utils for picture_*.c:
+// Returns true if 'picture' is non-NULL and dimensions/colorspace are within
+// their valid ranges. If returning false, the 'error_code' in 'picture' is
+// updated.
+int WebPValidatePicture(const WebPPicture* const picture);
+
// Remove reference to the ARGB/YUVA buffer (doesn't free anything).
void WebPPictureResetBuffers(WebPPicture* const picture);
-// Allocates ARGB buffer of given dimension (previous one is always free'd).
-// Preserves the YUV(A) buffer. Returns false in case of error (invalid param,
-// out-of-memory).
-int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height);
+// Allocates ARGB buffer according to set width/height (previous one is
+// always free'd). Preserves the YUV(A) buffer. Returns false in case of error
+// (invalid param, out-of-memory).
+int WebPPictureAllocARGB(WebPPicture* const picture);
-// Allocates YUVA buffer of given dimension (previous one is always free'd).
-// Uses picture->csp to determine whether an alpha buffer is needed.
+// Allocates YUVA buffer according to set width/height (previous one is always
+// free'd). Uses picture->csp to determine whether an alpha buffer is needed.
// Preserves the ARGB buffer.
// Returns false in case of error (invalid param, out-of-memory).
-int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height);
+int WebPPictureAllocYUVA(WebPPicture* const picture);
// Replace samples that are fully transparent by 'color' to help compressibility
// (no guarantee, though). Assumes pic->use_argb is true.
diff --git a/c-lib/src/enc/vp8l_enc.c b/c-lib/src/enc/vp8l_enc.c
@@ -14,310 +14,29 @@
#include <assert.h>
#include <stdlib.h>
+#include <string.h>
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
#include "src/enc/backward_references_enc.h"
#include "src/enc/histogram_enc.h"
#include "src/enc/vp8i_enc.h"
#include "src/enc/vp8li_enc.h"
-#include "src/dsp/lossless.h"
-#include "src/dsp/lossless_common.h"
#include "src/utils/bit_writer_utils.h"
#include "src/utils/huffman_encode_utils.h"
+#include "src/utils/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
-
-// Palette reordering for smaller sum of deltas (and for smaller storage).
-
-static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
- const uint32_t a = WebPMemToUint32((uint8_t*)p1);
- const uint32_t b = WebPMemToUint32((uint8_t*)p2);
- assert(a != b);
- return (a < b) ? -1 : 1;
-}
-
-static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) {
- return (v <= 128) ? v : (256 - v);
-}
-
-// Computes a value that is related to the entropy created by the
-// palette entry diff.
-//
-// Note that the last & 0xff is a no-operation in the next statement, but
-// removed by most compilers and is here only for regularity of the code.
-static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) {
- const uint32_t diff = VP8LSubPixels(col1, col2);
- const int kMoreWeightForRGBThanForAlpha = 9;
- uint32_t score;
- score = PaletteComponentDistance((diff >> 0) & 0xff);
- score += PaletteComponentDistance((diff >> 8) & 0xff);
- score += PaletteComponentDistance((diff >> 16) & 0xff);
- score *= kMoreWeightForRGBThanForAlpha;
- score += PaletteComponentDistance((diff >> 24) & 0xff);
- return score;
-}
-
-static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
- const uint32_t tmp = *col1;
- *col1 = *col2;
- *col2 = tmp;
-}
-
-static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
- int num_colors) {
- int low = 0, hi = num_colors;
- if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
- while (1) {
- const int mid = (low + hi) >> 1;
- if (sorted[mid] == color) {
- return mid;
- } else if (sorted[mid] < color) {
- low = mid;
- } else {
- hi = mid;
- }
- }
- assert(0);
- return 0;
-}
-
-// The palette has been sorted by alpha. This function checks if the other
-// components of the palette have a monotonic development with regards to
-// position in the palette. If all have monotonic development, there is
-// no benefit to re-organize them greedily. A monotonic development
-// would be spotted in green-only situations (like lossy alpha) or gray-scale
-// images.
-static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette,
- int num_colors) {
- uint32_t predict = 0x000000;
- int i;
- uint8_t sign_found = 0x00;
- for (i = 0; i < num_colors; ++i) {
- const uint32_t diff = VP8LSubPixels(palette[i], predict);
- const uint8_t rd = (diff >> 16) & 0xff;
- const uint8_t gd = (diff >> 8) & 0xff;
- const uint8_t bd = (diff >> 0) & 0xff;
- if (rd != 0x00) {
- sign_found |= (rd < 0x80) ? 1 : 2;
- }
- if (gd != 0x00) {
- sign_found |= (gd < 0x80) ? 8 : 16;
- }
- if (bd != 0x00) {
- sign_found |= (bd < 0x80) ? 64 : 128;
- }
- predict = palette[i];
- }
- return (sign_found & (sign_found << 1)) != 0; // two consequent signs.
-}
-
-static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted,
- int num_colors, uint32_t* const palette) {
- uint32_t predict = 0x00000000;
- int i, k;
- memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
- if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return;
- // Find greedily always the closest color of the predicted color to minimize
- // deltas in the palette. This reduces storage needs since the
- // palette is stored with delta encoding.
- for (i = 0; i < num_colors; ++i) {
- int best_ix = i;
- uint32_t best_score = ~0U;
- for (k = i; k < num_colors; ++k) {
- const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
- if (best_score > cur_score) {
- best_score = cur_score;
- best_ix = k;
- }
- }
- SwapColor(&palette[best_ix], &palette[i]);
- predict = palette[i];
- }
-}
-
-// Sort palette in increasing order and prepare an inverse mapping array.
-static void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
- uint32_t sorted[], uint32_t idx_map[]) {
- uint32_t i;
- memcpy(sorted, palette, num_colors * sizeof(*sorted));
- qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
- for (i = 0; i < num_colors; ++i) {
- idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
- }
-}
-
-// -----------------------------------------------------------------------------
-// Modified Zeng method from "A Survey on Palette Reordering
-// Methods for Improving the Compression of Color-Indexed Images" by Armando J.
-// Pinho and Antonio J. R. Neves.
-
-// Finds the biggest cooccurrence in the matrix.
-static void CoOccurrenceFindMax(const uint32_t* const cooccurrence,
- uint32_t num_colors, uint8_t* const c1,
- uint8_t* const c2) {
- // Find the index that is most frequently located adjacent to other
- // (different) indexes.
- uint32_t best_sum = 0u;
- uint32_t i, j, best_cooccurrence;
- *c1 = 0u;
- for (i = 0; i < num_colors; ++i) {
- uint32_t sum = 0;
- for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j];
- if (sum > best_sum) {
- best_sum = sum;
- *c1 = i;
- }
- }
- // Find the index that is most frequently found adjacent to *c1.
- *c2 = 0u;
- best_cooccurrence = 0u;
- for (i = 0; i < num_colors; ++i) {
- if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) {
- best_cooccurrence = cooccurrence[*c1 * num_colors + i];
- *c2 = i;
- }
- }
- assert(*c1 != *c2);
-}
-
-// Builds the cooccurrence matrix
-static WebPEncodingError CoOccurrenceBuild(const WebPPicture* const pic,
- const uint32_t* const palette,
- uint32_t num_colors,
- uint32_t* cooccurrence) {
- uint32_t *lines, *line_top, *line_current, *line_tmp;
- int x, y;
- const uint32_t* src = pic->argb;
- uint32_t prev_pix = ~src[0];
- uint32_t prev_idx = 0u;
- uint32_t idx_map[MAX_PALETTE_SIZE] = {0};
- uint32_t palette_sorted[MAX_PALETTE_SIZE];
- lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
- if (lines == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
- line_top = &lines[0];
- line_current = &lines[pic->width];
- PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map);
- for (y = 0; y < pic->height; ++y) {
- for (x = 0; x < pic->width; ++x) {
- const uint32_t pix = src[x];
- if (pix != prev_pix) {
- prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)];
- prev_pix = pix;
- }
- line_current[x] = prev_idx;
- // 4-connectivity is what works best as mentioned in "On the relation
- // between Memon's and the modified Zeng's palette reordering methods".
- if (x > 0 && prev_idx != line_current[x - 1]) {
- const uint32_t left_idx = line_current[x - 1];
- ++cooccurrence[prev_idx * num_colors + left_idx];
- ++cooccurrence[left_idx * num_colors + prev_idx];
- }
- if (y > 0 && prev_idx != line_top[x]) {
- const uint32_t top_idx = line_top[x];
- ++cooccurrence[prev_idx * num_colors + top_idx];
- ++cooccurrence[top_idx * num_colors + prev_idx];
- }
- }
- line_tmp = line_top;
- line_top = line_current;
- line_current = line_tmp;
- src += pic->argb_stride;
- }
- WebPSafeFree(lines);
- return VP8_ENC_OK;
-}
-
-struct Sum {
- uint8_t index;
- uint32_t sum;
-};
-
-// Implements the modified Zeng method from "A Survey on Palette Reordering
-// Methods for Improving the Compression of Color-Indexed Images" by Armando J.
-// Pinho and Antonio J. R. Neves.
-static WebPEncodingError PaletteSortModifiedZeng(
- const WebPPicture* const pic, const uint32_t* const palette_sorted,
- uint32_t num_colors, uint32_t* const palette) {
- uint32_t i, j, ind;
- uint8_t remapping[MAX_PALETTE_SIZE];
- uint32_t* cooccurrence;
- struct Sum sums[MAX_PALETTE_SIZE];
- uint32_t first, last;
- uint32_t num_sums;
- // TODO(vrabaud) check whether one color images should use palette or not.
- if (num_colors <= 1) return VP8_ENC_OK;
- // Build the co-occurrence matrix.
- cooccurrence =
- (uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
- if (cooccurrence == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
- if (CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence) !=
- VP8_ENC_OK) {
- WebPSafeFree(cooccurrence);
- return VP8_ENC_ERROR_OUT_OF_MEMORY;
- }
-
- // Initialize the mapping list with the two best indices.
- CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]);
-
- // We need to append and prepend to the list of remapping. To this end, we
- // actually define the next start/end of the list as indices in a vector (with
- // a wrap around when the end is reached).
- first = 0;
- last = 1;
- num_sums = num_colors - 2; // -2 because we know the first two values
- if (num_sums > 0) {
- // Initialize the sums with the first two remappings and find the best one
- struct Sum* best_sum = &sums[0];
- best_sum->index = 0u;
- best_sum->sum = 0u;
- for (i = 0, j = 0; i < num_colors; ++i) {
- if (i == remapping[0] || i == remapping[1]) continue;
- sums[j].index = i;
- sums[j].sum = cooccurrence[i * num_colors + remapping[0]] +
- cooccurrence[i * num_colors + remapping[1]];
- if (sums[j].sum > best_sum->sum) best_sum = &sums[j];
- ++j;
- }
-
- while (num_sums > 0) {
- const uint8_t best_index = best_sum->index;
- // Compute delta to know if we need to prepend or append the best index.
- int32_t delta = 0;
- const int32_t n = num_colors - num_sums;
- for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) {
- const uint16_t l_j = remapping[(ind + j) % num_colors];
- delta += (n - 1 - 2 * (int32_t)j) *
- (int32_t)cooccurrence[best_index * num_colors + l_j];
- }
- if (delta > 0) {
- first = (first == 0) ? num_colors - 1 : first - 1;
- remapping[first] = best_index;
- } else {
- ++last;
- remapping[last] = best_index;
- }
- // Remove best_sum from sums.
- *best_sum = sums[num_sums - 1];
- --num_sums;
- // Update all the sums and find the best one.
- best_sum = &sums[0];
- for (i = 0; i < num_sums; ++i) {
- sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index];
- if (sums[i].sum > best_sum->sum) best_sum = &sums[i];
- }
- }
- }
- assert((last + 1) % num_colors == first);
- WebPSafeFree(cooccurrence);
-
- // Re-map the palette.
- for (i = 0; i < num_colors; ++i) {
- palette[i] = palette_sorted[remapping[(first + i) % num_colors]];
- }
- return VP8_ENC_OK;
-}
+#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
@@ -334,13 +53,6 @@ typedef enum {
} EntropyIx;
typedef enum {
- kSortedDefault = 0,
- kMinimizeDelta = 1,
- kModifiedZeng = 2,
- kUnusedPalette = 3,
-} PaletteSorting;
-
-typedef enum {
kHistoAlpha = 0,
kHistoAlphaPred,
kHistoGreen,
@@ -357,22 +69,35 @@ typedef enum {
kHistoTotal // Must be last.
} HistoIx;
-static void AddSingleSubGreen(int p, uint32_t* const r, uint32_t* const b) {
- const int green = p >> 8; // The upper bits are masked away later.
- ++r[((p >> 16) - green) & 0xff];
- ++b[((p >> 0) - green) & 0xff];
+
+#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,
+ 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;
@@ -384,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
@@ -394,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;
@@ -409,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;
}
{
- double entropy_comp[kHistoTotal];
- double 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] +
@@ -473,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) {
@@ -506,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;
}
@@ -525,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) {
@@ -549,32 +290,32 @@ 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
// kPaletteAndSpatial.
-#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2)
+#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2 * kPaletteSortingNum)
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.
@@ -582,34 +323,33 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
assert(pic != NULL && pic->argb != NULL);
// Check whether a palette is possible.
- enc->palette_size_ = WebPGetColorPalette(pic, enc->palette_sorted_);
- use_palette = (enc->palette_size_ <= MAX_PALETTE_SIZE);
+ enc->palette_size = GetColorPalette(pic, enc->palette_sorted);
+ use_palette = (enc->palette_size <= MAX_PALETTE_SIZE);
if (!use_palette) {
- enc->palette_size_ = 0;
- } else {
- qsort(enc->palette_sorted_, enc->palette_size_,
- sizeof(*enc->palette_sorted_), PaletteCompareColorsForQsort);
+ 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) {
@@ -621,27 +361,36 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
// a palette.
if ((i != kPalette && i != kPaletteAndSpatial) || use_palette) {
assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX);
- crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
if (use_palette && (i == kPalette || i == kPaletteAndSpatial)) {
- crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
- kMinimizeDelta;
- ++*crunch_configs_size;
- // Also add modified Zeng's method.
- crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
- crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
- kModifiedZeng;
+ int sorting_method;
+ for (sorting_method = 0; sorting_method < kPaletteSortingNum;
+ ++sorting_method) {
+ const PaletteSorting typed_sorting_method =
+ (PaletteSorting)sorting_method;
+ // TODO(vrabaud) kSortedDefault should be tested. It is omitted
+ // for now for backward compatibility.
+ if (typed_sorting_method == kUnusedPalette ||
+ typed_sorting_method == kSortedDefault) {
+ continue;
+ }
+ 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)].palette_sorting_type_ =
+ crunch_configs[(*crunch_configs_size)].entropy_idx = i;
+ crunch_configs[(*crunch_configs_size)].palette_sorting_type =
kUnusedPalette;
+ ++*crunch_configs_size;
}
- ++*crunch_configs_size;
}
}
} else {
// Only choose the guessed best transform.
*crunch_configs_size = 1;
- crunch_configs[0].entropy_idx_ = min_entropy_ix;
- crunch_configs[0].palette_sorting_type_ =
+ 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.
@@ -649,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;
}
}
}
@@ -661,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;
@@ -679,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;
}
@@ -706,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;
@@ -744,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:
@@ -949,11 +698,12 @@ static WEBP_INLINE void WriteHuffmanCodeWithExtraBits(
VP8LPutBits(bw, (bits << depth) | symbol, depth + n_bits);
}
-static WebPEncodingError StoreImageToBitMask(
- VP8LBitWriter* const bw, int width, int histo_bits,
- const VP8LBackwardRefs* const refs,
- const uint16_t* histogram_symbols,
- const HuffmanTreeCode* const huffman_codes) {
+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.
@@ -961,7 +711,7 @@ static WebPEncodingError 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)) {
@@ -969,8 +719,10 @@ static WebPEncodingError 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)) {
@@ -1006,55 +758,65 @@ static WebPEncodingError StoreImageToBitMask(
}
VP8LRefsCursorNext(&c);
}
- return bw->error_ ? VP8_ENC_ERROR_OUT_OF_MEMORY : VP8_ENC_OK;
+ if (bw->error) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+ return 1;
}
-// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
-static WebPEncodingError EncodeImageNoHuffman(
- VP8LBitWriter* const bw, const uint32_t* const argb,
- VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_array,
- int width, int height, int quality, int low_effort) {
+// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31.
+// pic and percent are for progress.
+static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
+ const uint32_t* const argb,
+ VP8LHashChain* const hash_chain,
+ VP8LBackwardRefs* const refs_array, int width,
+ int height, int quality, int low_effort,
+ const WebPPicture* const pic, int percent_range,
+ int* const percent) {
int i;
int max_tokens = 0;
- WebPEncodingError err = VP8_ENC_OK;
VP8LBackwardRefs* refs;
HuffmanTreeToken* tokens = NULL;
- HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
- const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol
+ HuffmanTreeCode huffman_codes[5] = {{0, NULL, NULL}};
+ 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(
- 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
+ 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
if (huff_tree == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
// Calculate backward references from ARGB image.
- if (!VP8LHashChainFill(hash_chain, quality, argb, width, height,
- low_effort)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ if (!VP8LHashChainFill(hash_chain, quality, argb, width, height, low_effort,
+ pic, percent_range / 2, percent)) {
+ goto Error;
+ }
+ if (!VP8LGetBackwardReferences(width, height, argb, quality, /*low_effort=*/0,
+ kLZ77Standard | kLZ77RLE, cache_bits,
+ /*do_no_cache=*/0, hash_chain, refs_array,
+ &cache_bits, pic,
+ percent_range - percent_range / 2, percent)) {
goto Error;
}
- err = VP8LGetBackwardReferences(
- width, height, argb, quality, /*low_effort=*/0, kLZ77Standard | kLZ77RLE,
- cache_bits, /*do_no_cache=*/0, hash_chain, refs_array, &cache_bits);
- if (err != VP8_ENC_OK) goto Error;
refs = &refs_array[0];
histogram_image = VP8LAllocateHistogramSet(1, cache_bits);
if (histogram_image == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
VP8LHistogramSetClear(histogram_image);
// Build histogram image and symbols from backward references.
- VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]);
+ 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);
if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
@@ -1071,7 +833,7 @@ static WebPEncodingError EncodeImageNoHuffman(
tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
if (tokens == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
@@ -1083,84 +845,103 @@ static WebPEncodingError EncodeImageNoHuffman(
}
// Store actual literals.
- err = StoreImageToBitMask(bw, width, 0, refs, histogram_symbols,
- huffman_codes);
+ if (!StoreImageToBitMask(bw, width, 0, refs, histogram_symbols, huffman_codes,
+ pic)) {
+ goto Error;
+ }
Error:
WebPSafeFree(tokens);
WebPSafeFree(huff_tree);
VP8LFreeHistogramSet(histogram_image);
WebPSafeFree(huffman_codes[0].codes);
- return err;
+ return (pic->error_code == VP8_ENC_OK);
}
-static WebPEncodingError EncodeImageInternal(
+// pic and percent are for progress.
+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, int use_cache,
- const CrunchConfig* const config, int* cache_bits, int histogram_bits,
- size_t init_byte_position, int* const hdr_size, int* const data_size) {
- WebPEncodingError err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ int height, int quality, int low_effort, const CrunchConfig* const config,
+ 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);
- // Make sure we can allocate the different objects.
memset(&hash_chain_histogram, 0, sizeof(hash_chain_histogram));
- if (huff_tree == NULL || histogram_symbols == NULL ||
- !VP8LHashChainInit(&hash_chain_histogram, histogram_image_xysize) ||
- !VP8LHashChainFill(hash_chain, quality, argb, width, height,
- low_effort)) {
+ if (!VP8LBitWriterInit(&bw_best, 0)) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
- if (use_cache) {
- // If the value is different from zero, it has been set during the
- // palette analysis.
- cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits;
- } else {
- cache_bits_init = 0;
+
+ // Make sure we can allocate the different objects.
+ if (huff_tree == NULL || histogram_argb == NULL ||
+ !VP8LHashChainInit(&hash_chain_histogram, histogram_image_xysize)) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ goto Error;
+ }
+
+ percent_range = remaining_percent / 5;
+ if (!VP8LHashChainFill(hash_chain, quality, argb, width, height,
+ low_effort, pic, percent_range, percent)) {
+ goto Error;
}
+ percent_start += percent_range;
+ remaining_percent -= percent_range;
+
+ // If the value is different from zero, it has been set during the palette
+ // analysis.
+ cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits;
// If several iterations will happen, clone into bw_best.
- if (!VP8LBitWriterInit(&bw_best, 0) ||
- ((config->sub_configs_size_ > 1 ||
- config->sub_configs_[0].do_no_cache_) &&
- !VP8LBitWriterClone(bw, &bw_best))) {
+ 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;
- err = VP8LGetBackwardReferences(width, height, argb, quality, low_effort,
- sub_config->lz77_, cache_bits_init,
- sub_config->do_no_cache_, hash_chain,
- &refs_array[0], &cache_bits_best);
- if (err != VP8_ENC_OK) goto Error;
+ 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,
+ &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;
@@ -1172,11 +953,17 @@ static WebPEncodingError EncodeImageInternal(
histogram_image =
VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits_tmp);
tmp_histo = VP8LAllocateHistogram(cache_bits_tmp);
- if (histogram_image == NULL || tmp_histo == NULL ||
- !VP8LGetHistoImageSymbols(width, height, &refs_array[i_cache],
- quality, low_effort, histogram_bits,
- cache_bits_tmp, histogram_image, tmp_histo,
- histogram_symbols)) {
+ if (histogram_image == NULL || tmp_histo == NULL) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ goto Error;
+ }
+
+ i_percent_range = i_remaining_percent / 3;
+ i_remaining_percent -= i_percent_range;
+ if (!VP8LGetHistoImageSymbols(
+ width, height, &refs_array[i_cache], quality, low_effort,
+ histogram_bits, cache_bits_tmp, histogram_image, tmp_histo,
+ histogram_argb, pic, i_percent_range, percent)) {
goto Error;
}
// Create Huffman bit lengths and codes for each histogram image.
@@ -1189,6 +976,7 @@ static WebPEncodingError EncodeImageInternal(
// GetHuffBitLengthsAndCodes().
if (huffman_codes == NULL ||
!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
// Free combined histograms.
@@ -1208,36 +996,33 @@ static WebPEncodingError 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) 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);
- err = EncodeImageNoHuffman(
- bw, histogram_argb, &hash_chain_histogram, &refs_array[2],
- VP8LSubSampleSize(width, histogram_bits),
- VP8LSubSampleSize(height, histogram_bits), quality, low_effort);
- WebPSafeFree(histogram_argb);
- if (err != VP8_ENC_OK) goto Error;
+ i_percent_range = i_remaining_percent / 2;
+ i_remaining_percent -= i_percent_range;
+ if (!EncodeImageNoHuffman(
+ bw, histogram_argb, &hash_chain_histogram, &refs_array[2],
+ VP8LSubSampleSize(width, histogram_bits),
+ VP8LSubSampleSize(height, histogram_bits), quality, low_effort,
+ pic, i_percent_range, percent)) {
+ goto Error;
+ }
}
// Store Huffman codes.
{
- int i;
int max_tokens = 0;
// Find maximum number of symbols for the huffman tree-set.
for (i = 0; i < 5 * histogram_image_size; ++i) {
@@ -1247,7 +1032,10 @@ static WebPEncodingError EncodeImageInternal(
}
}
tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
- if (tokens == NULL) goto Error;
+ if (tokens == NULL) {
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ goto Error;
+ }
for (i = 0; i < 5 * histogram_image_size; ++i) {
HuffmanTreeCode* const codes = &huffman_codes[i];
StoreHuffmanCode(bw, huff_tree, tokens, codes);
@@ -1256,9 +1044,10 @@ static WebPEncodingError EncodeImageInternal(
}
// Store actual literals.
hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position);
- err = StoreImageToBitMask(bw, width, histogram_bits, &refs_array[i_cache],
- histogram_symbols, huffman_codes);
- if (err != VP8_ENC_OK) goto Error;
+ if (!StoreImageToBitMask(bw, width, histogram_bits, &refs_array[i_cache],
+ histogram_argb, huffman_codes, pic)) {
+ goto Error;
+ }
// Keep track of the smallest image so far.
if (VP8LBitWriterNumBytes(bw) < bw_size_best) {
bw_size_best = VP8LBitWriterNumBytes(bw);
@@ -1278,7 +1067,10 @@ static WebPEncodingError EncodeImageInternal(
}
}
VP8LBitWriterSwap(bw, &bw_best);
- err = VP8_ENC_OK;
+
+ if (!WebPReportProgress(pic, percent_start + remaining_percent, percent)) {
+ goto Error;
+ }
Error:
WebPSafeFree(tokens);
@@ -1290,9 +1082,9 @@ static WebPEncodingError EncodeImageInternal(
WebPSafeFree(huffman_codes->codes);
WebPSafeFree(huffman_codes);
}
- WebPSafeFree(histogram_symbols);
+ WebPSafeFree(histogram_argb);
VP8LBitWriterWipeOut(&bw_best);
- return err;
+ return (pic->error_code == VP8_ENC_OK);
}
// -----------------------------------------------------------------------------
@@ -1301,70 +1093,76 @@ static WebPEncodingError EncodeImageInternal(
static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height,
VP8LBitWriter* const bw) {
VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
- VP8LPutBits(bw, SUBTRACT_GREEN, 2);
- VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
+ VP8LPutBits(bw, SUBTRACT_GREEN_TRANSFORM, 2);
+ VP8LSubtractGreenFromBlueAndRed(enc->argb, width * height);
}
-static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
- int width, int height,
- int quality, int low_effort,
- int used_subtract_green,
- VP8LBitWriter* const bw) {
- const int pred_bits = enc->transform_bits_;
- const int transform_width = VP8LSubSampleSize(width, pred_bits);
- const int transform_height = VP8LSubSampleSize(height, pred_bits);
- // we disable near-lossless quantization if palette is used.
- const int near_lossless_strength = enc->use_palette_ ? 100
- : enc->config_->near_lossless;
-
- VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_,
- enc->argb_scratch_, enc->transform_data_,
- near_lossless_strength, enc->config_->exact,
- used_subtract_green);
+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,
+ int* const best_bits) {
+ const int near_lossless_strength =
+ 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, 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);
+ 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 WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
- int width, int height,
- int quality, int low_effort,
- VP8LBitWriter* const bw) {
- const int ccolor_transform_bits = enc->transform_bits_;
- const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
- const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits);
+static int ApplyCrossColorFilter(VP8LEncoder* const enc, int width, int height,
+ int quality, int low_effort,
+ VP8LBitWriter* const bw, int percent_range,
+ int* const percent, int* const best_bits) {
+ const int min_bits = enc->cross_color_transform_bits;
- VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality,
- enc->argb_, enc->transform_data_);
+ 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);
+ 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 WebPEncodingError WriteRiffHeader(const WebPPicture* const pic,
- size_t riff_size, size_t vp8l_size) {
+static int WriteRiffHeader(const WebPPicture* const pic, size_t riff_size,
+ size_t vp8l_size) {
uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = {
'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE,
};
PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
- if (!pic->writer(riff, sizeof(riff), pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
- return VP8_ENC_OK;
+ return pic->writer(riff, sizeof(riff), pic);
}
static int WriteImageSize(const WebPPicture* const pic,
@@ -1375,118 +1173,107 @@ 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 WebPEncodingError WriteImage(const WebPPicture* const pic,
- VP8LBitWriter* const bw,
- size_t* const coded_size) {
- WebPEncodingError err = VP8_ENC_OK;
+static int WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw,
+ size_t* const coded_size) {
const uint8_t* const webpll_data = VP8LBitWriterFinish(bw);
const size_t webpll_size = VP8LBitWriterNumBytes(bw);
const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
const size_t pad = vp8l_size & 1;
const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
+ *coded_size = 0;
- err = WriteRiffHeader(pic, riff_size, vp8l_size);
- if (err != VP8_ENC_OK) goto Error;
+ if (bw->error) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
- if (!pic->writer(webpll_data, webpll_size, pic)) {
- err = VP8_ENC_ERROR_BAD_WRITE;
- goto Error;
+ if (!WriteRiffHeader(pic, riff_size, vp8l_size) ||
+ !pic->writer(webpll_data, webpll_size, pic)) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
}
if (pad) {
const uint8_t pad_byte[1] = { 0 };
if (!pic->writer(pad_byte, 1, pic)) {
- err = VP8_ENC_ERROR_BAD_WRITE;
- goto Error;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
}
}
*coded_size = CHUNK_HEADER_SIZE + riff_size;
- return VP8_ENC_OK;
-
- Error:
- return err;
+ return 1;
}
// -----------------------------------------------------------------------------
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_
-static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
- int width, int height) {
- WebPEncodingError err = VP8_ENC_OK;
- const uint64_t image_size = width * height;
+// 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;
// VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra
// pixel in each, plus 2 regular scanlines of bytes.
// TODO(skal): Clean up by using arithmetic in bytes instead of words.
const uint64_t argb_scratch_size =
- enc->use_predict_
- ? (width + 1) * 2 +
- (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t)
- : 0;
+ enc->use_predict ? (width + 1) * 2 + (width * 2 + sizeof(uint32_t) - 1) /
+ sizeof(uint32_t)
+ : 0;
const uint64_t transform_data_size =
- (enc->use_predict_ || enc->use_cross_color_)
- ? VP8LSubSampleSize(width, enc->transform_bits_) *
- VP8LSubSampleSize(height, enc->transform_bits_)
+ (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_) {
+ const uint64_t mem_size = image_size + max_alignment_in_words +
+ argb_scratch_size + max_alignment_in_words +
+ transform_data_size;
+ uint32_t* mem = enc->transform_mem;
+ if (mem == NULL || mem_size > enc->transform_mem_size) {
ClearTransformBuffer(enc);
mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem));
if (mem == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
+ 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;
- Error:
- return err;
+ enc->current_width = width;
+ return 1;
}
-static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) {
- WebPEncodingError err = VP8_ENC_OK;
- const WebPPicture* const picture = enc->pic_;
+static int MakeInputImageCopy(VP8LEncoder* const enc) {
+ const WebPPicture* const picture = enc->pic;
const int width = picture->width;
const int height = picture->height;
- err = AllocateTransformBuffer(enc, width, height);
- if (err != VP8_ENC_OK) return err;
- if (enc->argb_content_ == kEncoderARGB) return VP8_ENC_OK;
+ if (!AllocateTransformBuffer(enc, width, height)) return 0;
+ 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) {
@@ -1495,9 +1282,9 @@ static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) {
src += picture->argb_stride;
}
}
- enc->argb_content_ = kEncoderARGB;
- assert(enc->current_width_ == width);
- return VP8_ENC_OK;
+ enc->argb_content = kEncoderARGB;
+ assert(enc->current_width == width);
+ return 1;
}
// -----------------------------------------------------------------------------
@@ -1559,16 +1346,18 @@ static WEBP_INLINE uint32_t ApplyPaletteHash2(uint32_t color) {
// using 'row' as a temporary buffer of size 'width'.
// We assume that all src[] values have a corresponding entry in the palette.
// Note: src[] can be the same as dst[]
-static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride,
- uint32_t* dst, uint32_t dst_stride,
- const uint32_t* palette, int palette_size,
- int width, int height, int xbits) {
+static int ApplyPalette(const uint32_t* src, uint32_t src_stride, uint32_t* dst,
+ uint32_t dst_stride, const uint32_t* palette,
+ int palette_size, int width, int height, int xbits,
+ const WebPPicture* const pic) {
// TODO(skal): this tmp buffer is not needed if VP8LBundleColorMap() can be
// made to work in-place.
uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row));
int x, y;
- if (tmp_row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+ if (tmp_row == NULL) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
if (palette_size < APPLY_PALETTE_GREEDY_MAX) {
APPLY_PALETTE_FOR(SearchColorGreedy(palette, palette_size, pix));
@@ -1613,24 +1402,20 @@ static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride,
}
}
WebPSafeFree(tmp_row);
- return VP8_ENC_OK;
+ return 1;
}
#undef APPLY_PALETTE_FOR
#undef PALETTE_INV_SIZE_BITS
#undef PALETTE_INV_SIZE
#undef APPLY_PALETTE_GREEDY_MAX
-// Note: Expects "enc->palette_" to be set properly.
-static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc,
- int in_place) {
- WebPEncodingError err = VP8_ENC_OK;
- const WebPPicture* const pic = enc->pic_;
+// 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.
@@ -1641,34 +1426,44 @@ static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc,
xbits = (palette_size <= 16) ? 1 : 0;
}
- err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height);
- if (err != VP8_ENC_OK) return err;
-
- err = ApplyPalette(src, src_stride,
- enc->argb_, enc->current_width_,
- palette, palette_size, width, height, xbits);
- enc->argb_content_ = kEncoderPalette;
- return err;
+ if (!AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height)) {
+ return 0;
+ }
+ 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;
+ return 1;
}
-// Save palette_[] to bitstream.
-static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort,
- VP8LEncoder* const enc) {
+// 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);
+ return EncodeImageNoHuffman(
+ bw, tmp_palette, &enc->hash_chain, &enc->refs[0], encoded_palette_size,
+ 1, /*quality=*/20, low_effort, enc->pic, percent_range, percent);
}
// -----------------------------------------------------------------------------
@@ -1681,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();
@@ -1693,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);
}
@@ -1704,32 +1499,28 @@ static void VP8LEncoderDelete(VP8LEncoder* enc) {
// Main call
typedef struct {
- const WebPConfig* config_;
- const WebPPicture* picture_;
- VP8LBitWriter* bw_;
- VP8LEncoder* enc_;
- int use_cache_;
- CrunchConfig crunch_configs_[CRUNCH_CONFIGS_MAX];
- int num_crunch_configs_;
- int red_and_blue_always_zero_;
- WebPEncodingError err_;
- WebPAuxStats* stats_;
+ 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 int use_cache = params->use_cache_;
- const CrunchConfig* const crunch_configs = params->crunch_configs_;
- const int num_crunch_configs = params->num_crunch_configs_;
- const int red_and_blue_always_zero = params->red_and_blue_always_zero_;
+ 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
- WebPEncodingError err = VP8_ENC_OK;
const int quality = (int)config->quality;
const int low_effort = (config->method == 0);
#if (WEBP_NEAR_LOSSLESS == 1)
@@ -1737,12 +1528,12 @@ static int EncodeStreamHook(void* input, void* data2) {
#endif
const int height = picture->height;
const size_t byte_position = VP8LBitWriterNumBytes(bw);
+ int percent = 2; // for WebPProgressHook
#if (WEBP_NEAR_LOSSLESS == 1)
int use_near_lossless = 0;
#endif
int hdr_size = 0;
int data_size = 0;
- int use_delta_palette = 0;
int idx;
size_t best_size = ~(size_t)0;
VP8LBitWriter bw_init = *bw, bw_best;
@@ -1750,113 +1541,116 @@ static int EncodeStreamHook(void* input, void* data2) {
if (!VP8LBitWriterInit(&bw_best, 0) ||
(num_crunch_configs > 1 && !VP8LBitWriterClone(bw, &bw_best))) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
for (idx = 0; idx < num_crunch_configs; ++idx) {
- const int entropy_idx = crunch_configs[idx].entropy_idx_;
- enc->use_palette_ =
+ const int entropy_idx = crunch_configs[idx].entropy_idx;
+ int remaining_percent = 97 / num_crunch_configs, percent_range;
+ 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) {
- err = AllocateTransformBuffer(enc, width, height);
- if (err != VP8_ENC_OK) goto Error;
- if ((enc->argb_content_ != kEncoderNearLossless) &&
- !VP8ApplyNearLossless(picture, config->near_lossless, enc->argb_)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ if (!AllocateTransformBuffer(enc, width, height)) goto Error;
+ 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 (crunch_configs[idx].palette_sorting_type_ == kSortedDefault) {
- // Nothing to do, we have already sorted the palette.
- memcpy(enc->palette_, enc->palette_sorted_,
- enc->palette_size_ * sizeof(*enc->palette_));
- } else if (crunch_configs[idx].palette_sorting_type_ == kMinimizeDelta) {
- PaletteSortMinimizeDeltas(enc->palette_sorted_, enc->palette_size_,
- enc->palette_);
- } else {
- assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng);
- err = PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
- enc->palette_size_, enc->palette_);
- if (err != VP8_ENC_OK) goto Error;
+ 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;
+ if (!EncodePalette(bw, low_effort, enc, percent_range, &percent)) {
+ goto Error;
}
- err = EncodePalette(bw, low_effort, enc);
- if (err != VP8_ENC_OK) goto Error;
- err = MapImageFromPalette(enc, use_delta_palette);
- if (err != VP8_ENC_OK) goto Error;
+ remaining_percent -= percent_range;
+ if (!MapImageFromPalette(enc)) goto Error;
// If using a color cache, do not have it bigger than the number of
// colors.
- if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) {
- enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1;
+ if (enc->palette_size < (1 << MAX_COLOR_CACHE_BITS)) {
+ enc->cache_bits = BitsLog2Floor(enc->palette_size) + 1;
}
}
- if (!use_delta_palette) {
- // In case image is not packed.
- if (enc->argb_content_ != kEncoderNearLossless &&
- enc->argb_content_ != kEncoderPalette) {
- err = MakeInputImageCopy(enc);
- if (err != VP8_ENC_OK) goto Error;
- }
+ // 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_) {
- err = ApplyPredictFilter(enc, enc->current_width_, height, quality,
- low_effort, enc->use_subtract_green_, bw);
- if (err != VP8_ENC_OK) goto Error;
+ if (enc->use_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_) {
- err = ApplyCrossColorFilter(enc, enc->current_width_, height, quality,
- low_effort, bw);
- if (err != VP8_ENC_OK) goto Error;
+ 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.
// -------------------------------------------------------------------------
// Encode and write the transformed image.
- err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
- enc->current_width_, height, quality, low_effort,
- use_cache, &crunch_configs[idx],
- &enc->cache_bits_, enc->histo_bits_,
- byte_position, &hdr_size, &data_size);
- if (err != VP8_ENC_OK) goto Error;
+ if (!EncodeImageInternal(
+ 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,
+ &data_size, picture, remaining_percent, &percent)) {
+ goto Error;
+ }
// If we are better than what we already have.
if (VP8LBitWriterNumBytes(bw) < best_size) {
@@ -1867,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;
@@ -1886,18 +1681,15 @@ static int EncodeStreamHook(void* input, void* data2) {
}
VP8LBitWriterSwap(&bw_best, bw);
-Error:
+ Error:
VP8LBitWriterWipeOut(&bw_best);
- params->err_ = err;
// The hook should return false in case of error.
- return (err == VP8_ENC_OK);
+ return (params->picture->error_code == VP8_ENC_OK);
}
-WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
- const WebPPicture* const picture,
- VP8LBitWriter* const bw_main,
- int use_cache) {
- WebPEncodingError err = VP8_ENC_OK;
+int VP8LEncodeStream(const WebPConfig* const config,
+ const WebPPicture* const picture,
+ VP8LBitWriter* const bw_main) {
VP8LEncoder* const enc_main = VP8LEncoderNew(config, picture);
VP8LEncoder* enc_side = NULL;
CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX];
@@ -1909,15 +1701,25 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
// The main thread uses picture->stats, the side thread uses stats_side.
WebPAuxStats stats_side;
VP8LBitWriter bw_side;
+ WebPPicture picture_side;
const WebPWorkerInterface* const worker_interface = WebPGetWorkerInterface();
int ok_main;
+ if (enc_main == NULL || !VP8LBitWriterInit(&bw_side, 0)) {
+ VP8LEncoderDelete(enc_main);
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+
+ // Avoid "garbage value" error from Clang's static analysis tool.
+ if (!WebPPictureInit(&picture_side)) {
+ goto Error;
+ }
+
// Analyze image (entropy, num_palettes etc)
- if (enc_main == NULL ||
- !EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main,
+ if (!EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main,
&red_and_blue_always_zero) ||
- !EncoderInit(enc_main) || !VP8LBitWriterInit(&bw_side, 0)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ !EncoderInit(enc_main)) {
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
@@ -1925,17 +1727,17 @@ WebPEncodingError 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.
{
@@ -1945,37 +1747,46 @@ WebPEncodingError 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->picture_ = picture;
- param->use_cache_ = use_cache;
- 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->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 {
- param->stats_ = (picture->stats == NULL) ? NULL : &stats_side;
+ // Create a side picture (error_code is not thread-safe).
+ if (!WebPPictureView(picture, /*left=*/0, /*top=*/0, picture->width,
+ picture->height, &picture_side)) {
+ 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;
// Create a side bit writer.
if (!VP8LBitWriterClone(bw_main, &bw_side)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ 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);
+ enc_side = VP8LEncoderNew(config, &picture_side);
if (enc_side == NULL || !EncoderInit(enc_side)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
// Copy the values that were computed for the main encoder.
- enc_side->histo_bits_ = enc_main->histo_bits_;
- enc_side->transform_bits_ = enc_main->transform_bits_;
- enc_side->palette_size_ = enc_main->palette_size_;
- memcpy(enc_side->palette_, enc_main->palette_,
- sizeof(enc_main->palette_));
- memcpy(enc_side->palette_sorted_, enc_main->palette_sorted_,
- sizeof(enc_main->palette_sorted_));
- param->enc_ = enc_side;
+ 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);
@@ -1988,7 +1799,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
// Start the second thread if needed.
if (num_crunch_configs_side != 0) {
if (!worker_interface->Reset(&worker_side)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
#if !defined(WEBP_DISABLE_STATS)
@@ -1998,8 +1809,6 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
memcpy(&stats_side, picture->stats, sizeof(stats_side));
}
#endif
- // This line is only useful to remove a Clang static analyzer warning.
- params_side.err_ = VP8_ENC_OK;
worker_interface->Launch(&worker_side);
}
// Execute the main thread.
@@ -2011,7 +1820,10 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
const int ok_side = worker_interface->Sync(&worker_side);
worker_interface->End(&worker_side);
if (!ok_main || !ok_side) {
- err = ok_main ? params_side.err_ : params_main.err_;
+ if (picture->error_code == VP8_ENC_OK) {
+ assert(picture_side.error_code != VP8_ENC_OK);
+ WebPEncodingSetError(picture, picture_side.error_code);
+ }
goto Error;
}
if (VP8LBitWriterNumBytes(&bw_side) < VP8LBitWriterNumBytes(bw_main)) {
@@ -2022,18 +1834,13 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
}
#endif
}
- } else {
- if (!ok_main) {
- err = params_main.err_;
- goto Error;
- }
}
-Error:
+ Error:
VP8LBitWriterWipeOut(&bw_side);
VP8LEncoderDelete(enc_main);
VP8LEncoderDelete(enc_side);
- return err;
+ return (picture->error_code == VP8_ENC_OK);
}
#undef CRUNCH_CONFIGS_MAX
@@ -2046,15 +1853,12 @@ int VP8LEncodeImage(const WebPConfig* const config,
size_t coded_size;
int percent = 0;
int initial_size;
- WebPEncodingError err = VP8_ENC_OK;
VP8LBitWriter bw;
if (picture == NULL) return 0;
if (config == NULL || picture->argb == NULL) {
- err = VP8_ENC_ERROR_NULL_PARAMETER;
- WebPEncodingSetError(picture, err);
- return 0;
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
}
width = picture->width;
@@ -2064,13 +1868,13 @@ int VP8LEncodeImage(const WebPConfig* const config,
initial_size = (config->image_hint == WEBP_HINT_GRAPH) ?
width * height : width * height * 2;
if (!VP8LBitWriterInit(&bw, initial_size)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
if (!WebPReportProgress(picture, 1, &percent)) {
UserAbort:
- err = VP8_ENC_ERROR_USER_ABORT;
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_USER_ABORT);
goto Error;
}
// Reset stats (for pure lossless coding)
@@ -2086,28 +1890,26 @@ int VP8LEncodeImage(const WebPConfig* const config,
// Write image size.
if (!WriteImageSize(picture, &bw)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
has_alpha = WebPPictureHasTransparency(picture);
// Write the non-trivial Alpha flag and lossless version.
if (!WriteRealAlphaAndVersion(&bw, has_alpha)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
goto Error;
}
- if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort;
+ if (!WebPReportProgress(picture, 2, &percent)) goto UserAbort;
// Encode main image stream.
- err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/);
- if (err != VP8_ENC_OK) goto Error;
+ if (!VP8LEncodeStream(config, picture, &bw)) goto Error;
- if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
+ if (!WebPReportProgress(picture, 99, &percent)) goto UserAbort;
// Finish the RIFF chunk.
- err = WriteImage(picture, &bw, &coded_size);
- if (err != VP8_ENC_OK) goto Error;
+ if (!WriteImage(picture, &bw, &coded_size)) goto Error;
if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
@@ -2126,13 +1928,11 @@ int VP8LEncodeImage(const WebPConfig* const config,
}
Error:
- if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- VP8LBitWriterWipeOut(&bw);
- if (err != VP8_ENC_OK) {
- WebPEncodingSetError(picture, err);
- return 0;
+ if (bw.error) {
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
}
- return 1;
+ VP8LBitWriterWipeOut(&bw);
+ return (picture->error_code == VP8_ENC_OK);
}
//------------------------------------------------------------------------------
diff --git a/c-lib/src/enc/vp8li_enc.h b/c-lib/src/enc/vp8li_enc.h
@@ -17,12 +17,16 @@
#ifdef HAVE_CONFIG_H
#include "src/webp/config.h"
#endif
+
+#include <stddef.h>
+
// 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;
//------------------------------------------------------------------------------
@@ -88,10 +93,9 @@ int VP8LEncodeImage(const WebPConfig* const config,
const WebPPicture* const picture);
// Encodes the main image stream using the supplied bit writer.
-// If 'use_cache' is false, disables the use of color cache.
-WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
- const WebPPicture* const picture,
- VP8LBitWriter* const bw, int use_cache);
+// Returns false in case of error (stored in picture->error_code).
+int VP8LEncodeStream(const WebPConfig* const config,
+ const WebPPicture* const picture, VP8LBitWriter* const bw);
#if (WEBP_NEAR_LOSSLESS == 1)
// in near_lossless.c
@@ -103,13 +107,23 @@ int VP8ApplyNearLossless(const WebPPicture* const picture, int quality,
//------------------------------------------------------------------------------
// Image transforms in predictor.c.
-void VP8LResidualImage(int width, int height, int bits, int low_effort,
- uint32_t* const argb, uint32_t* const argb_scratch,
- uint32_t* const image, int near_lossless, int exact,
- int used_subtract_green);
-
-void VP8LColorSpaceTransform(int width, int height, int bits, int quality,
- uint32_t* const argb, uint32_t* image);
+// pic and percent are for progress.
+// Returns false in case of error (stored in pic->error_code).
+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 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/c-lib/src/enc/webp_enc.c b/c-lib/src/enc/webp_enc.c
@@ -12,14 +12,18 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <math.h>
#include <stdlib.h>
#include <string.h>
-#include <math.h>
+#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)
}
@@ -307,7 +311,10 @@ int WebPEncodingSetError(const WebPPicture* const pic,
WebPEncodingError error) {
assert((int)error < VP8_ENC_ERROR_LAST);
assert((int)error >= VP8_ENC_OK);
- ((WebPPicture*)pic)->error_code = error;
+ // The oldest error reported takes precedence over the new one.
+ if (pic->error_code == VP8_ENC_OK) {
+ ((WebPPicture*)pic)->error_code = error;
+ }
return 0;
}
@@ -317,8 +324,7 @@ int WebPReportProgress(const WebPPicture* const pic,
*percent_store = percent;
if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
// user abort requested
- WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
- return 0;
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
}
}
return 1; // ok
@@ -329,16 +335,14 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
int ok = 0;
if (pic == NULL) return 0;
- WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far
+ pic->error_code = VP8_ENC_OK; // all ok so far
if (config == NULL) { // bad params
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
}
if (!WebPValidateConfig(config)) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
}
- if (pic->width <= 0 || pic->height <= 0) {
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
- }
+ if (!WebPValidatePicture(pic)) return 0;
if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
}
@@ -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/c-lib/src/libwebp.pc.in b/c-lib/src/libwebp.pc.in
@@ -6,6 +6,7 @@ includedir=@includedir@
Name: libwebp
Description: Library for the WebP graphics format
Version: @PACKAGE_VERSION@
+Requires.private: libsharpyuv
Cflags: -I${includedir}
-Libs: -L${libdir} -lwebp
+Libs: -L${libdir} -l@webp_libname_prefix@webp
Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
diff --git a/c-lib/src/libwebp.rc b/c-lib/src/libwebp.rc
@@ -6,8 +6,8 @@
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,0,2,2
- PRODUCTVERSION 1,0,2,2
+ FILEVERSION 1,0,6,0
+ PRODUCTVERSION 1,0,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -24,12 +24,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Google, Inc."
VALUE "FileDescription", "libwebp DLL"
- VALUE "FileVersion", "1.2.2"
+ VALUE "FileVersion", "1.6.0"
VALUE "InternalName", "libwebp.dll"
- VALUE "LegalCopyright", "Copyright (C) 2021"
+ VALUE "LegalCopyright", "Copyright (C) 2025"
VALUE "OriginalFilename", "libwebp.dll"
VALUE "ProductName", "WebP Image Codec"
- VALUE "ProductVersion", "1.2.2"
+ VALUE "ProductVersion", "1.6.0"
END
END
BLOCK "VarFileInfo"
diff --git a/c-lib/src/libwebpdecoder.pc.in b/c-lib/src/libwebpdecoder.pc.in
@@ -7,5 +7,5 @@ Name: libwebpdecoder
Description: Library for the WebP graphics format (decode only)
Version: @PACKAGE_VERSION@
Cflags: -I${includedir}
-Libs: -L${libdir} -lwebpdecoder
+Libs: -L${libdir} -l@webp_libname_prefix@webpdecoder
Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
diff --git a/c-lib/src/libwebpdecoder.rc b/c-lib/src/libwebpdecoder.rc
@@ -6,8 +6,8 @@
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,0,2,2
- PRODUCTVERSION 1,0,2,2
+ FILEVERSION 1,0,6,0
+ PRODUCTVERSION 1,0,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -24,12 +24,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Google, Inc."
VALUE "FileDescription", "libwebpdecoder DLL"
- VALUE "FileVersion", "1.2.2"
+ VALUE "FileVersion", "1.6.0"
VALUE "InternalName", "libwebpdecoder.dll"
- VALUE "LegalCopyright", "Copyright (C) 2021"
+ VALUE "LegalCopyright", "Copyright (C) 2025"
VALUE "OriginalFilename", "libwebpdecoder.dll"
VALUE "ProductName", "WebP Image Decoder"
- VALUE "ProductVersion", "1.2.2"
+ VALUE "ProductVersion", "1.6.0"
END
END
BLOCK "VarFileInfo"
diff --git a/c-lib/src/mux/Makefile.am b/c-lib/src/mux/Makefile.am
@@ -17,6 +17,6 @@ noinst_HEADERS =
noinst_HEADERS += ../webp/format_constants.h
libwebpmux_la_LIBADD = ../libwebp.la
-libwebpmux_la_LDFLAGS = -no-undefined -version-info 3:8:0 -lm
+libwebpmux_la_LDFLAGS = -no-undefined -version-info 4:2:1 -lm
libwebpmuxincludedir = $(includedir)/webp
pkgconfig_DATA = libwebpmux.pc
diff --git a/c-lib/src/mux/anim_encode.c b/c-lib/src/mux/anim_encode.c
@@ -15,6 +15,7 @@
#include <math.h> // for pow()
#include <stdio.h>
#include <stdlib.h> // for abs()
+#include <string.h>
#include "src/mux/animi.h"
#include "src/utils/utils.h"
@@ -22,6 +23,8 @@
#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
#define snprintf _snprintf
@@ -34,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.
};
// -----------------------------------------------------------------------------
@@ -108,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) {
@@ -190,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,
@@ -208,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!
}
@@ -251,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->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->size will be 1. So we handle that special case below.
+ if (enc->size < 2) enc->size = 2;
+ enc->encoded_frames =
+ (EncodedFrame*)WebPSafeCalloc(enc->size, sizeof(*enc->encoded_frames));
+ if (enc->encoded_frames == NULL) goto Err;
+
+ enc->mux = WebPMuxNew();
+ if (enc->mux == NULL) goto Err;
+
+ enc->count_since_key_frame = 0;
+ enc->first_timestamp = 0;
+ enc->prev_timestamp = 0;
+ enc->prev_candidate_undecided = 0;
+ enc->is_first_frame = 1;
+ enc->got_null_frame = 0;
return enc; // All OK.
@@ -308,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);
}
}
@@ -337,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,
@@ -398,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) {
@@ -420,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
@@ -549,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
@@ -568,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) {
@@ -593,27 +597,28 @@ int WebPAnimEncoderRefineRect(
int is_lossless, float quality, int* const x_offset, int* const y_offset,
int* const width, int* const height) {
FrameRectangle rect;
- const int right = clip(*x_offset + *width, 0, curr_canvas->width);
- const int left = clip(*x_offset, 0, curr_canvas->width - 1);
- const int bottom = clip(*y_offset + *height, 0, curr_canvas->height);
- const int top = clip(*y_offset, 0, curr_canvas->height - 1);
+ int right, left, bottom, top;
if (prev_canvas == NULL || curr_canvas == NULL ||
prev_canvas->width != curr_canvas->width ||
prev_canvas->height != curr_canvas->height ||
!prev_canvas->use_argb || !curr_canvas->use_argb) {
return 0;
}
- rect.x_offset_ = left;
- rect.y_offset_ = top;
- rect.width_ = clip(right - left, 0, curr_canvas->width - rect.x_offset_);
- rect.height_ = clip(bottom - top, 0, curr_canvas->height - rect.y_offset_);
+ right = clip(*x_offset + *width, 0, curr_canvas->width);
+ 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);
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;
}
@@ -627,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,
@@ -635,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;
@@ -659,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;
@@ -687,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;
@@ -713,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
@@ -774,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.
@@ -792,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
@@ -810,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;
}
}
@@ -857,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);
}
@@ -889,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;
}
@@ -924,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.
@@ -976,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;
}
@@ -1013,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;
@@ -1023,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
@@ -1040,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;
}
}
}
@@ -1060,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.
@@ -1085,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;
@@ -1094,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) ||
@@ -1112,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);
@@ -1123,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,
@@ -1134,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,
@@ -1173,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);
}
}
@@ -1187,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,
@@ -1196,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;
@@ -1228,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;
}
@@ -1342,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;
@@ -1357,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");
}
@@ -1397,20 +1402,23 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp,
}
config = *encoder_config;
} else {
- WebPConfigInit(&config);
+ if (!WebPConfigInit(&config)) {
+ MarkError(enc, "Cannot Init config");
+ return 0;
+ }
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;
}
@@ -1418,12 +1426,14 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp,
// -----------------------------------------------------------------------------
// Bitstream assembly.
-static int DecodeFrameOntoCanvas(const WebPMuxFrameInfo* const frame,
- WebPPicture* const canvas) {
+WEBP_NODISCARD static int DecodeFrameOntoCanvas(
+ const WebPMuxFrameInfo* const frame, WebPPicture* const canvas) {
const WebPData* const image = &frame->bitstream;
WebPPicture sub_image;
WebPDecoderConfig config;
- WebPInitDecoderConfig(&config);
+ if (!WebPInitDecoderConfig(&config)) {
+ return 0;
+ }
WebPUtilClearPic(canvas, NULL);
if (WebPGetFeatures(image->bytes, image->size, &config.input) !=
VP8_STATUS_OK) {
@@ -1448,17 +1458,17 @@ static int DecodeFrameOntoCanvas(const WebPMuxFrameInfo* const frame,
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);
@@ -1486,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);
@@ -1533,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;
}
@@ -1579,7 +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);
+}
+
+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);
+}
+
+WebPMuxError WebPAnimEncoderDeleteChunk(
+ WebPAnimEncoder* enc, const char fourcc[4]) {
+ if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+ return WebPMuxDeleteChunk(enc->mux, fourcc);
}
// -----------------------------------------------------------------------------
diff --git a/c-lib/src/mux/libwebpmux.pc.in b/c-lib/src/mux/libwebpmux.pc.in
@@ -6,7 +6,7 @@ includedir=@includedir@
Name: libwebpmux
Description: Library for manipulating the WebP graphics format container
Version: @PACKAGE_VERSION@
-Requires: libwebp >= 0.2.0
+Requires.private: libwebp >= 0.2.0
Cflags: -I${includedir}
-Libs: -L${libdir} -lwebpmux
+Libs: -L${libdir} -l@webp_libname_prefix@webpmux
Libs.private: -lm
diff --git a/c-lib/src/mux/libwebpmux.rc b/c-lib/src/mux/libwebpmux.rc
@@ -6,8 +6,8 @@
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,0,2,2
- PRODUCTVERSION 1,0,2,2
+ FILEVERSION 1,0,6,0
+ PRODUCTVERSION 1,0,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -24,12 +24,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Google, Inc."
VALUE "FileDescription", "libwebpmux DLL"
- VALUE "FileVersion", "1.2.2"
+ VALUE "FileVersion", "1.6.0"
VALUE "InternalName", "libwebpmux.dll"
- VALUE "LegalCopyright", "Copyright (C) 2021"
+ VALUE "LegalCopyright", "Copyright (C) 2025"
VALUE "OriginalFilename", "libwebpmux.dll"
VALUE "ProductName", "WebP Image Muxer"
- VALUE "ProductVersion", "1.2.2"
+ VALUE "ProductVersion", "1.6.0"
END
END
BLOCK "VarFileInfo"
diff --git a/c-lib/src/mux/muxedit.c b/c-lib/src/mux/muxedit.c
@@ -13,8 +13,16 @@
// Vikas (vikasa@google.com)
#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#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) {
@@ -66,13 +74,16 @@ void WebPMuxDelete(WebPMux* mux) {
// Handy MACRO, makes MuxSet() very symmetric to MuxGet().
#define SWITCH_ID_LIST(INDEX, LIST) \
- if (idx == (INDEX)) { \
- err = ChunkAssignData(&chunk, data, copy_data, tag); \
- if (err == WEBP_MUX_OK) { \
- err = ChunkSetHead(&chunk, (LIST)); \
+ do { \
+ if (idx == (INDEX)) { \
+ err = ChunkAssignData(&chunk, data, copy_data, tag); \
+ if (err == WEBP_MUX_OK) { \
+ err = ChunkSetHead(&chunk, (LIST)); \
+ if (err != WEBP_MUX_OK) ChunkRelease(&chunk); \
+ } \
+ return err; \
} \
- return err; \
- }
+ } while (0)
static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag,
const WebPData* const data, int copy_data) {
@@ -83,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
@@ -138,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);
}
@@ -155,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;
@@ -210,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;
@@ -222,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;
}
@@ -240,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);
@@ -250,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.
@@ -275,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.
}
@@ -287,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;
@@ -302,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.
@@ -364,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;
}
@@ -379,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);
}
//------------------------------------------------------------------------------
@@ -388,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;
@@ -404,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);
@@ -414,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;
}
@@ -426,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);
@@ -452,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;
}
@@ -473,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;
}
@@ -485,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;
}
@@ -514,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;
}
@@ -553,16 +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);
- assert(err == WEBP_MUX_OK); // We know that one frame does exist.
+ 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;
}
}
@@ -581,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;
}
@@ -590,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;
}
@@ -618,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/c-lib/src/mux/muxi.h b/c-lib/src/mux/muxi.h
@@ -16,9 +16,13 @@
#include <assert.h>
#include <stdlib.h>
+
#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 2
-#define MUX_REV_VERSION 2
+#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_;
-
- WebPChunk* unknown_;
- int canvas_width_;
- int canvas_height_;
+ WebPMuxImage* images;
+ WebPChunk* iccp;
+ WebPChunk* exif;
+ WebPChunk* xmp;
+ WebPChunk* anim;
+ WebPChunk* vp8x;
+
+ WebPChunk* unknown;
+ int canvas_width;
+ int canvas_height;
};
// CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only.
@@ -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/c-lib/src/mux/muxinternal.c b/c-lib/src/mux/muxinternal.c
@@ -13,8 +13,15 @@
// Vikas (vikasa@google.com)
#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
#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,25 +154,26 @@ 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;
}
WebPMuxError ChunkAppend(WebPChunk* const chunk,
WebPChunk*** const chunk_list) {
+ WebPMuxError err;
assert(chunk_list != NULL && *chunk_list != NULL);
if (**chunk_list == NULL) {
- ChunkSetHead(chunk, *chunk_list);
+ err = ChunkSetHead(chunk, *chunk_list);
} else {
WebPChunk* last_chunk = **chunk_list;
- while (last_chunk->next_ != NULL) last_chunk = last_chunk->next_;
- ChunkSetHead(chunk, &last_chunk->next_);
- *chunk_list = &last_chunk->next_;
+ 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 WEBP_MUX_OK;
+ return err;
}
//------------------------------------------------------------------------------
@@ -187,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);
@@ -202,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;
}
@@ -211,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;
}
@@ -227,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;
}
@@ -247,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;
}
}
@@ -257,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'.
}
}
@@ -271,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,
@@ -289,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.
@@ -303,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;
}
@@ -360,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.
}
@@ -389,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;
}
@@ -403,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;
}
@@ -420,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;
}
}
@@ -469,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;
@@ -502,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;
}
}
@@ -518,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);
@@ -527,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;
@@ -545,4 +553,3 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
#undef NO_FLAG
//------------------------------------------------------------------------------
-
diff --git a/c-lib/src/mux/muxread.c b/c-lib/src/mux/muxread.c
@@ -13,36 +13,46 @@
// Vikas (vikasa@google.com)
#include <assert.h>
+#include <stddef.h>
+
+#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).
// Handy MACRO.
#define SWITCH_ID_LIST(INDEX, LIST) \
- if (idx == (INDEX)) { \
- const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \
- kChunks[(INDEX)].tag); \
- if (chunk) { \
- *data = chunk->data_; \
- return WEBP_MUX_OK; \
- } else { \
- return WEBP_MUX_NOT_FOUND; \
+ do { \
+ if (idx == (INDEX)) { \
+ const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \
+ kChunks[(INDEX)].tag); \
+ if (chunk) { \
+ *data = chunk->data; \
+ return WEBP_MUX_OK; \
+ } else { \
+ return WEBP_MUX_NOT_FOUND; \
+ } \
} \
- }
+ } while (0)
static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
uint32_t nth, WebPData* const data) {
assert(mux != NULL);
+ assert(idx != IDX_LAST_CHUNK);
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;
}
@@ -74,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 ?
@@ -85,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.
{
@@ -116,10 +126,13 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
// Each of ANMF chunk contain a header at the beginning. So, its size should
// be at least 'hdr_size'.
if (size < hdr_size) goto Fail;
- ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
+ if (ChunkAssignData(&subchunk, &temp, copy_data,
+ chunk->tag) != WEBP_MUX_OK) {
+ goto Fail;
+ }
}
- ChunkSetHead(&subchunk, &wpi->header_);
- 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;
@@ -132,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.
}
@@ -160,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:
@@ -217,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;
@@ -241,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] =
@@ -272,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;
}
@@ -283,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;
@@ -303,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.
@@ -334,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;
@@ -388,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.
@@ -426,15 +441,16 @@ WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4],
return WEBP_MUX_INVALID_ARGUMENT;
}
idx = ChunkGetIndexFromFourCC(fourcc);
+ assert(idx != IDX_LAST_CHUNK);
if (IsWPI(kChunks[idx].id)) { // An image chunk.
return WEBP_MUX_INVALID_ARGUMENT;
} else if (idx != IDX_UNKNOWN) { // A known chunk type.
return MuxGet(mux, idx, 1, chunk_data);
} else { // An unknown chunk type.
const WebPChunk* const chunk =
- ChunkSearchList(mux->unknown_, 1, ChunkGetTagFromFourCC(fourcc));
+ 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;
}
}
@@ -448,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);
@@ -471,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);
}
@@ -485,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);
@@ -526,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.
}
}
@@ -541,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/c-lib/src/utils/Makefile.am b/c-lib/src/utils/Makefile.am
@@ -9,6 +9,7 @@ common_HEADERS = ../webp/types.h
commondir = $(includedir)/webp
noinst_HEADERS =
+noinst_HEADERS += ../dsp/cpu.h
noinst_HEADERS += ../dsp/dsp.h
noinst_HEADERS += ../webp/decode.h
noinst_HEADERS += ../webp/encode.h
@@ -25,6 +26,8 @@ COMMON_SOURCES += filters_utils.c
COMMON_SOURCES += filters_utils.h
COMMON_SOURCES += huffman_utils.c
COMMON_SOURCES += huffman_utils.h
+COMMON_SOURCES += palette.c
+COMMON_SOURCES += palette.h
COMMON_SOURCES += quant_levels_dec_utils.c
COMMON_SOURCES += quant_levels_dec_utils.h
COMMON_SOURCES += rescaler_utils.c
diff --git a/c-lib/src/utils/bit_reader_inl_utils.h b/c-lib/src/utils/bit_reader_inl_utils.h
@@ -20,12 +20,15 @@
#include "src/webp/config.h"
#endif
+#include <assert.h>
#include <string.h> // 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_ += mask;
- br->range_ |= 1;
- br->value_ -= (bit_t)((split + 1) & 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/c-lib/src/utils/bit_reader_utils.c b/c-lib/src/utils/bit_reader_utils.c
@@ -15,7 +15,14 @@
#include "src/webp/config.h"
#endif
+#include <assert.h>
+#include <stddef.h>
+
+#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"
//------------------------------------------------------------------------------
@@ -24,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,
@@ -36,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;
}
}
@@ -85,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.
}
}
@@ -121,9 +128,10 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int bits,
#define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits.
-#if defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \
+#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
@@ -145,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,
@@ -166,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);
@@ -191,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
@@ -208,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 {
@@ -271,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/c-lib/src/utils/bit_reader_utils.h b/c-lib/src/utils/bit_reader_utils.h
@@ -16,9 +16,12 @@
#define WEBP_UTILS_BIT_READER_UTILS_H_
#include <assert.h>
+#include <stddef.h>
+
#ifdef _MSC_VER
#include <stdlib.h> // _byteswap_ulong
#endif
+#include "src/dsp/cpu.h"
#include "src/webp/types.h"
// Warning! This macro triggers quite some MACRO wizardry around func signature!
@@ -46,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.
@@ -64,18 +67,20 @@ extern "C" {
#define BITS 56
#elif defined(__arm__) || defined(_M_ARM) // ARM
#define BITS 24
-#elif defined(__aarch64__) // ARM 64bit
+#elif WEBP_AARCH64 // ARM 64bit
#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;
@@ -91,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.
@@ -138,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,
@@ -157,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/c-lib/src/utils/bit_writer_utils.c b/c-lib/src/utils/bit_writer_utils.c
@@ -13,10 +13,11 @@
// Vikas Arora (vikaas.arora@gmail.com)
#include <assert.h>
-#include <string.h> // for memcpy()
#include <stdlib.h>
+#include <string.h> // 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/c-lib/src/utils/bit_writer_utils.h b/c-lib/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 <stddef.h>
+
#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/c-lib/src/utils/color_cache_utils.c b/c-lib/src/utils/color_cache_utils.c
@@ -14,7 +14,9 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+
#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/c-lib/src/utils/color_cache_utils.h b/c-lib/src/utils/color_cache_utils.h
@@ -17,6 +17,7 @@
#include <assert.h>
+#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/c-lib/src/utils/filters_utils.c b/c-lib/src/utils/filters_utils.c
@@ -11,10 +11,13 @@
//
// Author: Urvang (urvang@google.com)
-#include "src/utils/filters_utils.h"
#include <stdlib.h>
#include <string.h>
+#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/c-lib/src/utils/filters_utils.h b/c-lib/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/c-lib/src/utils/huffman_encode_utils.c b/c-lib/src/utils/huffman_encode_utils.c
@@ -14,7 +14,9 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+
#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/c-lib/src/utils/huffman_encode_utils.h b/c-lib/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/c-lib/src/utils/huffman_utils.c b/c-lib/src/utils/huffman_utils.c
@@ -14,9 +14,11 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+
#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.
@@ -122,6 +124,9 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
const int symbol_code_length = code_lengths[symbol];
if (code_lengths[symbol] > 0) {
if (sorted != NULL) {
+ if(offset[symbol_code_length] >= code_lengths_size) {
+ return 0;
+ }
sorted[offset[symbol_code_length]++] = symbol;
} else {
offset[symbol_code_length]++;
@@ -142,7 +147,7 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
{
int step; // step size to replicate values in current table
- uint32_t low = -1; // low bits for current root entry
+ uint32_t low = 0xffffffffu; // low bits for current root entry
uint32_t mask = total_size - 1; // mask for low bits
uint32_t key = 0; // reversed prefix code
int num_nodes = 1; // number of Huffman tree nodes
@@ -177,21 +182,24 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
if (num_open < 0) {
return 0;
}
- if (root_table == NULL) continue;
for (; count[len] > 0; --count[len]) {
HuffmanCode code;
if ((key & mask) != low) {
- table += table_size;
+ if (root_table != NULL) table += table_size;
table_bits = NextTableBitSize(count, len, root_bits);
table_size = 1 << table_bits;
total_size += table_size;
low = key & mask;
- root_table[low].bits = (uint8_t)(table_bits + root_bits);
- root_table[low].value = (uint16_t)((table - root_table) - low);
+ if (root_table != NULL) {
+ root_table[low].bits = (uint8_t)(table_bits + root_bits);
+ root_table[low].value = (uint16_t)((table - root_table) - low);
+ }
+ }
+ if (root_table != NULL) {
+ code.bits = (uint8_t)(len - root_bits);
+ code.value = (uint16_t)sorted[symbol++];
+ ReplicateValue(&table[key >> root_bits], step, table_size, code);
}
- code.bits = (uint8_t)(len - root_bits);
- code.value = (uint16_t)sorted[symbol++];
- ReplicateValue(&table[key >> root_bits], step, table_size, code);
key = GetNextKey(key, len);
}
}
@@ -211,25 +219,83 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
((1 << MAX_CACHE_BITS) + NUM_LITERAL_CODES + NUM_LENGTH_CODES)
// Cut-off value for switching between heap and stack allocation.
#define SORTED_SIZE_CUTOFF 512
-int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
+int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
const int code_lengths[], int code_lengths_size) {
- int total_size;
+ const int total_size =
+ BuildHuffmanTable(NULL, root_bits, code_lengths, code_lengths_size, NULL);
assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
- if (root_table == NULL) {
- total_size = BuildHuffmanTable(NULL, root_bits,
- code_lengths, code_lengths_size, NULL);
- } else if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
+ if (total_size == 0 || root_table == NULL) return total_size;
+
+ if (root_table->curr_segment->curr_table + total_size >=
+ root_table->curr_segment->start + root_table->curr_segment->size) {
+ // If 'root_table' does not have enough memory, allocate a new segment.
+ // The available part of root_table->curr_segment is left unused because we
+ // need a contiguous buffer.
+ const int segment_size = root_table->curr_segment->size;
+ struct HuffmanTablesSegment* next =
+ (HuffmanTablesSegment*)WebPSafeMalloc(1, sizeof(*next));
+ if (next == NULL) return 0;
+ // Fill the new segment.
+ // We need at least 'total_size' but if that value is small, it is better to
+ // allocate a big chunk to prevent more allocations later. 'segment_size' is
+ // therefore chosen (any other arbitrary value could be chosen).
+ next->size = total_size > segment_size ? total_size : segment_size;
+ next->start =
+ (HuffmanCode*)WebPSafeMalloc(next->size, sizeof(*next->start));
+ if (next->start == NULL) {
+ WebPSafeFree(next);
+ return 0;
+ }
+ next->curr_table = next->start;
+ next->next = NULL;
+ // Point to the new segment.
+ root_table->curr_segment->next = next;
+ root_table->curr_segment = next;
+ }
+ if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
// use local stack-allocated array.
uint16_t sorted[SORTED_SIZE_CUTOFF];
- total_size = BuildHuffmanTable(root_table, root_bits,
- code_lengths, code_lengths_size, sorted);
- } else { // rare case. Use heap allocation.
+ BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
+ code_lengths, code_lengths_size, sorted);
+ } else { // rare case. Use heap allocation.
uint16_t* const sorted =
(uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
if (sorted == NULL) return 0;
- total_size = BuildHuffmanTable(root_table, root_bits,
- code_lengths, code_lengths_size, sorted);
+ BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
+ code_lengths, code_lengths_size, sorted);
WebPSafeFree(sorted);
}
return total_size;
}
+
+int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables) {
+ // Have 'segment' point to the first segment for now, 'root'.
+ HuffmanTablesSegment* const root = &huffman_tables->root;
+ huffman_tables->curr_segment = root;
+ root->next = NULL;
+ // Allocate root.
+ root->start = (HuffmanCode*)WebPSafeMalloc(size, sizeof(*root->start));
+ if (root->start == NULL) return 0;
+ root->curr_table = root->start;
+ root->size = size;
+ return 1;
+}
+
+void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables) {
+ HuffmanTablesSegment *current, *next;
+ if (huffman_tables == NULL) return;
+ // Free the root node.
+ current = &huffman_tables->root;
+ next = current->next;
+ WebPSafeFree(current->start);
+ current->start = NULL;
+ current->next = NULL;
+ current = next;
+ // Free the following nodes.
+ while (current != NULL) {
+ next = current->next;
+ WebPSafeFree(current->start);
+ WebPSafeFree(current);
+ current = next;
+ }
+}
diff --git a/c-lib/src/utils/huffman_utils.h b/c-lib/src/utils/huffman_utils.h
@@ -15,6 +15,7 @@
#define WEBP_UTILS_HUFFMAN_UTILS_H_
#include <assert.h>
+
#include "src/webp/format_constants.h"
#include "src/webp/types.h"
@@ -43,6 +44,30 @@ typedef struct {
// or non-literal symbol otherwise
} HuffmanCode32;
+// Contiguous memory segment of HuffmanCodes.
+typedef struct HuffmanTablesSegment {
+ HuffmanCode* start;
+ // Pointer to where we are writing into the segment. Starts at 'start' and
+ // cannot go beyond 'start' + 'size'.
+ HuffmanCode* curr_table;
+ // Pointer to the next segment in the chain.
+ struct HuffmanTablesSegment* next;
+ int size;
+} HuffmanTablesSegment;
+
+// Chained memory segments of HuffmanCodes.
+typedef struct HuffmanTables {
+ HuffmanTablesSegment root;
+ // Currently processed segment. At first, this is 'root'.
+ HuffmanTablesSegment* curr_segment;
+} HuffmanTables;
+
+// Allocates a HuffmanTables with 'size' contiguous HuffmanCodes. Returns 0 on
+// memory allocation error, 1 otherwise.
+WEBP_NODISCARD int VP8LHuffmanTablesAllocate(int size,
+ HuffmanTables* huffman_tables);
+void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables);
+
#define HUFFMAN_PACKED_BITS 6
#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS)
@@ -68,7 +93,7 @@ struct HTreeGroup {
};
// Creates the instance of HTreeGroup with specified number of tree-groups.
-HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups);
+WEBP_NODISCARD HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups);
// Releases the memory allocated for HTreeGroup.
void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
@@ -78,10 +103,10 @@ void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
// the huffman table.
// Returns built table size or 0 in case of error (invalid tree or
// memory error).
-// If root_table is NULL, it returns 0 if a lookup cannot be built, something
-// > 0 otherwise (but not the table size).
-int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
- const int code_lengths[], int code_lengths_size);
+WEBP_NODISCARD int VP8LBuildHuffmanTable(HuffmanTables* const root_table,
+ int root_bits,
+ const int code_lengths[],
+ int code_lengths_size);
#ifdef __cplusplus
} // extern "C"
diff --git a/c-lib/src/utils/palette.c b/c-lib/src/utils/palette.c
@@ -0,0 +1,415 @@
+// Copyright 2023 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for palette analysis.
+//
+// Author: Vincent Rabaud (vrabaud@google.com)
+
+#include "src/utils/palette.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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"
+
+// -----------------------------------------------------------------------------
+
+// Palette reordering for smaller sum of deltas (and for smaller storage).
+
+static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
+ const uint32_t a = WebPMemToUint32((uint8_t*)p1);
+ const uint32_t b = WebPMemToUint32((uint8_t*)p2);
+ assert(a != b);
+ return (a < b) ? -1 : 1;
+}
+
+static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) {
+ return (v <= 128) ? v : (256 - v);
+}
+
+// Computes a value that is related to the entropy created by the
+// palette entry diff.
+//
+// Note that the last & 0xff is a no-operation in the next statement, but
+// removed by most compilers and is here only for regularity of the code.
+static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) {
+ const uint32_t diff = VP8LSubPixels(col1, col2);
+ const int kMoreWeightForRGBThanForAlpha = 9;
+ uint32_t score;
+ score = PaletteComponentDistance((diff >> 0) & 0xff);
+ score += PaletteComponentDistance((diff >> 8) & 0xff);
+ score += PaletteComponentDistance((diff >> 16) & 0xff);
+ score *= kMoreWeightForRGBThanForAlpha;
+ score += PaletteComponentDistance((diff >> 24) & 0xff);
+ return score;
+}
+
+static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
+ const uint32_t tmp = *col1;
+ *col1 = *col2;
+ *col2 = tmp;
+}
+
+int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, int num_colors) {
+ int low = 0, hi = num_colors;
+ if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
+ while (1) {
+ const int mid = (low + hi) >> 1;
+ if (sorted[mid] == color) {
+ return mid;
+ } else if (sorted[mid] < color) {
+ low = mid;
+ } else {
+ hi = mid;
+ }
+ }
+ assert(0);
+ return 0;
+}
+
+void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
+ uint32_t sorted[], uint32_t idx_map[]) {
+ uint32_t i;
+ memcpy(sorted, palette, num_colors * sizeof(*sorted));
+ qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
+ for (i = 0; i < num_colors; ++i) {
+ idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4)
+#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE).
+
+int GetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
+ int i;
+ int x, y;
+ int num_colors = 0;
+ uint8_t in_use[COLOR_HASH_SIZE] = {0};
+ uint32_t colors[COLOR_HASH_SIZE] = {0};
+ const uint32_t* argb = pic->argb;
+ const int width = pic->width;
+ const int height = pic->height;
+ uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
+ assert(pic != NULL);
+ assert(pic->use_argb);
+
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ int key;
+ if (argb[x] == last_pix) {
+ continue;
+ }
+ last_pix = argb[x];
+ key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT);
+ while (1) {
+ if (!in_use[key]) {
+ colors[key] = last_pix;
+ in_use[key] = 1;
+ ++num_colors;
+ if (num_colors > MAX_PALETTE_SIZE) {
+ return MAX_PALETTE_SIZE + 1; // Exact count not needed.
+ }
+ break;
+ } else if (colors[key] == last_pix) {
+ break; // The color is already there.
+ } else {
+ // Some other color sits here, so do linear conflict resolution.
+ ++key;
+ key &= (COLOR_HASH_SIZE - 1); // Key mask.
+ }
+ }
+ }
+ argb += pic->argb_stride;
+ }
+
+ if (palette != NULL) { // Fill the colors into palette.
+ num_colors = 0;
+ for (i = 0; i < COLOR_HASH_SIZE; ++i) {
+ if (in_use[i]) {
+ palette[num_colors] = colors[i];
+ ++num_colors;
+ }
+ }
+ qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort);
+ }
+ return num_colors;
+}
+
+#undef COLOR_HASH_SIZE
+#undef COLOR_HASH_RIGHT_SHIFT
+
+// -----------------------------------------------------------------------------
+
+// The palette has been sorted by alpha. This function checks if the other
+// components of the palette have a monotonic development with regards to
+// position in the palette. If all have monotonic development, there is
+// no benefit to re-organize them greedily. A monotonic development
+// would be spotted in green-only situations (like lossy alpha) or gray-scale
+// images.
+static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette,
+ int num_colors) {
+ uint32_t predict = 0x000000;
+ int i;
+ uint8_t sign_found = 0x00;
+ for (i = 0; i < num_colors; ++i) {
+ const uint32_t diff = VP8LSubPixels(palette[i], predict);
+ const uint8_t rd = (diff >> 16) & 0xff;
+ const uint8_t gd = (diff >> 8) & 0xff;
+ const uint8_t bd = (diff >> 0) & 0xff;
+ if (rd != 0x00) {
+ sign_found |= (rd < 0x80) ? 1 : 2;
+ }
+ if (gd != 0x00) {
+ sign_found |= (gd < 0x80) ? 8 : 16;
+ }
+ if (bd != 0x00) {
+ sign_found |= (bd < 0x80) ? 64 : 128;
+ }
+ predict = palette[i];
+ }
+ return (sign_found & (sign_found << 1)) != 0; // two consequent signs.
+}
+
+static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted,
+ int num_colors, uint32_t* const palette) {
+ uint32_t predict = 0x00000000;
+ int i, k;
+ memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
+ if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return;
+ // Find greedily always the closest color of the predicted color to minimize
+ // deltas in the palette. This reduces storage needs since the
+ // palette is stored with delta encoding.
+ 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;
+ for (k = i; k < num_colors; ++k) {
+ const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
+ if (best_score > cur_score) {
+ best_score = cur_score;
+ best_ix = k;
+ }
+ }
+ SwapColor(&palette[best_ix], &palette[i]);
+ predict = palette[i];
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Modified Zeng method from "A Survey on Palette Reordering
+// Methods for Improving the Compression of Color-Indexed Images" by Armando J.
+// Pinho and Antonio J. R. Neves.
+
+// Finds the biggest cooccurrence in the matrix.
+static void CoOccurrenceFindMax(const uint32_t* const cooccurrence,
+ uint32_t num_colors, uint8_t* const c1,
+ uint8_t* const c2) {
+ // Find the index that is most frequently located adjacent to other
+ // (different) indexes.
+ uint32_t best_sum = 0u;
+ uint32_t i, j, best_cooccurrence;
+ *c1 = 0u;
+ for (i = 0; i < num_colors; ++i) {
+ uint32_t sum = 0;
+ for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j];
+ if (sum > best_sum) {
+ best_sum = sum;
+ *c1 = i;
+ }
+ }
+ // Find the index that is most frequently found adjacent to *c1.
+ *c2 = 0u;
+ best_cooccurrence = 0u;
+ for (i = 0; i < num_colors; ++i) {
+ if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) {
+ best_cooccurrence = cooccurrence[*c1 * num_colors + i];
+ *c2 = i;
+ }
+ }
+ assert(*c1 != *c2);
+}
+
+// Builds the cooccurrence matrix
+static int CoOccurrenceBuild(const WebPPicture* const pic,
+ const uint32_t* const palette, uint32_t num_colors,
+ uint32_t* cooccurrence) {
+ uint32_t *lines, *line_top, *line_current, *line_tmp;
+ int x, y;
+ const uint32_t* src = pic->argb;
+ uint32_t prev_pix = ~src[0];
+ uint32_t prev_idx = 0u;
+ uint32_t idx_map[MAX_PALETTE_SIZE] = {0};
+ uint32_t palette_sorted[MAX_PALETTE_SIZE];
+ lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
+ if (lines == NULL) {
+ return 0;
+ }
+ line_top = &lines[0];
+ line_current = &lines[pic->width];
+ PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map);
+ for (y = 0; y < pic->height; ++y) {
+ for (x = 0; x < pic->width; ++x) {
+ const uint32_t pix = src[x];
+ if (pix != prev_pix) {
+ prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)];
+ prev_pix = pix;
+ }
+ line_current[x] = prev_idx;
+ // 4-connectivity is what works best as mentioned in "On the relation
+ // between Memon's and the modified Zeng's palette reordering methods".
+ if (x > 0 && prev_idx != line_current[x - 1]) {
+ const uint32_t left_idx = line_current[x - 1];
+ ++cooccurrence[prev_idx * num_colors + left_idx];
+ ++cooccurrence[left_idx * num_colors + prev_idx];
+ }
+ if (y > 0 && prev_idx != line_top[x]) {
+ const uint32_t top_idx = line_top[x];
+ ++cooccurrence[prev_idx * num_colors + top_idx];
+ ++cooccurrence[top_idx * num_colors + prev_idx];
+ }
+ }
+ line_tmp = line_top;
+ line_top = line_current;
+ line_current = line_tmp;
+ src += pic->argb_stride;
+ }
+ WebPSafeFree(lines);
+ return 1;
+}
+
+struct Sum {
+ uint8_t index;
+ uint32_t sum;
+};
+
+static int PaletteSortModifiedZeng(const WebPPicture* const pic,
+ const uint32_t* const palette_in,
+ uint32_t num_colors,
+ uint32_t* const palette) {
+ uint32_t i, j, ind;
+ uint8_t remapping[MAX_PALETTE_SIZE];
+ uint32_t* cooccurrence;
+ struct Sum sums[MAX_PALETTE_SIZE];
+ uint32_t first, last;
+ uint32_t num_sums;
+ // TODO(vrabaud) check whether one color images should use palette or not.
+ if (num_colors <= 1) return 1;
+ // Build the co-occurrence matrix.
+ cooccurrence =
+ (uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
+ if (cooccurrence == NULL) {
+ return 0;
+ }
+ if (!CoOccurrenceBuild(pic, palette_in, num_colors, cooccurrence)) {
+ WebPSafeFree(cooccurrence);
+ return 0;
+ }
+
+ // Initialize the mapping list with the two best indices.
+ CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]);
+
+ // We need to append and prepend to the list of remapping. To this end, we
+ // actually define the next start/end of the list as indices in a vector (with
+ // a wrap around when the end is reached).
+ first = 0;
+ last = 1;
+ num_sums = num_colors - 2; // -2 because we know the first two values
+ if (num_sums > 0) {
+ // Initialize the sums with the first two remappings and find the best one
+ struct Sum* best_sum = &sums[0];
+ best_sum->index = 0u;
+ best_sum->sum = 0u;
+ for (i = 0, j = 0; i < num_colors; ++i) {
+ if (i == remapping[0] || i == remapping[1]) continue;
+ sums[j].index = i;
+ sums[j].sum = cooccurrence[i * num_colors + remapping[0]] +
+ cooccurrence[i * num_colors + remapping[1]];
+ if (sums[j].sum > best_sum->sum) best_sum = &sums[j];
+ ++j;
+ }
+
+ while (num_sums > 0) {
+ const uint8_t best_index = best_sum->index;
+ // Compute delta to know if we need to prepend or append the best index.
+ int32_t delta = 0;
+ const int32_t n = num_colors - num_sums;
+ for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) {
+ const uint16_t l_j = remapping[(ind + j) % num_colors];
+ delta += (n - 1 - 2 * (int32_t)j) *
+ (int32_t)cooccurrence[best_index * num_colors + l_j];
+ }
+ if (delta > 0) {
+ first = (first == 0) ? num_colors - 1 : first - 1;
+ remapping[first] = best_index;
+ } else {
+ ++last;
+ remapping[last] = best_index;
+ }
+ // Remove best_sum from sums.
+ *best_sum = sums[num_sums - 1];
+ --num_sums;
+ // Update all the sums and find the best one.
+ best_sum = &sums[0];
+ for (i = 0; i < num_sums; ++i) {
+ sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index];
+ if (sums[i].sum > best_sum->sum) best_sum = &sums[i];
+ }
+ }
+ }
+ assert((last + 1) % num_colors == first);
+ WebPSafeFree(cooccurrence);
+
+ // Re-map the palette.
+ for (i = 0; i < num_colors; ++i) {
+ palette[i] = palette_in[remapping[(first + i) % num_colors]];
+ }
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+int PaletteSort(PaletteSorting method, const struct WebPPicture* const pic,
+ const uint32_t* const palette_sorted, uint32_t num_colors,
+ uint32_t* const palette) {
+ switch (method) {
+ case kSortedDefault:
+ 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);
+ return 1;
+ case kModifiedZeng:
+ return PaletteSortModifiedZeng(pic, palette_sorted, num_colors, palette);
+ case kUnusedPalette:
+ case kPaletteSortingNum:
+ break;
+ }
+
+ assert(0);
+ return 0;
+}
diff --git a/c-lib/src/utils/palette.h b/c-lib/src/utils/palette.h
@@ -0,0 +1,62 @@
+// Copyright 2023 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for palette analysis.
+//
+// Author: Vincent Rabaud (vrabaud@google.com)
+
+#ifndef WEBP_UTILS_PALETTE_H_
+#define WEBP_UTILS_PALETTE_H_
+
+#include "src/webp/types.h"
+
+struct WebPPicture;
+
+// The different ways a palette can be sorted.
+typedef enum PaletteSorting {
+ kSortedDefault = 0,
+ // Sorts by minimizing L1 deltas between consecutive colors, giving more
+ // weight to RGB colors.
+ kMinimizeDelta = 1,
+ // Implements the modified Zeng method from "A Survey on Palette Reordering
+ // Methods for Improving the Compression of Color-Indexed Images" by Armando
+ // J. Pinho and Antonio J. R. Neves.
+ kModifiedZeng = 2,
+ kUnusedPalette = 3,
+ kPaletteSortingNum = 4
+} PaletteSorting;
+
+// Returns the index of 'color' in the sorted palette 'sorted' of size
+// 'num_colors'.
+int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, int num_colors);
+
+// Sort palette in increasing order and prepare an inverse mapping array.
+void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
+ uint32_t sorted[], uint32_t idx_map[]);
+
+// Returns count of unique colors in 'pic', assuming pic->use_argb is true.
+// If the unique color count is more than MAX_PALETTE_SIZE, returns
+// MAX_PALETTE_SIZE+1.
+// If 'palette' is not NULL and the number of unique colors is less than or
+// equal to MAX_PALETTE_SIZE, also outputs the actual unique colors into
+// 'palette' in a sorted order. Note: 'palette' is assumed to be an array
+// already allocated with at least MAX_PALETTE_SIZE elements.
+int GetColorPalette(const struct WebPPicture* const pic,
+ uint32_t* const palette);
+
+// 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);
+
+#endif // WEBP_UTILS_PALETTE_H_
diff --git a/c-lib/src/utils/quant_levels_dec_utils.c b/c-lib/src/utils/quant_levels_dec_utils.c
@@ -19,6 +19,7 @@
#include <string.h> // 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/c-lib/src/utils/quant_levels_utils.c b/c-lib/src/utils/quant_levels_utils.c
@@ -13,7 +13,9 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
+#include <stddef.h>
+#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/c-lib/src/utils/random_utils.c b/c-lib/src/utils/random_utils.c
@@ -12,6 +12,8 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <string.h>
+
+#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/c-lib/src/utils/random_utils.h b/c-lib/src/utils/random_utils.h
@@ -15,6 +15,7 @@
#define WEBP_UTILS_RANDOM_UTILS_H_
#include <assert.h>
+
#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/c-lib/src/utils/rescaler_utils.c b/c-lib/src/utils/rescaler_utils.c
@@ -15,7 +15,9 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+
#include "src/dsp/dsp.h"
+#include "src/webp/types.h"
#include "src/utils/rescaler_utils.h"
#include "src/utils/utils.h"
diff --git a/c-lib/src/utils/thread_utils.c b/c-lib/src/utils/thread_utils.c
@@ -13,6 +13,7 @@
#include <assert.h>
#include <string.h> // 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/c-lib/src/utils/thread_utils.h b/c-lib/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/c-lib/src/utils/utils.c b/c-lib/src/utils/utils.c
@@ -11,13 +11,15 @@
//
// Author: Skal (pascal.massimino@gmail.com)
+#include "src/utils/utils.h"
+
+#include <assert.h>
#include <stdlib.h>
#include <string.h> // for memcpy()
-#include "src/webp/decode.h"
+
+#include "src/webp/types.h"
+#include "src/utils/palette.h"
#include "src/webp/encode.h"
-#include "src/webp/format_constants.h" // for MAX_PALETTE_SIZE
-#include "src/utils/color_cache_utils.h"
-#include "src/utils/utils.h"
// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
@@ -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);
}
@@ -252,66 +254,10 @@ void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
//------------------------------------------------------------------------------
-#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4)
-#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE).
-
int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
- int i;
- int x, y;
- int num_colors = 0;
- uint8_t in_use[COLOR_HASH_SIZE] = { 0 };
- uint32_t colors[COLOR_HASH_SIZE];
- const uint32_t* argb = pic->argb;
- const int width = pic->width;
- const int height = pic->height;
- uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
- assert(pic != NULL);
- assert(pic->use_argb);
-
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- int key;
- if (argb[x] == last_pix) {
- continue;
- }
- last_pix = argb[x];
- key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT);
- while (1) {
- if (!in_use[key]) {
- colors[key] = last_pix;
- in_use[key] = 1;
- ++num_colors;
- if (num_colors > MAX_PALETTE_SIZE) {
- return MAX_PALETTE_SIZE + 1; // Exact count not needed.
- }
- break;
- } else if (colors[key] == last_pix) {
- break; // The color is already there.
- } else {
- // Some other color sits here, so do linear conflict resolution.
- ++key;
- key &= (COLOR_HASH_SIZE - 1); // Key mask.
- }
- }
- }
- argb += pic->argb_stride;
- }
-
- if (palette != NULL) { // Fill the colors into palette.
- num_colors = 0;
- for (i = 0; i < COLOR_HASH_SIZE; ++i) {
- if (in_use[i]) {
- palette[num_colors] = colors[i];
- ++num_colors;
- }
- }
- }
- return num_colors;
+ return GetColorPalette(pic, palette);
}
-#undef COLOR_HASH_SIZE
-#undef COLOR_HASH_RIGHT_SHIFT
-
//------------------------------------------------------------------------------
#if defined(WEBP_NEED_LOG_TABLE_8BIT)
diff --git a/c-lib/src/utils/utils.h b/c-lib/src/utils/utils.h
@@ -20,9 +20,7 @@
#endif
#include <assert.h>
-#include <limits.h>
-#include "src/dsp/dsp.h"
#include "src/webp/types.h"
#ifdef __cplusplus
@@ -64,7 +62,8 @@ WEBP_EXTERN void WebPSafeFree(void* const ptr);
// Alignment
#define WEBP_ALIGN_CST 31
-#define WEBP_ALIGN(PTR) (((uintptr_t)(PTR) + WEBP_ALIGN_CST) & ~WEBP_ALIGN_CST)
+#define WEBP_ALIGN(PTR) (((uintptr_t)(PTR) + WEBP_ALIGN_CST) & \
+ ~(uintptr_t)WEBP_ALIGN_CST)
#include <string.h>
// memcpy() is the safe way of moving potentially unaligned 32b memory.
@@ -73,10 +72,19 @@ static WEBP_INLINE uint32_t WebPMemToUint32(const uint8_t* const ptr) {
memcpy(&A, ptr, sizeof(A));
return A;
}
+
+static WEBP_INLINE int32_t WebPMemToInt32(const uint8_t* const ptr) {
+ return (int32_t)WebPMemToUint32(ptr);
+}
+
static WEBP_INLINE void WebPUint32ToMem(uint8_t* const ptr, uint32_t val) {
memcpy(ptr, &val, sizeof(val));
}
+static WEBP_INLINE void WebPInt32ToMem(uint8_t* const ptr, int val) {
+ WebPUint32ToMem(ptr, (uint32_t)val);
+}
+
//------------------------------------------------------------------------------
// Reading/writing data.
@@ -188,6 +196,7 @@ WEBP_EXTERN void WebPCopyPixels(const struct WebPPicture* const src,
// MAX_PALETTE_SIZE, also outputs the actual unique colors into 'palette'.
// Note: 'palette' is assumed to be an array already allocated with at least
// MAX_PALETTE_SIZE elements.
+// TODO(vrabaud) remove whenever we can break the ABI.
WEBP_EXTERN int WebPGetColorPalette(const struct WebPPicture* const pic,
uint32_t* const palette);
diff --git a/c-lib/src/webp/decode.h b/c-lib/src/webp/decode.h
@@ -14,13 +14,15 @@
#ifndef WEBP_WEBP_DECODE_H_
#define WEBP_WEBP_DECODE_H_
+#include <stddef.h>
+
#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.
@@ -48,48 +50,47 @@ WEBP_EXTERN int WebPGetDecoderVersion(void);
// RIFF + VP8X + (optional chunks) + VP8(L)
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
-WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size,
- int* width, int* height);
+WEBP_NODISCARD WEBP_EXTERN int WebPGetInfo(
+ const uint8_t* data, size_t data_size, int* width, int* height);
// Decodes WebP images pointed to by 'data' and returns RGBA samples, along
// with the dimensions in *width and *height. The ordering of samples in
// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
// The returned pointer should be deleted calling WebPFree().
// Returns NULL in case of error.
-WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
- int* width, int* height);
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBA(
+ const uint8_t* data, size_t data_size, int* width, int* height);
// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data.
-WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
- int* width, int* height);
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGB(
+ const uint8_t* data, size_t data_size, int* width, int* height);
// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data.
-WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
- int* width, int* height);
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRA(
+ const uint8_t* data, size_t data_size, int* width, int* height);
// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data.
// If the bitstream contains transparency, it is ignored.
-WEBP_EXTERN uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
- int* width, int* height);
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGB(
+ const uint8_t* data, size_t data_size, int* width, int* height);
// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data.
-WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
- int* width, int* height);
-
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGR(
+ const uint8_t* data, size_t data_size, int* width, int* height);
// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer
// returned is the Y samples buffer. Upon return, *u and *v will point to
// the U and V chroma data. These U and V buffers need NOT be passed to
// WebPFree(), unlike the returned Y luma one. The dimension of the U and V
-// planes are both (*width + 1) / 2 and (*height + 1)/ 2.
+// planes are both (*width + 1) / 2 and (*height + 1) / 2.
// Upon return, the Y buffer has a stride returned as '*stride', while U and V
// have a common stride returned as '*uv_stride'.
-// Return NULL in case of error.
+// 'width' and 'height' may be NULL, the other pointers must not be.
+// Returns NULL in case of error.
// (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr
-WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
- int* width, int* height,
- uint8_t** u, uint8_t** v,
- int* stride, int* uv_stride);
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUV(
+ const uint8_t* data, size_t data_size, int* width, int* height,
+ uint8_t** u, uint8_t** v, int* stride, int* uv_stride);
// These five functions are variants of the above ones, that decode the image
// directly into a pre-allocated buffer 'output_buffer'. The maximum storage
@@ -99,22 +100,22 @@ WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
// The parameter 'output_stride' specifies the distance (in bytes)
// between scanlines. Hence, output_buffer_size is expected to be at least
// output_stride x picture-height.
-WEBP_EXTERN uint8_t* WebPDecodeRGBAInto(
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBAInto(
const uint8_t* data, size_t data_size,
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN uint8_t* WebPDecodeARGBInto(
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGBInto(
const uint8_t* data, size_t data_size,
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN uint8_t* WebPDecodeBGRAInto(
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRAInto(
const uint8_t* data, size_t data_size,
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
// RGB and BGR variants. Here too the transparency information, if present,
// will be dropped and ignored.
-WEBP_EXTERN uint8_t* WebPDecodeRGBInto(
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBInto(
const uint8_t* data, size_t data_size,
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN uint8_t* WebPDecodeBGRInto(
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRInto(
const uint8_t* data, size_t data_size,
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
@@ -125,7 +126,7 @@ WEBP_EXTERN uint8_t* WebPDecodeBGRInto(
// 'u_size' and 'v_size' respectively.
// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred
// during decoding (or because some buffers were found to be too small).
-WEBP_EXTERN uint8_t* WebPDecodeYUVInto(
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUVInto(
const uint8_t* data, size_t data_size,
uint8_t* luma, size_t luma_size, int luma_stride,
uint8_t* u, size_t u_size, int u_stride,
@@ -216,11 +217,11 @@ struct WebPDecBuffer {
};
// Internal, version-checked, entry point
-WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int);
+WEBP_NODISCARD WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int);
// Initialize the structure as empty. Must be called before any other use.
// Returns false in case of version mismatch
-static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
+WEBP_NODISCARD static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION);
}
@@ -231,7 +232,7 @@ WEBP_EXTERN void WebPFreeDecBuffer(WebPDecBuffer* buffer);
//------------------------------------------------------------------------------
// Enumeration of the status codes
-typedef enum VP8StatusCode {
+typedef enum WEBP_NODISCARD VP8StatusCode {
VP8_STATUS_OK = 0,
VP8_STATUS_OUT_OF_MEMORY,
VP8_STATUS_INVALID_PARAM,
@@ -250,23 +251,24 @@ typedef enum VP8StatusCode {
// WebPIDecoder object. This object can be left in a SUSPENDED state if the
// picture is only partially decoded, pending additional input.
// Code example:
-//
-// WebPInitDecBuffer(&output_buffer);
-// output_buffer.colorspace = mode;
-// ...
-// WebPIDecoder* idec = WebPINewDecoder(&output_buffer);
-// while (additional_data_is_available) {
-// // ... (get additional data in some new_data[] buffer)
-// status = WebPIAppend(idec, new_data, new_data_size);
-// if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
-// break; // an error occurred.
-// }
-//
-// // The above call decodes the current available buffer.
-// // Part of the image can now be refreshed by calling
-// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
-// }
-// WebPIDelete(idec);
+/*
+ WebPInitDecBuffer(&output_buffer);
+ output_buffer.colorspace = mode;
+ ...
+ WebPIDecoder* idec = WebPINewDecoder(&output_buffer);
+ while (additional_data_is_available) {
+ // ... (get additional data in some new_data[] buffer)
+ status = WebPIAppend(idec, new_data, new_data_size);
+ if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
+ break; // an error occurred.
+ }
+
+ // The above call decodes the current available buffer.
+ // Part of the image can now be refreshed by calling
+ // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
+ }
+ WebPIDelete(idec);
+*/
// Creates a new incremental decoder with the supplied buffer parameter.
// This output_buffer can be passed NULL, in which case a default output buffer
@@ -280,7 +282,8 @@ typedef enum VP8StatusCode {
// within valid bounds.
// All other fields of WebPDecBuffer MUST remain constant between calls.
// Returns NULL if the allocation failed.
-WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer);
+WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewDecoder(
+ WebPDecBuffer* output_buffer);
// This function allocates and initializes an incremental-decoder object, which
// will output the RGB/A samples specified by 'csp' into a preallocated
@@ -292,7 +295,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer);
// colorspace 'csp' is taken into account for allocating this buffer. All other
// parameters are ignored.
// Returns NULL if the allocation failed, or if some parameters are invalid.
-WEBP_EXTERN WebPIDecoder* WebPINewRGB(
+WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewRGB(
WEBP_CSP_MODE csp,
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
@@ -307,7 +310,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewRGB(
// In this case, the output buffer will be automatically allocated (using
// MODE_YUVA) when decoding starts. All parameters are then ignored.
// Returns NULL if the allocation failed or if a parameter is invalid.
-WEBP_EXTERN WebPIDecoder* WebPINewYUVA(
+WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUVA(
uint8_t* luma, size_t luma_size, int luma_stride,
uint8_t* u, size_t u_size, int u_stride,
uint8_t* v, size_t v_size, int v_stride,
@@ -315,7 +318,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewYUVA(
// Deprecated version of the above, without the alpha plane.
// Kept for backward compatibility.
-WEBP_EXTERN WebPIDecoder* WebPINewYUV(
+WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUV(
uint8_t* luma, size_t luma_size, int luma_stride,
uint8_t* u, size_t u_size, int u_stride,
uint8_t* v, size_t v_size, int v_stride);
@@ -345,21 +348,21 @@ WEBP_EXTERN VP8StatusCode WebPIUpdate(
// (*last_y, *width etc.) can be NULL if corresponding information is not
// needed. The values in these pointers are only valid on successful (non-NULL)
// return.
-WEBP_EXTERN uint8_t* WebPIDecGetRGB(
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPIDecGetRGB(
const WebPIDecoder* idec, int* last_y,
int* width, int* height, int* stride);
// Same as above function to get a YUVA image. Returns pointer to the luma
// plane or NULL in case of error. If there is no alpha information
// the alpha pointer '*a' will be returned NULL.
-WEBP_EXTERN uint8_t* WebPIDecGetYUVA(
+WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPIDecGetYUVA(
const WebPIDecoder* idec, int* last_y,
uint8_t** u, uint8_t** v, uint8_t** a,
int* width, int* height, int* stride, int* uv_stride, int* a_stride);
// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the
// alpha information (if present). Kept for backward compatibility.
-static WEBP_INLINE uint8_t* WebPIDecGetYUV(
+WEBP_NODISCARD static WEBP_INLINE uint8_t* WebPIDecGetYUV(
const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,
int* width, int* height, int* stride, int* uv_stride) {
return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,
@@ -372,7 +375,7 @@ static WEBP_INLINE uint8_t* WebPIDecGetYUV(
// Returns NULL in case the incremental decoder object is in an invalid state.
// Otherwise returns the pointer to the internal representation. This structure
// is read-only, tied to WebPIDecoder's lifespan and should not be modified.
-WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(
+WEBP_NODISCARD WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(
const WebPIDecoder* idec, int* left, int* top, int* width, int* height);
//------------------------------------------------------------------------------
@@ -388,7 +391,7 @@ WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(
CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
// C) Adjust 'config', if needed
- config.no_fancy_upsampling = 1;
+ config.options.no_fancy_upsampling = 1;
config.output.colorspace = MODE_BGRA;
// etc.
@@ -450,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
@@ -467,15 +472,22 @@ struct WebPDecoderConfig {
};
// Internal, version-checked, entry point
-WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*, int);
+WEBP_NODISCARD WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*,
+ int);
// Initialize the configuration as empty. This function must always be
// called first, unless WebPGetFeatures() is to be called.
// Returns false in case of mismatched version.
-static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
+WEBP_NODISCARD static WEBP_INLINE int WebPInitDecoderConfig(
+ WebPDecoderConfig* config) {
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
@@ -487,8 +499,8 @@ static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
// The return WebPIDecoder object must always be deleted calling WebPIDelete().
// Returns NULL in case of error (and config->status will then reflect
// the error condition, if available).
-WEBP_EXTERN WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
- WebPDecoderConfig* config);
+WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPIDecode(
+ const uint8_t* data, size_t data_size, WebPDecoderConfig* config);
// Non-incremental version. This version decodes the full data at once, taking
// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK
diff --git a/c-lib/src/webp/demux.h b/c-lib/src/webp/demux.h
@@ -48,8 +48,11 @@
#ifndef WEBP_WEBP_DEMUX_H_
#define WEBP_WEBP_DEMUX_H_
+#include <stddef.h>
+
#include "./decode.h" // for WEBP_CSP_MODE
#include "./mux_types.h"
+#include "./types.h"
#ifdef __cplusplus
extern "C" {
@@ -85,13 +88,13 @@ typedef enum WebPDemuxState {
} WebPDemuxState;
// Internal, version-checked, entry point
-WEBP_EXTERN WebPDemuxer* WebPDemuxInternal(
+WEBP_NODISCARD WEBP_EXTERN WebPDemuxer* WebPDemuxInternal(
const WebPData*, int, WebPDemuxState*, int);
// Parses the full WebP file given by 'data'. For single images the WebP file
// header alone or the file header and the chunk header may be absent.
// Returns a WebPDemuxer object on successful parse, NULL otherwise.
-static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
+WEBP_NODISCARD static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION);
}
@@ -103,7 +106,7 @@ static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
// If this data is volatile, the demuxer object should be deleted (by calling
// WebPDemuxDelete()) and WebPDemuxPartial() called again on the new data.
// This is usually an inexpensive operation.
-static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
+WEBP_NODISCARD static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
const WebPData* data, WebPDemuxState* state) {
return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION);
}
@@ -164,14 +167,14 @@ struct WebPIterator {
// Returns false if 'dmux' is NULL or frame 'frame_number' is not present.
// Call WebPDemuxReleaseIterator() when use of the iterator is complete.
// NOTE: 'dmux' must persist for the lifetime of 'iter'.
-WEBP_EXTERN int WebPDemuxGetFrame(
+WEBP_NODISCARD WEBP_EXTERN int WebPDemuxGetFrame(
const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or
// previous ('iter->frame_num' - 1) frame. These functions do not loop.
// Returns true on success, false otherwise.
-WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter);
-WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter);
+WEBP_NODISCARD WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter);
+WEBP_NODISCARD WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter);
// Releases any memory associated with 'iter'.
// Must be called before any subsequent calls to WebPDemuxGetChunk() on the same
@@ -202,15 +205,16 @@ struct WebPChunkIterator {
// payloads are accessed through WebPDemuxGetFrame() and related functions.
// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete.
// NOTE: 'dmux' must persist for the lifetime of the iterator.
-WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux,
- const char fourcc[4], int chunk_number,
- WebPChunkIterator* iter);
+WEBP_NODISCARD WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux,
+ const char fourcc[4],
+ int chunk_number,
+ WebPChunkIterator* iter);
// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous
// ('iter->chunk_num' - 1) chunk. These functions do not loop.
// Returns true on success, false otherwise.
-WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter);
-WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter);
+WEBP_NODISCARD WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter);
+WEBP_NODISCARD WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter);
// Releases any memory associated with 'iter'.
// Must be called before destroying the associated WebPDemuxer with
@@ -257,21 +261,21 @@ struct WebPAnimDecoderOptions {
};
// Internal, version-checked, entry point.
-WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal(
+WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal(
WebPAnimDecoderOptions*, int);
// Should always be called, to initialize a fresh WebPAnimDecoderOptions
// structure before modification. Returns false in case of version mismatch.
// WebPAnimDecoderOptionsInit() must have succeeded before using the
// 'dec_options' object.
-static WEBP_INLINE int WebPAnimDecoderOptionsInit(
+WEBP_NODISCARD static WEBP_INLINE int WebPAnimDecoderOptionsInit(
WebPAnimDecoderOptions* dec_options) {
return WebPAnimDecoderOptionsInitInternal(dec_options,
WEBP_DEMUX_ABI_VERSION);
}
// Internal, version-checked, entry point.
-WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
+WEBP_NODISCARD WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
const WebPData*, const WebPAnimDecoderOptions*, int);
// Creates and initializes a WebPAnimDecoder object.
@@ -284,7 +288,7 @@ WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
// Returns:
// A pointer to the newly created WebPAnimDecoder object, or NULL in case of
// parsing error, invalid option or memory error.
-static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
+WEBP_NODISCARD static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
return WebPAnimDecoderNewInternal(webp_data, dec_options,
WEBP_DEMUX_ABI_VERSION);
@@ -306,8 +310,8 @@ struct WebPAnimInfo {
// info - (out) global information fetched from the animation.
// Returns:
// True on success.
-WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
- WebPAnimInfo* info);
+WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderGetInfo(
+ const WebPAnimDecoder* dec, WebPAnimInfo* info);
// Fetch the next frame from 'dec' based on options supplied to
// WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size
@@ -321,8 +325,9 @@ WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
// Returns:
// False if any of the arguments are NULL, or if there is a parsing or
// decoding error, or if there are no more frames. Otherwise, returns true.
-WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
- uint8_t** buf, int* timestamp);
+WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
+ uint8_t** buf,
+ int* timestamp);
// Check if there are more frames left to decode.
// Parameters:
@@ -330,7 +335,8 @@ WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
// Returns:
// True if 'dec' is not NULL and some frames are yet to be decoded.
// Otherwise, returns false.
-WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec);
+WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(
+ const WebPAnimDecoder* dec);
// Resets the WebPAnimDecoder object, so that next call to
// WebPAnimDecoderGetNext() will restart decoding from 1st frame. This would be
@@ -348,7 +354,7 @@ WEBP_EXTERN void WebPAnimDecoderReset(WebPAnimDecoder* dec);
//
// Parameters:
// dec - (in) decoder instance from which the demuxer object is to be fetched.
-WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer(
+WEBP_NODISCARD WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer(
const WebPAnimDecoder* dec);
// Deletes the WebPAnimDecoder object.
diff --git a/c-lib/src/webp/encode.h b/c-lib/src/webp/encode.h
@@ -14,13 +14,15 @@
#ifndef WEBP_WEBP_ENCODE_H_
#define WEBP_WEBP_ENCODE_H_
+#include <stddef.h>
+
#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
@@ -164,13 +166,14 @@ typedef enum WebPPreset {
} WebPPreset;
// Internal, version-checked, entry point
-WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int);
+WEBP_NODISCARD WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset,
+ float, int);
// Should always be called, to initialize a fresh WebPConfig structure before
// modification. Returns false in case of version mismatch. WebPConfigInit()
// must have succeeded before using the 'config' object.
// Note that the default values are lossless=0 and quality=75.
-static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
+WEBP_NODISCARD static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f,
WEBP_ENCODER_ABI_VERSION);
}
@@ -179,8 +182,9 @@ static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
// set of parameters (referred to by 'preset') and a given quality factor.
// This function can be called as a replacement to WebPConfigInit(). Will
// return false in case of error.
-static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
- WebPPreset preset, float quality) {
+WEBP_NODISCARD static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
+ WebPPreset preset,
+ float quality) {
return WebPConfigInitInternal(config, preset, quality,
WEBP_ENCODER_ABI_VERSION);
}
@@ -191,11 +195,12 @@ static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
// speed and final compressed size.
// This function will overwrite several fields from config: 'method', 'quality'
// and 'lossless'. Returns false in case of parameter error.
-WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config, int level);
+WEBP_NODISCARD WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config,
+ int level);
// Returns true if 'config' is non-NULL and all configuration parameters are
// within their valid ranges.
-WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config);
+WEBP_NODISCARD WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config);
//------------------------------------------------------------------------------
// Input / Output
@@ -221,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.
@@ -255,8 +261,8 @@ WEBP_EXTERN void WebPMemoryWriterClear(WebPMemoryWriter* writer);
// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon
// completion, writer.mem and writer.size will hold the coded data.
// writer.mem must be freed by calling WebPMemoryWriterClear.
-WEBP_EXTERN int WebPMemoryWrite(const uint8_t* data, size_t data_size,
- const WebPPicture* picture);
+WEBP_NODISCARD WEBP_EXTERN int WebPMemoryWrite(
+ const uint8_t* data, size_t data_size, const WebPPicture* picture);
// Progress hook, called from time to time to report progress. It can return
// false to request an abort of the encoding process, or true otherwise if
@@ -364,13 +370,13 @@ struct WebPPicture {
};
// Internal, version-checked, entry point
-WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int);
// Should always be called, to initialize the structure. Returns false in case
// of version mismatch. WebPPictureInit() must have succeeded before using the
// 'picture' object.
// Note that, by default, use_argb is false and colorspace is WEBP_YUV420.
-static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
+WEBP_NODISCARD static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION);
}
@@ -381,7 +387,7 @@ static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
// Allocate y/u/v buffers as per colorspace/width/height specification.
// Note! This function will free the previous buffer if needed.
// Returns false in case of memory error.
-WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture);
// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*().
// Note that this function does _not_ free the memory used by the 'picture'
@@ -394,7 +400,8 @@ WEBP_EXTERN void WebPPictureFree(WebPPicture* picture);
// will fully own the copied pixels (this is not a view). The 'dst' picture need
// not be initialized as its content is overwritten.
// Returns false in case of memory allocation error.
-WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src,
+ WebPPicture* dst);
// Compute the single distortion for packed planes of samples.
// 'src' will be compared to 'ref', and the raw distortion stored into
@@ -403,19 +410,18 @@ WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
// 'x_step' is the horizontal stride (in bytes) between samples.
// 'src/ref_stride' is the byte distance between rows.
// Returns false in case of error (bad parameter, memory allocation error, ...).
-WEBP_EXTERN int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
- const uint8_t* ref, size_t ref_stride,
- int width, int height,
- size_t x_step,
- int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
- float* distortion, float* result);
+WEBP_NODISCARD WEBP_EXTERN int WebPPlaneDistortion(
+ const uint8_t* src, size_t src_stride,
+ const uint8_t* ref, size_t ref_stride, int width, int height, size_t x_step,
+ int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
+ float* distortion, float* result);
// Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results
// are in dB, stored in result[] in the B/G/R/A/All order. The distortion is
// always performed using ARGB samples. Hence if the input is YUV(A), the
// picture will be internally converted to ARGB (just for the measurement).
// Warning: this function is rather CPU-intensive.
-WEBP_EXTERN int WebPPictureDistortion(
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureDistortion(
const WebPPicture* src, const WebPPicture* ref,
int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
float result[5]);
@@ -428,8 +434,8 @@ WEBP_EXTERN int WebPPictureDistortion(
// must be fully be comprised inside the 'src' source picture. If the source
// picture uses the YUV420 colorspace, the top and left coordinates will be
// snapped to even values.
-WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture,
- int left, int top, int width, int height);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureCrop(
+ WebPPicture* picture, int left, int top, int width, int height);
// Extracts a view from 'src' picture into 'dst'. The rectangle for the view
// is defined by the top-left corner pixel coordinates (left, top) as well
@@ -441,10 +447,10 @@ WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture,
// the original dimension will be lost). Picture 'dst' need not be initialized
// with WebPPictureInit() if it is different from 'src', since its content will
// be overwritten.
-// Returns false in case of memory allocation error or invalid parameters.
-WEBP_EXTERN int WebPPictureView(const WebPPicture* src,
- int left, int top, int width, int height,
- WebPPicture* dst);
+// Returns false in case of invalid parameters.
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureView(
+ const WebPPicture* src, int left, int top, int width, int height,
+ WebPPicture* dst);
// Returns true if the 'picture' is actually a view and therefore does
// not own the memory for pixels.
@@ -455,29 +461,30 @@ WEBP_EXTERN int WebPPictureIsView(const WebPPicture* picture);
// dimension will be calculated preserving the aspect ratio.
// No gamma correction is applied.
// Returns false in case of error (invalid parameter or insufficient memory).
-WEBP_EXTERN int WebPPictureRescale(WebPPicture* pic, int width, int height);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureRescale(WebPPicture* picture,
+ int width, int height);
// Colorspace conversion function to import RGB samples.
// Previous buffer will be free'd, if any.
// *rgb buffer should have a size of at least height * rgb_stride.
// Returns false in case of memory error.
-WEBP_EXTERN int WebPPictureImportRGB(
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGB(
WebPPicture* picture, const uint8_t* rgb, int rgb_stride);
// Same, but for RGBA buffer.
-WEBP_EXTERN int WebPPictureImportRGBA(
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGBA(
WebPPicture* picture, const uint8_t* rgba, int rgba_stride);
// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format
// input buffer ignoring the alpha channel. Avoids needing to copy the data
// to a temporary 24-bit RGB buffer to import the RGB only.
-WEBP_EXTERN int WebPPictureImportRGBX(
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGBX(
WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride);
// Variants of the above, but taking BGR(A|X) input.
-WEBP_EXTERN int WebPPictureImportBGR(
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGR(
WebPPicture* picture, const uint8_t* bgr, int bgr_stride);
-WEBP_EXTERN int WebPPictureImportBGRA(
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGRA(
WebPPicture* picture, const uint8_t* bgra, int bgra_stride);
-WEBP_EXTERN int WebPPictureImportBGRX(
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGRX(
WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride);
// Converts picture->argb data to the YUV420A format. The 'colorspace'
@@ -486,24 +493,24 @@ WEBP_EXTERN int WebPPictureImportBGRX(
// non-opaque transparent values is detected, and 'colorspace' will be
// adjusted accordingly. Note that this method is lossy.
// Returns false in case of error.
-WEBP_EXTERN int WebPPictureARGBToYUVA(WebPPicture* picture,
- WebPEncCSP /*colorspace = WEBP_YUV420*/);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureARGBToYUVA(
+ WebPPicture* picture, WebPEncCSP /*colorspace = WEBP_YUV420*/);
// Same as WebPPictureARGBToYUVA(), but the conversion is done using
// pseudo-random dithering with a strength 'dithering' between
// 0.0 (no dithering) and 1.0 (maximum dithering). This is useful
// for photographic picture.
-WEBP_EXTERN int WebPPictureARGBToYUVADithered(
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureARGBToYUVADithered(
WebPPicture* picture, WebPEncCSP colorspace, float dithering);
-// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion.
+// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion
// Downsampling is handled with extra care in case of color clipping. This
// method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better
// and sharper YUV representation.
// Returns false in case of error.
-WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture);
// kept for backward compatibility:
-WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture);
// Converts picture->yuv to picture->argb and sets picture->use_argb to true.
// The input format must be YUV_420 or YUV_420A. The conversion from YUV420 to
@@ -511,7 +518,7 @@ WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture);
// Note that the use of this colorspace is discouraged if one has access to the
// raw ARGB samples, since using YUV420 is comparatively lossy.
// Returns false in case of error.
-WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture);
+WEBP_NODISCARD WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture);
// Helper function: given a width x height plane of RGBA or YUV(A) samples
// clean-up or smoothen the YUV or RGB samples under fully transparent area,
@@ -526,7 +533,7 @@ WEBP_EXTERN int WebPPictureHasTransparency(const WebPPicture* picture);
// Remove the transparency information (if present) by blending the color with
// the background color 'background_rgb' (specified as 24bit RGB triplet).
// After this call, all alpha values are reset to 0xff.
-WEBP_EXTERN void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb);
+WEBP_EXTERN void WebPBlendAlpha(WebPPicture* picture, uint32_t background_rgb);
//------------------------------------------------------------------------------
// Main call
@@ -541,7 +548,8 @@ WEBP_EXTERN void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb);
// the former for lossy encoding, and the latter for lossless encoding
// (when config.lossless is true). Automatic conversion from one format to
// another is provided but they both incur some loss.
-WEBP_EXTERN int WebPEncode(const WebPConfig* config, WebPPicture* picture);
+WEBP_NODISCARD WEBP_EXTERN int WebPEncode(const WebPConfig* config,
+ WebPPicture* picture);
//------------------------------------------------------------------------------
diff --git a/c-lib/src/webp/format_constants.h b/c-lib/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.
@@ -55,7 +60,7 @@
typedef enum {
PREDICTOR_TRANSFORM = 0,
CROSS_COLOR_TRANSFORM = 1,
- SUBTRACT_GREEN = 2,
+ SUBTRACT_GREEN_TRANSFORM = 2,
COLOR_INDEXING_TRANSFORM = 3
} VP8LImageTransformType;
diff --git a/c-lib/src/webp/mux.h b/c-lib/src/webp/mux.h
@@ -16,12 +16,13 @@
#define WEBP_WEBP_MUX_H_
#include "./mux_types.h"
+#include "./types.h"
#ifdef __cplusplus
extern "C" {
#endif
-#define WEBP_MUX_ABI_VERSION 0x0108 // MAJOR(8b) + MINOR(8b)
+#define WEBP_MUX_ABI_VERSION 0x0109 // MAJOR(8b) + MINOR(8b)
//------------------------------------------------------------------------------
// Mux API
@@ -70,7 +71,7 @@ typedef struct WebPMuxAnimParams WebPMuxAnimParams;
typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions;
// Error codes
-typedef enum WebPMuxError {
+typedef enum WEBP_NODISCARD WebPMuxError {
WEBP_MUX_OK = 1,
WEBP_MUX_NOT_FOUND = 0,
WEBP_MUX_INVALID_ARGUMENT = -1,
@@ -104,13 +105,13 @@ WEBP_EXTERN int WebPGetMuxVersion(void);
// Life of a Mux object
// Internal, version-checked, entry point
-WEBP_EXTERN WebPMux* WebPNewInternal(int);
+WEBP_NODISCARD WEBP_EXTERN WebPMux* WebPNewInternal(int);
// Creates an empty mux object.
// Returns:
// A pointer to the newly created empty mux object.
// Or NULL in case of memory error.
-static WEBP_INLINE WebPMux* WebPMuxNew(void) {
+WEBP_NODISCARD static WEBP_INLINE WebPMux* WebPMuxNew(void) {
return WebPNewInternal(WEBP_MUX_ABI_VERSION);
}
@@ -123,18 +124,21 @@ WEBP_EXTERN void WebPMuxDelete(WebPMux* mux);
// Mux creation.
// Internal, version-checked, entry point
-WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int, int);
+WEBP_NODISCARD WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int,
+ int);
// Creates a mux object from raw data given in WebP RIFF format.
// Parameters:
// bitstream - (in) the bitstream data in WebP RIFF format
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
-// object and value 0 indicates data will NOT be copied.
+// object and value 0 indicates data will NOT be copied. If the
+// data is not copied, it must exist for the lifetime of the
+// mux object.
// Returns:
// A pointer to the mux object created from given data - on success.
// NULL - In case of invalid data or memory error.
-static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
- int copy_data) {
+WEBP_NODISCARD static WEBP_INLINE WebPMux* WebPMuxCreate(
+ const WebPData* bitstream, int copy_data) {
return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION);
}
@@ -154,7 +158,9 @@ static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
// e.g., "ICCP", "XMP ", "EXIF" etc.
// chunk_data - (in) the chunk data to be added
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
-// object and value 0 indicates data will NOT be copied.
+// object and value 0 indicates data will NOT be copied. If the
+// data is not copied, it must exist until a call to
+// WebPMuxAssemble() is made.
// Returns:
// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
// or if fourcc corresponds to an image chunk.
@@ -217,7 +223,9 @@ struct WebPMuxFrameInfo {
// bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image
// WebP file (non-animated)
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
-// object and value 0 indicates data will NOT be copied.
+// object and value 0 indicates data will NOT be copied. If the
+// data is not copied, it must exist until a call to
+// WebPMuxAssemble() is made.
// Returns:
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL.
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
@@ -235,7 +243,9 @@ WEBP_EXTERN WebPMuxError WebPMuxSetImage(
// mux - (in/out) object to which the frame is to be added
// frame - (in) frame data.
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
-// object and value 0 indicates data will NOT be copied.
+// object and value 0 indicates data will NOT be copied. If the
+// data is not copied, it must exist until a call to
+// WebPMuxAssemble() is made.
// Returns:
// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL
// or if content of 'frame' is invalid.
@@ -449,7 +459,7 @@ WEBP_EXTERN int WebPAnimEncoderOptionsInitInternal(
// structure before modification. Returns false in case of version mismatch.
// WebPAnimEncoderOptionsInit() must have succeeded before using the
// 'enc_options' object.
-static WEBP_INLINE int WebPAnimEncoderOptionsInit(
+WEBP_NODISCARD static WEBP_INLINE int WebPAnimEncoderOptionsInit(
WebPAnimEncoderOptions* enc_options) {
return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION);
}
@@ -490,7 +500,7 @@ static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew(
// Returns:
// On error, returns false and frame->error_code is set appropriately.
// Otherwise, returns true.
-WEBP_EXTERN int WebPAnimEncoderAdd(
+WEBP_NODISCARD WEBP_EXTERN int WebPAnimEncoderAdd(
WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms,
const struct WebPConfig* config);
@@ -503,8 +513,8 @@ WEBP_EXTERN int WebPAnimEncoderAdd(
// webp_data - (out) generated WebP bitstream.
// Returns:
// True on success.
-WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc,
- WebPData* webp_data);
+WEBP_NODISCARD WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc,
+ WebPData* webp_data);
// Get error string corresponding to the most recent call using 'enc'. The
// returned string is owned by 'enc' and is valid only until the next call to
@@ -522,6 +532,57 @@ WEBP_EXTERN const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc);
WEBP_EXTERN void WebPAnimEncoderDelete(WebPAnimEncoder* enc);
//------------------------------------------------------------------------------
+// Non-image chunks.
+
+// Note: Only non-image related chunks should be managed through chunk APIs.
+// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH").
+
+// Adds a chunk with id 'fourcc' and data 'chunk_data' in the enc object.
+// Any existing chunk(s) with the same id will be removed.
+// Parameters:
+// enc - (in/out) object to which the chunk is to be added
+// fourcc - (in) a character array containing the fourcc of the given chunk;
+// e.g., "ICCP", "XMP ", "EXIF", etc.
+// chunk_data - (in) the chunk data to be added
+// copy_data - (in) value 1 indicates given data WILL be copied to the enc
+// object and value 0 indicates data will NOT be copied. If the
+// data is not copied, it must exist until a call to
+// WebPAnimEncoderAssemble() is made.
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if enc, fourcc or chunk_data is NULL.
+// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPAnimEncoderSetChunk(
+ WebPAnimEncoder* enc, const char fourcc[4], const WebPData* chunk_data,
+ int copy_data);
+
+// Gets a reference to the data of the chunk with id 'fourcc' in the enc object.
+// The caller should NOT free the returned data.
+// Parameters:
+// enc - (in) object from which the chunk data is to be fetched
+// fourcc - (in) a character array containing the fourcc of the chunk;
+// e.g., "ICCP", "XMP ", "EXIF", etc.
+// chunk_data - (out) returned chunk data
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if enc, fourcc or chunk_data is NULL.
+// WEBP_MUX_NOT_FOUND - If enc does not contain a chunk with the given id.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPAnimEncoderGetChunk(
+ const WebPAnimEncoder* enc, const char fourcc[4], WebPData* chunk_data);
+
+// Deletes the chunk with the given 'fourcc' from the enc object.
+// Parameters:
+// enc - (in/out) object from which the chunk is to be deleted
+// fourcc - (in) a character array containing the fourcc of the chunk;
+// e.g., "ICCP", "XMP ", "EXIF", etc.
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if enc or fourcc is NULL.
+// WEBP_MUX_NOT_FOUND - If enc does not contain a chunk with the given fourcc.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPAnimEncoderDeleteChunk(
+ WebPAnimEncoder* enc, const char fourcc[4]);
+
+//------------------------------------------------------------------------------
#ifdef __cplusplus
} // extern "C"
diff --git a/c-lib/src/webp/mux_types.h b/c-lib/src/webp/mux_types.h
@@ -15,6 +15,7 @@
#define WEBP_WEBP_MUX_TYPES_H_
#include <string.h> // memset()
+
#include "./types.h"
#ifdef __cplusplus
@@ -79,7 +80,8 @@ static WEBP_INLINE void WebPDataClear(WebPData* webp_data) {
// Allocates necessary storage for 'dst' and copies the contents of 'src'.
// Returns true on success.
-static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) {
+WEBP_NODISCARD static WEBP_INLINE int WebPDataCopy(const WebPData* src,
+ WebPData* dst) {
if (src == NULL || dst == NULL) return 0;
WebPDataInit(dst);
if (src->bytes != NULL && src->size != 0) {
diff --git a/c-lib/src/webp/types.h b/c-lib/src/webp/types.h
@@ -14,10 +14,10 @@
#ifndef WEBP_WEBP_TYPES_H_
#define WEBP_WEBP_TYPES_H_
-#include <stddef.h> // for size_t
+#include <stddef.h> // IWYU pragma: export for size_t
#ifndef _MSC_VER
-#include <inttypes.h>
+#include <inttypes.h> // IWYU pragma: export
#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
#define WEBP_INLINE inline
@@ -36,14 +36,39 @@ typedef long long int int64_t;
#define WEBP_INLINE __forceinline
#endif /* _MSC_VER */
+#ifndef WEBP_NODISCARD
+#if defined(WEBP_ENABLE_NODISCARD) && WEBP_ENABLE_NODISCARD
+#if (defined(__cplusplus) && __cplusplus >= 201703L) || \
+ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
+#define WEBP_NODISCARD [[nodiscard]]
+#else
+// 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))
+#else
+#define WEBP_NODISCARD
+#endif /* __has_attribute(warn_unused_result) */
+#else
+#define WEBP_NODISCARD
+#endif /* defined(__clang__) && defined(__has_attribute) */
+#endif /* (defined(__cplusplus) && __cplusplus >= 201700L) ||
+ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) */
+#else
+#define WEBP_NODISCARD
+#endif /* defined(WEBP_ENABLE_NODISCARD) && WEBP_ENABLE_NODISCARD */
+#endif /* WEBP_NODISCARD */
+
#ifndef WEBP_EXTERN
// This explicitly marks library functions and allows for changing the
// signature for e.g., Windows DLL builds.
-# if defined(__GNUC__) && __GNUC__ >= 4
+# if defined(_WIN32) && defined(WEBP_DLL)
+# define WEBP_EXTERN __declspec(dllexport)
+# elif defined(__GNUC__) && __GNUC__ >= 4
# define WEBP_EXTERN extern __attribute__ ((visibility ("default")))
# else
# define WEBP_EXTERN extern
-# endif /* __GNUC__ >= 4 */
+# endif /* defined(_WIN32) && defined(WEBP_DLL) */
#endif /* WEBP_EXTERN */
// Macro to check ABI compatibility (same major revision number)
@@ -56,7 +81,7 @@ extern "C" {
// Allocates 'size' bytes of memory. Returns NULL upon error. Memory
// must be deallocated by calling WebPFree(). This function is made available
// by the core 'libwebp' library.
-WEBP_EXTERN void* WebPMalloc(size_t size);
+WEBP_NODISCARD WEBP_EXTERN void* WebPMalloc(size_t size);
// Releases memory returned by the WebPDecode*() functions (from decode.h).
WEBP_EXTERN void WebPFree(void* ptr);
diff --git a/c-lib/swig/README b/c-lib/swig/README
@@ -1,56 +0,0 @@
-Building:
-=========
-
-JNI SWIG bindings:
-------------------
- $ gcc -shared -fPIC -fno-strict-aliasing -O2 \
- -I/path/to/your/jdk/includes \
- libwebp_java_wrap.c \
- -lwebp \
- -o libwebp_jni.so
-
--------------------------------------- BEGIN PSEUDO EXAMPLE
-import com.google.webp.libwebp;
-
-import java.lang.reflect.Method;
-
-public class libwebp_jni_example {
- static {
- System.loadLibrary("webp_jni");
- }
-
- /**
- * usage: java -cp libwebp.jar:. libwebp_jni_example
- */
- public static void main(String argv[]) {
- final int version = libwebp.WebPGetDecoderVersion();
- System.out.println("libwebp version: " + Integer.toHexString(version));
-
- System.out.println("libwebp methods:");
- final Method[] libwebpMethods = libwebp.class.getDeclaredMethods();
- for (int i = 0; i < libwebpMethods.length; i++) {
- System.out.println(libwebpMethods[i]);
- }
- }
-}
--------------------------------------- END PSEUDO EXAMPLE
-
- $ javac -cp libwebp.jar libwebp_jni_example.java
- $ java -Djava.library.path=. -cp libwebp.jar:. libwebp_jni_example
-
-Python SWIG bindings:
----------------------
- $ python setup.py build_ext
- $ python setup.py install --prefix=pylocal
-
--------------------------------------- BEGIN PSEUDO EXAMPLE
-import glob
-import sys
-sys.path.append(glob.glob('pylocal/lib/python*/site-packages')[0])
-
-from com.google.webp import libwebp
-print "libwebp decoder version: %x" % libwebp.WebPGetDecoderVersion()
-
-print "libwebp attributes:"
-for attr in dir(libwebp): print attr
--------------------------------------- END PSEUDO EXAMPLE
diff --git a/c-lib/swig/README.md b/c-lib/swig/README.md
@@ -0,0 +1,67 @@
+# SWIG bindings
+
+## Building
+
+### JNI SWIG bindings
+
+```shell
+ $ gcc -shared -fPIC -fno-strict-aliasing -O2 \
+ -I/path/to/your/jdk/includes \
+ libwebp_java_wrap.c \
+ -lwebp \
+ -o libwebp_jni.so
+```
+
+Example usage:
+
+```java
+import com.google.webp.libwebp;
+
+import java.lang.reflect.Method;
+
+public class libwebp_jni_example {
+ static {
+ System.loadLibrary("webp_jni");
+ }
+
+ /**
+ * usage: java -cp libwebp.jar:. libwebp_jni_example
+ */
+ public static void main(String argv[]) {
+ final int version = libwebp.WebPGetDecoderVersion();
+ System.out.println("libwebp version: " + Integer.toHexString(version));
+
+ System.out.println("libwebp methods:");
+ final Method[] libwebpMethods = libwebp.class.getDeclaredMethods();
+ for (int i = 0; i < libwebpMethods.length; i++) {
+ System.out.println(libwebpMethods[i]);
+ }
+ }
+}
+```
+
+```shell
+ $ javac -cp libwebp.jar libwebp_jni_example.java
+ $ java -Djava.library.path=. -cp libwebp.jar:. libwebp_jni_example
+```
+
+### Python SWIG bindings:
+
+```shell
+ $ python setup.py build_ext
+ $ python setup.py install --prefix=pylocal
+```
+
+Example usage:
+
+```python
+import glob
+import sys
+sys.path.append(glob.glob('pylocal/lib/python*/site-packages')[0])
+
+from com.google.webp import libwebp
+print "libwebp decoder version: %x" % libwebp.WebPGetDecoderVersion()
+
+print "libwebp attributes:"
+for attr in dir(libwebp): print attr
+```
diff --git a/c-lib/swig/libwebp.swig b/c-lib/swig/libwebp.swig
@@ -154,6 +154,7 @@ int WebPGetEncoderVersion(void);
%{
#include "webp/decode.h"
#include "webp/encode.h"
+#include "webp/types.h"
%}
#ifdef SWIGJAVA
diff --git a/c-lib/swig/libwebp_go_wrap.c b/c-lib/swig/libwebp_go_wrap.c
@@ -196,6 +196,7 @@ static _gostring_ _swig_makegostring(const char *p, size_t l) {
#include "webp/decode.h"
#include "webp/encode.h"
+#include "webp/types.h"
#ifdef __cplusplus
extern "C" {
diff --git a/c-lib/swig/libwebp_python_wrap.c b/c-lib/swig/libwebp_python_wrap.c
@@ -327,6 +327,9 @@ SWIGINTERNINLINE int SWIG_CheckState(int r) {
#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
#include <string.h>
#ifdef __cplusplus
@@ -3382,6 +3385,7 @@ SWIG_AsVal_size_t (PyObject * obj, size_t *val)
#include "webp/decode.h"
#include "webp/encode.h"
+#include "webp/types.h"
static size_t ReturnedBufferSize(
diff --git a/c-lib/tests/README b/c-lib/tests/README
@@ -1,19 +0,0 @@
-Description:
-============
-
-This is a collection of tests for the libwebp libraries, currently covering
-fuzzing through the APIs. Additional test vector coverage can be found at:
-https://chromium.googlesource.com/webm/libwebp-test-data
-
-Building:
-=========
-
-Fuzzers:
---------
-
-Follow the build instructions in ../README for libwebp, optionally adding build
-flags for various sanitizers (e.g., -fsanitize=address).
-
-fuzzer/makefile.unix can then be used to compile the fuzzer targets:
-
-$ make -C fuzzer -f makefile.unix
diff --git a/c-lib/tests/README.md b/c-lib/tests/README.md
@@ -0,0 +1,19 @@
+# Tests
+
+This is a collection of tests for the libwebp libraries, currently covering
+fuzzing through the APIs. Additional test vector coverage can be found at:
+https://chromium.googlesource.com/webm/libwebp-test-data
+
+## Building
+
+### Fuzzers
+
+Follow the [build instructions](../doc/building.md) for libwebp, optionally
+adding build flags for various sanitizers (e.g., -fsanitize=address).
+
+`-DWEBP_BUILD_FUZZTEST=ON` can then be used to compile the fuzzer targets:
+
+```shell
+$ cmake -B ./build -S . -DWEBP_BUILD_FUZZTEST=ON
+$ make -C build
+```
diff --git a/c-lib/tests/fuzzer/CMakeLists.txt b/c-lib/tests/fuzzer/CMakeLists.txt
@@ -0,0 +1,69 @@
+# Copyright (c) 2024 Google LLC
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+# Adds a fuzztest from file TEST_NAME.cc located in the gtest folder. Extra
+# arguments are considered as extra source files.
+
+if(CMAKE_VERSION VERSION_LESS "3.19.0")
+ return()
+endif()
+
+macro(add_webp_fuzztest TEST_NAME)
+ add_executable(${TEST_NAME} ${TEST_NAME}.cc)
+ # FuzzTest bundles GoogleTest so no need to link to gtest libraries.
+ target_link_libraries(${TEST_NAME} PRIVATE fuzz_utils webp ${ARGN})
+ target_include_directories(${TEST_NAME} PRIVATE ${CMAKE_BINARY_DIR}/src)
+ link_fuzztest(${TEST_NAME})
+ add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
+ set_property(
+ TEST ${TEST_NAME}
+ PROPERTY ENVIRONMENT "TEST_DATA_DIRS=${CMAKE_CURRENT_SOURCE_DIR}/data/")
+endmacro()
+
+enable_language(CXX)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+include(FetchContent)
+
+set(FETCHCONTENT_QUIET FALSE)
+set(fuzztest_SOURCE_DIR ${CMAKE_BINARY_DIR}/_deps/fuzztest-src)
+FetchContent_Declare(
+ fuzztest
+ GIT_REPOSITORY https://github.com/google/fuzztest.git
+ GIT_TAG 078ea0871cc96d3a69bad406577f176a4fa14ae9
+ GIT_PROGRESS TRUE
+ PATCH_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/patch.sh)
+
+FetchContent_MakeAvailable(fuzztest)
+
+fuzztest_setup_fuzzing_flags()
+
+add_library(fuzz_utils fuzz_utils.h fuzz_utils.cc img_alpha.h img_grid.h
+ img_peak.h)
+target_link_libraries(fuzz_utils PUBLIC webpdecoder)
+link_fuzztest(fuzz_utils)
+
+add_webp_fuzztest(advanced_api_fuzzer webpdecode webpdspdecode webputilsdecode)
+add_webp_fuzztest(dec_fuzzer)
+add_webp_fuzztest(enc_dec_fuzzer)
+add_webp_fuzztest(enc_fuzzer imagedec)
+add_webp_fuzztest(huffman_fuzzer webpdecode webpdspdecode webputilsdecode)
+add_webp_fuzztest(imageio_fuzzer imagedec)
+add_webp_fuzztest(simple_api_fuzzer)
+
+if(WEBP_BUILD_LIBWEBPMUX)
+ add_webp_fuzztest(animation_api_fuzzer webpdemux)
+ add_webp_fuzztest(animdecoder_fuzzer imageioutil webpdemux)
+ add_webp_fuzztest(animencoder_fuzzer libwebpmux)
+ add_webp_fuzztest(mux_demux_api_fuzzer libwebpmux webpdemux)
+endif()
+
+if(WEBP_BUILD_WEBPINFO)
+ add_webp_fuzztest(webp_info_fuzzer imageioutil)
+endif()
diff --git a/c-lib/tests/fuzzer/advanced_api_fuzzer.c b/c-lib/tests/fuzzer/advanced_api_fuzzer.c
@@ -1,114 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <string.h>
-
-#include "./fuzz_utils.h"
-#include "src/webp/decode.h"
-
-int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
- int i;
- WebPDecoderConfig config;
- if (!WebPInitDecoderConfig(&config)) return 0;
- if (WebPGetFeatures(data, size, &config.input) != VP8_STATUS_OK) return 0;
- if ((size_t)config.input.width * config.input.height > kFuzzPxLimit) return 0;
-
- // Using two independent criteria ensures that all combinations of options
- // can reach each path at the decoding stage, with meaningful differences.
-
- const uint8_t value = FuzzHash(data, size);
- const float factor = value / 255.f; // 0-1
-
- config.options.flip = value & 1;
- config.options.bypass_filtering = value & 2;
- config.options.no_fancy_upsampling = value & 4;
- config.options.use_threads = value & 8;
- if (size & 1) {
- config.options.use_cropping = 1;
- config.options.crop_width = (int)(config.input.width * (1 - factor));
- config.options.crop_height = (int)(config.input.height * (1 - factor));
- config.options.crop_left = config.input.width - config.options.crop_width;
- config.options.crop_top = config.input.height - config.options.crop_height;
- }
- if (size & 2) {
- int strength = (int)(factor * 100);
- config.options.dithering_strength = strength;
- config.options.alpha_dithering_strength = 100 - strength;
- }
- if (size & 4) {
- config.options.use_scaling = 1;
- config.options.scaled_width = (int)(config.input.width * factor * 2);
- config.options.scaled_height = (int)(config.input.height * factor * 2);
- }
-
-#if defined(WEBP_REDUCE_CSP)
- config.output.colorspace = (value & 1)
- ? ((value & 2) ? MODE_RGBA : MODE_BGRA)
- : ((value & 2) ? MODE_rgbA : MODE_bgrA);
-#else
- config.output.colorspace = (WEBP_CSP_MODE)(value % MODE_LAST);
-#endif // WEBP_REDUCE_CSP
-
- for (i = 0; i < 2; ++i) {
- if (i == 1) {
- // Use the bitstream data to generate extreme ranges for the options. An
- // alternative approach would be to use a custom corpus containing webp
- // files prepended with sizeof(config.options) zeroes to allow the fuzzer
- // to modify these independently.
- const int data_offset = 50;
- if (size > data_offset + sizeof(config.options)) {
- memcpy(&config.options, data + data_offset, sizeof(config.options));
- } else {
- break;
- }
- }
- if (size % 3) {
- // Decodes incrementally in chunks of increasing size.
- WebPIDecoder* idec = WebPIDecode(NULL, 0, &config);
- if (!idec) return 0;
- VP8StatusCode status;
- if (size & 8) {
- size_t available_size = value + 1;
- while (1) {
- if (available_size > size) available_size = size;
- status = WebPIUpdate(idec, data, available_size);
- if (status != VP8_STATUS_SUSPENDED || available_size == size) break;
- available_size *= 2;
- }
- } else {
- // WebPIAppend expects new data and its size with each call.
- // Implemented here by simply advancing the pointer into data.
- const uint8_t* new_data = data;
- size_t new_size = value + 1;
- while (1) {
- if (new_data + new_size > data + size) {
- new_size = data + size - new_data;
- }
- status = WebPIAppend(idec, new_data, new_size);
- if (status != VP8_STATUS_SUSPENDED || new_size == 0) break;
- new_data += new_size;
- new_size *= 2;
- }
- }
- WebPIDelete(idec);
- } else {
- WebPDecode(data, size, &config);
- }
-
- WebPFreeDecBuffer(&config.output);
- }
- return 0;
-}
diff --git a/c-lib/tests/fuzzer/advanced_api_fuzzer.cc b/c-lib/tests/fuzzer/advanced_api_fuzzer.cc
@@ -0,0 +1,181 @@
+// Copyright 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+
+#include "./fuzz_utils.h"
+#include "src/dec/webpi_dec.h"
+#include "src/utils/rescaler_utils.h"
+#include "src/webp/decode.h"
+
+namespace {
+
+void AdvancedApiTest(std::string_view blob, uint8_t factor_u8, bool flip,
+ bool bypass_filtering, bool no_fancy_upsampling,
+ bool use_threads, bool use_cropping, bool use_scaling,
+ bool use_dithering, int colorspace, bool incremental) {
+ WebPDecoderConfig config;
+ if (!WebPInitDecoderConfig(&config)) return;
+ const uint8_t* const data = reinterpret_cast<const uint8_t*>(blob.data());
+ const size_t size = blob.size();
+ if (WebPGetFeatures(data, size, &config.input) != VP8_STATUS_OK) return;
+ if ((size_t)config.input.width * config.input.height >
+ fuzz_utils::kFuzzPxLimit) {
+ return;
+ }
+
+ // Using two independent criteria ensures that all combinations of options
+ // can reach each path at the decoding stage, with meaningful differences.
+
+ const uint8_t value = fuzz_utils::FuzzHash(data, size);
+ const float factor = factor_u8 / 255.f; // 0-1
+
+ config.options.flip = flip;
+ config.options.bypass_filtering = bypass_filtering;
+ config.options.no_fancy_upsampling = no_fancy_upsampling;
+ config.options.use_threads = use_threads;
+ if (use_cropping) {
+ config.options.use_cropping = 1;
+ config.options.crop_width = (int)(config.input.width * (1 - factor));
+ config.options.crop_height = (int)(config.input.height * (1 - factor));
+ config.options.crop_left = config.input.width - config.options.crop_width;
+ config.options.crop_top = config.input.height - config.options.crop_height;
+ }
+ if (use_dithering) {
+ int strength = (int)(factor * 100);
+ config.options.dithering_strength = strength;
+ config.options.alpha_dithering_strength = 100 - strength;
+ }
+ if (use_scaling) {
+ config.options.use_scaling = 1;
+ config.options.scaled_width = (int)(config.input.width * factor * 2);
+ config.options.scaled_height = (int)(config.input.height * factor * 2);
+ }
+ config.output.colorspace = static_cast<WEBP_CSP_MODE>(colorspace);
+
+ for (int i = 0; i < 2; ++i) {
+ if (i == 1) {
+ // Use the bitstream data to generate extreme ranges for the options. An
+ // alternative approach would be to use a custom corpus containing webp
+ // files prepended with sizeof(config.options) zeroes to allow the fuzzer
+ // to modify these independently.
+ const int data_offset = 50;
+ if (data_offset + sizeof(config.options) >= size) break;
+ memcpy(&config.options, data + data_offset, sizeof(config.options));
+
+ // Skip easily avoidable out-of-memory fuzzing errors.
+ if (config.options.use_scaling) {
+ int input_width = config.input.width;
+ int input_height = config.input.height;
+ if (config.options.use_cropping) {
+ const int cw = config.options.crop_width;
+ const int ch = config.options.crop_height;
+ const int x = config.options.crop_left & ~1;
+ const int y = config.options.crop_top & ~1;
+ if (WebPCheckCropDimensions(input_width, input_height, x, y, cw,
+ ch)) {
+ input_width = cw;
+ input_height = ch;
+ }
+ }
+
+ int scaled_width = config.options.scaled_width;
+ int scaled_height = config.options.scaled_height;
+ if (WebPRescalerGetScaledDimensions(input_width, input_height,
+ &scaled_width, &scaled_height)) {
+ size_t fuzz_px_limit = fuzz_utils::kFuzzPxLimit;
+ if (scaled_width != config.input.width ||
+ scaled_height != config.input.height) {
+ // Using the WebPRescalerImport internally can significantly slow
+ // down the execution. Avoid timeouts due to that.
+ fuzz_px_limit /= 2;
+ }
+ // A big output canvas can lead to out-of-memory and timeout issues,
+ // but a big internal working buffer can too. Also, rescaling from a
+ // very wide input image to a very tall canvas can be as slow as
+ // decoding a huge number of pixels. Avoid timeouts due to these.
+ const uint64_t max_num_operations =
+ (uint64_t)std::max(scaled_width, config.input.width) *
+ std::max(scaled_height, config.input.height);
+ if (max_num_operations > fuzz_px_limit) {
+ break;
+ }
+ }
+ }
+ }
+ if (incremental) {
+ // Decodes incrementally in chunks of increasing size.
+ WebPIDecoder* idec = WebPIDecode(NULL, 0, &config);
+ if (!idec) return;
+ VP8StatusCode status;
+ if (size & 8) {
+ size_t available_size = value + 1;
+ while (1) {
+ if (available_size > size) available_size = size;
+ status = WebPIUpdate(idec, data, available_size);
+ if (status != VP8_STATUS_SUSPENDED || available_size == size) break;
+ available_size *= 2;
+ }
+ } else {
+ // WebPIAppend expects new data and its size with each call.
+ // Implemented here by simply advancing the pointer into data.
+ const uint8_t* new_data = data;
+ size_t new_size = value + 1;
+ while (1) {
+ if (new_data + new_size > data + size) {
+ new_size = data + size - new_data;
+ }
+ status = WebPIAppend(idec, new_data, new_size);
+ if (status != VP8_STATUS_SUSPENDED || new_size == 0) break;
+ new_data += new_size;
+ new_size *= 2;
+ }
+ }
+ WebPIDelete(idec);
+ } else {
+ (void)WebPDecode(data, size, &config);
+ }
+
+ WebPFreeDecBuffer(&config.output);
+ }
+}
+
+} // namespace
+
+FUZZ_TEST(AdvancedApi, AdvancedApiTest)
+ .WithDomains(
+ fuzztest::String()
+ .WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1),
+ /*factor_u8=*/fuzztest::Arbitrary<uint8_t>(),
+ /*flip=*/fuzztest::Arbitrary<bool>(),
+ /*bypass_filtering=*/fuzztest::Arbitrary<bool>(),
+ /*no_fancy_upsampling=*/fuzztest::Arbitrary<bool>(),
+ /*use_threads=*/fuzztest::Arbitrary<bool>(),
+ /*use_cropping=*/fuzztest::Arbitrary<bool>(),
+ /*use_scaling=*/fuzztest::Arbitrary<bool>(),
+ /*use_dithering=*/fuzztest::Arbitrary<bool>(),
+#if defined(WEBP_REDUCE_CSP)
+ fuzztest::ElementOf<int>({static_cast<int>(MODE_RGBA),
+ static_cast<int>(MODE_BGRA),
+ static_cast<int>(MODE_rgbA),
+ static_cast<int>(MODE_bgrA)}),
+#else
+ fuzztest::InRange<int>(0, static_cast<int>(MODE_LAST) - 1),
+#endif
+ /*incremental=*/fuzztest::Arbitrary<bool>());
diff --git a/c-lib/tests/fuzzer/animation_api_fuzzer.c b/c-lib/tests/fuzzer/animation_api_fuzzer.c
@@ -1,78 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include "./fuzz_utils.h"
-#include "src/webp/decode.h"
-#include "src/webp/demux.h"
-#include "src/webp/mux_types.h"
-
-int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
- WebPData webp_data;
- WebPDataInit(&webp_data);
- webp_data.size = size;
- webp_data.bytes = data;
-
- // WebPAnimDecoderNew uses WebPDemux internally to calloc canvas size.
- WebPDemuxer* const demux = WebPDemux(&webp_data);
- if (!demux) return 0;
- const uint32_t cw = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
- const uint32_t ch = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
- if ((size_t)cw * ch > kFuzzPxLimit) {
- WebPDemuxDelete(demux);
- return 0;
- }
-
- // In addition to canvas size, check each frame separately.
- WebPIterator iter;
- for (int i = 0; i < kFuzzFrameLimit; i++) {
- if (!WebPDemuxGetFrame(demux, i + 1, &iter)) break;
- int w, h;
- if (WebPGetInfo(iter.fragment.bytes, iter.fragment.size, &w, &h)) {
- if ((size_t)w * h > kFuzzPxLimit) { // image size of the frame payload
- WebPDemuxReleaseIterator(&iter);
- WebPDemuxDelete(demux);
- return 0;
- }
- }
- }
-
- WebPDemuxReleaseIterator(&iter);
- WebPDemuxDelete(demux);
-
- WebPAnimDecoderOptions dec_options;
- if (!WebPAnimDecoderOptionsInit(&dec_options)) return 0;
-
- dec_options.use_threads = size & 1;
- // Animations only support 4 (of 12) modes.
- dec_options.color_mode = (WEBP_CSP_MODE)(size % MODE_LAST);
- if (dec_options.color_mode != MODE_BGRA &&
- dec_options.color_mode != MODE_rgbA &&
- dec_options.color_mode != MODE_bgrA) {
- dec_options.color_mode = MODE_RGBA;
- }
-
- WebPAnimDecoder* dec = WebPAnimDecoderNew(&webp_data, &dec_options);
- if (!dec) return 0;
-
- for (int i = 0; i < kFuzzFrameLimit; i++) {
- uint8_t* buf;
- int timestamp;
- if (!WebPAnimDecoderGetNext(dec, &buf, ×tamp)) break;
- }
-
- WebPAnimDecoderDelete(dec);
- return 0;
-}
diff --git a/c-lib/tests/fuzzer/animation_api_fuzzer.cc b/c-lib/tests/fuzzer/animation_api_fuzzer.cc
@@ -0,0 +1,91 @@
+// Copyright 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+
+#include "./fuzz_utils.h"
+#include "src/webp/decode.h"
+#include "src/webp/demux.h"
+#include "src/webp/mux_types.h"
+
+namespace {
+
+void AnimationApiTest(std::string_view blob, bool use_threads,
+ WEBP_CSP_MODE color_mode) {
+ const size_t size = blob.size();
+ WebPData webp_data;
+ WebPDataInit(&webp_data);
+ webp_data.size = size;
+ webp_data.bytes = reinterpret_cast<const uint8_t*>(blob.data());
+
+ // WebPAnimDecoderNew uses WebPDemux internally to calloc canvas size.
+ WebPDemuxer* const demux = WebPDemux(&webp_data);
+ if (!demux) return;
+ const uint32_t cw = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
+ const uint32_t ch = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
+ if ((size_t)cw * ch > fuzz_utils::kFuzzPxLimit) {
+ WebPDemuxDelete(demux);
+ return;
+ }
+
+ // In addition to canvas size, check each frame separately.
+ WebPIterator iter;
+ for (int i = 0; i < fuzz_utils::kFuzzFrameLimit; i++) {
+ if (!WebPDemuxGetFrame(demux, i + 1, &iter)) break;
+ int w, h;
+ if (WebPGetInfo(iter.fragment.bytes, iter.fragment.size, &w, &h)) {
+ if ((size_t)w * h >
+ fuzz_utils::kFuzzPxLimit) { // image size of the frame payload
+ WebPDemuxReleaseIterator(&iter);
+ WebPDemuxDelete(demux);
+ return;
+ }
+ }
+ }
+
+ WebPDemuxReleaseIterator(&iter);
+ WebPDemuxDelete(demux);
+
+ WebPAnimDecoderOptions dec_options;
+ if (!WebPAnimDecoderOptionsInit(&dec_options)) return;
+
+ dec_options.use_threads = use_threads;
+ dec_options.color_mode = color_mode;
+
+ WebPAnimDecoder* dec = WebPAnimDecoderNew(&webp_data, &dec_options);
+ if (!dec) return;
+
+ for (int i = 0; i < fuzz_utils::kFuzzFrameLimit; i++) {
+ uint8_t* buf;
+ int timestamp;
+ if (!WebPAnimDecoderGetNext(dec, &buf, ×tamp)) break;
+ }
+
+ WebPAnimDecoderDelete(dec);
+}
+
+} // namespace
+
+FUZZ_TEST(AnimationApi, AnimationApiTest)
+ .WithDomains(
+ fuzztest::String()
+ .WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1),
+ /*use_threads=*/fuzztest::Arbitrary<bool>(),
+ // Animations only support 4 (out of 12) modes.
+ fuzztest::ElementOf<WEBP_CSP_MODE>({MODE_RGBA, MODE_BGRA, MODE_rgbA,
+ MODE_bgrA}));
diff --git a/c-lib/tests/fuzzer/animdecoder_fuzzer.cc b/c-lib/tests/fuzzer/animdecoder_fuzzer.cc
@@ -14,25 +14,41 @@
//
////////////////////////////////////////////////////////////////////////////////
-#include "examples/anim_util.h"
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+
+#include "./fuzz_utils.h"
#include "imageio/imageio_util.h"
+#include "src/webp/decode.h"
#include "src/webp/demux.h"
+#include "src/webp/mux_types.h"
+
+namespace {
+
+void AnimDecoderTest(std::string_view blob) {
+ const uint8_t* const data = reinterpret_cast<const uint8_t*>(blob.data());
+ const size_t size = blob.size();
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// WebPAnimDecoderGetInfo() is too late to check the canvas size as
// WebPAnimDecoderNew() will handle the allocations.
+ const size_t kMaxNumBytes = 2684354560; // RSS (resident set size) limit.
+ const size_t kMaxNumPixels = kMaxNumBytes / 4; // At most ARGB.
+ const size_t kMaxNumPixelsSafe = kMaxNumPixels / 2; // Allow one buffer copy.
WebPBitstreamFeatures features;
if (WebPGetFeatures(data, size, &features) == VP8_STATUS_OK) {
if (!ImgIoUtilCheckSizeArgumentsOverflow(features.width * 4,
- features.height)) {
- return 0;
+ features.height) ||
+ static_cast<size_t>(features.width) * features.height >
+ kMaxNumPixelsSafe) {
+ return;
}
}
// decode everything as an animation
- WebPData webp_data = { data, size };
- WebPAnimDecoder* const dec = WebPAnimDecoderNew(&webp_data, NULL);
- if (dec == NULL) return 0;
+ WebPData webp_data = {data, size};
+ WebPAnimDecoder* const dec = WebPAnimDecoderNew(&webp_data, nullptr);
+ if (dec == nullptr) return;
WebPAnimInfo info;
if (!WebPAnimDecoderGetInfo(dec, &info)) goto End;
@@ -46,7 +62,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
int timestamp;
if (!WebPAnimDecoderGetNext(dec, &buf, ×tamp)) break;
}
- End:
+End:
WebPAnimDecoderDelete(dec);
- return 0;
}
+
+} // namespace
+
+FUZZ_TEST(AnimDecoder, AnimDecoderTest)
+ .WithDomains(
+ fuzztest::String()
+ .WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1));
diff --git a/c-lib/tests/fuzzer/animencoder_fuzzer.cc b/c-lib/tests/fuzzer/animencoder_fuzzer.cc
@@ -14,54 +14,67 @@
//
////////////////////////////////////////////////////////////////////////////////
-#include <stdio.h>
-#include <stdlib.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <string_view>
+#include <utility>
+#include <vector>
#include "./fuzz_utils.h"
+#include "src/dsp/cpu.h"
#include "src/webp/encode.h"
#include "src/webp/mux.h"
+#include "src/webp/mux_types.h"
namespace {
-const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo;
+const VP8CPUInfo default_VP8GetCPUInfo = fuzz_utils::VP8GetCPUInfo;
+
+struct FrameConfig {
+ int use_argb;
+ int timestamp;
+ WebPConfig webp_config;
+ fuzz_utils::CropOrScaleParams crop_or_scale_params;
+ int source_image_index;
+};
+
+auto ArbitraryKMinKMax() {
+ return fuzztest::FlatMap(
+ [](int kmax) {
+ const int min_kmin = (kmax > 1) ? (kmax / 2) : 0;
+ const int max_kmin = (kmax > 1) ? (kmax - 1) : 0;
+ return fuzztest::PairOf(fuzztest::InRange(min_kmin, max_kmin),
+ fuzztest::Just(kmax));
+ },
+ fuzztest::InRange(0, 15));
+}
int AddFrame(WebPAnimEncoder** const enc,
const WebPAnimEncoderOptions& anim_config, int* const width,
- int* const height, int timestamp_ms, const uint8_t data[],
- size_t size, uint32_t* const bit_pos) {
+ int* const height, int timestamp_ms,
+ const FrameConfig& frame_config, const uint8_t data[], size_t size,
+ uint32_t* const bit_pos) {
if (enc == nullptr || width == nullptr || height == nullptr) {
fprintf(stderr, "NULL parameters.\n");
if (enc != nullptr) WebPAnimEncoderDelete(*enc);
- abort();
+ std::abort();
}
// Init the source picture.
- WebPPicture pic;
- if (!WebPPictureInit(&pic)) {
- fprintf(stderr, "WebPPictureInit failed.\n");
- WebPAnimEncoderDelete(*enc);
- abort();
- }
- pic.use_argb = Extract(1, data, size, bit_pos);
-
- // Read the source picture.
- if (!ExtractSourcePicture(&pic, data, size, bit_pos)) {
- const WebPEncodingError error_code = pic.error_code;
- WebPPictureFree(&pic);
- if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
- fprintf(stderr, "Can't read input image. Error code: %d\n", error_code);
- abort();
- }
+ WebPPicture pic = fuzz_utils::GetSourcePicture(
+ frame_config.source_image_index, frame_config.use_argb);
// Crop and scale.
if (*enc == nullptr) { // First frame will set canvas width and height.
- if (!ExtractAndCropOrScale(&pic, data, size, bit_pos)) {
+ if (!fuzz_utils::CropOrScale(&pic, frame_config.crop_or_scale_params)) {
const WebPEncodingError error_code = pic.error_code;
WebPPictureFree(&pic);
if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n",
error_code);
- abort();
+ std::abort();
}
} else { // Other frames will be resized to the first frame's dimensions.
if (!WebPPictureRescale(&pic, *width, *height)) {
@@ -72,7 +85,7 @@ int AddFrame(WebPAnimEncoder** const enc,
fprintf(stderr,
"WebPPictureRescale failed. Size: %d,%d. Error code: %d\n",
*width, *height, error_code);
- abort();
+ std::abort();
}
}
@@ -88,13 +101,7 @@ int AddFrame(WebPAnimEncoder** const enc,
}
// Create frame encoding config.
- WebPConfig config;
- if (!ExtractWebPConfig(&config, data, size, bit_pos)) {
- fprintf(stderr, "ExtractWebPConfig failed.\n");
- WebPAnimEncoderDelete(*enc);
- WebPPictureFree(&pic);
- abort();
- }
+ WebPConfig config = frame_config.webp_config;
// Skip slow settings on big images, it's likely to timeout.
if (pic.width * pic.height > 32 * 32) {
config.method = (config.method > 4) ? 4 : config.method;
@@ -108,50 +115,54 @@ int AddFrame(WebPAnimEncoder** const enc,
const WebPEncodingError error_code = pic.error_code;
WebPAnimEncoderDelete(*enc);
WebPPictureFree(&pic);
- if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+ // Tolerate failures when running under the nallocfuzz engine as
+ // WebPAnimEncoderAdd() may fail due to memory allocation errors outside of
+ // the encoder; in muxer functions that return booleans for instance.
+ if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY ||
+ error_code == VP8_ENC_ERROR_BAD_WRITE ||
+ getenv("NALLOC_FUZZ_VERSION") != nullptr) {
+ return 0;
+ }
fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code);
- abort();
+ std::abort();
}
WebPPictureFree(&pic);
return 1;
}
-} // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
+void AnimEncoderTest(std::string_view blob, bool minimize_size,
+ std::pair<int, int> kmin_kmax, bool allow_mixed,
+ const std::vector<FrameConfig>& frame_configs,
+ int optimization_index) {
WebPAnimEncoder* enc = nullptr;
int width = 0, height = 0, timestamp_ms = 0;
uint32_t bit_pos = 0;
+ const uint8_t* const data = reinterpret_cast<const uint8_t*>(blob.data());
+ const size_t size = blob.size();
- ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos);
+ fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
// Extract a configuration from the packed bits.
WebPAnimEncoderOptions anim_config;
if (!WebPAnimEncoderOptionsInit(&anim_config)) {
fprintf(stderr, "WebPAnimEncoderOptionsInit failed.\n");
- abort();
+ std::abort();
}
- anim_config.minimize_size = Extract(1, data, size, &bit_pos);
- anim_config.kmax = Extract(15, data, size, &bit_pos);
- const int min_kmin = (anim_config.kmax > 1) ? (anim_config.kmax / 2) : 0;
- const int max_kmin = (anim_config.kmax > 1) ? (anim_config.kmax - 1) : 0;
- anim_config.kmin =
- min_kmin + Extract((uint32_t)(max_kmin - min_kmin), data, size, &bit_pos);
- anim_config.allow_mixed = Extract(1, data, size, &bit_pos);
+ anim_config.minimize_size = minimize_size;
+ anim_config.kmin = kmin_kmax.first;
+ anim_config.kmax = kmin_kmax.second;
+ anim_config.allow_mixed = allow_mixed;
anim_config.verbose = 0;
- const int nb_frames = 1 + Extract(15, data, size, &bit_pos);
-
// For each frame.
- for (int i = 0; i < nb_frames; ++i) {
- if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms, data, size,
- &bit_pos)) {
- return 0;
+ for (const FrameConfig& frame_config : frame_configs) {
+ if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms,
+ frame_config, data, size, &bit_pos)) {
+ return;
}
- timestamp_ms += (1 << (2 + Extract(15, data, size, &bit_pos))) +
- Extract(1, data, size, &bit_pos); // [1..131073], arbitrary
+ timestamp_ms += frame_config.timestamp;
}
// Assemble.
@@ -159,19 +170,39 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
fprintf(stderr, "Last WebPAnimEncoderAdd failed: %s.\n",
WebPAnimEncoderGetError(enc));
WebPAnimEncoderDelete(enc);
- abort();
+ std::abort();
}
WebPData webp_data;
WebPDataInit(&webp_data);
- if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
+ // Tolerate failures when running under the nallocfuzz engine as allocations
+ // during assembly may fail.
+ if (!WebPAnimEncoderAssemble(enc, &webp_data) &&
+ getenv("NALLOC_FUZZ_VERSION") == nullptr) {
fprintf(stderr, "WebPAnimEncoderAssemble failed: %s.\n",
WebPAnimEncoderGetError(enc));
WebPAnimEncoderDelete(enc);
WebPDataClear(&webp_data);
- abort();
+ std::abort();
}
WebPAnimEncoderDelete(enc);
WebPDataClear(&webp_data);
- return 0;
}
+
+} // namespace
+
+FUZZ_TEST(AnimEncoder, AnimEncoderTest)
+ .WithDomains(
+ fuzztest::String(),
+ /*minimize_size=*/fuzztest::Arbitrary<bool>(), ArbitraryKMinKMax(),
+ /*allow_mixed=*/fuzztest::Arbitrary<bool>(),
+ fuzztest::VectorOf(
+ fuzztest::StructOf<FrameConfig>(
+ fuzztest::InRange<int>(0, 1), fuzztest::InRange<int>(0, 131073),
+ fuzz_utils::ArbitraryWebPConfig(),
+ fuzz_utils::ArbitraryCropOrScaleParams(),
+ fuzztest::InRange<int>(0, fuzz_utils::kNumSourceImages - 1)))
+ .WithMinSize(1)
+ .WithMaxSize(15),
+ /*optimization_index=*/
+ fuzztest::InRange<uint32_t>(0, fuzz_utils::kMaxOptimizationIndex));
diff --git a/c-lib/tests/fuzzer/dec_fuzzer.cc b/c-lib/tests/fuzzer/dec_fuzzer.cc
@@ -0,0 +1,49 @@
+// Copyright 2024 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <string_view>
+
+#include "src/webp/decode.h"
+#include "tests/fuzzer/fuzz_utils.h"
+
+namespace {
+
+void DecodeWebP(std::string_view arbitrary_bytes) {
+ WebPDecoderConfig decoder_config;
+ if (!WebPInitDecoderConfig(&decoder_config)) {
+ fprintf(stderr, "WebPInitDecoderConfig failed.\n");
+ std::abort();
+ }
+ const VP8StatusCode status =
+ WebPDecode(reinterpret_cast<const uint8_t*>(arbitrary_bytes.data()),
+ arbitrary_bytes.size(), &decoder_config);
+ WebPFreeDecBuffer(&decoder_config.output);
+ // The decoding may fail (because the fuzzed input can be anything) but not
+ // for these reasons.
+ if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_USER_ABORT) {
+ std::abort();
+ }
+}
+
+FUZZ_TEST(WebPSuite, DecodeWebP)
+ .WithDomains(
+ fuzztest::String()
+ .WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1));
+
+} // namespace
diff --git a/c-lib/tests/fuzzer/enc_dec_fuzzer.cc b/c-lib/tests/fuzzer/enc_dec_fuzzer.cc
@@ -14,57 +14,38 @@
//
////////////////////////////////////////////////////////////////////////////////
-#include <stdio.h>
-#include <stdlib.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
#include "./fuzz_utils.h"
+#include "src/dsp/cpu.h"
#include "src/webp/decode.h"
#include "src/webp/encode.h"
namespace {
-const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo;
+const VP8CPUInfo default_VP8GetCPUInfo = fuzz_utils::VP8GetCPUInfo;
-} // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
- uint32_t bit_pos = 0;
-
- ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos);
+void EncDecTest(bool use_argb, int source_image_index, WebPConfig config,
+ int optimization_index,
+ const fuzz_utils::CropOrScaleParams& crop_or_scale_params) {
+ fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
// Init the source picture.
- WebPPicture pic;
- if (!WebPPictureInit(&pic)) {
- fprintf(stderr, "WebPPictureInit failed.\n");
- abort();
- }
- pic.use_argb = Extract(1, data, size, &bit_pos);
-
- // Read the source picture.
- if (!ExtractSourcePicture(&pic, data, size, &bit_pos)) {
- const WebPEncodingError error_code = pic.error_code;
- WebPPictureFree(&pic);
- if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
- fprintf(stderr, "Can't read input image. Error code: %d\n", error_code);
- abort();
- }
+ WebPPicture pic = fuzz_utils::GetSourcePicture(source_image_index, use_argb);
// Crop and scale.
- if (!ExtractAndCropOrScale(&pic, data, size, &bit_pos)) {
+ if (!fuzz_utils::CropOrScale(&pic, crop_or_scale_params)) {
const WebPEncodingError error_code = pic.error_code;
WebPPictureFree(&pic);
- if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+ if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return;
fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n",
error_code);
- abort();
+ std::abort();
}
- // Extract a configuration from the packed bits.
- WebPConfig config;
- if (!ExtractWebPConfig(&config, data, size, &bit_pos)) {
- fprintf(stderr, "ExtractWebPConfig failed.\n");
- abort();
- }
// Skip slow settings on big images, it's likely to timeout.
if (pic.width * pic.height > 32 * 32) {
if (config.lossless) {
@@ -91,51 +72,82 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
const WebPEncodingError error_code = pic.error_code;
WebPMemoryWriterClear(&memory_writer);
WebPPictureFree(&pic);
- if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+ if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY ||
+ error_code == VP8_ENC_ERROR_BAD_WRITE) {
+ return;
+ }
fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code);
- abort();
+ std::abort();
}
// Try decoding the result.
- int w, h;
const uint8_t* const out_data = memory_writer.mem;
const size_t out_size = memory_writer.size;
- uint8_t* const rgba = WebPDecodeBGRA(out_data, out_size, &w, &h);
- if (rgba == nullptr || w != pic.width || h != pic.height) {
- fprintf(stderr, "WebPDecodeBGRA failed.\n");
- WebPFree(rgba);
+ WebPDecoderConfig dec_config;
+ if (!WebPInitDecoderConfig(&dec_config)) {
+ fprintf(stderr, "WebPInitDecoderConfig failed.\n");
WebPMemoryWriterClear(&memory_writer);
WebPPictureFree(&pic);
- abort();
+ std::abort();
}
- // Compare the results if exact encoding.
- if (pic.use_argb && config.lossless && config.near_lossless == 100) {
- const uint32_t* src1 = (const uint32_t*)rgba;
- const uint32_t* src2 = pic.argb;
- for (int y = 0; y < h; ++y, src1 += w, src2 += pic.argb_stride) {
- for (int x = 0; x < w; ++x) {
- uint32_t v1 = src1[x], v2 = src2[x];
- if (!config.exact) {
- if ((v1 & 0xff000000u) == 0 || (v2 & 0xff000000u) == 0) {
- // Only keep alpha for comparison of fully transparent area.
- v1 &= 0xff000000u;
- v2 &= 0xff000000u;
+ dec_config.output.colorspace = MODE_BGRA;
+ const VP8StatusCode status = WebPDecode(out_data, out_size, &dec_config);
+ if ((status != VP8_STATUS_OK && status != VP8_STATUS_OUT_OF_MEMORY &&
+ status != VP8_STATUS_USER_ABORT) ||
+ (status == VP8_STATUS_OK && (dec_config.output.width != pic.width ||
+ dec_config.output.height != pic.height))) {
+ fprintf(stderr, "WebPDecode failed. status: %d.\n", status);
+ WebPFreeDecBuffer(&dec_config.output);
+ WebPMemoryWriterClear(&memory_writer);
+ WebPPictureFree(&pic);
+ std::abort();
+ }
+
+ if (status == VP8_STATUS_OK) {
+ const uint8_t* const rgba = dec_config.output.u.RGBA.rgba;
+ const int w = dec_config.output.width;
+ const int h = dec_config.output.height;
+
+ // Compare the results if exact encoding.
+ if (pic.use_argb && config.lossless && config.near_lossless == 100) {
+ const uint32_t* src1 = (const uint32_t*)rgba;
+ const uint32_t* src2 = pic.argb;
+ for (int y = 0; y < h; ++y, src1 += w, src2 += pic.argb_stride) {
+ for (int x = 0; x < w; ++x) {
+ uint32_t v1 = src1[x], v2 = src2[x];
+ if (!config.exact) {
+ if ((v1 & 0xff000000u) == 0 || (v2 & 0xff000000u) == 0) {
+ // Only keep alpha for comparison of fully transparent area.
+ v1 &= 0xff000000u;
+ v2 &= 0xff000000u;
+ }
+ }
+ if (v1 != v2) {
+ fprintf(stderr, "Lossless compression failed pixel-exactness.\n");
+ WebPFreeDecBuffer(&dec_config.output);
+ WebPMemoryWriterClear(&memory_writer);
+ WebPPictureFree(&pic);
+ std::abort();
}
- }
- if (v1 != v2) {
- fprintf(stderr, "Lossless compression failed pixel-exactness.\n");
- WebPFree(rgba);
- WebPMemoryWriterClear(&memory_writer);
- WebPPictureFree(&pic);
- abort();
}
}
}
}
- WebPFree(rgba);
+ WebPFreeDecBuffer(&dec_config.output);
WebPMemoryWriterClear(&memory_writer);
WebPPictureFree(&pic);
- return 0;
}
+
+} // namespace
+
+FUZZ_TEST(EncDec, EncDecTest)
+ .WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
+ /*source_image_index=*/
+ fuzztest::InRange<int>(0, fuzz_utils::kNumSourceImages - 1),
+ fuzz_utils::ArbitraryWebPConfig(),
+ /*optimization_index=*/
+ fuzztest::InRange<uint32_t>(0,
+ fuzz_utils::kMaxOptimizationIndex),
+ fuzz_utils::ArbitraryCropOrScaleParams());
diff --git a/c-lib/tests/fuzzer/enc_fuzzer.cc b/c-lib/tests/fuzzer/enc_fuzzer.cc
@@ -0,0 +1,140 @@
+// Copyright 2024 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+#include <string>
+#include <string_view>
+
+#include "imageio/image_dec.h"
+#include "src/dsp/cpu.h"
+#include "src/webp/decode.h"
+#include "src/webp/encode.h"
+#include "src/webp/types.h"
+#include "tests/fuzzer/fuzz_utils.h"
+
+namespace {
+
+const VP8CPUInfo default_VP8GetCPUInfo = fuzz_utils::VP8GetCPUInfo;
+
+void EncTest(std::string_view file, uint32_t optimization_index, bool use_argb,
+ WebPConfig config,
+ const fuzz_utils::CropOrScaleParams& crop_or_scale_params) {
+ fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
+
+ // Init the source picture.
+ WebPPicture pic;
+ if (!WebPPictureInit(&pic)) {
+ std::cerr << "WebPPictureInit failed.\n";
+ std::abort();
+ }
+ pic.use_argb = use_argb;
+
+ const uint8_t* const file_data =
+ reinterpret_cast<const uint8_t*>(file.data());
+ if (fuzz_utils::IsImageTooBig(file_data, file.size())) return;
+ WebPImageReader reader = WebPGuessImageReader(file_data, file.size());
+ if (!reader(file_data, file.size(), &pic, 1, NULL)) return;
+
+ // Crop and scale.
+ if (!CropOrScale(&pic, crop_or_scale_params)) {
+ const WebPEncodingError error_code = pic.error_code;
+ WebPPictureFree(&pic);
+ if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return;
+ std::cerr << "CropOrScale failed. Error code: " << error_code << "\n";
+ std::abort();
+ }
+
+ // Skip the cruncher except on small images, it's likely to timeout.
+ if (config.lossless && config.quality == 100. && config.method == 6 &&
+ pic.width * pic.height >= 16384) {
+ config.lossless = 0;
+ }
+
+ // Encode.
+ WebPMemoryWriter memory_writer;
+ WebPMemoryWriterInit(&memory_writer);
+ pic.writer = WebPMemoryWrite;
+ pic.custom_ptr = &memory_writer;
+ if (!WebPEncode(&config, &pic)) {
+ const WebPEncodingError error_code = pic.error_code;
+ WebPMemoryWriterClear(&memory_writer);
+ WebPPictureFree(&pic);
+ if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return;
+ std::cerr << "WebPEncode failed. Error code: " << error_code
+ << " \nFile starts with: " << file.substr(0, 20) << "\n";
+ std::abort();
+ }
+
+ // Try decoding the result.
+ int w, h;
+ const uint8_t* const out_data = memory_writer.mem;
+ const size_t out_size = memory_writer.size;
+ uint8_t* const rgba = WebPDecodeBGRA(out_data, out_size, &w, &h);
+ if (rgba == nullptr || w != pic.width || h != pic.height) {
+ std::cerr << "WebPDecodeBGRA failed.\nFile starts with: "
+ << file.substr(0, 20) << "\n";
+ WebPFree(rgba);
+ WebPMemoryWriterClear(&memory_writer);
+ WebPPictureFree(&pic);
+ std::abort();
+ }
+
+ // Compare the results if exact encoding.
+ if (pic.use_argb && config.lossless && config.near_lossless == 100) {
+ const uint32_t* src1 = (const uint32_t*)rgba;
+ const uint32_t* src2 = pic.argb;
+ for (int y = 0; y < h; ++y, src1 += w, src2 += pic.argb_stride) {
+ for (int x = 0; x < w; ++x) {
+ uint32_t v1 = src1[x], v2 = src2[x];
+ if (!config.exact) {
+ if ((v1 & 0xff000000u) == 0 || (v2 & 0xff000000u) == 0) {
+ // Only keep alpha for comparison of fully transparent area.
+ v1 &= 0xff000000u;
+ v2 &= 0xff000000u;
+ }
+ }
+ if (v1 != v2) {
+ std::cerr
+ << "Lossless compression failed pixel-exactness.\nFile starts "
+ "with: "
+ << file.substr(0, 20) << "\n";
+ WebPFree(rgba);
+ WebPMemoryWriterClear(&memory_writer);
+ WebPPictureFree(&pic);
+ std::abort();
+ }
+ }
+ }
+ }
+
+ WebPFree(rgba);
+ WebPMemoryWriterClear(&memory_writer);
+ WebPPictureFree(&pic);
+}
+
+} // namespace
+
+FUZZ_TEST(Enc, EncTest)
+ .WithDomains(
+ fuzztest::Arbitrary<std::string>(),
+ /*optimization_index=*/
+ fuzztest::InRange<uint32_t>(0, fuzz_utils::kMaxOptimizationIndex),
+ /*use_argb=*/fuzztest::Arbitrary<bool>(),
+ fuzz_utils::ArbitraryWebPConfig(),
+ fuzz_utils::ArbitraryCropOrScaleParams());
diff --git a/c-lib/tests/fuzzer/fuzz_utils.cc b/c-lib/tests/fuzzer/fuzz_utils.cc
@@ -0,0 +1,198 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "./fuzz_utils.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <vector>
+
+#include "./img_alpha.h"
+#include "./img_grid.h"
+#include "./img_peak.h"
+#include "src/dsp/cpu.h"
+#include "src/webp/decode.h"
+#include "src/webp/encode.h"
+#include "src/webp/types.h"
+
+namespace fuzz_utils {
+
+WebPPicture GetSourcePicture(int image_index, bool use_argb) {
+ WebPPicture pic;
+ if (!WebPPictureInit(&pic)) std::abort();
+ pic.use_argb = use_argb;
+
+ // Pick a source picture.
+ const int kImagesWidth[] = {kImgAlphaWidth, kImgGridWidth, kImgPeakWidth};
+ const int kImagesHeight[] = {kImgAlphaHeight, kImgGridHeight, kImgPeakHeight};
+ const uint8_t* const image_data = kImagesData[image_index];
+ pic.width = kImagesWidth[image_index];
+ pic.height = kImagesHeight[image_index];
+ pic.argb_stride = pic.width * 4 * sizeof(uint8_t);
+
+ // Read the bytes.
+ if (!WebPPictureImportRGBA(&pic, image_data, pic.argb_stride)) std::abort();
+ return pic;
+}
+
+//------------------------------------------------------------------------------
+
+int CropOrScale(WebPPicture* const pic, const CropOrScaleParams& params) {
+ if (pic == NULL) return 0;
+#if !defined(WEBP_REDUCE_SIZE)
+ if (params.alter_input) {
+ if (params.crop_or_scale) {
+ const int cropped_width = std::max(1, pic->width / params.width_ratio);
+ const int cropped_height = std::max(1, pic->height / params.height_ratio);
+ const int cropped_left = (pic->width - cropped_width) / params.left_ratio;
+ const int cropped_top = (pic->height - cropped_height) / params.top_ratio;
+ return WebPPictureCrop(pic, cropped_left, cropped_top, cropped_width,
+ cropped_height);
+ } else {
+ const int scaled_width = 1 + (pic->width * params.width_ratio) / 8;
+ const int scaled_height = 1 + (pic->height * params.height_ratio) / 8;
+ return WebPPictureRescale(pic, scaled_width, scaled_height);
+ }
+ }
+#else // defined(WEBP_REDUCE_SIZE)
+ (void)pic;
+ (void)params;
+#endif // !defined(WEBP_REDUCE_SIZE)
+ return 1;
+}
+
+extern "C" VP8CPUInfo VP8GetCPUInfo;
+static VP8CPUInfo GetCPUInfo;
+
+static WEBP_INLINE int GetCPUInfoNoSSE41(CPUFeature feature) {
+ if (feature == kSSE4_1 || feature == kAVX) return 0;
+ return GetCPUInfo(feature);
+}
+
+static WEBP_INLINE int GetCPUInfoNoAVX(CPUFeature feature) {
+ if (feature == kAVX) return 0;
+ return GetCPUInfo(feature);
+}
+
+static WEBP_INLINE int GetCPUInfoForceSlowSSSE3(CPUFeature feature) {
+ if (feature == kSlowSSSE3 && GetCPUInfo(kSSE3)) {
+ return 1; // we have SSE3 -> force SlowSSSE3
+ }
+ return GetCPUInfo(feature);
+}
+
+static WEBP_INLINE int GetCPUInfoOnlyC(CPUFeature feature) {
+ (void)feature;
+ return 0;
+}
+
+void SetOptimization(VP8CPUInfo default_VP8GetCPUInfo, uint32_t index) {
+ assert(index <= kMaxOptimizationIndex);
+ GetCPUInfo = default_VP8GetCPUInfo;
+ const VP8CPUInfo kVP8CPUInfos[kMaxOptimizationIndex + 1] = {
+ GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3, GetCPUInfoNoSSE41,
+ GetCPUInfoNoAVX, GetCPUInfo};
+ VP8GetCPUInfo = kVP8CPUInfos[index];
+}
+
+//------------------------------------------------------------------------------
+
+std::vector<std::string> ReadFilesFromDirectory(std::string_view dir) {
+ std::vector<std::tuple<std::string>> tuples =
+ fuzztest::ReadFilesFromDirectory(dir);
+ std::vector<std::string> strings(tuples.size());
+ for (size_t i = 0; i < tuples.size(); ++i) {
+ using std::swap;
+ swap(std::get<0>(tuples[i]), strings[i]);
+ }
+ return strings;
+}
+
+//------------------------------------------------------------------------------
+// The code in this section is copied from
+// https://github.com/webmproject/sjpeg/blob/
+// 1c025b3dbc2246de3e1d7c287970f1a01291800f/src/jpeg_tools.cc#L47
+// (same license as this file).
+
+namespace {
+// Constants below are marker codes defined in JPEG spec
+// ISO/IEC 10918-1 : 1993(E) Table B.1
+// See also: http://www.w3.org/Graphics/JPEG/itu-t81.pdf
+
+#define M_SOF0 0xffc0
+#define M_SOF1 0xffc1
+
+const uint8_t* GetSOFData(const uint8_t* src, int size) {
+ if (src == NULL) return NULL;
+ const uint8_t* const end = src + size - 8; // 8 bytes of safety, for marker
+ src += 2; // skip M_SOI
+ for (; src < end && *src != 0xff; ++src) { /* search first 0xff marker */
+ }
+ while (src < end) {
+ const uint32_t marker = static_cast<uint32_t>((src[0] << 8) | src[1]);
+ if (marker == M_SOF0 || marker == M_SOF1) return src;
+ const size_t s = 2 + ((src[2] << 8) | src[3]);
+ src += s;
+ }
+ return NULL; // No SOF marker found
+}
+
+bool SjpegDimensions(const uint8_t* src0, size_t size, int* width, int* height,
+ int* is_yuv420) {
+ if (width == NULL || height == NULL) return false;
+ const uint8_t* src = GetSOFData(src0, size);
+ const size_t left_over = size - (src - src0);
+ if (src == NULL || left_over < 8 + 3 * 1) return false;
+ if (height != NULL) *height = (src[5] << 8) | src[6];
+ if (width != NULL) *width = (src[7] << 8) | src[8];
+ if (is_yuv420 != NULL) {
+ const size_t nb_comps = src[9];
+ *is_yuv420 = (nb_comps == 3);
+ if (left_over < 11 + 3 * nb_comps) return false;
+ for (int c = 0; *is_yuv420 && c < 3; ++c) {
+ const int expected_dim = (c == 0 ? 0x22 : 0x11);
+ *is_yuv420 &= (src[11 + c * 3] == expected_dim);
+ }
+ }
+ return true;
+}
+} // namespace
+
+//------------------------------------------------------------------------------
+
+bool IsImageTooBig(const uint8_t* data, size_t size) {
+ int width, height, components;
+ if (SjpegDimensions(data, size, &width, &height, &components) ||
+ WebPGetInfo(data, size, &width, &height)) {
+ // Look at the number of 8x8px blocks rather than the overall pixel count
+ // when comparing to memory and duration thresholds.
+ const size_t ceiled_width = ((size_t)width + 7) / 8 * 8;
+ const size_t ceiled_height = ((size_t)height + 7) / 8 * 8;
+ // Threshold to avoid out-of-memory and timeout issues.
+ // The threshold is arbitrary but below the fuzzer limit of 2 GB.
+ // The value cannot be 2 GB because of the added memory by MSAN.
+ if (ceiled_width * ceiled_height > kFuzzPxLimit) return true;
+ }
+ return false;
+}
+
+} // namespace fuzz_utils
diff --git a/c-lib/tests/fuzzer/fuzz_utils.h b/c-lib/tests/fuzzer/fuzz_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2018 Google Inc.
+// Copyright 2018-2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,20 +17,41 @@
#ifndef WEBP_TESTS_FUZZER_FUZZ_UTILS_H_
#define WEBP_TESTS_FUZZER_FUZZ_UTILS_H_
-#include <stdint.h>
-#include <stdlib.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
#include "./img_alpha.h"
#include "./img_grid.h"
#include "./img_peak.h"
-#include "src/dsp/dsp.h"
+#include "fuzztest/fuzztest.h"
+#include "src/dsp/cpu.h"
#include "src/webp/encode.h"
+#include "src/webp/types.h"
+
+namespace fuzz_utils {
//------------------------------------------------------------------------------
// Arbitrary limits to prevent OOM, timeout, or slow execution.
-//
+
// The decoded image size, and for animations additionally the canvas size.
+// Enabling some sanitizers slow down runtime significantly.
+// Use a very low threshold in this case to avoid timeouts.
+#if defined(__SANITIZE_ADDRESS__) // GCC
+static const size_t kFuzzPxLimit = 1024 * 1024 / 10;
+#elif !defined(__has_feature) // Clang
static const size_t kFuzzPxLimit = 1024 * 1024;
+#elif __has_feature(address_sanitizer) || __has_feature(memory_sanitizer)
+static const size_t kFuzzPxLimit = 1024 * 1024 / 18;
+#else
+static const size_t kFuzzPxLimit = 1024 * 1024;
+#endif
+
// Demuxed or decoded animation frames.
static const int kFuzzFrameLimit = 3;
@@ -43,165 +64,139 @@ static WEBP_INLINE uint8_t FuzzHash(const uint8_t* const data, size_t size) {
return value;
}
-//------------------------------------------------------------------------------
-// Extract an integer in [0, max_value].
-
-static WEBP_INLINE uint32_t Extract(uint32_t max_value,
- const uint8_t data[], size_t size,
- uint32_t* const bit_pos) {
- uint32_t v = 0;
- uint32_t range = 1;
- while (*bit_pos < 8 * size && range <= max_value) {
- const uint8_t mask = 1u << (*bit_pos & 7);
- v = (v << 1) | !!(data[*bit_pos >> 3] & mask);
- range <<= 1;
- ++*bit_pos;
- }
- return v % (max_value + 1);
-}
+#ifdef __cplusplus
+extern "C" VP8CPUInfo VP8GetCPUInfo;
+#else
+extern VP8CPUInfo VP8GetCPUInfo;
+#endif
//------------------------------------------------------------------------------
-// Some functions to override VP8GetCPUInfo and disable some optimizations.
-
-static VP8CPUInfo GetCPUInfo;
-
-static WEBP_INLINE int GetCPUInfoNoSSE41(CPUFeature feature) {
- if (feature == kSSE4_1 || feature == kAVX) return 0;
- return GetCPUInfo(feature);
-}
-static WEBP_INLINE int GetCPUInfoNoAVX(CPUFeature feature) {
- if (feature == kAVX) return 0;
- return GetCPUInfo(feature);
+constexpr const uint8_t* kImagesData[] = {kImgAlphaData, kImgGridData,
+ kImgPeakData};
+constexpr size_t kNumSourceImages =
+ sizeof(kImagesData) / sizeof(kImagesData[0]);
+
+WebPPicture GetSourcePicture(int image_index, bool use_argb);
+
+static inline auto ArbitraryWebPConfig() {
+ return fuzztest::Map(
+ [](int lossless, int quality, int method, int image_hint, int segments,
+ int sns_strength, int filter_strength, int filter_sharpness,
+ int filter_type, int autofilter, int alpha_compression,
+ int alpha_filtering, int alpha_quality, int pass, int preprocessing,
+ int partitions, int partition_limit, int emulate_jpeg_size,
+ int thread_level, int low_memory, int near_lossless, int exact,
+ int use_delta_palette, int use_sharp_yuv) -> WebPConfig {
+ WebPConfig config;
+ if (!WebPConfigInit(&config)) abort();
+ config.lossless = lossless;
+ config.quality = quality;
+ config.method = method;
+ config.image_hint = (WebPImageHint)image_hint;
+ config.segments = segments;
+ config.sns_strength = sns_strength;
+ config.filter_strength = filter_strength;
+ config.filter_sharpness = filter_sharpness;
+ config.filter_type = filter_type;
+ config.autofilter = autofilter;
+ config.alpha_compression = alpha_compression;
+ config.alpha_filtering = alpha_filtering;
+ config.alpha_quality = alpha_quality;
+ config.pass = pass;
+ config.show_compressed = 1;
+ config.preprocessing = preprocessing;
+ config.partitions = partitions;
+ config.partition_limit = 10 * partition_limit;
+ config.emulate_jpeg_size = emulate_jpeg_size;
+ config.thread_level = thread_level;
+ config.low_memory = low_memory;
+ config.near_lossless = 20 * near_lossless;
+ config.exact = exact;
+ config.use_delta_palette = use_delta_palette;
+ config.use_sharp_yuv = use_sharp_yuv;
+ if (!WebPValidateConfig(&config)) abort();
+ return config;
+ },
+ /*lossless=*/fuzztest::InRange<int>(0, 1),
+ /*quality=*/fuzztest::InRange<int>(0, 100),
+ /*method=*/fuzztest::InRange<int>(0, 6),
+ /*image_hint=*/fuzztest::InRange<int>(0, WEBP_HINT_LAST - 1),
+ /*segments=*/fuzztest::InRange<int>(1, 4),
+ /*sns_strength=*/fuzztest::InRange<int>(0, 100),
+ /*filter_strength=*/fuzztest::InRange<int>(0, 100),
+ /*filter_sharpness=*/fuzztest::InRange<int>(0, 7),
+ /*filter_type=*/fuzztest::InRange<int>(0, 1),
+ /*autofilter=*/fuzztest::InRange<int>(0, 1),
+ /*alpha_compression=*/fuzztest::InRange<int>(0, 1),
+ /*alpha_filtering=*/fuzztest::InRange<int>(0, 2),
+ /*alpha_quality=*/fuzztest::InRange<int>(0, 100),
+ /*pass=*/fuzztest::InRange<int>(1, 10),
+ /*preprocessing=*/fuzztest::InRange<int>(0, 2),
+ /*partitions=*/fuzztest::InRange<int>(0, 3),
+ /*partition_limit=*/fuzztest::InRange<int>(0, 10),
+ /*emulate_jpeg_size=*/fuzztest::InRange<int>(0, 1),
+ /*thread_level=*/fuzztest::InRange<int>(0, 1),
+ /*low_memory=*/fuzztest::InRange<int>(0, 1),
+ /*near_lossless=*/fuzztest::InRange<int>(0, 5),
+ /*exact=*/fuzztest::InRange<int>(0, 1),
+ /*use_delta_palette=*/fuzztest::InRange<int>(0, 1),
+ /*use_sharp_yuv=*/fuzztest::InRange<int>(0, 1));
}
-static WEBP_INLINE int GetCPUInfoForceSlowSSSE3(CPUFeature feature) {
- if (feature == kSlowSSSE3 && GetCPUInfo(kSSE3)) {
- return 1; // we have SSE3 -> force SlowSSSE3
- }
- return GetCPUInfo(feature);
+struct CropOrScaleParams {
+ bool alter_input;
+ bool crop_or_scale;
+ int width_ratio;
+ int height_ratio;
+ int left_ratio;
+ int top_ratio;
+};
+
+static inline auto ArbitraryCropOrScaleParams() {
+ return fuzztest::Map(
+ [](const std::optional<std::pair<int, int>>& width_height_ratio,
+ const std::optional<std::pair<int, int>>& left_top_ratio)
+ -> CropOrScaleParams {
+ CropOrScaleParams params;
+ params.alter_input = width_height_ratio.has_value();
+ if (params.alter_input) {
+ params.width_ratio = width_height_ratio->first;
+ params.height_ratio = width_height_ratio->second;
+ params.crop_or_scale = left_top_ratio.has_value();
+ if (params.crop_or_scale) {
+ params.left_ratio = left_top_ratio->first;
+ params.top_ratio = left_top_ratio->second;
+ }
+ }
+ return params;
+ },
+ fuzztest::OptionalOf(
+ fuzztest::PairOf(fuzztest::InRange(1, 8), fuzztest::InRange(1, 8))),
+ fuzztest::OptionalOf(
+ fuzztest::PairOf(fuzztest::InRange(1, 8), fuzztest::InRange(1, 8))));
}
-static WEBP_INLINE int GetCPUInfoOnlyC(CPUFeature feature) {
- (void)feature;
- return 0;
-}
+// Crops or scales a picture according to the given params.
+int CropOrScale(WebPPicture* pic, const CropOrScaleParams& params);
-static WEBP_INLINE void ExtractAndDisableOptimizations(
- VP8CPUInfo default_VP8GetCPUInfo, const uint8_t data[], size_t size,
- uint32_t* const bit_pos) {
- GetCPUInfo = default_VP8GetCPUInfo;
- const VP8CPUInfo kVP8CPUInfos[5] = {GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3,
- GetCPUInfoNoSSE41, GetCPUInfoNoAVX,
- GetCPUInfo};
- int VP8GetCPUInfo_index = Extract(4, data, size, bit_pos);
- VP8GetCPUInfo = kVP8CPUInfos[VP8GetCPUInfo_index];
-}
+// Imposes a level of optimization among one of the kMaxOptimizationIndex+1
+// possible values: OnlyC, ForceSlowSSSE3, NoSSE41, NoAVX, default.
+static constexpr uint32_t kMaxOptimizationIndex = 4;
+void SetOptimization(VP8CPUInfo default_VP8GetCPUInfo, uint32_t index);
//------------------------------------------------------------------------------
-static WEBP_INLINE int ExtractWebPConfig(WebPConfig* const config,
- const uint8_t data[], size_t size,
- uint32_t* const bit_pos) {
- if (config == NULL || !WebPConfigInit(config)) return 0;
- config->lossless = Extract(1, data, size, bit_pos);
- config->quality = Extract(100, data, size, bit_pos);
- config->method = Extract(6, data, size, bit_pos);
- config->image_hint =
- (WebPImageHint)Extract(WEBP_HINT_LAST - 1, data, size, bit_pos);
- config->segments = 1 + Extract(3, data, size, bit_pos);
- config->sns_strength = Extract(100, data, size, bit_pos);
- config->filter_strength = Extract(100, data, size, bit_pos);
- config->filter_sharpness = Extract(7, data, size, bit_pos);
- config->filter_type = Extract(1, data, size, bit_pos);
- config->autofilter = Extract(1, data, size, bit_pos);
- config->alpha_compression = Extract(1, data, size, bit_pos);
- config->alpha_filtering = Extract(2, data, size, bit_pos);
- config->alpha_quality = Extract(100, data, size, bit_pos);
- config->pass = 1 + Extract(9, data, size, bit_pos);
- config->show_compressed = 1;
- config->preprocessing = Extract(2, data, size, bit_pos);
- config->partitions = Extract(3, data, size, bit_pos);
- config->partition_limit = 10 * Extract(10, data, size, bit_pos);
- config->emulate_jpeg_size = Extract(1, data, size, bit_pos);
- config->thread_level = Extract(1, data, size, bit_pos);
- config->low_memory = Extract(1, data, size, bit_pos);
- config->near_lossless = 20 * Extract(5, data, size, bit_pos);
- config->exact = Extract(1, data, size, bit_pos);
- config->use_delta_palette = Extract(1, data, size, bit_pos);
- config->use_sharp_yuv = Extract(1, data, size, bit_pos);
- return WebPValidateConfig(config);
-}
+// See https://developers.google.com/speed/webp/docs/riff_container.
+static constexpr size_t kMaxWebPFileSize = (1ull << 32) - 2; // 4 GiB - 2
-//------------------------------------------------------------------------------
+std::vector<std::string> GetDictionaryFromFiles(
+ const std::vector<std::string_view>& file_paths);
-static WEBP_INLINE int ExtractSourcePicture(WebPPicture* const pic,
- const uint8_t data[], size_t size,
- uint32_t* const bit_pos) {
- if (pic == NULL) return 0;
-
- // Pick a source picture.
- const uint8_t* kImagesData[] = {
- kImgAlphaData,
- kImgGridData,
- kImgPeakData
- };
- const int kImagesWidth[] = {
- kImgAlphaWidth,
- kImgGridWidth,
- kImgPeakWidth
- };
- const int kImagesHeight[] = {
- kImgAlphaHeight,
- kImgGridHeight,
- kImgPeakHeight
- };
- const size_t kNbImages = sizeof(kImagesData) / sizeof(kImagesData[0]);
- const size_t image_index = Extract(kNbImages - 1, data, size, bit_pos);
- const uint8_t* const image_data = kImagesData[image_index];
- pic->width = kImagesWidth[image_index];
- pic->height = kImagesHeight[image_index];
- pic->argb_stride = pic->width * 4 * sizeof(uint8_t);
-
- // Read the bytes.
- return WebPPictureImportRGBA(pic, image_data, pic->argb_stride);
-}
-
-//------------------------------------------------------------------------------
+// Checks whether the binary blob containing a JPEG or WebP is too big for the
+// fuzzer.
+bool IsImageTooBig(const uint8_t* data, size_t size);
-static WEBP_INLINE int Max(int a, int b) { return ((a < b) ? b : a); }
-
-static WEBP_INLINE int ExtractAndCropOrScale(WebPPicture* const pic,
- const uint8_t data[], size_t size,
- uint32_t* const bit_pos) {
- if (pic == NULL) return 0;
-#if !defined(WEBP_REDUCE_SIZE)
- const int alter_input = Extract(1, data, size, bit_pos);
- const int crop_or_scale = Extract(1, data, size, bit_pos);
- const int width_ratio = 1 + Extract(7, data, size, bit_pos);
- const int height_ratio = 1 + Extract(7, data, size, bit_pos);
- if (alter_input) {
- if (crop_or_scale) {
- const uint32_t left_ratio = 1 + Extract(7, data, size, bit_pos);
- const uint32_t top_ratio = 1 + Extract(7, data, size, bit_pos);
- const int cropped_width = Max(1, pic->width / width_ratio);
- const int cropped_height = Max(1, pic->height / height_ratio);
- const int cropped_left = (pic->width - cropped_width) / left_ratio;
- const int cropped_top = (pic->height - cropped_height) / top_ratio;
- return WebPPictureCrop(pic, cropped_left, cropped_top, cropped_width,
- cropped_height);
- } else {
- const int scaled_width = 1 + (pic->width * width_ratio) / 8;
- const int scaled_height = 1 + (pic->height * height_ratio) / 8;
- return WebPPictureRescale(pic, scaled_width, scaled_height);
- }
- }
-#else // defined(WEBP_REDUCE_SIZE)
- (void)data;
- (void)size;
- (void)bit_pos;
-#endif // !defined(WEBP_REDUCE_SIZE)
- return 1;
-}
+} // namespace fuzz_utils
#endif // WEBP_TESTS_FUZZER_FUZZ_UTILS_H_
diff --git a/c-lib/tests/fuzzer/huffman_fuzzer.cc b/c-lib/tests/fuzzer/huffman_fuzzer.cc
@@ -0,0 +1,78 @@
+// Copyright 2023 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <string_view>
+
+#include "./fuzz_utils.h"
+#include "src/dec/vp8li_dec.h"
+#include "src/utils/bit_reader_utils.h"
+#include "src/utils/huffman_utils.h"
+#include "src/utils/utils.h"
+#include "src/webp/format_constants.h"
+
+namespace {
+
+void HuffmanTest(std::string_view blob) {
+ const uint8_t* const data = reinterpret_cast<const uint8_t*>(blob.data());
+ const size_t size = blob.size();
+
+ // Number of bits to initialize data.
+ static const int kColorCacheBitsBits = 4;
+ // 'num_htree_groups' is contained in the RG channel, hence 16 bits.
+ static const int kNumHtreeGroupsBits = 16;
+ if (size * sizeof(*data) < kColorCacheBitsBits + kNumHtreeGroupsBits) {
+ return;
+ }
+
+ // A non-NULL mapping brings minor changes that are tested by the normal
+ // fuzzer.
+ int* const mapping = NULL;
+ HuffmanTables huffman_tables;
+ memset(&huffman_tables, 0, sizeof(huffman_tables));
+ HTreeGroup* htree_groups = NULL;
+
+ int num_htree_groups, num_htree_groups_max, color_cache_bits;
+ VP8LBitReader* br;
+ VP8LDecoder* dec = VP8LNew();
+ if (dec == NULL) goto Error;
+ br = &dec->br;
+ VP8LInitBitReader(br, data, size);
+
+ color_cache_bits = VP8LReadBits(br, kColorCacheBitsBits);
+ if (color_cache_bits < 1 || color_cache_bits > MAX_CACHE_BITS) goto Error;
+
+ num_htree_groups = VP8LReadBits(br, kNumHtreeGroupsBits);
+ // 'num_htree_groups' cannot be 0 as it is built from a non-empty image.
+ if (num_htree_groups == 0) goto Error;
+ // This variable is only useful when mapping is not NULL.
+ num_htree_groups_max = num_htree_groups;
+ (void)ReadHuffmanCodesHelper(color_cache_bits, num_htree_groups,
+ num_htree_groups_max, mapping, dec,
+ &huffman_tables, &htree_groups);
+
+Error:
+ WebPSafeFree(mapping);
+ VP8LHtreeGroupsFree(htree_groups);
+ VP8LHuffmanTablesDeallocate(&huffman_tables);
+ VP8LDelete(dec);
+}
+
+} // namespace
+
+FUZZ_TEST(Huffman, HuffmanTest).WithDomains(fuzztest::String());
diff --git a/c-lib/tests/fuzzer/imageio_fuzzer.cc b/c-lib/tests/fuzzer/imageio_fuzzer.cc
@@ -0,0 +1,76 @@
+// Copyright 2024 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// Fuzzing of libwebp's image readers
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+#include <string_view>
+
+#include "imageio/image_dec.h"
+#include "imageio/metadata.h"
+#include "src/webp/encode.h"
+#include "tests/fuzzer/fuzz_utils.h"
+
+namespace {
+
+void TestReader(const uint8_t *data, size_t size, WebPImageReader reader,
+ bool keep_alpha, bool use_argb) {
+ WebPPicture pic;
+ if (!WebPPictureInit(&pic)) {
+ std::cerr << "WebPPictureInit failed" << std::endl;
+ std::abort();
+ }
+ Metadata metadata;
+ MetadataInit(&metadata);
+ pic.use_argb = use_argb ? 1 : 0;
+
+ if (!fuzz_utils::IsImageTooBig(data, size)) {
+ (void)(*reader)(data, size, &pic, keep_alpha ? 1 : 0, &metadata);
+ }
+ WebPPictureFree(&pic);
+ MetadataFree(&metadata);
+}
+
+constexpr WebPInputFileFormat kUnknown = WEBP_UNSUPPORTED_FORMAT;
+
+void Decode(std::string_view arbitrary_bytes, WebPInputFileFormat format,
+ bool keep_alpha, bool use_argb) {
+ const uint8_t *data =
+ reinterpret_cast<const uint8_t *>(arbitrary_bytes.data());
+ const size_t size = arbitrary_bytes.size();
+ if (format == kUnknown) {
+ (void)WebPGuessImageType(data, size); // shouldn't fail
+ TestReader(data, size, WebPGuessImageReader(data, size), keep_alpha,
+ use_argb);
+ } else {
+ TestReader(data, size, WebPGetImageReader(format), keep_alpha, use_argb);
+ }
+}
+
+FUZZ_TEST(ImageIOSuite, Decode)
+ .WithDomains(
+ fuzztest::String()
+ .WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1),
+ fuzztest::ElementOf<WebPInputFileFormat>(
+ {WEBP_PNG_FORMAT, WEBP_JPEG_FORMAT, WEBP_TIFF_FORMAT,
+ WEBP_WEBP_FORMAT, WEBP_PNM_FORMAT, kUnknown}),
+ /*keep_alpha=*/fuzztest::Arbitrary<bool>(),
+ /*use_argb=*/fuzztest::Arbitrary<bool>());
+
+} // namespace
diff --git a/c-lib/tests/fuzzer/makefile.unix b/c-lib/tests/fuzzer/makefile.unix
@@ -1,30 +0,0 @@
-# This Makefile will compile all fuzzing targets. It doesn't check tool
-# requirements and paths may need to be updated depending on your environment.
-# Note a clang 6+ toolchain is assumed for use of -fsanitize=fuzzer.
-
-CC = clang
-CXX = clang++
-CFLAGS = -fsanitize=fuzzer -I../../src -I../.. -Wall -Wextra
-CXXFLAGS = $(CFLAGS)
-LDFLAGS = -fsanitize=fuzzer
-LDLIBS = ../../src/mux/libwebpmux.a ../../src/demux/libwebpdemux.a
-LDLIBS += ../../src/libwebp.a ../../imageio/libimageio_util.a
-
-FUZZERS = advanced_api_fuzzer animation_api_fuzzer animencoder_fuzzer
-FUZZERS += animdecoder_fuzzer mux_demux_api_fuzzer enc_dec_fuzzer
-FUZZERS += simple_api_fuzzer
-
-%.o: fuzz_utils.h img_alpha.h img_grid.h img_peak.h
-all: $(FUZZERS)
-
-define FUZZER_template
-$(1): $$(addsuffix .o, $(1)) $(LDLIBS)
-OBJS += $$(addsuffix .o, $(1))
-endef
-
-$(foreach fuzzer, $(FUZZERS), $(eval $(call FUZZER_template, $(fuzzer))))
-
-clean:
- $(RM) $(FUZZERS) $(OBJS)
-
-.PHONY: all clean
diff --git a/c-lib/tests/fuzzer/mux_demux_api_fuzzer.c b/c-lib/tests/fuzzer/mux_demux_api_fuzzer.c
@@ -1,96 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include "./fuzz_utils.h"
-#include "src/webp/demux.h"
-#include "src/webp/mux.h"
-
-int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
- WebPData webp_data;
- WebPDataInit(&webp_data);
- webp_data.size = size;
- webp_data.bytes = data;
-
- // Extracted chunks and frames are not processed or decoded,
- // which is already covered extensively by the other fuzz targets.
-
- if (size & 1) {
- // Mux API
- WebPMux* mux = WebPMuxCreate(&webp_data, size & 2);
- if (!mux) return 0;
-
- WebPData chunk;
- WebPMuxGetChunk(mux, "EXIF", &chunk);
- WebPMuxGetChunk(mux, "ICCP", &chunk);
- WebPMuxGetChunk(mux, "FUZZ", &chunk); // unknown
-
- uint32_t flags;
- WebPMuxGetFeatures(mux, &flags);
-
- WebPMuxAnimParams params;
- WebPMuxGetAnimationParams(mux, ¶ms);
-
- WebPMuxError status;
- WebPMuxFrameInfo info;
- for (int i = 0; i < kFuzzFrameLimit; i++) {
- status = WebPMuxGetFrame(mux, i + 1, &info);
- if (status == WEBP_MUX_NOT_FOUND) {
- break;
- } else if (status == WEBP_MUX_OK) {
- WebPDataClear(&info.bitstream);
- }
- }
-
- WebPMuxDelete(mux);
- } else {
- // Demux API
- WebPDemuxer* demux;
- if (size & 2) {
- WebPDemuxState state;
- demux = WebPDemuxPartial(&webp_data, &state);
- if (state < WEBP_DEMUX_PARSED_HEADER) {
- WebPDemuxDelete(demux);
- return 0;
- }
- } else {
- demux = WebPDemux(&webp_data);
- if (!demux) return 0;
- }
-
- WebPChunkIterator chunk_iter;
- if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) {
- WebPDemuxNextChunk(&chunk_iter);
- }
- WebPDemuxReleaseChunkIterator(&chunk_iter);
- if (WebPDemuxGetChunk(demux, "ICCP", 0, &chunk_iter)) { // 0 == last
- WebPDemuxPrevChunk(&chunk_iter);
- }
- WebPDemuxReleaseChunkIterator(&chunk_iter);
- // Skips FUZZ because the Demux API has no concept of (un)known chunks.
-
- WebPIterator iter;
- if (WebPDemuxGetFrame(demux, 1, &iter)) {
- for (int i = 1; i < kFuzzFrameLimit; i++) {
- if (!WebPDemuxNextFrame(&iter)) break;
- }
- }
-
- WebPDemuxReleaseIterator(&iter);
- WebPDemuxDelete(demux);
- }
-
- return 0;
-}
diff --git a/c-lib/tests/fuzzer/mux_demux_api_fuzzer.cc b/c-lib/tests/fuzzer/mux_demux_api_fuzzer.cc
@@ -0,0 +1,110 @@
+// Copyright 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+
+#include "./fuzz_utils.h"
+#include "src/webp/demux.h"
+#include "src/webp/mux.h"
+#include "src/webp/mux_types.h"
+
+namespace {
+
+void MuxDemuxApiTest(std::string_view data_in, bool use_mux_api) {
+ const size_t size = data_in.size();
+ WebPData webp_data;
+ WebPDataInit(&webp_data);
+ webp_data.size = size;
+ webp_data.bytes = reinterpret_cast<const uint8_t*>(data_in.data());
+
+ // Extracted chunks and frames are not processed or decoded,
+ // which is already covered extensively by the other fuzz targets.
+
+ if (use_mux_api) {
+ // Mux API
+ WebPMux* mux = WebPMuxCreate(&webp_data, size & 2);
+ if (!mux) return;
+
+ WebPData chunk;
+ (void)WebPMuxGetChunk(mux, "EXIF", &chunk);
+ (void)WebPMuxGetChunk(mux, "ICCP", &chunk);
+ (void)WebPMuxGetChunk(mux, "FUZZ", &chunk); // unknown
+
+ uint32_t flags;
+ (void)WebPMuxGetFeatures(mux, &flags);
+
+ WebPMuxAnimParams params;
+ (void)WebPMuxGetAnimationParams(mux, ¶ms);
+
+ WebPMuxError status;
+ WebPMuxFrameInfo info;
+ for (int i = 0; i < fuzz_utils::kFuzzFrameLimit; i++) {
+ status = WebPMuxGetFrame(mux, i + 1, &info);
+ if (status == WEBP_MUX_NOT_FOUND) {
+ break;
+ } else if (status == WEBP_MUX_OK) {
+ WebPDataClear(&info.bitstream);
+ }
+ }
+
+ WebPMuxDelete(mux);
+ } else {
+ // Demux API
+ WebPDemuxer* demux;
+ if (size & 2) {
+ WebPDemuxState state;
+ demux = WebPDemuxPartial(&webp_data, &state);
+ if (state < WEBP_DEMUX_PARSED_HEADER) {
+ WebPDemuxDelete(demux);
+ return;
+ }
+ } else {
+ demux = WebPDemux(&webp_data);
+ if (!demux) return;
+ }
+
+ WebPChunkIterator chunk_iter;
+ if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) {
+ (void)WebPDemuxNextChunk(&chunk_iter);
+ }
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ if (WebPDemuxGetChunk(demux, "ICCP", 0, &chunk_iter)) { // 0 == last
+ (void)WebPDemuxPrevChunk(&chunk_iter);
+ }
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ // Skips FUZZ because the Demux API has no concept of (un)known chunks.
+
+ WebPIterator iter;
+ if (WebPDemuxGetFrame(demux, 1, &iter)) {
+ for (int i = 1; i < fuzz_utils::kFuzzFrameLimit; i++) {
+ if (!WebPDemuxNextFrame(&iter)) break;
+ }
+ }
+
+ WebPDemuxReleaseIterator(&iter);
+ WebPDemuxDelete(demux);
+ }
+}
+
+} // namespace
+
+FUZZ_TEST(MuxDemuxApi, MuxDemuxApiTest)
+ .WithDomains(
+ fuzztest::String()
+ .WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1),
+ /*mux=*/fuzztest::Arbitrary<bool>());
diff --git a/c-lib/tests/fuzzer/oss-fuzz/build.sh b/c-lib/tests/fuzzer/oss-fuzz/build.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+# Copyright 2018 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+
+# This script is meant to be run by the oss-fuzz infrastructure from the script
+# https://github.com/google/oss-fuzz/blob/master/projects/libwebp/build.sh
+# It builds the different fuzz targets.
+# Only the libfuzzer engine is supported.
+
+# To test changes to this file:
+# - make changes and commit to your REPO
+# - run:
+# git clone --depth=1 git@github.com:google/oss-fuzz.git
+# cd oss-fuzz
+# - modify projects/libwebp/Dockerfile to point to your REPO
+# - run:
+# python3 infra/helper.py build_image libwebp
+# # enter 'y' and wait for everything to be downloaded
+# - run:
+# python3 infra/helper.py build_fuzzers --sanitizer address libwebp
+# # wait for the tests to be built
+# And then run the fuzzer locally, for example:
+# python3 infra/helper.py run_fuzzer libwebp \
+# --sanitizer address \
+# animencoder_fuzzer@AnimEncoder.AnimEncoderTest
+
+set -eu
+
+EXTRA_CMAKE_FLAGS=""
+export CXXFLAGS="${CXXFLAGS} -DFUZZTEST_COMPATIBILITY_MODE"
+EXTRA_CMAKE_FLAGS="-DFUZZTEST_COMPATIBILITY_MODE=libfuzzer"
+
+# limit allocation size to reduce spurious OOMs
+WEBP_CFLAGS="$CFLAGS -DWEBP_MAX_IMAGE_SIZE=838860800" # 800MiB
+
+export CFLAGS="$WEBP_CFLAGS"
+cmake -S . -B build -DWEBP_BUILD_FUZZTEST=ON ${EXTRA_CMAKE_FLAGS}
+cd build && make -j$(nproc) && cd ..
+
+find $SRC/libwebp-test-data -type f -size -32k -iname "*.webp" \
+ -exec zip -qju fuzz_seed_corpus.zip "{}" \;
+
+# The following is taken from https://github.com/google/oss-fuzz/blob/31ac7244748ea7390015455fb034b1f4eda039d9/infra/base-images/base-builder/compile_fuzztests.sh#L59
+# Iterate the fuzz binaries and list each fuzz entrypoint in the binary. For
+# each entrypoint create a wrapper script that calls into the binaries the
+# given entrypoint as argument.
+# The scripts will be named:
+# {binary_name}@{fuzztest_entrypoint}
+FUZZ_TEST_BINARIES_OUT_PATHS=$(find ./build/tests/fuzzer/ -executable -type f)
+echo "Fuzz binaries: $FUZZ_TEST_BINARIES_OUT_PATHS"
+for fuzz_main_file in $FUZZ_TEST_BINARIES_OUT_PATHS; do
+ FUZZ_TESTS=$($fuzz_main_file --list_fuzz_tests | cut -d ' ' -f 4)
+ cp -f ${fuzz_main_file} $OUT/
+ fuzz_basename=$(basename $fuzz_main_file)
+ chmod -x $OUT/$fuzz_basename
+ for fuzz_entrypoint in $FUZZ_TESTS; do
+ TARGET_FUZZER="${fuzz_basename}@$fuzz_entrypoint"
+ # Write executer script
+ cat << EOF > $OUT/$TARGET_FUZZER
+#!/bin/sh
+# LLVMFuzzerTestOneInput for fuzzer detection.
+this_dir=\$(dirname "\$0")
+export TEST_DATA_DIRS=\$this_dir/corpus
+chmod +x \$this_dir/$fuzz_basename
+\$this_dir/$fuzz_basename --fuzz=$fuzz_entrypoint -- \$@
+chmod -x \$this_dir/$fuzz_basename
+EOF
+ chmod +x $OUT/$TARGET_FUZZER
+ done
+ # Copy data.
+ cp fuzz_seed_corpus.zip $OUT/${fuzz_basename}_seed_corpus.zip
+ cp tests/fuzzer/fuzz.dict $OUT/${fuzz_basename}.dict
+done
diff --git a/c-lib/tests/fuzzer/patch.sh b/c-lib/tests/fuzzer/patch.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Fixes for https://github.com/google/fuzztest/issues/1124
+sed -i -e "s/-fsanitize=address//g" -e "s/-DADDRESS_SANITIZER//g" \
+ ./cmake/FuzzTestFlagSetup.cmake
+# Fixes for https://github.com/google/fuzztest/issues/1125
+before="if (IsEnginePlaceholderInput(data)) return;"
+after="if (data.size() == 0) return;"
+sed -i "s/${before}/${after}/" ./fuzztest/internal/compatibility_mode.cc
+sed -i "s/set(GTEST_HAS_ABSL ON)/set(GTEST_HAS_ABSL OFF)/" \
+ ./cmake/BuildDependencies.cmake
diff --git a/c-lib/tests/fuzzer/simple_api_fuzzer.c b/c-lib/tests/fuzzer/simple_api_fuzzer.c
@@ -1,88 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include "./fuzz_utils.h"
-#include "src/webp/decode.h"
-
-int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
- int w, h;
- if (!WebPGetInfo(data, size, &w, &h)) return 0;
- if ((size_t)w * h > kFuzzPxLimit) return 0;
-
- const uint8_t value = FuzzHash(data, size);
- uint8_t* buf = NULL;
-
- // For *Into functions, which decode into an external buffer, an
- // intentionally too small buffer can be given with low probability.
- if (value < 0x16) {
- buf = WebPDecodeRGBA(data, size, &w, &h);
- } else if (value < 0x2b) {
- buf = WebPDecodeBGRA(data, size, &w, &h);
-#if !defined(WEBP_REDUCE_CSP)
- } else if (value < 0x40) {
- buf = WebPDecodeARGB(data, size, &w, &h);
- } else if (value < 0x55) {
- buf = WebPDecodeRGB(data, size, &w, &h);
- } else if (value < 0x6a) {
- buf = WebPDecodeBGR(data, size, &w, &h);
-#endif // !defined(WEBP_REDUCE_CSP)
- } else if (value < 0x7f) {
- uint8_t *u, *v;
- int stride, uv_stride;
- buf = WebPDecodeYUV(data, size, &w, &h, &u, &v, &stride, &uv_stride);
- } else if (value < 0xe8) {
- const int stride = (value < 0xbe ? 4 : 3) * w;
- size_t buf_size = stride * h;
- if (value % 0x10 == 0) buf_size--;
- uint8_t* const ext_buf = (uint8_t*)malloc(buf_size);
- if (value < 0x94) {
- WebPDecodeRGBAInto(data, size, ext_buf, buf_size, stride);
-#if !defined(WEBP_REDUCE_CSP)
- } else if (value < 0xa9) {
- WebPDecodeARGBInto(data, size, ext_buf, buf_size, stride);
- } else if (value < 0xbe) {
- WebPDecodeBGRInto(data, size, ext_buf, buf_size, stride);
- } else if (value < 0xd3) {
- WebPDecodeRGBInto(data, size, ext_buf, buf_size, stride);
-#endif // !defined(WEBP_REDUCE_CSP)
- } else {
- WebPDecodeBGRAInto(data, size, ext_buf, buf_size, stride);
- }
- free(ext_buf);
- } else {
- size_t luma_size = w * h;
- const int uv_stride = (w + 1) / 2;
- size_t u_size = uv_stride * (h + 1) / 2;
- size_t v_size = uv_stride * (h + 1) / 2;
- if (value % 0x10 == 0) {
- if (size & 1) luma_size--;
- if (size & 2) u_size--;
- if (size & 4) v_size--;
- }
- uint8_t* const luma_buf = (uint8_t*)malloc(luma_size);
- uint8_t* const u_buf = (uint8_t*)malloc(u_size);
- uint8_t* const v_buf = (uint8_t*)malloc(v_size);
- WebPDecodeYUVInto(data, size, luma_buf, luma_size, w /* luma_stride */,
- u_buf, u_size, uv_stride, v_buf, v_size, uv_stride);
- free(luma_buf);
- free(u_buf);
- free(v_buf);
- }
-
- if (buf) WebPFree(buf);
-
- return 0;
-}
diff --git a/c-lib/tests/fuzzer/simple_api_fuzzer.cc b/c-lib/tests/fuzzer/simple_api_fuzzer.cc
@@ -0,0 +1,104 @@
+// Copyright 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <string_view>
+
+#include "./fuzz_utils.h"
+#include "src/webp/decode.h"
+#include "src/webp/types.h"
+
+namespace {
+
+void SimpleApiTest(std::string_view data_in) {
+ const uint8_t* const data = reinterpret_cast<const uint8_t*>(data_in.data());
+ const size_t size = data_in.size();
+ int w, h;
+ if (!WebPGetInfo(data, size, &w, &h)) return;
+ if ((size_t)w * h > fuzz_utils::kFuzzPxLimit) return;
+
+ const uint8_t value = fuzz_utils::FuzzHash(data, size);
+ uint8_t* buf = NULL;
+
+ // For *Into functions, which decode into an external buffer, an
+ // intentionally too small buffer can be given with low probability.
+ if (value < 0x16) {
+ buf = WebPDecodeRGBA(data, size, &w, &h);
+ } else if (value < 0x2b) {
+ buf = WebPDecodeBGRA(data, size, &w, &h);
+#if !defined(WEBP_REDUCE_CSP)
+ } else if (value < 0x40) {
+ buf = WebPDecodeARGB(data, size, &w, &h);
+ } else if (value < 0x55) {
+ buf = WebPDecodeRGB(data, size, &w, &h);
+ } else if (value < 0x6a) {
+ buf = WebPDecodeBGR(data, size, &w, &h);
+#endif // !defined(WEBP_REDUCE_CSP)
+ } else if (value < 0x7f) {
+ uint8_t *u, *v;
+ int stride, uv_stride;
+ buf = WebPDecodeYUV(data, size, &w, &h, &u, &v, &stride, &uv_stride);
+ } else if (value < 0xe8) {
+ const int stride = (value < 0xbe ? 4 : 3) * w;
+ size_t buf_size = stride * h;
+ if (value % 0x10 == 0) buf_size--;
+ uint8_t* const ext_buf = (uint8_t*)malloc(buf_size);
+ if (value < 0x94) {
+ (void)WebPDecodeRGBAInto(data, size, ext_buf, buf_size, stride);
+#if !defined(WEBP_REDUCE_CSP)
+ } else if (value < 0xa9) {
+ (void)WebPDecodeARGBInto(data, size, ext_buf, buf_size, stride);
+ } else if (value < 0xbe) {
+ (void)WebPDecodeBGRInto(data, size, ext_buf, buf_size, stride);
+ } else if (value < 0xd3) {
+ (void)WebPDecodeRGBInto(data, size, ext_buf, buf_size, stride);
+#endif // !defined(WEBP_REDUCE_CSP)
+ } else {
+ (void)WebPDecodeBGRAInto(data, size, ext_buf, buf_size, stride);
+ }
+ free(ext_buf);
+ } else {
+ size_t luma_size = w * h;
+ const int uv_stride = (w + 1) / 2;
+ size_t u_size = uv_stride * (h + 1) / 2;
+ size_t v_size = uv_stride * (h + 1) / 2;
+ if (value % 0x10 == 0) {
+ if (size & 1) luma_size--;
+ if (size & 2) u_size--;
+ if (size & 4) v_size--;
+ }
+ uint8_t* const luma_buf = (uint8_t*)malloc(luma_size);
+ uint8_t* const u_buf = (uint8_t*)malloc(u_size);
+ uint8_t* const v_buf = (uint8_t*)malloc(v_size);
+ (void)WebPDecodeYUVInto(data, size, luma_buf, luma_size,
+ w /* luma_stride */, u_buf, u_size, uv_stride,
+ v_buf, v_size, uv_stride);
+ free(luma_buf);
+ free(u_buf);
+ free(v_buf);
+ }
+
+ if (buf) WebPFree(buf);
+}
+
+} // namespace
+
+FUZZ_TEST(SimpleApi, SimpleApiTest)
+ .WithDomains(
+ fuzztest::String()
+ .WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1));
diff --git a/c-lib/tests/fuzzer/webp_info_fuzzer.cc b/c-lib/tests/fuzzer/webp_info_fuzzer.cc
@@ -0,0 +1,43 @@
+// Copyright 2024 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <cstdint>
+#include <string_view>
+
+#include "src/webp/mux_types.h"
+#include "tests/fuzzer/fuzz_utils.h"
+
+// Don't do that at home!
+#define main exec_main
+#include "examples/webpinfo.c"
+#undef main
+
+void WebPInfoTest(std::string_view data) {
+ WebPInfo webp_info;
+ WebPInfoInit(&webp_info);
+ webp_info.quiet = 1;
+ webp_info.show_summary = 0;
+ webp_info.show_diagnosis = 0;
+ webp_info.parse_bitstream = 1;
+ WebPData webp_data = {reinterpret_cast<const uint8_t *>(data.data()),
+ data.size()};
+ AnalyzeWebP(&webp_info, &webp_data);
+}
+
+FUZZ_TEST(WebPInfo, WebPInfoTest)
+ .WithDomains(
+ fuzztest::String()
+ .WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1));
diff --git a/c-lib/webp_js/README.md b/c-lib/webp_js/README.md
@@ -0,0 +1,60 @@
+# WebP JavaScript decoder
+
+```
+ __ __ ____ ____ ____ __ ____
+/ \\/ \ _ \ _ \ _ \ (__)/ __\
+\ / __/ _ \ __/ _) \_ \
+ \__\__/_____/____/_/ /____/____/
+```
+
+This file describes the compilation of libwebp into a JavaScript decoder using
+Emscripten and CMake.
+
+- install the Emscripten SDK following the procedure described at:
+ https://emscripten.org/docs/getting_started/downloads.html#installation-instructions-using-the-emsdk-recommended
+ After installation, you should have some global variable positioned to the
+ location of the SDK. In particular, `$EMSDK` should point to the top-level
+ directory containing Emscripten tools.
+
+- configure the project 'WEBP_JS' with CMake using:
+
+ ```shell
+ cd webp_js && \
+ emcmake cmake -DWEBP_BUILD_WEBP_JS=ON ../
+ ```
+
+- compile webp.js using `emmake make`.
+
+- that's it! Upon completion, you should have the 'webp.js', 'webp.js.mem',
+ 'webp_wasm.js' and 'webp_wasm.wasm' files generated.
+
+The callable JavaScript function is `WebPToSDL()`, which decodes a raw WebP
+bitstream into a canvas. See webp_js/index.html for a simple usage sample (see
+below for instructions).
+
+## Demo HTML page
+
+The HTML page webp_js/index.html requires the built files 'webp.js' and
+'webp.js.mem' to be copied to webp_js/. An HTTP server to serve the WebP image
+example is also needed. With Python, just run:
+
+```shell
+cd webp_js && python3 -m http.server 8080
+```
+
+and then navigate to http://localhost:8080 in your favorite browser.
+
+## Web-Assembly (WASM) version:
+
+CMakeLists.txt is configured to build the WASM version when using the option
+WEBP_BUILD_WEBP_JS=ON. The compilation step will assemble the files
+'webp_wasm.js' and 'webp_wasm.wasm' that you then need to copy to the webp_js/
+directory.
+
+See webp_js/index_wasm.html for a simple demo page using the WASM version of the
+library.
+
+## Caveats
+
+- First decoding using the library is usually slower, due to just-in-time
+ compilation.
diff --git a/c-lib/webp_js/index.html b/c-lib/webp_js/index.html
@@ -18,7 +18,7 @@
var WebpToCanvas;
function init() {
- WebpToCanvas = Module.cwrap('WebpToSDL', 'number', ['array', 'number']);
+ WebpToCanvas = Module.cwrap('WebPToSDL', 'number', ['array', 'number']);
}
window.onload = init;
@@ -29,6 +29,8 @@ function decode(webp_data, canvas_id) {
// clear previous picture (if any)
Module.canvas = canvas;
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
+ // Map this canvas to the default selector used by emscripten/SDL2.
+ specialHTMLTargets["#canvas"] = Module.canvas;
// decode and measure timing
var start = new Date();
var ret = WebpToCanvas(webp_data, webp_data.length);
diff --git a/c-lib/webp_js/index_wasm.html b/c-lib/webp_js/index_wasm.html
@@ -9,6 +9,7 @@
noInitialRun : true
};
</script>
+ <script src="./webp_wasm.js"></script>
<script type="text/javascript">
'use strict';
@@ -16,40 +17,27 @@
// main wrapper for the function decoding a WebP into a canvas object
var WebpToCanvas;
-function init() {
- var xhr = new XMLHttpRequest();
- xhr.open('GET', 'webp_wasm.wasm', true);
- xhr.responseType = 'arraybuffer';
- xhr.onload = function() {
- Module.wasmBinary = xhr.response;
- var script = document.createElement('script');
- script.src = "webp_wasm.js";
- document.body.appendChild(script);
- };
- xhr.send(null);
-}
-window.onload = init;
+Module.onRuntimeInitialized = async () => {
+ // wrapper for the function decoding a WebP into a canvas object
+ WebpToCanvas = Module.cwrap('WebPToSDL', 'number', ['array', 'number']);
+};
function decode(webp_data, canvas_id) {
var result;
- if (Module["asm"] != undefined) {
- // wrapper for the function decoding a WebP into a canvas object
- WebpToCanvas = Module.cwrap('WebpToSDL', 'number', ['array', 'number']);
- // get the canvas to decode into
- var canvas = document.getElementById(canvas_id);
- if (canvas == null) return;
- // clear previous picture (if any)
- Module.canvas = canvas;
- canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
- // decode and measure timing
- var start = new Date();
- var ret = WebpToCanvas(webp_data, webp_data.length);
- var end = new Date();
- var decode_time = end - start;
- result = 'decoding time: ' + decode_time +' ms.';
- } else {
- result = "WASM module not finished loading! Please retry";
- }
+ // get the canvas to decode into
+ var canvas = document.getElementById(canvas_id);
+ if (canvas == null) return;
+ // clear previous picture (if any)
+ Module.canvas = canvas;
+ canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
+ // Map this canvas to the default selector used by emscripten/SDL2.
+ specialHTMLTargets["#canvas"] = Module.canvas;
+ // decode and measure timing
+ var start = new Date();
+ var ret = WebpToCanvas(webp_data, webp_data.length);
+ var end = new Date();
+ var decodeTime = end - start;
+ result = 'decoding time: ' + decodeTime +' ms.';
// display timing result
var speed_result = document.getElementById('timing');
if (speed_result != null) {
diff --git a/c-lib/xcframeworkbuild.sh b/c-lib/xcframeworkbuild.sh
@@ -15,7 +15,7 @@ set -e
# Set these variables based on the desired minimum deployment target.
readonly IOS_MIN_VERSION=6.0
readonly MACOSX_MIN_VERSION=10.15
-readonly MACOSX_CATALYST_MIN_VERSION=13.0
+readonly MACOSX_CATALYST_MIN_VERSION=14.0
# Extract Xcode version.
readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
@@ -69,6 +69,7 @@ readonly TARGETDIR="${TOPDIR}/WebP.xcframework"
readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.xcframework"
readonly MUXTARGETDIR="${TOPDIR}/WebPMux.xcframework"
readonly DEMUXTARGETDIR="${TOPDIR}/WebPDemux.xcframework"
+readonly SHARPYUVTARGETDIR="${TOPDIR}/SharpYuv.xcframework"
readonly DEVELOPER=$(xcode-select --print-path)
readonly DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
@@ -94,8 +95,15 @@ update_headers_path() {
local subdir
for d in $(find "$1" -path "*/Headers"); do
subdir="$d/$framework_name"
- mkdir "$subdir"
- mv "$d/"*.h "$subdir"
+ if [[ -d "$subdir" ]]; then
+ # SharpYuv will have a sharpyuv subdirectory. macOS is case insensitive,
+ # but for consistency with the other frameworks, rename the directory to
+ # match the case of the framework name.
+ mv "$(echo ${subdir} | tr 'A-Z' 'a-z')" "$subdir"
+ else
+ mkdir "$subdir"
+ mv "$d/"*.h "$subdir"
+ fi
done
}
@@ -104,7 +112,8 @@ echo "iOS SDK Version: ${SDK[$IOS]}"
echo "MacOS SDK Version: ${SDK[$MACOS]}"
if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \
- || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" ]]; then
+ || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" \
+ || -e "${SHARPYUVTARGETDIR}" ]]; then
cat << EOF
WARNING: The following directories will be deleted:
WARNING: ${BUILDDIR}
@@ -112,12 +121,13 @@ WARNING: ${TARGETDIR}
WARNING: ${DECTARGETDIR}
WARNING: ${MUXTARGETDIR}
WARNING: ${DEMUXTARGETDIR}
+WARNING: ${SHARPYUVTARGETDIR}
WARNING: The build will continue in 5 seconds...
EOF
sleep 5
fi
rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} \
- ${MUXTARGETDIR} ${DEMUXTARGETDIR}
+ ${MUXTARGETDIR} ${DEMUXTARGETDIR} ${SHARPYUVTARGETDIR}
if [[ ! -e ${SRCDIR}/configure ]]; then
if ! (cd ${SRCDIR} && sh autogen.sh); then
@@ -137,6 +147,7 @@ for (( i = 0; i < $NUM_PLATFORMS; ++i )); do
DECLIBLIST=()
MUXLIBLIST=()
DEMUXLIBLIST=()
+ SHARPYUVLIBLIST=()
for PLATFORM in ${PLATFORMS[$i]}; do
ROOTDIR="${BUILDDIR}/${PLATFORM}"
@@ -161,7 +172,9 @@ for (( i = 0; i < $NUM_PLATFORMS; ++i )); do
CFLAGS="-pipe -isysroot ${SDKROOT} -O3 -DNDEBUG"
case "${PLATFORM}" in
iPhone*)
- CFLAGS+=" -fembed-bitcode"
+ if [[ "${XCODE%%.*}" -lt 16 ]]; then
+ CFLAGS+=" -fembed-bitcode"
+ fi
CFLAGS+=" -target ${ARCH}-apple-ios${IOS_MIN_VERSION}"
[[ "${PLATFORM}" == *Simulator* ]] && CFLAGS+="-simulator"
;;
@@ -186,28 +199,29 @@ for (( i = 0; i < $NUM_PLATFORMS; ++i )); do
CFLAGS="${CFLAGS}"
set +x
- # run make only in the src/ directory to create libwebp.a/libwebpdecoder.a
- cd src/
- make V=0
- make install
+ # Build only the libraries, skip the examples.
+ make V=0 -C sharpyuv install
+ make V=0 -C src install
LIBLIST+=("${ROOTDIR}/lib/libwebp.a")
DECLIBLIST+=("${ROOTDIR}/lib/libwebpdecoder.a")
MUXLIBLIST+=("${ROOTDIR}/lib/libwebpmux.a")
DEMUXLIBLIST+=("${ROOTDIR}/lib/libwebpdemux.a")
+ SHARPYUVLIBLIST+=("${ROOTDIR}/lib/libsharpyuv.a")
# xcodebuild requires a directory for the -headers option, these will match
# for all builds.
- make install-data DESTDIR="${ROOTDIR}/lib-headers"
- make install-commonHEADERS DESTDIR="${ROOTDIR}/dec-headers"
- make -C demux install-data DESTDIR="${ROOTDIR}/demux-headers"
- make -C mux install-data DESTDIR="${ROOTDIR}/mux-headers"
+ make -C src install-data DESTDIR="${ROOTDIR}/lib-headers"
+ make -C src install-commonHEADERS DESTDIR="${ROOTDIR}/dec-headers"
+ make -C src/demux install-data DESTDIR="${ROOTDIR}/demux-headers"
+ make -C src/mux install-data DESTDIR="${ROOTDIR}/mux-headers"
+ make -C sharpyuv install-data DESTDIR="${ROOTDIR}/sharpyuv-headers"
LIB_HEADERS="${ROOTDIR}/lib-headers/${ROOTDIR}/include/webp"
DEC_HEADERS="${ROOTDIR}/dec-headers/${ROOTDIR}/include/webp"
DEMUX_HEADERS="${ROOTDIR}/demux-headers/${ROOTDIR}/include/webp"
MUX_HEADERS="${ROOTDIR}/mux-headers/${ROOTDIR}/include/webp"
+ SHARPYUV_HEADERS="${ROOTDIR}/sharpyuv-headers/${ROOTDIR}/include/webp"
make distclean
- cd ..
export PATH=${OLDPATH}
done
@@ -222,16 +236,20 @@ for (( i = 0; i < $NUM_PLATFORMS; ++i )); do
target_declib="${target_dir}/$(basename ${DECLIBLIST[0]})"
target_demuxlib="${target_dir}/$(basename ${DEMUXLIBLIST[0]})"
target_muxlib="${target_dir}/$(basename ${MUXLIBLIST[0]})"
+ target_sharpyuvlib="${target_dir}/$(basename ${SHARPYUVLIBLIST[0]})"
mkdir -p "${target_dir}"
${LIPO} -create ${LIBLIST[@]} -output "${target_lib}"
${LIPO} -create ${DECLIBLIST[@]} -output "${target_declib}"
${LIPO} -create ${DEMUXLIBLIST[@]} -output "${target_demuxlib}"
${LIPO} -create ${MUXLIBLIST[@]} -output "${target_muxlib}"
+ ${LIPO} -create ${SHARPYUVLIBLIST[@]} -output "${target_sharpyuvlib}"
FAT_LIBLIST+=(-library "${target_lib}" -headers "${LIB_HEADERS}")
FAT_DECLIBLIST+=(-library "${target_declib}" -headers "${DEC_HEADERS}")
FAT_DEMUXLIBLIST+=(-library "${target_demuxlib}" -headers "${DEMUX_HEADERS}")
FAT_MUXLIBLIST+=(-library "${target_muxlib}" -headers "${MUX_HEADERS}")
+ FAT_SHARPYUVLIBLIST+=(-library "${target_sharpyuvlib}")
+ FAT_SHARPYUVLIBLIST+=(-headers "${SHARPYUV_HEADERS}")
done
# lipo will not put archives with the same architecture (e.g., x86_64
@@ -248,10 +266,13 @@ xcodebuild -create-xcframework "${FAT_DEMUXLIBLIST[@]}" \
-output ${DEMUXTARGETDIR}
xcodebuild -create-xcframework "${FAT_MUXLIBLIST[@]}" \
-output ${MUXTARGETDIR}
+xcodebuild -create-xcframework "${FAT_SHARPYUVLIBLIST[@]}" \
+ -output ${SHARPYUVTARGETDIR}
update_headers_path "${TARGETDIR}"
update_headers_path "${DECTARGETDIR}"
update_headers_path "${DEMUXTARGETDIR}"
update_headers_path "${MUXTARGETDIR}"
+update_headers_path "${SHARPYUVTARGETDIR}"
set +x
echo "SUCCESS"