mirror of
https://github.com/webmproject/libwebp.git
synced 2025-12-24 14:06:27 +01:00
Compare commits
4 Commits
main
...
chrome-m11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a450afed44 | ||
|
|
fd7b5d4846 | ||
|
|
4654e1e738 | ||
|
|
d23169349f |
@@ -1,4 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
DerivePointerAlignment: false
|
||||
240
.cmake-format.py
240
.cmake-format.py
@@ -1,240 +0,0 @@
|
||||
# ----------------------------------
|
||||
# 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 = {}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -52,6 +52,5 @@ 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
|
||||
|
||||
2
.mailmap
2
.mailmap
@@ -16,5 +16,3 @@ 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>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
[style]
|
||||
based_on_style = yapf
|
||||
based_on_style = chromium
|
||||
14
AUTHORS
14
AUTHORS
@@ -2,31 +2,23 @@ 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)
|
||||
@@ -36,16 +28,12 @@ Contributors:
|
||||
- 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)
|
||||
@@ -59,14 +47,12 @@ 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)
|
||||
|
||||
@@ -164,7 +164,6 @@ 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 \
|
||||
|
||||
156
CMakeLists.txt
156
CMakeLists.txt
@@ -9,7 +9,11 @@
|
||||
if(APPLE)
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0072)
|
||||
cmake_policy(SET CMP0072 NEW)
|
||||
endif()
|
||||
|
||||
project(WebP C)
|
||||
@@ -41,19 +45,12 @@ 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)
|
||||
option(
|
||||
WEBP_ENABLE_FBOUNDS_SAFETY
|
||||
"Enable -fbounds-safety for the part of the codebase which supports it. This expects an experimental toolchain."
|
||||
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)
|
||||
@@ -85,17 +82,7 @@ if(WEBP_BUILD_WEBP_JS)
|
||||
set(WEBP_USE_THREAD OFF)
|
||||
|
||||
if(WEBP_ENABLE_SIMD)
|
||||
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."
|
||||
)
|
||||
message("wasm2js does not support SIMD, disabling webp.js generation.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -111,20 +98,10 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
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)
|
||||
if(BUILD_SHARED_LIBS AND NOT 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}")
|
||||
@@ -145,20 +122,10 @@ if(WEBP_UNICODE)
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND BUILD_SHARED_LIBS)
|
||||
if(MSVC AND BUILD_SHARED_LIBS)
|
||||
add_definitions(-DWEBP_DLL)
|
||||
endif()
|
||||
|
||||
# Compatibility with autoconf configure script's way of setting this
|
||||
if(CMAKE_C_BYTE_ORDER STREQUAL "BIG_ENDIAN")
|
||||
set(WORDS_BIGENDIAN TRUE)
|
||||
elseif(CMAKE_C_BYTE_ORDER STREQUAL "LITTLE_ENDIAN")
|
||||
set(WORDS_BIGENDIAN FALSE)
|
||||
else()
|
||||
set(WORDS_BIGENDIAN FALSE)
|
||||
message(WARNING "Endianness could not be determined.")
|
||||
endif()
|
||||
|
||||
# pkg-config variables used by *.pc.in.
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix "\${prefix}")
|
||||
@@ -183,20 +150,7 @@ if(MSVC)
|
||||
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()
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
|
||||
# ##############################################################################
|
||||
# Android only.
|
||||
@@ -395,11 +349,9 @@ if(XCODE)
|
||||
endif()
|
||||
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};${CMAKE_CURRENT_BINARY_DIR}>"
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
webpdecoder PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
set_target_properties(
|
||||
webpdecoder
|
||||
PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
|
||||
@@ -422,15 +374,6 @@ 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>)
|
||||
if(WEBP_ENABLE_FBOUNDS_SAFETY)
|
||||
# Enable -fbounds-safety only for webputils and webpdecode(r) for now.
|
||||
add_definitions(-DWEBP_SUPPORT_FBOUNDS_SAFETY=1)
|
||||
target_compile_options(webputils PRIVATE -fbounds-safety)
|
||||
target_compile_options(webputilsdecode PRIVATE -fbounds-safety)
|
||||
target_compile_options(webpdecode PRIVATE -fbounds-safety)
|
||||
target_compile_options(webpdecoder PRIVATE -fbounds-safety)
|
||||
endif()
|
||||
|
||||
target_link_libraries(webp sharpyuv)
|
||||
if(XCODE)
|
||||
libwebp_add_stub_file(webp)
|
||||
@@ -509,8 +452,6 @@ 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
|
||||
@@ -547,8 +488,6 @@ if(WEBP_BUILD_ANIM_UTILS
|
||||
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)
|
||||
@@ -595,8 +534,7 @@ if(WEBP_BUILD_GIF2WEBP)
|
||||
add_executable(gif2webp ${GIF2WEBP_SRCS})
|
||||
target_link_libraries(gif2webp exampleutil imageioutil webp libwebpmux
|
||||
${WEBP_DEP_GIF_LIBRARIES})
|
||||
target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
|
||||
install(TARGETS gif2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
@@ -608,8 +546,7 @@ if(WEBP_BUILD_IMG2WEBP)
|
||||
add_executable(img2webp ${IMG2WEBP_SRCS})
|
||||
target_link_libraries(img2webp exampleutil imagedec imageioutil webp
|
||||
libwebpmux)
|
||||
target_include_directories(img2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(img2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
|
||||
install(TARGETS img2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
@@ -689,82 +626,44 @@ if(WEBP_BUILD_EXTRAS)
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# vwebp_sdl
|
||||
find_package(SDL2 QUIET)
|
||||
if(WEBP_BUILD_VWEBP AND SDL2_FOUND)
|
||||
find_package(SDL)
|
||||
if(WEBP_BUILD_VWEBP AND SDL_FOUND)
|
||||
add_executable(vwebp_sdl ${VWEBP_SDL_SRCS})
|
||||
target_link_libraries(vwebp_sdl ${SDL2_LIBRARIES} imageioutil webp)
|
||||
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 ${SDL2_INCLUDE_DIRS})
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src ${SDL_INCLUDE_DIR})
|
||||
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 SDL2)
|
||||
target_link_libraries(webp_js webpdecoder SDL)
|
||||
target_include_directories(webp_js PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(WEBP_HAVE_SDL 1)
|
||||
set_target_properties(
|
||||
webp_js
|
||||
PROPERTIES
|
||||
# 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} \
|
||||
PROPERTIES LINK_FLAGS "-sWASM=0 \
|
||||
-sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
|
||||
-sEXPORTED_RUNTIME_METHODS=cwrap ${SDL2_LIBRARIES} \
|
||||
-sALLOW_MEMORY_GROWTH")
|
||||
-sEXPORTED_RUNTIME_METHODS=cwrap")
|
||||
set_target_properties(webp_js PROPERTIES OUTPUT_NAME webp)
|
||||
target_compile_definitions(webp_js PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
|
||||
endif()
|
||||
|
||||
# WASM version
|
||||
add_executable(webp_wasm ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
|
||||
target_link_libraries(webp_wasm webpdecoder SDL2)
|
||||
target_link_libraries(webp_wasm webpdecoder SDL)
|
||||
target_include_directories(webp_wasm PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set_target_properties(
|
||||
webp_wasm
|
||||
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} \
|
||||
PROPERTIES LINK_FLAGS "-sWASM=1 \
|
||||
-sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
|
||||
-sEXPORTED_RUNTIME_METHODS=cwrap ${SDL2_LIBRARIES} \
|
||||
-sALLOW_MEMORY_GROWTH")
|
||||
-sEXPORTED_RUNTIME_METHODS=cwrap")
|
||||
target_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
|
||||
|
||||
target_compile_definitions(webpdspdecode PUBLIC EMSCRIPTEN)
|
||||
@@ -804,10 +703,6 @@ if(WEBP_BUILD_ANIM_UTILS)
|
||||
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}
|
||||
@@ -847,8 +742,7 @@ endif()
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/WebPConfig.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake
|
||||
INSTALL_DESTINATION ${ConfigPackageLocation}
|
||||
PATH_VARS CMAKE_INSTALL_INCLUDEDIR)
|
||||
INSTALL_DESTINATION ${ConfigPackageLocation})
|
||||
|
||||
# Install the generated CMake files.
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
|
||||
|
||||
@@ -19,58 +19,10 @@ 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.
|
||||
|
||||
## 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
|
||||
https://chromium-review.googlesource.com for this purpose. 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
|
||||
the formatting is enforced using `clang-format --style=Google`.
|
||||
|
||||
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
|
||||
|
||||
435
ChangeLog
435
ChangeLog
@@ -1,432 +1,3 @@
|
||||
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
|
||||
@@ -532,7 +103,7 @@ 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)
|
||||
0d1f1254 update ChangeLog (tag: v1.2.4, origin/1.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
|
||||
@@ -1563,7 +1134,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)
|
||||
ece9684f update ChangeLog (tag: v0.5.2-rc2, tag: v0.5.2, origin/0.5.2)
|
||||
aa7744ca anim_util: quiet implicit conv warnings in 32-bit
|
||||
d9120271 jpegdec: correct ContextFill signature
|
||||
24eb3940 Remove some errors when compiling the code as C++.
|
||||
@@ -1850,7 +1421,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)
|
||||
3d97bb75 update ChangeLog (tag: v0.5.1, origin/0.5.1)
|
||||
deb54d91 Clarify the expected 'config' lifespan in WebPIDecode()
|
||||
435308e0 Add MSA optimized encoder transform functions
|
||||
dce64bfa Add MSA optimized alpha filter functions
|
||||
|
||||
12
Makefile.vc
12
Makefile.vc
@@ -12,8 +12,6 @@ LIBSHARPYUV_BASENAME = libsharpyuv
|
||||
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
|
||||
@@ -32,7 +30,7 @@ PLATFORM_LDFLAGS = /SAFESEH
|
||||
NOLOGO = /nologo
|
||||
CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG
|
||||
CCDEBUG = cl.exe $(NOLOGO) /Od /Zi /D_DEBUG /RTC1
|
||||
CFLAGS = /I. /Isrc $(NOLOGO) /MP /W3 /EHsc /c
|
||||
CFLAGS = /I. /Isrc $(NOLOGO) /W3 /EHsc /c
|
||||
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
|
||||
LDFLAGS = /LARGEADDRESSAWARE /MANIFEST:EMBED /NXCOMPAT /DYNAMICBASE
|
||||
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
|
||||
@@ -231,7 +229,6 @@ 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 \
|
||||
@@ -271,7 +268,6 @@ 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 \
|
||||
|
||||
@@ -325,7 +321,6 @@ 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 \
|
||||
@@ -341,7 +336,6 @@ 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 \
|
||||
@@ -395,7 +389,7 @@ $(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) $(LIBSHARPYUV)
|
||||
$(DIRBIN)\gif2webp.exe: $(LIBWEBP)
|
||||
$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj $(EX_UTIL_OBJS)
|
||||
$(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
|
||||
$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\vwebp_sdl.obj
|
||||
@@ -406,7 +400,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) $(LIBSHARPYUV)
|
||||
$(DIRBIN)\img2webp.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
|
||||
$(DIRBIN)\get_disto.exe: $(DIROBJ)\extras\get_disto.obj
|
||||
$(DIRBIN)\get_disto.exe: $(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS)
|
||||
$(DIRBIN)\get_disto.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
|
||||
|
||||
69
NEWS
69
NEWS
@@ -1,72 +1,3 @@
|
||||
- 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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
\__\__/\____/\_____/__/ ____ ___
|
||||
/ _/ / \ \ / _ \/ _/
|
||||
/ \_/ / / \ \ __/ \__
|
||||
\____/____/\_____/_____/____/v1.6.0
|
||||
\____/____/\_____/_____/____/v1.3.0
|
||||
```
|
||||
|
||||
WebP codec is a library to encode and decode images in WebP format. This package
|
||||
@@ -42,8 +42,7 @@ See the [APIs documentation](doc/api.md), and API usage examples in the
|
||||
|
||||
## Bugs
|
||||
|
||||
Please report all bugs to the [issue tracker](https://issues.webmproject.org).
|
||||
For security reports, select 'Security report' from the Template dropdown.
|
||||
Please report all bugs to the issue tracker: https://bugs.chromium.org/p/webp
|
||||
|
||||
Patches welcome! See [how to contribute](CONTRIBUTING.md).
|
||||
|
||||
|
||||
@@ -173,7 +173,6 @@ 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"
|
||||
|
||||
@@ -8,12 +8,9 @@ if(@WEBP_USE_THREAD@)
|
||||
find_dependency(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
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_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
|
||||
set(WEBP_INCLUDE_DIRS ${WebP_INCLUDE_DIRS})
|
||||
set(WebP_LIBRARIES "@INSTALLED_LIBRARIES@")
|
||||
set(WEBP_LIBRARIES "${WebP_LIBRARIES}")
|
||||
|
||||
check_required_components(WebP)
|
||||
|
||||
@@ -16,18 +16,48 @@
|
||||
/* 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
|
||||
|
||||
@@ -63,6 +93,9 @@
|
||||
/* 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@"
|
||||
|
||||
@@ -94,9 +127,6 @@
|
||||
/* 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
|
||||
|
||||
@@ -108,4 +138,12 @@
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
#cmakedefine WORDS_BIGENDIAN 1
|
||||
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||
# if defined __BIG_ENDIAN__
|
||||
# define WORDS_BIGENDIAN 1
|
||||
# endif
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
# undef WORDS_BIGENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -18,36 +18,11 @@ 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})
|
||||
|
||||
# If building a universal binary on macOS, we need to check if one of the
|
||||
# architectures supports the SIMD flag.
|
||||
set(OSX_CHECK "")
|
||||
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
list(LENGTH CMAKE_OSX_ARCHITECTURES ARCH_COUNT)
|
||||
if(ARCH_COUNT EQUAL 2)
|
||||
set(OSX_CHECK "defined(WEBP_CAN_HAVE_${WEBP_SIMD_FLAG}) &&")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
check_c_source_compiles(
|
||||
"
|
||||
#include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\"
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__x86_64__)
|
||||
#define WEBP_CAN_HAVE_SSE2
|
||||
#define WEBP_CAN_HAVE_SSE41
|
||||
#define WEBP_CAN_HAVE_AVX2
|
||||
#elif defined(__aarch64__)
|
||||
#define WEBP_CAN_HAVE_NEON
|
||||
#endif
|
||||
// MIPS intrinsics are not supported on macOS, but we have to define them
|
||||
// so that the check happens.
|
||||
#define WEBP_CAN_HAVE_MIPS32
|
||||
#define WEBP_CAN_HAVE_MIPS_DSP_R2
|
||||
#define WEBP_CAN_HAVE_MSA
|
||||
#endif
|
||||
int main(void) {
|
||||
#if ${OSX_CHECK} !defined(WEBP_USE_${WEBP_SIMD_FLAG})
|
||||
#if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
|
||||
this is not valid code
|
||||
#endif
|
||||
return 0;
|
||||
@@ -63,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 "AVX2;SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA")
|
||||
set(WEBP_SIMD_FLAGS "SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA")
|
||||
set(WEBP_SIMD_FILE_EXTENSIONS
|
||||
"_avx2.c;_sse41.c;_sse2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.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
|
||||
@@ -75,20 +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:AVX2;/arch:AVX;/arch:SSE2;;;;")
|
||||
set(SIMD_ENABLE_FLAGS "/arch:AVX;/arch:SSE2;;;;")
|
||||
endif()
|
||||
set(SIMD_DISABLE_FLAGS)
|
||||
else()
|
||||
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")
|
||||
set(SIMD_ENABLE_FLAGS "-msse4.1;-msse2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
|
||||
set(SIMD_DISABLE_FLAGS "-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa")
|
||||
endif()
|
||||
|
||||
set(WEBP_SIMD_FILES_TO_INCLUDE)
|
||||
set(WEBP_SIMD_FLAGS_TO_INCLUDE)
|
||||
|
||||
if(ANDROID AND ANDROID_ABI)
|
||||
if(${ANDROID})
|
||||
if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
|
||||
# This is because Android studio uses the configuration "-march=armv7-a
|
||||
# -mfloat-abi=softfp -mfpu=vfpv3-d16" that does not trigger neon
|
||||
@@ -133,9 +106,8 @@ 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}/../sharpyuv/*${WEBP_SIMD_FILE_EXTENSION}"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/*${WEBP_SIMD_FILE_EXTENSION}")
|
||||
file(GLOB SIMD_FILES "${CMAKE_CURRENT_LIST_DIR}/../"
|
||||
"src/dsp/*${WEBP_SIMD_FILE_EXTENSION}")
|
||||
if(WEBP_HAVE_${WEBP_SIMD_FLAG})
|
||||
# Memorize the file and flags.
|
||||
foreach(FILE ${SIMD_FILES})
|
||||
|
||||
100
cmake/deps.cmake
100
cmake/deps.cmake
@@ -43,6 +43,16 @@ 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 Threads::Threads)
|
||||
endif()
|
||||
set(WEBP_USE_THREAD ${Threads_FOUND})
|
||||
@@ -74,64 +84,72 @@ endif()
|
||||
# Find the standard image libraries.
|
||||
set(WEBP_DEP_IMG_LIBRARIES)
|
||||
set(WEBP_DEP_IMG_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)
|
||||
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("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()
|
||||
|
||||
# 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)
|
||||
endif()
|
||||
GIF_COMPILES)
|
||||
cmake_pop_check_state()
|
||||
if(GIF_COMPILES)
|
||||
list(APPEND WEBP_DEP_GIF_LIBRARIES ${GIF_LIBRARIES})
|
||||
list(APPEND WEBP_DEP_GIF_INCLUDE_DIRS ${GIF_INCLUDE_DIR})
|
||||
else()
|
||||
unset(GIF_FOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check for specific headers.
|
||||
include(CheckIncludeFiles)
|
||||
check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
|
||||
check_include_files(dlfcn.h HAVE_DLFCN_H)
|
||||
check_include_files(GLUT/glut.h HAVE_GLUT_GLUT_H)
|
||||
check_include_files(GL/glut.h HAVE_GL_GLUT_H)
|
||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files(memory.h HAVE_MEMORY_H)
|
||||
check_include_files(OpenGL/glut.h HAVE_OPENGL_GLUT_H)
|
||||
check_include_files(shlwapi.h HAVE_SHLWAPI_H)
|
||||
check_include_files(stdint.h HAVE_STDINT_H)
|
||||
check_include_files(stdlib.h HAVE_STDLIB_H)
|
||||
check_include_files(strings.h HAVE_STRINGS_H)
|
||||
check_include_files(string.h HAVE_STRING_H)
|
||||
check_include_files(sys/stat.h HAVE_SYS_STAT_H)
|
||||
check_include_files(sys/types.h HAVE_SYS_TYPES_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(wincodec.h HAVE_WINCODEC_H)
|
||||
check_include_files(windows.h HAVE_WINDOWS_H)
|
||||
|
||||
38
configure.ac
38
configure.ac
@@ -1,5 +1,5 @@
|
||||
AC_INIT([libwebp], [1.6.0],
|
||||
[https://issues.webmproject.org],,
|
||||
AC_INIT([libwebp], [1.3.0],
|
||||
[https://bugs.chromium.org/p/webp],,
|
||||
[https://developers.google.com/speed/webp])
|
||||
AC_CANONICAL_HOST
|
||||
AC_PREREQ([2.60])
|
||||
@@ -106,7 +106,6 @@ 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])
|
||||
@@ -114,10 +113,8 @@ TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wparentheses-equality])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wself-assign])
|
||||
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])
|
||||
@@ -162,25 +159,6 @@ 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
|
||||
@@ -486,7 +464,7 @@ AC_ARG_ENABLE([sdl],
|
||||
@<:@default=auto@:>@]))
|
||||
AS_IF([test "x$enable_sdl" != "xno"], [
|
||||
CLEAR_LIBVARS([SDL])
|
||||
AC_PATH_PROGS([LIBSDL_CONFIG], [sdl2-config])
|
||||
AC_PATH_PROGS([LIBSDL_CONFIG], [sdl-config])
|
||||
if test -n "$LIBSDL_CONFIG"; then
|
||||
SDL_INCLUDES=`$LIBSDL_CONFIG --cflags`
|
||||
SDL_LIBS="`$LIBSDL_CONFIG --libs`"
|
||||
@@ -496,12 +474,13 @@ AS_IF([test "x$enable_sdl" != "xno"], [
|
||||
|
||||
sdl_header="no"
|
||||
LIBCHECK_PROLOGUE([SDL])
|
||||
AC_CHECK_HEADER([SDL2/SDL.h], [sdl_header="SDL2/SDL.h"],
|
||||
[AC_MSG_WARN(SDL2 library not available - no SDL.h)])
|
||||
AC_CHECK_HEADER([SDL/SDL.h], [sdl_header="SDL/SDL.h"],
|
||||
[AC_CHECK_HEADER([SDL.h], [sdl_header="SDL.h"],
|
||||
[AC_MSG_WARN(SDL library not available - no sdl.h)])])
|
||||
if test x"$sdl_header" != "xno"; then
|
||||
AC_LANG_PUSH(C)
|
||||
SDL_SAVED_LIBS="$LIBS"
|
||||
for lib in "" "-lSDL2" "-lSDL2main -lSDL2"; do
|
||||
for lib in "" "-lSDL" "-lSDLmain -lSDL"; do
|
||||
LIBS="$SDL_SAVED_LIBS $lib"
|
||||
# Perform a full link to ensure SDL_main is resolved if needed.
|
||||
AC_LINK_IFELSE(
|
||||
@@ -783,8 +762,7 @@ AC_CONFIG_FILES([Makefile src/Makefile man/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([
|
||||
|
||||
@@ -36,12 +36,6 @@ size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
|
||||
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.
|
||||
|
||||
Note these functions, like the lossy versions, use the library's default
|
||||
settings. For lossless this means 'exact' is disabled. RGB values in fully
|
||||
transparent areas (that is, areas with alpha values equal to `0`) will be
|
||||
modified to improve compression. To avoid this, use `WebPEncode()` and set
|
||||
`WebPConfig::exact` to `1`.
|
||||
|
||||
### Advanced encoding API
|
||||
|
||||
A more advanced API is based on the WebPConfig and WebPPicture structures.
|
||||
@@ -163,7 +157,7 @@ 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).
|
||||
calling: WebPDelete(idec).
|
||||
|
||||
To retrieve partially decoded picture samples, one must use the corresponding
|
||||
method: WebPIDecGetRGB or WebPIDecGetYUVA. It will return the last displayable
|
||||
@@ -208,7 +202,6 @@ 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
|
||||
|
||||
@@ -96,24 +96,6 @@ 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
|
||||
@@ -228,4 +210,4 @@ 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)
|
||||
See the [corresponding documentation](../README.md)
|
||||
|
||||
@@ -17,11 +17,10 @@ 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
|
||||
$ 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.
|
||||
|
||||
28
doc/tools.md
28
doc/tools.md
@@ -65,7 +65,6 @@ Options:
|
||||
(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
|
||||
@@ -83,8 +82,8 @@ Options:
|
||||
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
|
||||
-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
|
||||
|
||||
@@ -296,23 +295,19 @@ etc.
|
||||
Usage:
|
||||
|
||||
```shell
|
||||
img2webp [file_options] [[frame_options] frame_file]... [-o webp_file]
|
||||
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
|
||||
-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
|
||||
@@ -322,13 +317,10 @@ 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
|
||||
-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)
|
||||
-m <int> ............. method to use
|
||||
```
|
||||
|
||||
example: `img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp`
|
||||
@@ -355,12 +347,8 @@ Options:
|
||||
-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
|
||||
-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
|
||||
|
||||
@@ -20,48 +20,47 @@ 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. 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.
|
||||
WebP is an image format that uses either (i) the VP8 key frame encoding
|
||||
to compress image data in a lossy way, or (ii) the WebP lossless encoding
|
||||
(and possibly other encodings in the future). These encoding schemes should
|
||||
make it more efficient than currently used formats. It is optimized for fast
|
||||
image transfer over the network (e.g., for websites). The WebP format has
|
||||
feature parity (color profile, metadata, animation, etc.) with other formats as
|
||||
well. This document describes the structure of a WebP file.
|
||||
|
||||
The WebP container (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:
|
||||
The WebP container (i.e., RIFF container for WebP) allows feature support over
|
||||
and above the basic use case of WebP (i.e., a file containing a single image
|
||||
encoded as a VP8 key frame). The WebP container provides additional support
|
||||
for:
|
||||
|
||||
* Lossless Compression: An image can be losslessly compressed, using the
|
||||
* **Lossless compression.** An image can be losslessly compressed, using the
|
||||
WebP Lossless Format.
|
||||
|
||||
* Metadata: An image may have metadata stored in Exchangeable Image File
|
||||
Format (Exif) or Extensible Metadata Platform (XMP) format.
|
||||
* **Metadata.** An image may have metadata stored in Exif or XMP formats.
|
||||
|
||||
* Transparency: An image may have transparency, that is, an alpha channel.
|
||||
* **Transparency.** An image may have transparency, i.e., 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.
|
||||
|
||||
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 (that is, an encoded matrix of pixels)
|
||||
or an [animation](#animation). Optionally, it can also contain transparency
|
||||
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][].
|
||||
('MSB 0') as described in [RFC 1166][].
|
||||
|
||||
Terminology & Basics
|
||||
--------------------
|
||||
|
||||
A WebP file contains either a still image (i.e., an encoded matrix of pixels)
|
||||
or an [animation](#animation). Optionally, it can also contain transparency
|
||||
information, color profile and metadata. In case we need to refer only to the
|
||||
matrix of pixels, we will call it the _canvas_ of the image.
|
||||
|
||||
Below are additional terms used throughout this document:
|
||||
|
||||
@@ -84,19 +83,20 @@ _uint32_
|
||||
|
||||
_FourCC_
|
||||
|
||||
: A four-character code (FourCC) is a _uint32_ created by concatenating four
|
||||
: A _FourCC_ (four-character code) 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`, for example, such a
|
||||
field would store value _25_ as _24_.
|
||||
: An unsigned integer field storing values offset by `-1`. e.g., 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.
|
||||
: 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.
|
||||
|
||||
|
||||
RIFF File Format
|
||||
@@ -124,14 +124,14 @@ Chunk FourCC: 32 bits
|
||||
Chunk Size: 32 bits (_uint32_)
|
||||
|
||||
: The size of the chunk in bytes, not including this field, the chunk
|
||||
identifier, or padding.
|
||||
identifier or padding.
|
||||
|
||||
Chunk Payload: _Chunk Size_ bytes
|
||||
|
||||
: The data payload. If _Chunk Size_ is odd, a single padding byte -- which MUST
|
||||
: The data payload. If _Chunk Size_ is odd, a single padding byte -- that 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.
|
||||
|
||||
@@ -151,24 +151,24 @@ 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 4 GiB minus 2 bytes.
|
||||
most 4GiB 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 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
|
||||
also even. The contents of individual chunks will be described in the following
|
||||
sections.
|
||||
|
||||
|
||||
@@ -188,10 +188,10 @@ Simple WebP (lossy) file format:
|
||||
| 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
|
||||
@@ -206,21 +206,21 @@ VP8 data: _Chunk Size_ bytes
|
||||
|
||||
: VP8 bitstream data.
|
||||
|
||||
Note that the fourth character in the 'VP8 ' FourCC is an ASCII space (0x20).
|
||||
Note 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
|
||||
The VP8 bitstream format specification can be found at [VP8 Data Format and
|
||||
Decoding Guide][vp8spec]. Note that the VP8 frame header contains the VP8 frame
|
||||
width and height. That is assumed to be the width and height of the canvas.
|
||||
|
||||
The VP8 specification describes how to decode the image into Y'CbCr format. To
|
||||
convert to RGB, [Recommendation BT.601][rec601] SHOULD be used. Applications MAY
|
||||
use another conversion method, but visual results may differ among decoders.
|
||||
convert to RGB, Rec. 601 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
|
||||
@@ -235,10 +235,10 @@ Simple WebP (lossless) file format:
|
||||
| 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
|
||||
@@ -262,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 a color profile.
|
||||
* An optional 'ICCP' chunk with 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).
|
||||
|
||||
@@ -290,18 +290,15 @@ 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 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.
|
||||
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 are out of order.
|
||||
|
||||
[Metadata](#metadata) and [unknown chunks](#unknown-chunks) MAY appear out of
|
||||
order.
|
||||
|
||||
**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.
|
||||
**Rationale:** Setting the order of chunks should allow quicker file
|
||||
parsing. For example, if an 'ALPH' chunk does not appear in its required
|
||||
position, a decoder can choose to stop searching for it. The rule of
|
||||
ignoring late chunks should make programs that need to do a full search
|
||||
give the same results as the ones stopping early.
|
||||
|
||||
Extended WebP file header:
|
||||
{:#extended_header}
|
||||
@@ -329,7 +326,7 @@ Reserved (Rsv): 2 bits
|
||||
|
||||
ICC profile (I): 1 bit
|
||||
|
||||
: Set if the file contains an 'ICCP' Chunk.
|
||||
: Set if the file contains an ICC profile.
|
||||
|
||||
Alpha (L): 1 bit
|
||||
|
||||
@@ -346,7 +343,7 @@ 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
|
||||
@@ -375,9 +372,9 @@ Future specifications may add more fields. Unknown fields MUST be ignored.
|
||||
|
||||
#### 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
|
||||
@@ -399,14 +396,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.
|
||||
The background color is also used when the Disposal method is `1`.
|
||||
Background color is also used when disposal method is `1`.
|
||||
|
||||
**Notes**:
|
||||
**Note**:
|
||||
|
||||
* The background color MAY contain a non-opaque alpha value, even if the
|
||||
_Alpha_ flag in the ['VP8X' Chunk](#extended_header) is unset.
|
||||
* Background color MAY contain a non-opaque alpha value, even if the _Alpha_
|
||||
flag in [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
|
||||
@@ -414,14 +411,13 @@ Background Color: 32 bits (_uint32_)
|
||||
|
||||
Loop Count: 16 bits (_uint16_)
|
||||
|
||||
: The number of times to loop the animation. If it is `0`, this means
|
||||
infinitely.
|
||||
: The number of times to loop the animation. `0` means infinitely.
|
||||
|
||||
This chunk MUST appear if the _Animation_ flag in the 'VP8X' Chunk is set.
|
||||
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.
|
||||
@@ -463,10 +459,10 @@ Frame Height Minus One: 24 bits (_uint24_)
|
||||
|
||||
Frame Duration: 24 bits (_uint24_)
|
||||
|
||||
: 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.
|
||||
: The time to wait before displaying the next frame, in 1 millisecond units.
|
||||
Note the interpretation of frame duration of 0 (and often <= 10) is
|
||||
implementation defined. Many tools and browsers assign a minimum duration
|
||||
similar to GIF.
|
||||
|
||||
Reserved: 6 bits
|
||||
|
||||
@@ -477,10 +473,10 @@ 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 the alpha value
|
||||
is 255, effectively replacing the rectangle.
|
||||
the current frame does not have an alpha channel, assume alpha value of
|
||||
255, effectively replacing the rectangle.
|
||||
|
||||
* `1`: Do not blend. After disposing of the previous frame, render the
|
||||
current frame on the canvas by overwriting the rectangle covered by the
|
||||
@@ -493,20 +489,20 @@ Disposal method (D): 1 bit
|
||||
|
||||
* `0`: Do not dispose. Leave the canvas as is.
|
||||
|
||||
* `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).
|
||||
* `1`: Dispose to background color. Fill the _rectangle_ on the canvas
|
||||
covered by the _current frame_ with background color specified in the
|
||||
[ANIM chunk](#anim_chunk).
|
||||
|
||||
**Notes**:
|
||||
|
||||
* The frame disposal only applies to the _frame rectangle_, that is, the
|
||||
rectangle defined by _Frame X_, _Frame Y_, _frame width_, and _frame
|
||||
height_. It may or may not cover the whole canvas.
|
||||
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 bits, and the RGB
|
||||
Given that each of the R, G, B and A channels is 8-bit, and the RGB
|
||||
channels are _not premultiplied_ by alpha, the formula for blending
|
||||
'dst' onto 'src' is:
|
||||
|
||||
@@ -522,10 +518,10 @@ Disposal method (D): 1 bit
|
||||
|
||||
* 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, standard RGB (sRGB) is to be assumed. (Note that sRGB also
|
||||
needs to be linearized due to a gamma of ~2.2.)
|
||||
not present, sRGB is to be assumed. (Note that sRGB also needs to be
|
||||
linearized due to a gamma of ~2.2).
|
||||
|
||||
Frame Data: _Chunk Size_ bytes - `16`
|
||||
Frame Data: _Chunk Size_ - `16` bytes
|
||||
|
||||
: Consists of:
|
||||
|
||||
@@ -535,8 +531,8 @@ Frame Data: _Chunk Size_ bytes - `16`
|
||||
|
||||
* An optional list of [unknown chunks](#unknown-chunks).
|
||||
|
||||
**Note**: The 'ANMF' payload, _Frame Data_, consists of individual
|
||||
_padded_ chunks, as described by the [RIFF file format](#riff-file-format).
|
||||
**Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual
|
||||
_padded_ chunks as described by the [RIFF file format](#riff-file-format).
|
||||
|
||||
#### Alpha
|
||||
|
||||
@@ -553,20 +549,18 @@ Reserved (Rsv): 2 bits
|
||||
|
||||
: MUST be `0`. Readers MUST ignore this field.
|
||||
|
||||
Preprocessing (P): 2 bits
|
||||
Pre-processing (P): 2 bits
|
||||
|
||||
: These _informative_ bits are used to signal the preprocessing that has
|
||||
: These _informative_ bits are used to signal the pre-processing that has
|
||||
been performed during compression. The decoder can use this information to
|
||||
for example, dither the values or smooth the gradients prior to display.
|
||||
e.g. dither the values or smooth the gradients prior to display.
|
||||
|
||||
* `0`: No preprocessing.
|
||||
* `0`: No pre-processing.
|
||||
* `1`: Level reduction.
|
||||
|
||||
Decoders are not required to use this information in any specified way.
|
||||
|
||||
Filtering method (F): 2 bits
|
||||
|
||||
: The filtering methods used are described as follows:
|
||||
: The filtering method used:
|
||||
|
||||
* `0`: None.
|
||||
* `1`: Horizontal filter.
|
||||
@@ -590,8 +584,8 @@ made depending on the filtering method:
|
||||
|
||||
where `clip(v)` is equal to:
|
||||
|
||||
* 0 if v < 0,
|
||||
* 255 if v > 255, or
|
||||
* 0 if v < 0
|
||||
* 255 if v > 255
|
||||
* v otherwise
|
||||
|
||||
The final value is derived by adding the decompressed value `X` to the
|
||||
@@ -600,15 +594,17 @@ into the \[0..255\] one:
|
||||
|
||||
`alpha = (predictor + X) % 256`
|
||||
|
||||
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:
|
||||
There are special cases for the left-most and top-most pixel positions:
|
||||
|
||||
* The 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:
|
||||
@@ -616,37 +612,37 @@ Compression method (C): 2 bits
|
||||
* `0`: No compression.
|
||||
* `1`: Compressed using the WebP lossless format.
|
||||
|
||||
Alpha bitstream: _Chunk Size_ bytes - `1`
|
||||
Alpha bitstream: _Chunk Size_ - `1` bytes
|
||||
|
||||
: Encoded alpha bitstream.
|
||||
|
||||
This optional chunk contains encoded alpha data for this frame. A frame
|
||||
containing a 'VP8L' Chunk SHOULD NOT contain this chunk.
|
||||
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
|
||||
The alpha channel data is stored as uncompressed raw data (when
|
||||
compression method is '0') or compressed using the lossless format
|
||||
(when the compression method is '1').
|
||||
|
||||
* Raw data: This consists of a byte sequence of length = width * height,
|
||||
* Raw data: consists of a byte sequence of length width * height,
|
||||
containing all the 8-bit transparency values in scan order.
|
||||
|
||||
* Lossless format compression: The byte sequence is a compressed
|
||||
image-stream (as described in ["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.
|
||||
* Lossless format compression: the byte sequence is a compressed
|
||||
image-stream (as described in the [WebP Lossless Bitstream Format]
|
||||
[webpllspec]) of implicit dimension width x height. That is, this
|
||||
image-stream does NOT contain any headers describing the image dimension.
|
||||
|
||||
**Rationale**: The dimensions are already known from other sources,
|
||||
so storing them again would be redundant and prone to error.
|
||||
**Rationale**: the dimension is already known from other sources,
|
||||
so storing it again would be redundant and error-prone.
|
||||
|
||||
Once the image-stream is decoded into 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.
|
||||
Once the image-stream is decoded into ARGB color values, following
|
||||
the process described in the lossless format specification, the
|
||||
transparency information must be extracted from the *green* channel
|
||||
of the ARGB quadruplet.
|
||||
|
||||
**Rationale**: The green channel is allowed extra transformation
|
||||
**Rationale**: the green channel is allowed extra transformation
|
||||
steps in the specification -- unlike the other channels -- that can
|
||||
improve compression.
|
||||
|
||||
@@ -654,13 +650,13 @@ 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 FourCC, _or_ (ii) a 'VP8L' Chunk,
|
||||
using 'VP8L' as its FourCC.
|
||||
A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the
|
||||
significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using
|
||||
"VP8L" as its tag.
|
||||
|
||||
The formats of 'VP8 ' and 'VP8L' Chunks are as described in sections
|
||||
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
|
||||
|
||||
@@ -687,14 +683,14 @@ 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.
|
||||
|
||||
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
|
||||
@@ -709,7 +705,7 @@ Exif Metadata: _Chunk Size_ bytes
|
||||
|
||||
: 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
|
||||
@@ -724,73 +720,72 @@ XMP Metadata: _Chunk Size_ bytes
|
||||
|
||||
: Image metadata in XMP format.
|
||||
|
||||
Note that the fourth character in the 'XMP ' FourCC is an ASCII space (0x20).
|
||||
Note 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 the [RIFF File Format](#riff-file-format) section)
|
||||
whose FourCC is different from any of the chunks described in this document, is
|
||||
A RIFF chunk (described in [this](#terminology-amp-basics) section) whose _chunk
|
||||
tag_ is different from any of the chunks described in this document, is
|
||||
considered an _unknown chunk_.
|
||||
|
||||
**Rationale**: Allowing unknown chunks gives a provision for future extension
|
||||
of the format and also allows storage of any application-specific data.
|
||||
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, or
|
||||
* 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.
|
||||
* 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).
|
||||
|
||||
### Canvas Assembly from Frames
|
||||
### Assembling the Canvas From Frames
|
||||
|
||||
Here we provide an overview of how a reader MUST assemble a canvas in the case
|
||||
of an animated image.
|
||||
|
||||
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
|
||||
'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.
|
||||
non-zero `Loop Count` values or infinitely if `Loop Count` is zero.
|
||||
|
||||
At the beginning of each loop iteration, the canvas is filled using the
|
||||
background color from the 'ANIM' Chunk or an application-defined color.
|
||||
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
|
||||
'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 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
|
||||
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.
|
||||
_VP8X.field_ means the field in the 'VP8X' chunk with the same description.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
VP8X.flags.hasAnimation MUST be TRUE
|
||||
assert VP8X.flags.hasAnimation
|
||||
canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
|
||||
background color ANIM.background_color or
|
||||
application-defined color.
|
||||
background color ANIM.background_color.
|
||||
loop_count ← ANIM.loopCount
|
||||
dispose_method ← Dispose to background color
|
||||
if loop_count == 0:
|
||||
loop_count = ∞
|
||||
frame_params ← nil
|
||||
next chunk in image_data is ANMF MUST be TRUE
|
||||
assert next chunk in image_data is ANMF
|
||||
for loop = 0..loop_count - 1
|
||||
clear canvas to ANIM.background_color or application-defined color
|
||||
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
|
||||
@@ -799,25 +794,22 @@ for loop = 0..loop_count - 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
|
||||
assert VP8X.canvasWidth >= frame_right
|
||||
assert VP8X.canvasHeight >= frame_bottom
|
||||
for subchunk in 'Frame Data':
|
||||
if subchunk.tag == "ALPH":
|
||||
alpha subchunks not found in 'Frame Data' earlier MUST be
|
||||
TRUE
|
||||
assert alpha subchunks not found in 'Frame Data' earlier
|
||||
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
|
||||
assert bitstream subchunks not found in 'Frame Data' earlier
|
||||
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.frameY), using blending method
|
||||
frame_params.blendingMethod.
|
||||
canvas contains the decoded image.
|
||||
Show the contents of the canvas for
|
||||
frame_params.frameDuration * 1 ms.
|
||||
frame_params.frameDuration * 1ms.
|
||||
dispose_method = frame_params.disposeMethod
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -825,7 +817,7 @@ for loop = 0..loop_count - 1
|
||||
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
|
||||
@@ -834,16 +826,16 @@ RIFF/WEBP
|
||||
+- VP8 (bitstream)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A lossless-encoded image may look as follows:
|
||||
A losslessly encoded image may look as follows:
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
RIFF/WEBP
|
||||
+- VP8X (descriptions of features used)
|
||||
+- VP8L (lossless bitstream)
|
||||
+- XYZW (unknown chunk)
|
||||
+- VP8L (lossless bitstream)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A lossless image with an ICC profile and XMP metadata may
|
||||
A lossless image with ICC profile and XMP metadata may
|
||||
look as follows:
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -867,11 +859,10 @@ 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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) -I$(top_srcdir)
|
||||
gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES)
|
||||
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) -I$(top_srcdir)
|
||||
img2webp_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
img2webp_LDADD =
|
||||
img2webp_LDADD += libexample_util.la
|
||||
img2webp_LDADD += ../imageio/libimageio_util.la
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdlib.h> // for 'strtod'.
|
||||
#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
|
||||
@@ -57,8 +56,8 @@ static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
|
||||
}
|
||||
|
||||
static int FramesAreSimilar(const uint8_t* const rgba1,
|
||||
const uint8_t* const rgba2, int width, int height,
|
||||
int max_allowed_diff) {
|
||||
const uint8_t* const rgba2,
|
||||
int width, int height, int max_allowed_diff) {
|
||||
int i, j;
|
||||
assert(max_allowed_diff > 0);
|
||||
for (j = 0; j < height; ++j) {
|
||||
@@ -120,7 +119,8 @@ static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) {
|
||||
if (alpha1 == 0 && alpha2 == 0) return 1;
|
||||
}
|
||||
if (bg1 != bg2) {
|
||||
fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n", bg1, bg2);
|
||||
fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n",
|
||||
bg1, bg2);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -130,17 +130,18 @@ static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) {
|
||||
// is OK for other aspects like offsets, dispose/blend method to vary.
|
||||
static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||
const AnimatedImage* const img2,
|
||||
int premultiply, double min_psnr) {
|
||||
int premultiply,
|
||||
double min_psnr) {
|
||||
int ok = 1;
|
||||
const int is_multi_frame_image = (img1->num_frames > 1);
|
||||
uint32_t i;
|
||||
|
||||
ok &= CompareValues(img1->canvas_width, img2->canvas_width,
|
||||
"Canvas width mismatch");
|
||||
ok &= CompareValues(img1->canvas_height, img2->canvas_height,
|
||||
"Canvas height mismatch");
|
||||
ok &=
|
||||
CompareValues(img1->num_frames, img2->num_frames, "Frame count mismatch");
|
||||
ok = CompareValues(img1->canvas_width, img2->canvas_width,
|
||||
"Canvas width mismatch") && ok;
|
||||
ok = CompareValues(img1->canvas_height, img2->canvas_height,
|
||||
"Canvas height mismatch") && ok;
|
||||
ok = CompareValues(img1->num_frames, img2->num_frames,
|
||||
"Frame count mismatch") && ok;
|
||||
if (!ok) return 0; // These are fatal failures, can't proceed.
|
||||
|
||||
if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
|
||||
@@ -153,10 +154,11 @@ static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||
img2->format == ANIM_GIF && img2->loop_count == 65536)) {
|
||||
max_loop_count_workaround = 1;
|
||||
}
|
||||
ok &= (max_loop_count_workaround ||
|
||||
CompareValues(img1->loop_count, img2->loop_count,
|
||||
"Loop count mismatch"));
|
||||
ok &= CompareBackgroundColor(img1->bgcolor, img2->bgcolor, premultiply);
|
||||
ok = (max_loop_count_workaround ||
|
||||
CompareValues(img1->loop_count, img2->loop_count,
|
||||
"Loop count mismatch")) && ok;
|
||||
ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
|
||||
premultiply) && ok;
|
||||
}
|
||||
|
||||
for (i = 0; i < img1->num_frames; ++i) {
|
||||
@@ -176,8 +178,8 @@ static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||
premultiply, &max_diff, &psnr);
|
||||
if (min_psnr > 0.) {
|
||||
if (psnr < min_psnr) {
|
||||
fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i, psnr,
|
||||
min_psnr);
|
||||
fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i,
|
||||
psnr, min_psnr);
|
||||
ok = 0;
|
||||
}
|
||||
} else {
|
||||
@@ -197,17 +199,15 @@ static void Help(void) {
|
||||
printf(" -min_psnr <float> ... minimum per-frame PSNR\n");
|
||||
printf(" -raw_comparison ..... if this flag is not used, RGB is\n");
|
||||
printf(" premultiplied before comparison\n");
|
||||
printf(
|
||||
" -max_diff <int> ..... maximum allowed difference per channel\n"
|
||||
" between corresponding pixels in subsequent\n"
|
||||
" frames\n");
|
||||
printf(" -max_diff <int> ..... maximum allowed difference per channel\n"
|
||||
" between corresponding pixels in subsequent\n"
|
||||
" frames\n");
|
||||
printf(" -h .................. this help\n");
|
||||
printf(" -version ............ print version number and exit\n");
|
||||
}
|
||||
|
||||
// Returns 0 on success, 1 if animation files differ, and 2 for any error.
|
||||
int main(int argc, const char* argv[]) {
|
||||
int return_code = 2;
|
||||
int return_code = -1;
|
||||
int dump_frames = 0;
|
||||
const char* dump_folder = NULL;
|
||||
double min_psnr = 0.;
|
||||
@@ -216,7 +216,7 @@ int main(int argc, const char* argv[]) {
|
||||
int premultiply = 1;
|
||||
int max_diff = 0;
|
||||
int i, c;
|
||||
const char* files[2] = {NULL, NULL};
|
||||
const char* files[2] = { NULL, NULL };
|
||||
AnimatedImage images[2];
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
@@ -252,8 +252,9 @@ int main(int argc, const char* argv[]) {
|
||||
GetAnimatedImageVersions(&dec_version, &demux_version);
|
||||
printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
|
||||
(dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
|
||||
(dec_version >> 0) & 0xff, (demux_version >> 16) & 0xff,
|
||||
(demux_version >> 8) & 0xff, (demux_version >> 0) & 0xff);
|
||||
(dec_version >> 0) & 0xff,
|
||||
(demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
|
||||
(demux_version >> 0) & 0xff);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
if (!got_input1) {
|
||||
@@ -268,17 +269,18 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
if (parse_error) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(return_code);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
if (argc < 3) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(return_code);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
|
||||
if (!got_input2) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(return_code);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
if (dump_frames) {
|
||||
@@ -291,24 +293,24 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!CompareAnimatedImagePair(&images[0], &images[1], premultiply,
|
||||
min_psnr)) {
|
||||
if (!CompareAnimatedImagePair(&images[0], &images[1],
|
||||
premultiply, min_psnr)) {
|
||||
WFPRINTF(stderr, "\nFiles %s and %s differ.\n", (const W_CHAR*)files[0],
|
||||
(const W_CHAR*)files[1]);
|
||||
return_code = 1;
|
||||
return_code = -3;
|
||||
} else {
|
||||
WPRINTF("\nFiles %s and %s are identical.\n", (const W_CHAR*)files[0],
|
||||
(const W_CHAR*)files[1]);
|
||||
return_code = 0;
|
||||
}
|
||||
End:
|
||||
End:
|
||||
ClearAnimatedImage(&images[0]);
|
||||
ClearAnimatedImage(&images[1]);
|
||||
FREE_WARGV_AND_RETURN(return_code);
|
||||
|
||||
@@ -12,14 +12,12 @@
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // for 'strcmp'.
|
||||
|
||||
#include "../imageio/image_enc.h"
|
||||
#include "./anim_util.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
#include "../imageio/image_enc.h"
|
||||
#include "./unicode.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@@ -29,16 +27,14 @@ static void Help(void) {
|
||||
printf("Usage: anim_dump [options] files...\n");
|
||||
printf("\nOptions:\n");
|
||||
printf(" -folder <string> .... dump folder (default: '.')\n");
|
||||
printf(
|
||||
" -prefix <string> .... prefix for dumped frames "
|
||||
"(default: 'dump_')\n");
|
||||
printf(" -prefix <string> .... prefix for dumped frames "
|
||||
"(default: 'dump_')\n");
|
||||
printf(" -tiff ............... save frames as TIFF\n");
|
||||
printf(" -pam ................ save frames as PAM\n");
|
||||
printf(" -h .................. this help\n");
|
||||
printf(" -version ............ print version number and exit\n");
|
||||
}
|
||||
|
||||
// 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(".");
|
||||
@@ -51,7 +47,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (argc < 2) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
for (c = 1; !error && c < argc; ++c) {
|
||||
@@ -77,22 +73,23 @@ 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(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
int dec_version, demux_version;
|
||||
GetAnimatedImageVersions(&dec_version, &demux_version);
|
||||
printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
|
||||
(dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
|
||||
(dec_version >> 0) & 0xff, (demux_version >> 16) & 0xff,
|
||||
(demux_version >> 8) & 0xff, (demux_version >> 0) & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
(dec_version >> 0) & 0xff,
|
||||
(demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
|
||||
(demux_version >> 0) & 0xff);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
uint32_t i;
|
||||
AnimatedImage image;
|
||||
const W_CHAR* const file = GET_WARGV(argv, c);
|
||||
memset(&image, 0, sizeof(image));
|
||||
WPRINTF("Decoding file: %s as %s/%sxxxx.%s\n", file, dump_folder, prefix,
|
||||
suffix);
|
||||
WPRINTF("Decoding file: %s as %s/%sxxxx.%s\n",
|
||||
file, dump_folder, prefix, suffix);
|
||||
if (!ReadAnimatedImage((const char*)file, &image, 0, NULL)) {
|
||||
WFPRINTF(stderr, "Error decoding file: %s\n Aborting.\n", file);
|
||||
error = 1;
|
||||
@@ -101,11 +98,7 @@ int main(int argc, const char* argv[]) {
|
||||
for (i = 0; !error && i < image.num_frames; ++i) {
|
||||
W_CHAR out_file[1024];
|
||||
WebPDecBuffer buffer;
|
||||
if (!WebPInitDecBuffer(&buffer)) {
|
||||
fprintf(stderr, "Cannot init dec buffer\n");
|
||||
error = 1;
|
||||
continue;
|
||||
}
|
||||
WebPInitDecBuffer(&buffer);
|
||||
buffer.colorspace = MODE_RGBA;
|
||||
buffer.is_external_memory = 1;
|
||||
buffer.width = image.canvas_width;
|
||||
@@ -113,8 +106,8 @@ int main(int argc, const char* argv[]) {
|
||||
buffer.u.RGBA.rgba = image.frames[i].rgba;
|
||||
buffer.u.RGBA.stride = buffer.width * sizeof(uint32_t);
|
||||
buffer.u.RGBA.size = buffer.u.RGBA.stride * buffer.height;
|
||||
WSNPRINTF(out_file, sizeof(out_file), "%s/%s%.4d.%s", dump_folder,
|
||||
prefix, i, suffix);
|
||||
WSNPRINTF(out_file, sizeof(out_file), "%s/%s%.4d.%s",
|
||||
dump_folder, prefix, i, suffix);
|
||||
if (!WebPSaveImage(&buffer, format, (const char*)out_file)) {
|
||||
WFPRINTF(stderr, "Error while saving image '%s'\n", out_file);
|
||||
error = 1;
|
||||
@@ -124,5 +117,5 @@ int main(int argc, const char* argv[]) {
|
||||
ClearAnimatedImage(&image);
|
||||
}
|
||||
}
|
||||
FREE_WARGV_AND_RETURN(error ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(error ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -19,16 +19,13 @@
|
||||
#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
|
||||
@@ -41,11 +38,11 @@ static const int kNumChannels = 4;
|
||||
|
||||
#if defined(WEBP_HAVE_GIF)
|
||||
// Returns true if the frame covers the full canvas.
|
||||
static int IsFullFrame(int width, int height, int canvas_width,
|
||||
int canvas_height) {
|
||||
static int IsFullFrame(int width, int height,
|
||||
int canvas_width, int canvas_height) {
|
||||
return (width == canvas_width && height == canvas_height);
|
||||
}
|
||||
#endif // WEBP_HAVE_GIF
|
||||
#endif // WEBP_HAVE_GIF
|
||||
|
||||
static int CheckSizeForOverflow(uint64_t size) {
|
||||
return (size == (size_t)size);
|
||||
@@ -95,8 +92,8 @@ void ClearAnimatedImage(AnimatedImage* const image) {
|
||||
|
||||
#if defined(WEBP_HAVE_GIF)
|
||||
// Clear the canvas to transparent.
|
||||
static void ZeroFillCanvas(uint8_t* rgba, uint32_t canvas_width,
|
||||
uint32_t canvas_height) {
|
||||
static void ZeroFillCanvas(uint8_t* rgba,
|
||||
uint32_t canvas_width, uint32_t canvas_height) {
|
||||
memset(rgba, 0, canvas_width * kNumChannels * canvas_height);
|
||||
}
|
||||
|
||||
@@ -113,16 +110,16 @@ static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset,
|
||||
}
|
||||
|
||||
// Copy width * height pixels from 'src' to 'dst'.
|
||||
static void CopyCanvas(const uint8_t* src, uint8_t* dst, uint32_t width,
|
||||
uint32_t height) {
|
||||
static void CopyCanvas(const uint8_t* src, uint8_t* dst,
|
||||
uint32_t width, uint32_t height) {
|
||||
assert(src != NULL && dst != NULL);
|
||||
memcpy(dst, src, width * kNumChannels * height);
|
||||
}
|
||||
|
||||
// Copy pixels in the given rectangle from 'src' to 'dst' honoring the 'stride'.
|
||||
static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
|
||||
int x_offset, int y_offset, int width,
|
||||
int height) {
|
||||
int x_offset, int y_offset,
|
||||
int width, int height) {
|
||||
int j;
|
||||
const int width_in_bytes = width * kNumChannels;
|
||||
const size_t offset = y_offset * stride + x_offset * kNumChannels;
|
||||
@@ -135,11 +132,11 @@ static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
|
||||
dst += stride;
|
||||
}
|
||||
}
|
||||
#endif // WEBP_HAVE_GIF
|
||||
#endif // WEBP_HAVE_GIF
|
||||
|
||||
// Canonicalize all transparent pixels to transparent black to aid comparison.
|
||||
static void CleanupTransparentPixels(uint32_t* rgba, uint32_t width,
|
||||
uint32_t height) {
|
||||
static void CleanupTransparentPixels(uint32_t* rgba,
|
||||
uint32_t width, uint32_t height) {
|
||||
const uint32_t* const rgba_end = rgba + width * height;
|
||||
while (rgba < rgba_end) {
|
||||
const uint8_t alpha = (*rgba >> 24) & 0xff;
|
||||
@@ -152,8 +149,8 @@ static void CleanupTransparentPixels(uint32_t* rgba, uint32_t width,
|
||||
|
||||
// Dump frame to a PAM file. Returns true on success.
|
||||
static int DumpFrame(const char filename[], const char dump_folder[],
|
||||
uint32_t frame_num, const uint8_t rgba[], int canvas_width,
|
||||
int canvas_height) {
|
||||
uint32_t frame_num, const uint8_t rgba[],
|
||||
int canvas_width, int canvas_height) {
|
||||
int ok = 0;
|
||||
size_t max_len;
|
||||
int y;
|
||||
@@ -166,8 +163,8 @@ static int DumpFrame(const char filename[], const char dump_folder[],
|
||||
|
||||
base_name = WSTRRCHR(filename, '/');
|
||||
base_name = (base_name == NULL) ? (const W_CHAR*)filename : base_name + 1;
|
||||
max_len = WSTRLEN(dump_folder) + 1 + WSTRLEN(base_name) + strlen("_frame_") +
|
||||
strlen(".pam") + 8;
|
||||
max_len = WSTRLEN(dump_folder) + 1 + WSTRLEN(base_name)
|
||||
+ strlen("_frame_") + strlen(".pam") + 8;
|
||||
file_name = (W_CHAR*)WebPMalloc(max_len * sizeof(*file_name));
|
||||
if (file_name == NULL) goto End;
|
||||
|
||||
@@ -183,8 +180,7 @@ static int DumpFrame(const char filename[], const char dump_folder[],
|
||||
ok = 0;
|
||||
goto End;
|
||||
}
|
||||
if (fprintf(f,
|
||||
"P7\nWIDTH %d\nHEIGHT %d\n"
|
||||
if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n"
|
||||
"DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
|
||||
canvas_width, canvas_height) < 0) {
|
||||
WFPRINTF(stderr, "Write error for file %s\n", file_name);
|
||||
@@ -199,7 +195,7 @@ static int DumpFrame(const char filename[], const char dump_folder[],
|
||||
row += canvas_width * kNumChannels;
|
||||
}
|
||||
ok = 1;
|
||||
End:
|
||||
End:
|
||||
if (f != NULL) fclose(f);
|
||||
WebPFree(file_name);
|
||||
return ok;
|
||||
@@ -267,8 +263,8 @@ static int ReadAnimatedWebP(const char filename[],
|
||||
image->canvas_width * kNumChannels * image->canvas_height);
|
||||
|
||||
// Needed only because we may want to compare with GIF later.
|
||||
CleanupTransparentPixels((uint32_t*)curr_rgba, image->canvas_width,
|
||||
image->canvas_height);
|
||||
CleanupTransparentPixels((uint32_t*)curr_rgba,
|
||||
image->canvas_width, image->canvas_height);
|
||||
|
||||
if (dump_frames && dump_ok) {
|
||||
dump_ok = DumpFrame(filename, dump_folder, frame_index, curr_rgba,
|
||||
@@ -284,7 +280,7 @@ static int ReadAnimatedWebP(const char filename[],
|
||||
ok = dump_ok;
|
||||
if (ok) image->format = ANIM_WEBP;
|
||||
|
||||
End:
|
||||
End:
|
||||
WebPAnimDecoderDelete(dec);
|
||||
return ok;
|
||||
}
|
||||
@@ -304,11 +300,12 @@ static int IsGIF(const WebPData* const data) {
|
||||
|
||||
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
|
||||
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
|
||||
#define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||
#define LOCAL_GIF_PREREQ(maj, min) (LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||
# define LOCAL_GIF_PREREQ(maj, min) \
|
||||
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||
#else
|
||||
#define LOCAL_GIF_VERSION 0
|
||||
#define LOCAL_GIF_PREREQ(maj, min) 0
|
||||
# define LOCAL_GIF_VERSION 0
|
||||
# define LOCAL_GIF_PREREQ(maj, min) 0
|
||||
#endif
|
||||
|
||||
#if !LOCAL_GIF_PREREQ(5, 0)
|
||||
@@ -316,14 +313,14 @@ static int IsGIF(const WebPData* const data) {
|
||||
// Added in v5.0
|
||||
typedef struct {
|
||||
int DisposalMode;
|
||||
#define DISPOSAL_UNSPECIFIED 0 // No disposal specified
|
||||
#define DISPOSE_DO_NOT 1 // Leave image in place
|
||||
#define DISPOSE_BACKGROUND 2 // Set area to background color
|
||||
#define DISPOSE_PREVIOUS 3 // Restore to previous content
|
||||
int UserInputFlag; // User confirmation required before disposal
|
||||
int DelayTime; // Pre-display delay in 0.01sec units
|
||||
int TransparentColor; // Palette index for transparency, -1 if none
|
||||
#define NO_TRANSPARENT_COLOR -1
|
||||
#define DISPOSAL_UNSPECIFIED 0 // No disposal specified
|
||||
#define DISPOSE_DO_NOT 1 // Leave image in place
|
||||
#define DISPOSE_BACKGROUND 2 // Set area to background color
|
||||
#define DISPOSE_PREVIOUS 3 // Restore to previous content
|
||||
int UserInputFlag; // User confirmation required before disposal
|
||||
int DelayTime; // Pre-display delay in 0.01sec units
|
||||
int TransparentColor; // Palette index for transparency, -1 if none
|
||||
#define NO_TRANSPARENT_COLOR -1
|
||||
} GraphicsControlBlock;
|
||||
|
||||
static int DGifExtensionToGCB(const size_t GifExtensionLength,
|
||||
@@ -357,8 +354,8 @@ static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex,
|
||||
for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
|
||||
ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
|
||||
if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
|
||||
return DGifExtensionToGCB(ep->ByteCount, (const GifByteType*)ep->Bytes,
|
||||
gcb);
|
||||
return DGifExtensionToGCB(
|
||||
ep->ByteCount, (const GifByteType*)ep->Bytes, gcb);
|
||||
}
|
||||
}
|
||||
return GIF_ERROR;
|
||||
@@ -377,12 +374,12 @@ static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex,
|
||||
#endif
|
||||
|
||||
static int IsKeyFrameGIF(const GifImageDesc* prev_desc, int prev_dispose,
|
||||
const DecodedFrame* const prev_frame, int canvas_width,
|
||||
int canvas_height) {
|
||||
const DecodedFrame* const prev_frame,
|
||||
int canvas_width, int canvas_height) {
|
||||
if (prev_frame == NULL) return 1;
|
||||
if (prev_dispose == DISPOSE_BACKGROUND) {
|
||||
if (IsFullFrame(prev_desc->Width, prev_desc->Height, canvas_width,
|
||||
canvas_height)) {
|
||||
if (IsFullFrame(prev_desc->Width, prev_desc->Height,
|
||||
canvas_width, canvas_height)) {
|
||||
return 1;
|
||||
}
|
||||
if (prev_frame->is_key_frame) return 1;
|
||||
@@ -403,12 +400,14 @@ static uint32_t GetBackgroundColorGIF(GifFileType* gif) {
|
||||
if (transparent_index != NO_TRANSPARENT_COLOR &&
|
||||
gif->SBackGroundColor == transparent_index) {
|
||||
return 0x00000000; // Special case: transparent black.
|
||||
} else if (color_map == NULL || color_map->Colors == NULL ||
|
||||
gif->SBackGroundColor >= color_map->ColorCount) {
|
||||
} else if (color_map == NULL || color_map->Colors == NULL
|
||||
|| gif->SBackGroundColor >= color_map->ColorCount) {
|
||||
return 0xffffffff; // Invalid: assume white.
|
||||
} else {
|
||||
const GifColorType color = color_map->Colors[gif->SBackGroundColor];
|
||||
return (0xffu << 24) | (color.Red << 16) | (color.Green << 8) |
|
||||
return (0xffu << 24) |
|
||||
(color.Red << 16) |
|
||||
(color.Green << 8) |
|
||||
(color.Blue << 0);
|
||||
}
|
||||
}
|
||||
@@ -433,10 +432,11 @@ static uint32_t GetLoopCountGIF(const GifFileType* const gif) {
|
||||
(eb1->ByteCount == 11) &&
|
||||
(!memcmp(signature, "NETSCAPE2.0", 11) ||
|
||||
!memcmp(signature, "ANIMEXTS1.0", 11));
|
||||
if (signature_is_ok && eb2->Function == CONTINUE_EXT_FUNC_CODE &&
|
||||
eb2->ByteCount >= 3 && eb2->Bytes[0] == 1) {
|
||||
const uint32_t extra_loop =
|
||||
((uint32_t)(eb2->Bytes[2]) << 8) + ((uint32_t)(eb2->Bytes[1]) << 0);
|
||||
if (signature_is_ok &&
|
||||
eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 &&
|
||||
eb2->Bytes[0] == 1) {
|
||||
const uint32_t extra_loop = ((uint32_t)(eb2->Bytes[2]) << 8) +
|
||||
((uint32_t)(eb2->Bytes[1]) << 0);
|
||||
return (extra_loop > 0) ? extra_loop + 1 : 0;
|
||||
}
|
||||
}
|
||||
@@ -532,8 +532,8 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
image->canvas_height = (uint32_t)gif->SHeight;
|
||||
if (image->canvas_width > MAX_CANVAS_SIZE ||
|
||||
image->canvas_height > MAX_CANVAS_SIZE) {
|
||||
fprintf(stderr, "Invalid canvas dimension: %d x %d\n", image->canvas_width,
|
||||
image->canvas_height);
|
||||
fprintf(stderr, "Invalid canvas dimension: %d x %d\n",
|
||||
image->canvas_width, image->canvas_height);
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -608,9 +608,11 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height);
|
||||
|
||||
// Dispose previous frame rectangle.
|
||||
prev_frame_disposed = (prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
|
||||
prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
|
||||
curr_frame_opaque = (curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
|
||||
prev_frame_disposed =
|
||||
(prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
|
||||
prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
|
||||
curr_frame_opaque =
|
||||
(curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
|
||||
prev_frame_completely_covered =
|
||||
curr_frame_opaque &&
|
||||
CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc);
|
||||
@@ -638,9 +640,9 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
uint8_t* const src_frame_rgba =
|
||||
image->frames[src_frame_num].rgba;
|
||||
CopyFrameRectangle(src_frame_rgba, curr_rgba,
|
||||
canvas_width_in_bytes, prev_desc->Left,
|
||||
prev_desc->Top, prev_desc->Width,
|
||||
prev_desc->Height);
|
||||
canvas_width_in_bytes,
|
||||
prev_desc->Left, prev_desc->Top,
|
||||
prev_desc->Width, prev_desc->Height);
|
||||
} else {
|
||||
// Source canvas doesn't exist. So clear previous frame
|
||||
// rectangle to background.
|
||||
@@ -665,8 +667,8 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
}
|
||||
|
||||
if (dump_frames) {
|
||||
if (!DumpFrame(filename, dump_folder, i, curr_rgba, canvas_width,
|
||||
canvas_height)) {
|
||||
if (!DumpFrame(filename, dump_folder, i, curr_rgba,
|
||||
canvas_width, canvas_height)) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -690,8 +692,7 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
(void)image;
|
||||
(void)dump_frames;
|
||||
(void)dump_folder;
|
||||
fprintf(stderr,
|
||||
"GIF support not compiled. Please install the libgif-dev "
|
||||
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
|
||||
"package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -714,8 +715,8 @@ int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
|
||||
}
|
||||
|
||||
if (IsWebP(&webp_data)) {
|
||||
ok =
|
||||
ReadAnimatedWebP(filename, &webp_data, image, dump_frames, dump_folder);
|
||||
ok = ReadAnimatedWebP(filename, &webp_data, image, dump_frames,
|
||||
dump_folder);
|
||||
} else if (IsGIF(&webp_data)) {
|
||||
ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder);
|
||||
} else {
|
||||
@@ -759,7 +760,8 @@ void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
|
||||
// premultiply R/G/B channels with alpha value
|
||||
for (k = 0; k < kAlphaChannel; ++k) {
|
||||
Accumulate(rgba1[offset + k] * alpha1 / 255.,
|
||||
rgba2[offset + k] * alpha2 / 255., &f_max_diff, &sse);
|
||||
rgba2[offset + k] * alpha2 / 255.,
|
||||
&f_max_diff, &sse);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -769,7 +771,6 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum { ANIM_GIF, ANIM_WEBP } AnimatedFileFormat;
|
||||
typedef enum {
|
||||
ANIM_GIF,
|
||||
ANIM_WEBP
|
||||
} AnimatedFileFormat;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* rgba; // Decoded and reconstructed full frame.
|
||||
int duration; // Frame duration in milliseconds.
|
||||
int is_key_frame; // True if this frame is a key-frame.
|
||||
uint8_t* rgba; // Decoded and reconstructed full frame.
|
||||
int duration; // Frame duration in milliseconds.
|
||||
int is_key_frame; // True if this frame is a key-frame.
|
||||
} DecodedFrame;
|
||||
|
||||
typedef struct {
|
||||
@@ -64,7 +67,7 @@ void GetAnimatedImageVersions(int* const decoder_version,
|
||||
int* const demux_version);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_EXAMPLES_ANIM_UTIL_H_
|
||||
|
||||
423
examples/cwebp.c
423
examples/cwebp.c
@@ -27,20 +27,18 @@
|
||||
#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
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif // WEBP_DLL
|
||||
|
||||
@@ -66,12 +64,12 @@ static int ReadYUV(const uint8_t* const data, size_t data_size,
|
||||
|
||||
pic->use_argb = 0;
|
||||
if (!WebPPictureAlloc(pic)) return 0;
|
||||
ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride, pic->width,
|
||||
pic->height);
|
||||
ImgIoUtilCopyPlane(data + y_plane_size, uv_width, pic->u, pic->uv_stride,
|
||||
uv_width, uv_height);
|
||||
ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width, pic->v,
|
||||
pic->uv_stride, uv_width, uv_height);
|
||||
ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride,
|
||||
pic->width, pic->height);
|
||||
ImgIoUtilCopyPlane(data + y_plane_size, uv_width,
|
||||
pic->u, pic->uv_stride, uv_width, uv_height);
|
||||
ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width,
|
||||
pic->v, pic->uv_stride, uv_width, uv_height);
|
||||
return use_argb ? WebPPictureYUVAToARGB(pic) : 1;
|
||||
}
|
||||
|
||||
@@ -119,7 +117,7 @@ static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
// If image size is specified, infer it as YUV format.
|
||||
ok = ReadYUV(data, data_size, pic);
|
||||
}
|
||||
End:
|
||||
End:
|
||||
if (!ok) {
|
||||
WFPRINTF(stderr, "Error! Could not process file %s\n",
|
||||
(const W_CHAR*)filename);
|
||||
@@ -168,8 +166,8 @@ static void PrintValues(const int values[4]) {
|
||||
|
||||
static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
|
||||
const char* const description) {
|
||||
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n", description,
|
||||
stats->lossless_size);
|
||||
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
|
||||
description, stats->lossless_size);
|
||||
fprintf(stderr, " * Header size: %d bytes, image data size: %d\n",
|
||||
stats->lossless_hdr_size, stats->lossless_data_size);
|
||||
if (stats->lossless_features) {
|
||||
@@ -180,14 +178,8 @@ static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
|
||||
if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
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);
|
||||
fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n",
|
||||
stats->histogram_bits, stats->transform_bits, stats->cache_bits);
|
||||
if (stats->palette_size > 0) {
|
||||
fprintf(stderr, " * Palette size: %d\n", stats->palette_size);
|
||||
}
|
||||
@@ -209,7 +201,8 @@ static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
||||
}
|
||||
|
||||
static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
||||
int full_details, const char* const file_name) {
|
||||
int full_details,
|
||||
const char* const file_name) {
|
||||
const WebPAuxStats* const stats = pic->stats;
|
||||
if (short_output) {
|
||||
fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
|
||||
@@ -219,25 +212,25 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
||||
const int num_skip = stats->block_count[2];
|
||||
const int total = num_i4 + num_i16;
|
||||
WFPRINTF(stderr, "File: %s\n", (const W_CHAR*)file_name);
|
||||
fprintf(stderr, "Dimension: %d x %d%s\n", pic->width, pic->height,
|
||||
fprintf(stderr, "Dimension: %d x %d%s\n",
|
||||
pic->width, pic->height,
|
||||
stats->alpha_data_size ? " (with alpha)" : "");
|
||||
fprintf(stderr,
|
||||
"Output: "
|
||||
fprintf(stderr, "Output: "
|
||||
"%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f %2.2f dB\n"
|
||||
" (%.2f bpp)\n",
|
||||
stats->coded_size, stats->PSNR[0], stats->PSNR[1], stats->PSNR[2],
|
||||
stats->PSNR[3], 8.f * stats->coded_size / pic->width / pic->height);
|
||||
stats->coded_size,
|
||||
stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3],
|
||||
8.f * stats->coded_size / pic->width / pic->height);
|
||||
if (total > 0) {
|
||||
int totals[4] = {0, 0, 0, 0};
|
||||
fprintf(stderr,
|
||||
"block count: intra4: %6d (%.2f%%)\n"
|
||||
" intra16: %6d (%.2f%%)\n"
|
||||
" skipped: %6d (%.2f%%)\n",
|
||||
num_i4, 100.f * num_i4 / total, num_i16, 100.f * num_i16 / total,
|
||||
int totals[4] = { 0, 0, 0, 0 };
|
||||
fprintf(stderr, "block count: intra4: %6d (%.2f%%)\n"
|
||||
" intra16: %6d (%.2f%%)\n"
|
||||
" skipped: %6d (%.2f%%)\n",
|
||||
num_i4, 100.f * num_i4 / total,
|
||||
num_i16, 100.f * num_i16 / total,
|
||||
num_skip, 100.f * num_skip / total);
|
||||
fprintf(stderr,
|
||||
"bytes used: header: %6d (%.1f%%)\n"
|
||||
" mode-partition: %6d (%.1f%%)\n",
|
||||
fprintf(stderr, "bytes used: header: %6d (%.1f%%)\n"
|
||||
" mode-partition: %6d (%.1f%%)\n",
|
||||
stats->header_bytes[0],
|
||||
100.f * stats->header_bytes[0] / stats->coded_size,
|
||||
stats->header_bytes[1],
|
||||
@@ -246,10 +239,9 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
||||
fprintf(stderr, " transparency: %6d (%.1f dB)\n",
|
||||
stats->alpha_data_size, stats->PSNR[4]);
|
||||
}
|
||||
fprintf(stderr,
|
||||
" Residuals bytes "
|
||||
"|segment 1|segment 2|segment 3"
|
||||
"|segment 4| total\n");
|
||||
fprintf(stderr, " Residuals bytes "
|
||||
"|segment 1|segment 2|segment 3"
|
||||
"|segment 4| total\n");
|
||||
if (full_details) {
|
||||
fprintf(stderr, " intra4-coeffs: ");
|
||||
PrintByteCount(stats->residual_bytes[0], stats->coded_size, totals);
|
||||
@@ -286,11 +278,11 @@ static void PrintMapInfo(const WebPPicture* const pic) {
|
||||
for (y = 0; y < mb_h; ++y) {
|
||||
for (x = 0; x < mb_w; ++x) {
|
||||
const int c = pic->extra_info[x + y * mb_w];
|
||||
if (type == 1) { // intra4/intra16
|
||||
if (type == 1) { // intra4/intra16
|
||||
fprintf(stderr, "%c", "+."[c]);
|
||||
} else if (type == 2) { // segments
|
||||
} else if (type == 2) { // segments
|
||||
fprintf(stderr, "%c", ".-*X"[c]);
|
||||
} else if (type == 3) { // quantizers
|
||||
} else if (type == 3) { // quantizers
|
||||
fprintf(stderr, "%.2d ", c);
|
||||
} else if (type == 6 || type == 7) {
|
||||
fprintf(stderr, "%3d ", c);
|
||||
@@ -314,7 +306,6 @@ 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;
|
||||
@@ -329,26 +320,23 @@ 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) goto Error;
|
||||
if (fwrite(src_y, picture->width, 1, f) != 1) return 0;
|
||||
if (picture->width & 1) fputc(0, f); // pad
|
||||
src_y += picture->y_stride;
|
||||
}
|
||||
for (y = 0; y < uv_height; ++y) {
|
||||
if (fwrite(src_u, uv_width, 1, f) != 1) goto Error;
|
||||
if (fwrite(src_v, uv_width, 1, f) != 1) goto Error;
|
||||
if (fwrite(src_u, uv_width, 1, f) != 1) return 0;
|
||||
if (fwrite(src_v, uv_width, 1, f) != 1) return 0;
|
||||
src_u += picture->uv_stride;
|
||||
src_v += picture->uv_stride;
|
||||
}
|
||||
for (y = 0; y < alpha_height; ++y) {
|
||||
if (fwrite(src_a, picture->width, 1, f) != 1) goto Error;
|
||||
if (fwrite(src_a, picture->width, 1, f) != 1) return 0;
|
||||
if (picture->width & 1) fputc(0, f); // pad
|
||||
src_a += picture->a_stride;
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
fclose(f);
|
||||
return ok;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -356,9 +344,9 @@ Error:
|
||||
|
||||
enum {
|
||||
METADATA_EXIF = (1 << 0),
|
||||
METADATA_ICC = (1 << 1),
|
||||
METADATA_XMP = (1 << 2),
|
||||
METADATA_ALL = METADATA_EXIF | METADATA_ICC | METADATA_XMP
|
||||
METADATA_ICC = (1 << 1),
|
||||
METADATA_XMP = (1 << 2),
|
||||
METADATA_ALL = METADATA_EXIF | METADATA_ICC | METADATA_XMP
|
||||
};
|
||||
|
||||
static const int kChunkHeaderSize = 8;
|
||||
@@ -411,9 +399,9 @@ static int WriteMetadataChunk(FILE* const out, const char fourcc[4],
|
||||
|
||||
// Sets 'flag' in 'vp8x_flags' and updates 'metadata_size' with the size of the
|
||||
// chunk if there is metadata and 'keep' is true.
|
||||
static int UpdateFlagsAndSize(const MetadataPayload* const payload, int keep,
|
||||
int flag, uint32_t* vp8x_flags,
|
||||
uint64_t* metadata_size) {
|
||||
static int UpdateFlagsAndSize(const MetadataPayload* const payload,
|
||||
int keep, int flag,
|
||||
uint32_t* vp8x_flags, uint64_t* metadata_size) {
|
||||
if (keep && payload->bytes != NULL && payload->size > 0) {
|
||||
*vp8x_flags |= flag;
|
||||
*metadata_size += kChunkHeaderSize + payload->size + (payload->size & 1);
|
||||
@@ -434,23 +422,23 @@ static int WriteWebPWithMetadata(FILE* const out,
|
||||
int* const metadata_written) {
|
||||
const char kVP8XHeader[] = "VP8X\x0a\x00\x00\x00";
|
||||
const int kAlphaFlag = 0x10;
|
||||
const int kEXIFFlag = 0x08;
|
||||
const int kICCPFlag = 0x20;
|
||||
const int kXMPFlag = 0x04;
|
||||
const int kEXIFFlag = 0x08;
|
||||
const int kICCPFlag = 0x20;
|
||||
const int kXMPFlag = 0x04;
|
||||
const size_t kRiffHeaderSize = 12;
|
||||
const size_t kMaxChunkPayload = ~0 - kChunkHeaderSize - 1;
|
||||
const size_t kMinSize = kRiffHeaderSize + kChunkHeaderSize;
|
||||
uint32_t flags = 0;
|
||||
uint64_t metadata_size = 0;
|
||||
const int write_exif =
|
||||
UpdateFlagsAndSize(&metadata->exif, !!(keep_metadata & METADATA_EXIF),
|
||||
kEXIFFlag, &flags, &metadata_size);
|
||||
const int write_iccp =
|
||||
UpdateFlagsAndSize(&metadata->iccp, !!(keep_metadata & METADATA_ICC),
|
||||
kICCPFlag, &flags, &metadata_size);
|
||||
const int write_xmp =
|
||||
UpdateFlagsAndSize(&metadata->xmp, !!(keep_metadata & METADATA_XMP),
|
||||
kXMPFlag, &flags, &metadata_size);
|
||||
const int write_exif = UpdateFlagsAndSize(&metadata->exif,
|
||||
!!(keep_metadata & METADATA_EXIF),
|
||||
kEXIFFlag, &flags, &metadata_size);
|
||||
const int write_iccp = UpdateFlagsAndSize(&metadata->iccp,
|
||||
!!(keep_metadata & METADATA_ICC),
|
||||
kICCPFlag, &flags, &metadata_size);
|
||||
const int write_xmp = UpdateFlagsAndSize(&metadata->xmp,
|
||||
!!(keep_metadata & METADATA_XMP),
|
||||
kXMPFlag, &flags, &metadata_size);
|
||||
uint8_t* webp = memory_writer->mem;
|
||||
size_t webp_size = memory_writer->size;
|
||||
|
||||
@@ -458,18 +446,17 @@ static int WriteWebPWithMetadata(FILE* const out,
|
||||
|
||||
if (webp_size < kMinSize) return 0;
|
||||
if (webp_size - kChunkHeaderSize + metadata_size > kMaxChunkPayload) {
|
||||
fprintf(stderr,
|
||||
"Error! Addition of metadata would exceed "
|
||||
"container size limit.\n");
|
||||
fprintf(stderr, "Error! Addition of metadata would exceed "
|
||||
"container size limit.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (metadata_size > 0) {
|
||||
const int kVP8XChunkSize = 18;
|
||||
const int has_vp8x = !memcmp(webp + kRiffHeaderSize, "VP8X", kTagSize);
|
||||
const uint32_t riff_size =
|
||||
(uint32_t)(webp_size - kChunkHeaderSize +
|
||||
(has_vp8x ? 0 : kVP8XChunkSize) + metadata_size);
|
||||
const uint32_t riff_size = (uint32_t)(webp_size - kChunkHeaderSize +
|
||||
(has_vp8x ? 0 : kVP8XChunkSize) +
|
||||
metadata_size);
|
||||
// RIFF
|
||||
int ok = (fwrite(webp, kTagSize, 1, out) == 1);
|
||||
// RIFF size (file header size is not recorded)
|
||||
@@ -518,39 +505,11 @@ static int WriteWebPWithMetadata(FILE* const out,
|
||||
return (fwrite(webp, webp_size, 1, out) == 1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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", (char*)picture->user_data, percent);
|
||||
fprintf(stderr, "[%s]: %3d %% \r",
|
||||
(char*)picture->user_data, percent);
|
||||
return 1; // all ok
|
||||
}
|
||||
|
||||
@@ -567,9 +526,8 @@ static void HelpShort(void) {
|
||||
static void HelpLong(void) {
|
||||
printf("Usage:\n");
|
||||
printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
|
||||
printf(
|
||||
"If input size (-s) for an image is not specified, it is\n"
|
||||
"assumed to be a PNG, JPEG, TIFF or WebP file.\n");
|
||||
printf("If input size (-s) for an image is not specified, it is\n"
|
||||
"assumed to be a PNG, JPEG, TIFF or WebP file.\n");
|
||||
printf("Note: Animated PNG and WebP files are not supported.\n");
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
printf("Windows builds can take as input any of the files handled by WIC.\n");
|
||||
@@ -577,59 +535,44 @@ static void HelpLong(void) {
|
||||
printf("\nOptions:\n");
|
||||
printf(" -h / -help ............. short help\n");
|
||||
printf(" -H / -longhelp ......... long help\n");
|
||||
printf(
|
||||
" -q <float> ............. quality factor (0:small..100:big), "
|
||||
"default=75\n");
|
||||
printf(
|
||||
" -alpha_q <int> ......... transparency-compression quality (0..100),"
|
||||
"\n default=100\n");
|
||||
printf(" -q <float> ............. quality factor (0:small..100:big), "
|
||||
"default=75\n");
|
||||
printf(" -alpha_q <int> ......... transparency-compression quality (0..100),"
|
||||
"\n default=100\n");
|
||||
printf(" -preset <string> ....... preset setting, one of:\n");
|
||||
printf(" default, photo, picture,\n");
|
||||
printf(" drawing, icon, text\n");
|
||||
printf(" -preset must come first, as it overwrites other parameters\n");
|
||||
printf(
|
||||
" -z <int> ............... activates lossless preset with given\n"
|
||||
" level in [0:fast, ..., 9:slowest]\n");
|
||||
printf(" -z <int> ............... activates lossless preset with given\n"
|
||||
" level in [0:fast, ..., 9:slowest]\n");
|
||||
printf("\n");
|
||||
printf(
|
||||
" -m <int> ............... compression method (0=fast, 6=slowest), "
|
||||
"default=4\n");
|
||||
printf(
|
||||
" -segments <int> ........ number of segments to use (1..4), "
|
||||
"default=4\n");
|
||||
printf(" -m <int> ............... compression method (0=fast, 6=slowest), "
|
||||
"default=4\n");
|
||||
printf(" -segments <int> ........ number of segments to use (1..4), "
|
||||
"default=4\n");
|
||||
printf(" -size <int> ............ target size (in bytes)\n");
|
||||
printf(" -psnr <float> .......... target PSNR (in dB. typically: 42)\n");
|
||||
printf("\n");
|
||||
printf(" -s <int> <int> ......... input size (width x height) for YUV\n");
|
||||
printf(
|
||||
" -sns <int> ............. spatial noise shaping (0:off, 100:max), "
|
||||
"default=50\n");
|
||||
printf(
|
||||
" -f <int> ............... filter strength (0=off..100), "
|
||||
"default=60\n");
|
||||
printf(
|
||||
" -sharpness <int> ....... "
|
||||
"filter sharpness (0:most .. 7:least sharp), default=0\n");
|
||||
printf(
|
||||
" -strong ................ use strong filter instead "
|
||||
"of simple (default)\n");
|
||||
printf(" -sns <int> ............. spatial noise shaping (0:off, 100:max), "
|
||||
"default=50\n");
|
||||
printf(" -f <int> ............... filter strength (0=off..100), "
|
||||
"default=60\n");
|
||||
printf(" -sharpness <int> ....... "
|
||||
"filter sharpness (0:most .. 7:least sharp), default=0\n");
|
||||
printf(" -strong ................ use strong filter instead "
|
||||
"of simple (default)\n");
|
||||
printf(" -nostrong .............. use simple filter instead of strong\n");
|
||||
printf(
|
||||
" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
|
||||
"conversion\n");
|
||||
printf(" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
|
||||
"conversion\n");
|
||||
printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n");
|
||||
printf(
|
||||
" "
|
||||
"the first partition (0=no degradation ... 100=full)\n");
|
||||
printf(" "
|
||||
"the first partition (0=no degradation ... 100=full)\n");
|
||||
printf(" -pass <int> ............ analysis pass number (1..10)\n");
|
||||
printf(
|
||||
" -qrange <min> <max> .... specifies the permissible quality range\n"
|
||||
" (default: 0 100)\n");
|
||||
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_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");
|
||||
@@ -637,24 +580,21 @@ static void HelpLong(void) {
|
||||
printf(" -print_ssim ............ prints averaged SSIM distortion\n");
|
||||
printf(" -print_lsim ............ prints local-similarity distortion\n");
|
||||
printf(" -d <file.pgm> .......... dump the compressed output (PGM file)\n");
|
||||
printf(
|
||||
" -alpha_method <int> .... transparency-compression method (0..1), "
|
||||
"default=1\n");
|
||||
printf(" -alpha_method <int> .... transparency-compression method (0..1), "
|
||||
"default=1\n");
|
||||
printf(" -alpha_filter <string> . predictive filtering for alpha plane,\n");
|
||||
printf(" one of: none, fast (default) or best\n");
|
||||
printf(
|
||||
" -exact ................. preserve RGB values in transparent area, "
|
||||
"default=off\n");
|
||||
printf(
|
||||
" -blend_alpha <hex> ..... blend colors against background color\n"
|
||||
" expressed as RGB values written in\n"
|
||||
" hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
|
||||
" green=0xe0 and blue=0xd0\n");
|
||||
printf(" -exact ................. preserve RGB values in transparent area, "
|
||||
"default=off\n");
|
||||
printf(" -blend_alpha <hex> ..... blend colors against background color\n"
|
||||
" expressed as RGB values written in\n"
|
||||
" hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
|
||||
" green=0xe0 and blue=0xd0\n");
|
||||
printf(" -noalpha ............... discard any transparency information\n");
|
||||
printf(" -lossless .............. encode image losslessly, default=off\n");
|
||||
printf(
|
||||
" -near_lossless <int> ... use near-lossless image preprocessing\n"
|
||||
" (0..100=off), default=100\n");
|
||||
printf(" -near_lossless <int> ... use near-lossless image\n"
|
||||
" preprocessing (0..100=off), "
|
||||
"default=100\n");
|
||||
printf(" -hint <string> ......... specify image characteristics hint,\n");
|
||||
printf(" one of: photo, picture or graph\n");
|
||||
|
||||
@@ -662,9 +602,8 @@ static void HelpLong(void) {
|
||||
printf(" -metadata <string> ..... comma separated list of metadata to\n");
|
||||
printf(" ");
|
||||
printf("copy from the input to the output if present.\n");
|
||||
printf(
|
||||
" "
|
||||
"Valid values: all, none (default), exif, icc, xmp\n");
|
||||
printf(" "
|
||||
"Valid values: all, none (default), exif, icc, xmp\n");
|
||||
|
||||
printf("\n");
|
||||
printf(" -short ................. condense printed message\n");
|
||||
@@ -673,9 +612,8 @@ static void HelpLong(void) {
|
||||
#ifndef WEBP_DLL
|
||||
printf(" -noasm ................. disable all assembly optimizations\n");
|
||||
#endif
|
||||
printf(
|
||||
" -v ..................... verbose, e.g. print encoding/decoding "
|
||||
"times\n");
|
||||
printf(" -v ..................... verbose, e.g. print encoding/decoding "
|
||||
"times\n");
|
||||
printf(" -progress .............. report encoding progress\n");
|
||||
printf("\n");
|
||||
printf("Experimental Options:\n");
|
||||
@@ -690,29 +628,29 @@ static void HelpLong(void) {
|
||||
// Error messages
|
||||
|
||||
static const char* const kErrorMessages[VP8_ENC_ERROR_LAST] = {
|
||||
"OK",
|
||||
"OUT_OF_MEMORY: Out of memory allocating objects",
|
||||
"BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
|
||||
"NULL_PARAMETER: NULL parameter passed to function",
|
||||
"INVALID_CONFIGURATION: configuration is invalid",
|
||||
"BAD_DIMENSION: Bad picture dimension. Maximum width and height "
|
||||
"allowed is 16383 pixels.",
|
||||
"PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
|
||||
"To reduce the size of this partition, try using less segments "
|
||||
"with the -segments option, and eventually reduce the number of "
|
||||
"header bits using -partition_limit. More details are available "
|
||||
"in the manual (`man cwebp`)",
|
||||
"PARTITION_OVERFLOW: Partition is too big to fit 16M",
|
||||
"BAD_WRITE: Picture writer returned an I/O error",
|
||||
"FILE_TOO_BIG: File would be too big to fit in 4G",
|
||||
"USER_ABORT: encoding abort requested by user"};
|
||||
"OK",
|
||||
"OUT_OF_MEMORY: Out of memory allocating objects",
|
||||
"BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
|
||||
"NULL_PARAMETER: NULL parameter passed to function",
|
||||
"INVALID_CONFIGURATION: configuration is invalid",
|
||||
"BAD_DIMENSION: Bad picture dimension. Maximum width and height "
|
||||
"allowed is 16383 pixels.",
|
||||
"PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
|
||||
"To reduce the size of this partition, try using less segments "
|
||||
"with the -segments option, and eventually reduce the number of "
|
||||
"header bits using -partition_limit. More details are available "
|
||||
"in the manual (`man cwebp`)",
|
||||
"PARTITION_OVERFLOW: Partition is too big to fit 16M",
|
||||
"BAD_WRITE: Picture writer returned an I/O error",
|
||||
"FILE_TOO_BIG: File would be too big to fit in 4G",
|
||||
"USER_ABORT: encoding abort requested by user"
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
|
||||
int main(int argc, const char* argv[]) {
|
||||
int return_value = EXIT_FAILURE;
|
||||
const char *in_file = NULL, *out_file = NULL, *dump_file = NULL;
|
||||
int return_value = -1;
|
||||
const char* in_file = NULL, *out_file = NULL, *dump_file = NULL;
|
||||
FILE* out = NULL;
|
||||
int c;
|
||||
int short_output = 0;
|
||||
@@ -722,15 +660,14 @@ 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;
|
||||
int keep_metadata = 0;
|
||||
int metadata_written = 0;
|
||||
WebPPicture picture;
|
||||
int print_distortion = -1; // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
|
||||
WebPPicture original_picture; // when PSNR or SSIM is requested
|
||||
int print_distortion = -1; // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
|
||||
WebPPicture original_picture; // when PSNR or SSIM is requested
|
||||
WebPConfig config;
|
||||
WebPAuxStats stats;
|
||||
WebPMemoryWriter memory_writer;
|
||||
@@ -742,25 +679,26 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
MetadataInit(&metadata);
|
||||
WebPMemoryWriterInit(&memory_writer);
|
||||
if (!WebPPictureInit(&picture) || !WebPPictureInit(&original_picture) ||
|
||||
if (!WebPPictureInit(&picture) ||
|
||||
!WebPPictureInit(&original_picture) ||
|
||||
!WebPConfigInit(&config)) {
|
||||
fprintf(stderr, "Error! Version mismatch!\n");
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
HelpShort();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
HelpShort();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
|
||||
HelpLong();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-o") && c + 1 < argc) {
|
||||
out_file = (const char*)GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
|
||||
@@ -781,17 +719,18 @@ int main(int argc, const char* argv[]) {
|
||||
picture.width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
picture.height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 ||
|
||||
picture.height > WEBP_MAX_DIMENSION || picture.height < 0) {
|
||||
fprintf(stderr, "Specified dimension (%d x %d) is out of range.\n",
|
||||
picture.height > WEBP_MAX_DIMENSION || picture.height < 0) {
|
||||
fprintf(stderr,
|
||||
"Specified dimension (%d x %d) is out of range.\n",
|
||||
picture.width, picture.height);
|
||||
goto Error;
|
||||
}
|
||||
} else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
|
||||
config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
} else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
|
||||
config.quality = ExUtilGetFloat(argv[++c], &parse_error);
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
} else if (!strcmp(argv[c], "-z") && c + 1 < argc) {
|
||||
lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
if (use_lossless_preset != 0) use_lossless_preset = 1;
|
||||
@@ -888,18 +827,6 @@ 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;
|
||||
@@ -907,11 +834,12 @@ int main(int argc, const char* argv[]) {
|
||||
} 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);
|
||||
printf("libsharpyuv: %d.%d.%d\n", (sharpyuv_version >> 24) & 0xff,
|
||||
(sharpyuv_version >> 16) & 0xffff, sharpyuv_version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
printf("%d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
printf("libsharpyuv: %d.%d.%d\n",
|
||||
(sharpyuv_version >> 24) & 0xff, (sharpyuv_version >> 16) & 0xffff,
|
||||
sharpyuv_version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-progress")) {
|
||||
show_progress = 1;
|
||||
} else if (!strcmp(argv[c], "-quiet")) {
|
||||
@@ -944,11 +872,11 @@ int main(int argc, const char* argv[]) {
|
||||
const char* option;
|
||||
int flag;
|
||||
} kTokens[] = {
|
||||
{"all", METADATA_ALL}, //
|
||||
{"none", 0}, //
|
||||
{"exif", METADATA_EXIF}, //
|
||||
{"icc", METADATA_ICC}, //
|
||||
{"xmp", METADATA_XMP}, //
|
||||
{ "all", METADATA_ALL },
|
||||
{ "none", 0 },
|
||||
{ "exif", METADATA_EXIF },
|
||||
{ "icc", METADATA_ICC },
|
||||
{ "xmp", METADATA_XMP },
|
||||
};
|
||||
const size_t kNumTokens = sizeof(kTokens) / sizeof(kTokens[0]);
|
||||
const char* start = argv[++c];
|
||||
@@ -973,16 +901,15 @@ 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(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
start = token + 1;
|
||||
}
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
if (keep_metadata != 0 && keep_metadata != METADATA_ICC) {
|
||||
// TODO(jzern): remove when -metadata is supported on all platforms.
|
||||
fprintf(stderr,
|
||||
"Warning: only ICC profile extraction is currently"
|
||||
" supported on this platform!\n");
|
||||
fprintf(stderr, "Warning: only ICC profile extraction is currently"
|
||||
" supported on this platform!\n");
|
||||
}
|
||||
#endif
|
||||
} else if (!strcmp(argv[c], "-v")) {
|
||||
@@ -993,14 +920,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(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
} else {
|
||||
in_file = (const char*)GET_WARGV(argv, c);
|
||||
}
|
||||
|
||||
if (parse_error) {
|
||||
HelpLong();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
if (in_file == NULL) {
|
||||
@@ -1020,14 +947,12 @@ int main(int argc, const char* argv[]) {
|
||||
// warning for such options.
|
||||
if (!quiet && config.lossless == 1) {
|
||||
if (config.target_size > 0 || config.target_PSNR > 0) {
|
||||
fprintf(stderr,
|
||||
"Encoding for specified size or PSNR is not supported"
|
||||
" for lossless encoding. Ignoring such option(s)!\n");
|
||||
fprintf(stderr, "Encoding for specified size or PSNR is not supported"
|
||||
" for lossless encoding. Ignoring such option(s)!\n");
|
||||
}
|
||||
if (config.partition_limit > 0) {
|
||||
fprintf(stderr,
|
||||
"Partition limit option is not required for lossless"
|
||||
" encoding. Ignoring this option!\n");
|
||||
fprintf(stderr, "Partition limit option is not required for lossless"
|
||||
" encoding. Ignoring this option!\n");
|
||||
}
|
||||
}
|
||||
// If a target size or PSNR was given, but somehow the -pass option was
|
||||
@@ -1044,9 +969,9 @@ int main(int argc, const char* argv[]) {
|
||||
// Read the input. We need to decide if we prefer ARGB or YUVA
|
||||
// samples, depending on the expected compression mode (this saves
|
||||
// some conversion steps).
|
||||
picture.use_argb =
|
||||
(config.lossless || config.use_sharp_yuv || config.preprocessing > 0 ||
|
||||
crop || (resize_w | resize_h) > 0);
|
||||
picture.use_argb = (config.lossless || config.use_sharp_yuv ||
|
||||
config.preprocessing > 0 ||
|
||||
crop || (resize_w | resize_h) > 0);
|
||||
if (verbose) {
|
||||
StopwatchReset(&stop_watch);
|
||||
}
|
||||
@@ -1122,7 +1047,6 @@ 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) {
|
||||
@@ -1194,8 +1118,8 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
if (!WebPEncode(&config, &picture)) {
|
||||
fprintf(stderr, "Error! Cannot encode picture as WebP\n");
|
||||
fprintf(stderr, "Error code: %d (%s)\n", picture.error_code,
|
||||
kErrorMessages[picture.error_code]);
|
||||
fprintf(stderr, "Error code: %d (%s)\n",
|
||||
picture.error_code, kErrorMessages[picture.error_code]);
|
||||
goto Error;
|
||||
}
|
||||
if (verbose) {
|
||||
@@ -1238,9 +1162,8 @@ int main(int argc, const char* argv[]) {
|
||||
// Write the YUV planes to a PGM file. Only available for lossy.
|
||||
if (dump_file) {
|
||||
if (picture.use_argb) {
|
||||
fprintf(stderr,
|
||||
"Warning: can't dump file (-d option) "
|
||||
"in lossless mode.\n");
|
||||
fprintf(stderr, "Warning: can't dump file (-d option) "
|
||||
"in lossless mode.\n");
|
||||
} else if (!DumpPicture(&picture, dump_file)) {
|
||||
WFPRINTF(stderr, "Warning, couldn't dump picture %s\n",
|
||||
(const W_CHAR*)dump_file);
|
||||
@@ -1285,18 +1208,18 @@ int main(int argc, const char* argv[]) {
|
||||
if (!short_output && picture.extra_info_type > 0) {
|
||||
PrintMapInfo(&picture);
|
||||
}
|
||||
if (print_distortion >= 0) { // print distortion
|
||||
static const char* distortion_names[] = {"PSNR", "SSIM", "LSIM"};
|
||||
if (print_distortion >= 0) { // print distortion
|
||||
static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
|
||||
float values[5];
|
||||
if (!WebPPictureDistortion(&picture, &original_picture, print_distortion,
|
||||
values)) {
|
||||
if (!WebPPictureDistortion(&picture, &original_picture,
|
||||
print_distortion, values)) {
|
||||
fprintf(stderr, "Error while computing the distortion.\n");
|
||||
goto Error;
|
||||
}
|
||||
if (!short_output) {
|
||||
fprintf(stderr, "%s: ", distortion_names[print_distortion]);
|
||||
fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f Total:%.2f\n", values[0],
|
||||
values[1], values[2], values[3], values[4]);
|
||||
fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f Total:%.2f\n",
|
||||
values[0], values[1], values[2], values[3], values[4]);
|
||||
} else {
|
||||
fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]);
|
||||
}
|
||||
@@ -1305,9 +1228,9 @@ int main(int argc, const char* argv[]) {
|
||||
PrintMetadataInfo(&metadata, metadata_written);
|
||||
}
|
||||
}
|
||||
return_value = EXIT_SUCCESS;
|
||||
return_value = 0;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
WebPMemoryWriterClear(&memory_writer);
|
||||
WebPFree(picture.extra_info);
|
||||
MetadataFree(&metadata);
|
||||
|
||||
224
examples/dwebp.c
224
examples/dwebp.c
@@ -25,8 +25,6 @@
|
||||
#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;
|
||||
@@ -35,13 +33,14 @@ static int quiet = 0;
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif // WEBP_DLL
|
||||
|
||||
|
||||
static int SaveOutput(const WebPDecBuffer* const buffer,
|
||||
WebPOutputFileFormat format, const char* const out_file) {
|
||||
const int use_stdout = (out_file != NULL) && !WSTRCMP(out_file, "-");
|
||||
@@ -76,42 +75,43 @@ static int SaveOutput(const WebPDecBuffer* const buffer,
|
||||
}
|
||||
|
||||
static void Help(void) {
|
||||
printf(
|
||||
"Usage: dwebp in_file [options] [-o out_file]\n\n"
|
||||
"Decodes the WebP image file to PNG format [Default].\n"
|
||||
"Note: Animated WebP files are not supported.\n\n"
|
||||
"Use following options to convert into alternate image formats:\n"
|
||||
" -pam ......... save the raw RGBA samples as a color PAM\n"
|
||||
" -ppm ......... save the raw RGB samples as a color PPM\n"
|
||||
" -bmp ......... save as uncompressed BMP format\n"
|
||||
" -tiff ........ save as uncompressed TIFF format\n"
|
||||
" -pgm ......... save the raw YUV samples as a grayscale PGM\n"
|
||||
" file with IMC4 layout\n"
|
||||
" -yuv ......... save the raw YUV samples in flat layout\n"
|
||||
"\n"
|
||||
" Other options are:\n"
|
||||
" -version ..... print version number and exit\n"
|
||||
" -nofancy ..... don't use the fancy YUV420 upscaler\n"
|
||||
" -nofilter .... disable in-loop filtering\n"
|
||||
" -nodither .... disable dithering\n"
|
||||
" -dither <d> .. dithering strength (in 0..100)\n"
|
||||
" -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> ......... 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"
|
||||
" -h ........... this help message\n"
|
||||
" -v ........... verbose (e.g. print encoding/decoding times)\n"
|
||||
" -quiet ....... quiet mode, don't print anything\n"
|
||||
printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
|
||||
"Decodes the WebP image file to PNG format [Default].\n"
|
||||
"Note: Animated WebP files are not supported.\n\n"
|
||||
"Use following options to convert into alternate image formats:\n"
|
||||
" -pam ......... save the raw RGBA samples as a color PAM\n"
|
||||
" -ppm ......... save the raw RGB samples as a color PPM\n"
|
||||
" -bmp ......... save as uncompressed BMP format\n"
|
||||
" -tiff ........ save as uncompressed TIFF format\n"
|
||||
" -pgm ......... save the raw YUV samples as a grayscale PGM\n"
|
||||
" file with IMC4 layout\n"
|
||||
" -yuv ......... save the raw YUV samples in flat layout\n"
|
||||
"\n"
|
||||
" Other options are:\n"
|
||||
" -version ..... print version number and exit\n"
|
||||
" -nofancy ..... don't use the fancy YUV420 upscaler\n"
|
||||
" -nofilter .... disable in-loop filtering\n"
|
||||
" -nodither .... disable dithering\n"
|
||||
" -dither <d> .. dithering strength (in 0..100)\n"
|
||||
" -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> ......... 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"
|
||||
" -h ........... this help message\n"
|
||||
" -v ........... verbose (e.g. print encoding/decoding times)\n"
|
||||
" -quiet ....... quiet mode, don't print anything\n"
|
||||
#ifndef WEBP_DLL
|
||||
" -noasm ....... disable all assembly optimizations\n"
|
||||
" -noasm ....... disable all assembly optimizations\n"
|
||||
#endif
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
static const char* const kFormatType[] = {"unspecified", "lossy", "lossless"};
|
||||
static const char* const kFormatType[] = {
|
||||
"unspecified", "lossy", "lossless"
|
||||
};
|
||||
|
||||
static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
|
||||
WebPOutputFileFormat format,
|
||||
@@ -128,23 +128,23 @@ static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
|
||||
h = config->options.crop_height;
|
||||
}
|
||||
if (format >= RGB && format <= rgbA_4444) {
|
||||
const int bpp =
|
||||
(format == RGB || format == BGR) ? 3
|
||||
: (format == RGBA_4444 || format == rgbA_4444 || format == RGB_565) ? 2
|
||||
: 4;
|
||||
uint32_t stride = bpp * w + 7; // <- just for exercising
|
||||
const int bpp = (format == RGB || format == BGR) ? 3
|
||||
: (format == RGBA_4444 || format == rgbA_4444 ||
|
||||
format == RGB_565) ? 2
|
||||
: 4;
|
||||
uint32_t stride = bpp * w + 7; // <- just for exercising
|
||||
external_buffer = (uint8_t*)WebPMalloc(stride * h);
|
||||
if (external_buffer == NULL) return NULL;
|
||||
output_buffer->u.RGBA.stride = stride;
|
||||
output_buffer->u.RGBA.size = stride * h;
|
||||
output_buffer->u.RGBA.rgba = external_buffer;
|
||||
} else { // YUV and YUVA
|
||||
} else { // YUV and YUVA
|
||||
const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace);
|
||||
uint8_t* tmp;
|
||||
uint32_t stride = w + 3;
|
||||
uint32_t uv_stride = (w + 1) / 2 + 13;
|
||||
uint32_t total_size =
|
||||
stride * h * (has_alpha ? 2 : 1) + 2 * uv_stride * (h + 1) / 2;
|
||||
uint32_t total_size = stride * h * (has_alpha ? 2 : 1)
|
||||
+ 2 * uv_stride * (h + 1) / 2;
|
||||
assert(format >= YUV && format <= YUVA);
|
||||
external_buffer = (uint8_t*)WebPMalloc(total_size);
|
||||
if (external_buffer == NULL) return NULL;
|
||||
@@ -177,7 +177,6 @@ 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;
|
||||
@@ -198,14 +197,14 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (!WebPInitDecoderConfig(&config)) {
|
||||
fprintf(stderr, "Library version mismatch!\n");
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
|
||||
out_file = (const char*)GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-alpha")) {
|
||||
@@ -226,42 +225,29 @@ int main(int argc, const char* argv[]) {
|
||||
quiet = 1;
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
const int version = WebPGetDecoderVersion();
|
||||
printf("%d.%d.%d\n", (version >> 16) & 0xff, (version >> 8) & 0xff,
|
||||
version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
printf("%d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-pgm")) {
|
||||
format = PGM;
|
||||
} else if (!strcmp(argv[c], "-yuv")) {
|
||||
format = RAW_YUV;
|
||||
} else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) {
|
||||
const char* const fmt = argv[++c];
|
||||
if (!strcmp(fmt, "RGB")) {
|
||||
format = RGB;
|
||||
} else if (!strcmp(fmt, "RGBA")) {
|
||||
format = RGBA;
|
||||
} else if (!strcmp(fmt, "BGR")) {
|
||||
format = BGR;
|
||||
} else if (!strcmp(fmt, "BGRA")) {
|
||||
format = BGRA;
|
||||
} else if (!strcmp(fmt, "ARGB")) {
|
||||
format = ARGB;
|
||||
} else if (!strcmp(fmt, "RGBA_4444")) {
|
||||
format = RGBA_4444;
|
||||
} else if (!strcmp(fmt, "RGB_565")) {
|
||||
format = RGB_565;
|
||||
} else if (!strcmp(fmt, "rgbA")) {
|
||||
format = rgbA;
|
||||
} else if (!strcmp(fmt, "bgrA")) {
|
||||
format = bgrA;
|
||||
} else if (!strcmp(fmt, "Argb")) {
|
||||
format = Argb;
|
||||
} else if (!strcmp(fmt, "rgbA_4444")) {
|
||||
format = rgbA_4444;
|
||||
} else if (!strcmp(fmt, "YUV")) {
|
||||
format = YUV;
|
||||
} else if (!strcmp(fmt, "YUVA")) {
|
||||
format = YUVA;
|
||||
} else {
|
||||
if (!strcmp(fmt, "RGB")) format = RGB;
|
||||
else if (!strcmp(fmt, "RGBA")) format = RGBA;
|
||||
else if (!strcmp(fmt, "BGR")) format = BGR;
|
||||
else if (!strcmp(fmt, "BGRA")) format = BGRA;
|
||||
else if (!strcmp(fmt, "ARGB")) format = ARGB;
|
||||
else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444;
|
||||
else if (!strcmp(fmt, "RGB_565")) format = RGB_565;
|
||||
else if (!strcmp(fmt, "rgbA")) format = rgbA;
|
||||
else if (!strcmp(fmt, "bgrA")) format = bgrA;
|
||||
else if (!strcmp(fmt, "Argb")) format = Argb;
|
||||
else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444;
|
||||
else if (!strcmp(fmt, "YUV")) format = YUV;
|
||||
else if (!strcmp(fmt, "YUVA")) format = YUVA;
|
||||
else {
|
||||
fprintf(stderr, "Can't parse pixel_format %s\n", fmt);
|
||||
parse_error = 1;
|
||||
}
|
||||
@@ -282,14 +268,14 @@ int main(int argc, const char* argv[]) {
|
||||
ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
||||
config.options.use_cropping = 1;
|
||||
config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if ((!strcmp(argv[c], "-scale") || !strcmp(argv[c], "-resize")) &&
|
||||
c < argc - 2) { // '-scale' is left for compatibility
|
||||
config.options.use_scaling = 1;
|
||||
config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.scaled_height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-flip")) {
|
||||
config.options.flip = 1;
|
||||
@@ -307,21 +293,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(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
} else {
|
||||
in_file = (const char*)GET_WARGV(argv, c);
|
||||
}
|
||||
|
||||
if (parse_error) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_file == NULL) {
|
||||
fprintf(stderr, "missing input file!!\n");
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
if (quiet) verbose = 0;
|
||||
@@ -330,7 +316,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(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
@@ -361,52 +347,25 @@ int main(int argc, const char* argv[]) {
|
||||
output_buffer->colorspace = MODE_YUVA;
|
||||
break;
|
||||
// forced modes:
|
||||
case RGB:
|
||||
output_buffer->colorspace = MODE_RGB;
|
||||
break;
|
||||
case RGBA:
|
||||
output_buffer->colorspace = MODE_RGBA;
|
||||
break;
|
||||
case BGR:
|
||||
output_buffer->colorspace = MODE_BGR;
|
||||
break;
|
||||
case BGRA:
|
||||
output_buffer->colorspace = MODE_BGRA;
|
||||
break;
|
||||
case ARGB:
|
||||
output_buffer->colorspace = MODE_ARGB;
|
||||
break;
|
||||
case RGBA_4444:
|
||||
output_buffer->colorspace = MODE_RGBA_4444;
|
||||
break;
|
||||
case RGB_565:
|
||||
output_buffer->colorspace = MODE_RGB_565;
|
||||
break;
|
||||
case rgbA:
|
||||
output_buffer->colorspace = MODE_rgbA;
|
||||
break;
|
||||
case bgrA:
|
||||
output_buffer->colorspace = MODE_bgrA;
|
||||
break;
|
||||
case Argb:
|
||||
output_buffer->colorspace = MODE_Argb;
|
||||
break;
|
||||
case rgbA_4444:
|
||||
output_buffer->colorspace = MODE_rgbA_4444;
|
||||
break;
|
||||
case YUV:
|
||||
output_buffer->colorspace = MODE_YUV;
|
||||
break;
|
||||
case YUVA:
|
||||
output_buffer->colorspace = MODE_YUVA;
|
||||
break;
|
||||
default:
|
||||
goto Exit;
|
||||
case RGB: output_buffer->colorspace = MODE_RGB; break;
|
||||
case RGBA: output_buffer->colorspace = MODE_RGBA; break;
|
||||
case BGR: output_buffer->colorspace = MODE_BGR; break;
|
||||
case BGRA: output_buffer->colorspace = MODE_BGRA; break;
|
||||
case ARGB: output_buffer->colorspace = MODE_ARGB; break;
|
||||
case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break;
|
||||
case RGB_565: output_buffer->colorspace = MODE_RGB_565; break;
|
||||
case rgbA: output_buffer->colorspace = MODE_rgbA; break;
|
||||
case bgrA: output_buffer->colorspace = MODE_bgrA; break;
|
||||
case Argb: output_buffer->colorspace = MODE_Argb; break;
|
||||
case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break;
|
||||
case YUV: output_buffer->colorspace = MODE_YUV; break;
|
||||
case YUVA: output_buffer->colorspace = MODE_YUVA; break;
|
||||
default: goto Exit;
|
||||
}
|
||||
|
||||
if (use_external_memory > 0 && format >= RGB) {
|
||||
external_buffer =
|
||||
AllocateExternalBuffer(&config, format, use_external_memory);
|
||||
external_buffer = AllocateExternalBuffer(&config, format,
|
||||
use_external_memory);
|
||||
if (external_buffer == NULL) goto Exit;
|
||||
}
|
||||
|
||||
@@ -448,16 +407,15 @@ int main(int argc, const char* argv[]) {
|
||||
output_buffer->width, output_buffer->height,
|
||||
bitstream->has_alpha ? " (with alpha)" : "",
|
||||
kFormatType[bitstream->format]);
|
||||
fprintf(stderr,
|
||||
"Nothing written; "
|
||||
"use -o flag to save the result as e.g. PNG.\n");
|
||||
fprintf(stderr, "Nothing written; "
|
||||
"use -o flag to save the result as e.g. PNG.\n");
|
||||
}
|
||||
}
|
||||
Exit:
|
||||
Exit:
|
||||
WebPFreeDecBuffer(output_buffer);
|
||||
WebPFree((void*)external_buffer);
|
||||
WebPFree((void*)data);
|
||||
FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(ok ? 0 : -1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// String parsing
|
||||
@@ -46,7 +45,7 @@ int ExUtilGetInts(const char* v, int base, int max_output, int output[]) {
|
||||
if (error) return -1;
|
||||
output[n] = value;
|
||||
v = strchr(v, ',');
|
||||
if (v != NULL) ++v; // skip over the trailing ','
|
||||
if (v != NULL) ++v; // skip over the trailing ','
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -67,17 +66,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);
|
||||
}
|
||||
@@ -99,18 +98,19 @@ 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) {
|
||||
args->own_argv_ = 1;
|
||||
args->argv_ = (const char**)WebPMalloc(MAX_ARGC * sizeof(*args->argv_));
|
||||
if (args->argv_ == NULL) {
|
||||
ExUtilDeleteCommandLineArguments(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
argc = 0;
|
||||
for (cur = strtok((char*)args->argv_data.bytes, sep); cur != NULL;
|
||||
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);
|
||||
@@ -118,9 +118,9 @@ int ExUtilInitCommandLineArguments(int argc, const char* argv[],
|
||||
return 0;
|
||||
}
|
||||
assert(strlen(cur) != 0);
|
||||
args->argv[argc++] = cur;
|
||||
args->argv_[argc++] = cur;
|
||||
}
|
||||
args->argc = argc;
|
||||
args->argc_ = argc;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#ifndef WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
||||
#define WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
||||
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
#include "webp/mux_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -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
|
||||
@@ -64,7 +64,7 @@ int ExUtilInitCommandLineArguments(int argc, const char* argv[],
|
||||
void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
||||
|
||||
@@ -28,15 +28,13 @@
|
||||
#endif
|
||||
|
||||
#include <gif_lib.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux.h"
|
||||
#include "../examples/example_util.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./gifdec.h"
|
||||
#include "./unicode.h"
|
||||
#include "./unicode_gif.h"
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux.h"
|
||||
|
||||
#if !defined(STDIN_FILENO)
|
||||
#define STDIN_FILENO 0
|
||||
@@ -47,8 +45,9 @@
|
||||
static int transparent_index = GIF_INDEX_INVALID; // Opaque by default.
|
||||
|
||||
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
|
||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"};
|
||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
|
||||
};
|
||||
|
||||
static const char* ErrorString(WebPMuxError err) {
|
||||
assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
|
||||
@@ -56,9 +55,9 @@ static const char* ErrorString(WebPMuxError err) {
|
||||
}
|
||||
|
||||
enum {
|
||||
METADATA_ICC = (1 << 0),
|
||||
METADATA_XMP = (1 << 1),
|
||||
METADATA_ALL = METADATA_ICC | METADATA_XMP
|
||||
METADATA_ICC = (1 << 0),
|
||||
METADATA_XMP = (1 << 1),
|
||||
METADATA_ALL = METADATA_ICC | METADATA_XMP
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -69,25 +68,14 @@ static void Help(void) {
|
||||
printf("Options:\n");
|
||||
printf(" -h / -help ............. this help\n");
|
||||
printf(" -lossy ................. encode image using lossy compression\n");
|
||||
printf(
|
||||
" -mixed ................. for each frame in the image, pick lossy\n"
|
||||
" or lossless compression heuristically\n");
|
||||
printf(
|
||||
" -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(" -mixed ................. for each frame in the image, pick lossy\n"
|
||||
" or lossless compression heuristically\n");
|
||||
printf(" -q <float> ............. quality factor (0:small..100:big)\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"
|
||||
" options\n");
|
||||
printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
|
||||
printf(" -min_size .............. minimize output size (default:off)\n"
|
||||
" lossless compression by default; can be\n"
|
||||
" combined with -q, -m, -lossy or -mixed\n"
|
||||
" options\n");
|
||||
printf(" -kmin <int> ............ min distance between key frames\n");
|
||||
printf(" -kmax <int> ............ max distance between key frames\n");
|
||||
printf(" -f <int> ............... filter strength (0=off..100)\n");
|
||||
@@ -108,27 +96,26 @@ 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;
|
||||
WebPMuxError err = WEBP_MUX_OK;
|
||||
int ok = 0;
|
||||
const W_CHAR *in_file = NULL, *out_file = NULL;
|
||||
const W_CHAR* in_file = NULL, *out_file = NULL;
|
||||
GifFileType* gif = NULL;
|
||||
int frame_duration = 0;
|
||||
int frame_timestamp = 0;
|
||||
GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE;
|
||||
|
||||
WebPPicture frame; // Frame rectangle only (not disposed).
|
||||
WebPPicture curr_canvas; // Not disposed.
|
||||
WebPPicture prev_canvas; // Disposed.
|
||||
WebPPicture frame; // Frame rectangle only (not disposed).
|
||||
WebPPicture curr_canvas; // Not disposed.
|
||||
WebPPicture prev_canvas; // Disposed.
|
||||
|
||||
WebPAnimEncoder* enc = NULL;
|
||||
WebPAnimEncoderOptions enc_options;
|
||||
WebPConfig config;
|
||||
|
||||
int frame_number = 0; // Whether we are processing the first frame.
|
||||
int frame_number = 0; // Whether we are processing the first frame.
|
||||
int done;
|
||||
int c;
|
||||
int quiet = 0;
|
||||
@@ -136,7 +123,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
int keep_metadata = METADATA_XMP; // ICC not output by default.
|
||||
WebPData icc_data;
|
||||
int stored_icc = 0; // Whether we have already stored an ICC profile.
|
||||
int stored_icc = 0; // Whether we have already stored an ICC profile.
|
||||
WebPData xmp_data;
|
||||
int stored_xmp = 0; // Whether we have already stored an XMP profile.
|
||||
int loop_count = 0; // default: infinite
|
||||
@@ -153,7 +140,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(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
config.lossless = 1; // Use lossless compression by default.
|
||||
|
||||
@@ -163,14 +150,14 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (argc == 1) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
|
||||
out_file = GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-lossy")) {
|
||||
@@ -178,10 +165,6 @@ 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) {
|
||||
@@ -203,10 +186,10 @@ int main(int argc, const char* argv[]) {
|
||||
const char* option;
|
||||
int flag;
|
||||
} kTokens[] = {
|
||||
{"all", METADATA_ALL},
|
||||
{"none", 0},
|
||||
{"icc", METADATA_ICC},
|
||||
{"xmp", METADATA_XMP},
|
||||
{ "all", METADATA_ALL },
|
||||
{ "none", 0 },
|
||||
{ "icc", METADATA_ICC },
|
||||
{ "xmp", METADATA_XMP },
|
||||
};
|
||||
const size_t kNumTokens = sizeof(kTokens) / sizeof(*kTokens);
|
||||
const char* start = argv[++c];
|
||||
@@ -233,7 +216,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(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
start = token + 1;
|
||||
}
|
||||
@@ -242,14 +225,11 @@ 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);
|
||||
printf("libsharpyuv: %d.%d.%d\n", (sharpyuv_version >> 24) & 0xff,
|
||||
(sharpyuv_version >> 16) & 0xffff, sharpyuv_version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-quiet")) {
|
||||
quiet = 1;
|
||||
enc_options.verbose = 0;
|
||||
@@ -262,14 +242,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(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
} else {
|
||||
in_file = GET_WARGV(argv, c);
|
||||
}
|
||||
|
||||
if (parse_error) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,8 +304,8 @@ int main(int argc, const char* argv[]) {
|
||||
goto End;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("Fixed canvas screen dimension to: %d x %d\n", gif->SWidth,
|
||||
gif->SHeight);
|
||||
printf("Fixed canvas screen dimension to: %d x %d\n",
|
||||
gif->SWidth, gif->SHeight);
|
||||
}
|
||||
}
|
||||
// Allocate current buffer.
|
||||
@@ -423,7 +403,7 @@ int main(int argc, const char* argv[]) {
|
||||
break;
|
||||
}
|
||||
case APPLICATION_EXT_FUNC_CODE: {
|
||||
if (data[0] != 11) break; // Chunk is too short
|
||||
if (data[0] != 11) break; // Chunk is too short
|
||||
if (!memcmp(data + 1, "NETSCAPE2.0", 11) ||
|
||||
!memcmp(data + 1, "ANIMEXTS1.0", 11)) {
|
||||
if (!GIFReadLoopCount(gif, &data, &loop_count)) {
|
||||
@@ -534,7 +514,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (stored_icc) { // Add ICCP chunk.
|
||||
if (stored_icc) { // Add ICCP chunk.
|
||||
err = WebPMuxSetChunk(mux, "ICCP", &icc_data, 1);
|
||||
if (verbose) {
|
||||
fprintf(stderr, "ICC size: %d\n", (int)icc_data.size);
|
||||
@@ -546,7 +526,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (stored_xmp) { // Add XMP chunk.
|
||||
if (stored_xmp) { // Add XMP chunk.
|
||||
err = WebPMuxSetChunk(mux, "XMP ", &xmp_data, 1);
|
||||
if (verbose) {
|
||||
fprintf(stderr, "XMP size: %d\n", (int)xmp_data.size);
|
||||
@@ -560,10 +540,8 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
err = WebPMuxAssemble(mux, &webp_data);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
fprintf(stderr,
|
||||
"ERROR (%s): Could not assemble when re-muxing to add "
|
||||
"loop count/metadata.\n",
|
||||
ErrorString(err));
|
||||
fprintf(stderr, "ERROR (%s): Could not assemble when re-muxing to add "
|
||||
"loop count/metadata.\n", ErrorString(err));
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
@@ -576,7 +554,8 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
if (!quiet) {
|
||||
if (!WSTRCMP(out_file, "-")) {
|
||||
fprintf(stderr, "Saved %d bytes to STDIO\n", (int)webp_data.size);
|
||||
fprintf(stderr, "Saved %d bytes to STDIO\n",
|
||||
(int)webp_data.size);
|
||||
} else {
|
||||
WFPRINTF(stderr, "Saved output file (%d bytes): %s\n",
|
||||
(int)webp_data.size, out_file);
|
||||
@@ -584,10 +563,8 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
} else {
|
||||
if (!quiet) {
|
||||
fprintf(stderr,
|
||||
"Nothing written; use -o flag to save the result "
|
||||
"(%d bytes).\n",
|
||||
(int)webp_data.size);
|
||||
fprintf(stderr, "Nothing written; use -o flag to save the result "
|
||||
"(%d bytes).\n", (int)webp_data.size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,7 +572,7 @@ int main(int argc, const char* argv[]) {
|
||||
ok = 1;
|
||||
gif_error = GIF_OK;
|
||||
|
||||
End:
|
||||
End:
|
||||
WebPDataClear(&icc_data);
|
||||
WebPDataClear(&xmp_data);
|
||||
WebPMuxDelete(mux);
|
||||
@@ -609,14 +586,14 @@ End:
|
||||
GIFDisplayError(gif, gif_error);
|
||||
}
|
||||
if (gif != NULL) {
|
||||
#if LOCAL_GIF_PREREQ(5, 1)
|
||||
#if LOCAL_GIF_PREREQ(5,1)
|
||||
DGifCloseFile(gif, &gif_error);
|
||||
#else
|
||||
DGifCloseFile(gif);
|
||||
#endif
|
||||
}
|
||||
|
||||
FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(!ok);
|
||||
}
|
||||
|
||||
#else // !WEBP_HAVE_GIF
|
||||
@@ -624,7 +601,7 @@ End:
|
||||
int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "GIF support not enabled in %s.\n", argv[0]);
|
||||
(void)argc;
|
||||
return EXIT_FAILURE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,20 +20,20 @@
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#define GIF_TRANSPARENT_COLOR 0x00000000u
|
||||
#define GIF_WHITE_COLOR 0xffffffffu
|
||||
#define GIF_TRANSPARENT_MASK 0x01
|
||||
#define GIF_DISPOSE_MASK 0x07
|
||||
#define GIF_DISPOSE_SHIFT 2
|
||||
#define GIF_WHITE_COLOR 0xffffffffu
|
||||
#define GIF_TRANSPARENT_MASK 0x01
|
||||
#define GIF_DISPOSE_MASK 0x07
|
||||
#define GIF_DISPOSE_SHIFT 2
|
||||
|
||||
// from utils/utils.h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern void WebPCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
|
||||
int dst_stride, int width, int height);
|
||||
extern void WebPCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride,
|
||||
int width, int height);
|
||||
extern void WebPCopyPixels(const WebPPicture* const src,
|
||||
WebPPicture* const dst);
|
||||
#ifdef __cplusplus
|
||||
@@ -46,16 +46,18 @@ void GIFGetBackgroundColor(const ColorMapObject* const color_map,
|
||||
if (transparent_index != GIF_INDEX_INVALID &&
|
||||
bgcolor_index == transparent_index) {
|
||||
*bgcolor = GIF_TRANSPARENT_COLOR; // Special case.
|
||||
} else if (color_map == NULL || color_map->Colors == NULL ||
|
||||
bgcolor_index >= color_map->ColorCount) {
|
||||
} else if (color_map == NULL || color_map->Colors == NULL
|
||||
|| bgcolor_index >= color_map->ColorCount) {
|
||||
*bgcolor = GIF_WHITE_COLOR;
|
||||
fprintf(stderr,
|
||||
"GIF decode warning: invalid background color index. Assuming "
|
||||
"white background.\n");
|
||||
} else {
|
||||
const GifColorType color = color_map->Colors[bgcolor_index];
|
||||
*bgcolor = (0xffu << 24) | (color.Red << 16) | (color.Green << 8) |
|
||||
(color.Blue << 0);
|
||||
*bgcolor = (0xffu << 24)
|
||||
| (color.Red << 16)
|
||||
| (color.Green << 8)
|
||||
| (color.Blue << 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +116,9 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
const GifImageDesc* const image_desc = &gif->Image;
|
||||
uint32_t* dst = NULL;
|
||||
uint8_t* tmp = NULL;
|
||||
const GIFFrameRect rect = {image_desc->Left, image_desc->Top,
|
||||
image_desc->Width, image_desc->Height};
|
||||
const GIFFrameRect rect = {
|
||||
image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
|
||||
};
|
||||
const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height;
|
||||
int ok = 0;
|
||||
*gif_rect = rect;
|
||||
@@ -126,8 +129,8 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
}
|
||||
|
||||
// Use a view for the sub-picture:
|
||||
if (!WebPPictureView(picture, rect.x_offset, rect.y_offset, rect.width,
|
||||
rect.height, &sub_image)) {
|
||||
if (!WebPPictureView(picture, rect.x_offset, rect.y_offset,
|
||||
rect.width, rect.height, &sub_image)) {
|
||||
fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
|
||||
rect.width, rect.height, rect.x_offset, rect.y_offset);
|
||||
return 0;
|
||||
@@ -139,8 +142,8 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
|
||||
if (image_desc->Interlace) { // Interlaced image.
|
||||
// We need 4 passes, with the following offsets and jumps.
|
||||
const int interlace_offsets[] = {0, 4, 2, 1};
|
||||
const int interlace_jumps[] = {8, 8, 4, 2};
|
||||
const int interlace_offsets[] = { 0, 4, 2, 1 };
|
||||
const int interlace_jumps[] = { 8, 8, 4, 2 };
|
||||
int pass;
|
||||
for (pass = 0; pass < 4; ++pass) {
|
||||
const size_t stride = (size_t)sub_image.argb_stride;
|
||||
@@ -162,7 +165,7 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
End:
|
||||
End:
|
||||
if (!ok) picture->error_code = sub_image.error_code;
|
||||
WebPPictureFree(&sub_image);
|
||||
WebPFree(tmp);
|
||||
@@ -180,7 +183,7 @@ int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf,
|
||||
return 0; // Loop count sub-block missing.
|
||||
}
|
||||
if ((*buf)[0] < 3 || (*buf)[1] != 1) {
|
||||
return 0; // wrong size/marker
|
||||
return 0; // wrong size/marker
|
||||
}
|
||||
*loop_count = (*buf)[2] | ((*buf)[3] << 8);
|
||||
return 1;
|
||||
@@ -216,7 +219,8 @@ int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
|
||||
if (tmp == NULL) {
|
||||
return 0;
|
||||
}
|
||||
memcpy((void*)(tmp + metadata->size), subblock.bytes, subblock.size);
|
||||
memcpy((void*)(tmp + metadata->size),
|
||||
subblock.bytes, subblock.size);
|
||||
metadata->bytes = tmp;
|
||||
metadata->size += subblock.size;
|
||||
}
|
||||
@@ -230,8 +234,8 @@ int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ClearRectangle(WebPPicture* const picture, int left, int top,
|
||||
int width, int height) {
|
||||
static void ClearRectangle(WebPPicture* const picture,
|
||||
int left, int top, int width, int height) {
|
||||
int i, j;
|
||||
const size_t stride = picture->argb_stride;
|
||||
uint32_t* dst = picture->argb + top * stride + left;
|
||||
@@ -242,8 +246,8 @@ static void ClearRectangle(WebPPicture* const picture, int left, int top,
|
||||
|
||||
void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) {
|
||||
if (rect != NULL) {
|
||||
ClearRectangle(pic, rect->x_offset, rect->y_offset, rect->width,
|
||||
rect->height);
|
||||
ClearRectangle(pic, rect->x_offset, rect->y_offset,
|
||||
rect->width, rect->height);
|
||||
} else {
|
||||
ClearRectangle(pic, 0, 0, pic->width, pic->height);
|
||||
}
|
||||
@@ -261,14 +265,15 @@ void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
|
||||
GIFClearPic(curr_canvas, rect);
|
||||
} else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
|
||||
const size_t src_stride = prev_canvas->argb_stride;
|
||||
const uint32_t* const src =
|
||||
prev_canvas->argb + rect->x_offset + rect->y_offset * src_stride;
|
||||
const uint32_t* const src = prev_canvas->argb + rect->x_offset
|
||||
+ rect->y_offset * src_stride;
|
||||
const size_t dst_stride = curr_canvas->argb_stride;
|
||||
uint32_t* const dst =
|
||||
curr_canvas->argb + rect->x_offset + rect->y_offset * dst_stride;
|
||||
uint32_t* const dst = curr_canvas->argb + rect->x_offset
|
||||
+ rect->y_offset * dst_stride;
|
||||
assert(prev_canvas != NULL);
|
||||
WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride), (uint8_t*)dst,
|
||||
(int)(4 * dst_stride), 4 * rect->width, rect->height);
|
||||
WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride),
|
||||
(uint8_t*)dst, (int)(4 * dst_stride),
|
||||
4 * rect->width, rect->height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,11 +296,11 @@ void GIFBlendFrames(const WebPPicture* const src,
|
||||
|
||||
void GIFDisplayError(const GifFileType* const gif, int gif_error) {
|
||||
// libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
|
||||
#if LOCAL_GIF_PREREQ(4, 2)
|
||||
#if LOCAL_GIF_PREREQ(5, 0)
|
||||
#if LOCAL_GIF_PREREQ(4,2)
|
||||
#if LOCAL_GIF_PREREQ(5,0)
|
||||
// Static string actually, hence the const char* cast.
|
||||
const char* error_str =
|
||||
(const char*)GifErrorString((gif == NULL) ? gif_error : gif->Error);
|
||||
const char* error_str = (const char*)GifErrorString(
|
||||
(gif == NULL) ? gif_error : gif->Error);
|
||||
#else
|
||||
const char* error_str = (const char*)GifErrorString();
|
||||
(void)gif;
|
||||
@@ -312,9 +317,8 @@ void GIFDisplayError(const GifFileType* const gif, int gif_error) {
|
||||
|
||||
#else // !WEBP_HAVE_GIF
|
||||
|
||||
static void ErrorGIFNotAvailable(void) {
|
||||
fprintf(stderr,
|
||||
"GIF support not compiled. Please install the libgif-dev "
|
||||
static void ErrorGIFNotAvailable() {
|
||||
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
|
||||
"package before building.\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#define WEBP_EXAMPLES_GIFDEC_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@@ -30,11 +29,12 @@ extern "C" {
|
||||
|
||||
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
|
||||
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
|
||||
#define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||
#define LOCAL_GIF_PREREQ(maj, min) (LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||
# define LOCAL_GIF_PREREQ(maj, min) \
|
||||
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||
#else
|
||||
#define LOCAL_GIF_VERSION 0
|
||||
#define LOCAL_GIF_PREREQ(maj, min) 0
|
||||
# define LOCAL_GIF_VERSION 0
|
||||
# define LOCAL_GIF_PREREQ(maj, min) 0
|
||||
#endif
|
||||
|
||||
#define GIF_INDEX_INVALID (-1)
|
||||
@@ -110,7 +110,7 @@ void GIFCopyPixels(const struct WebPPicture* const src,
|
||||
struct WebPPicture* const dst);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_EXAMPLES_GIFDEC_H_
|
||||
|
||||
@@ -28,36 +28,24 @@
|
||||
#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]...");
|
||||
printf(" [-o webp_file]\n\n");
|
||||
printf(" img2webp [file_options] [[frame_options] frame_file]...\n");
|
||||
printf("\n");
|
||||
|
||||
printf("File-level options (only used at the start of compression):\n");
|
||||
printf(" -min_size ............ minimize size\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(" -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(" -v ................... verbose mode\n");
|
||||
printf(" -h ................... this help\n");
|
||||
printf(" -version ............. print version number and exit\n");
|
||||
@@ -65,28 +53,18 @@ 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> ............. 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(" -m <int> ............. method to use\n");
|
||||
|
||||
printf("\n");
|
||||
printf(
|
||||
"example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
|
||||
" -d 80 in2.tiff -o out.webp\n");
|
||||
printf(
|
||||
"\nNote: if a single file name is passed as the argument, the "
|
||||
"arguments will be\n");
|
||||
printf(
|
||||
"tokenized from this file. The file name must not start with "
|
||||
"the character '-'.\n");
|
||||
printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
|
||||
" -d 80 in2.tiff -o out.webp\n");
|
||||
printf("\nNote: if a single file name is passed as the argument, the "
|
||||
"arguments will be\n");
|
||||
printf("tokenized from this file. The file name must not start with "
|
||||
"the character '-'.\n");
|
||||
printf("\nSupported input formats:\n %s\n",
|
||||
WebPGetEnabledInputFileFormats());
|
||||
}
|
||||
@@ -136,7 +114,7 @@ static int SetLoopCount(int loop_count, WebPData* const webp_data) {
|
||||
ok = (err == WEBP_MUX_OK);
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
WebPMuxDelete(mux);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Error during loop-count setting\n");
|
||||
@@ -146,7 +124,6 @@ End:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
|
||||
int main(int argc, const char* argv[]) {
|
||||
const char* output = NULL;
|
||||
WebPAnimEncoder* enc = NULL;
|
||||
@@ -162,20 +139,20 @@ 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(EXIT_FAILURE);
|
||||
if (!ok) FREE_WARGV_AND_RETURN(1);
|
||||
|
||||
argc = cmd_args.argc;
|
||||
argv = cmd_args.argv;
|
||||
argc = cmd_args.argc_;
|
||||
argv = cmd_args.argv_;
|
||||
|
||||
WebPDataInit(&webp_data);
|
||||
if (!WebPAnimEncoderOptionsInit(&anim_config) || !WebPConfigInit(&config) ||
|
||||
if (!WebPAnimEncoderOptionsInit(&anim_config) ||
|
||||
!WebPConfigInit(&config) ||
|
||||
!WebPPictureInit(&pic)) {
|
||||
fprintf(stderr, "Library version mismatch!\n");
|
||||
ok = 0;
|
||||
@@ -207,41 +184,31 @@ 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();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} 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;
|
||||
}
|
||||
ok = !parse_error;
|
||||
if (!ok) goto End;
|
||||
argv[c] = NULL; // mark option as 'parsed' during 1st pass
|
||||
argv[c] = NULL; // mark option as 'parsed' during 1st pass
|
||||
} else {
|
||||
have_input |= 1;
|
||||
}
|
||||
}
|
||||
if (!have_input) {
|
||||
fprintf(stderr, "No input file(s) for generating animation!\n");
|
||||
ok = 0;
|
||||
Help();
|
||||
goto End;
|
||||
}
|
||||
|
||||
@@ -250,7 +217,7 @@ int main(int argc, const char* argv[]) {
|
||||
config.lossless = 1;
|
||||
for (c = 0; ok && c < argc; ++c) {
|
||||
if (argv[c] == NULL) continue;
|
||||
if (argv[c][0] == '-') { // parse local options
|
||||
if (argv[c][0] == '-') { // parse local options
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-lossy")) {
|
||||
if (!anim_config.allow_mixed) config.lossless = 0;
|
||||
@@ -266,12 +233,8 @@ 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.
|
||||
parse_error = 1; // shouldn't be here.
|
||||
fprintf(stderr, "Unknown option [%s]\n", argv[c]);
|
||||
}
|
||||
ok = !parse_error;
|
||||
@@ -290,11 +253,10 @@ 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) {
|
||||
width = pic.width;
|
||||
width = pic.width;
|
||||
height = pic.height;
|
||||
enc = WebPAnimEncoderNew(width, height, &anim_config);
|
||||
ok = (enc != NULL);
|
||||
@@ -306,9 +268,8 @@ int main(int argc, const char* argv[]) {
|
||||
if (ok) {
|
||||
ok = (width == pic.width && height == pic.height);
|
||||
if (!ok) {
|
||||
fprintf(stderr,
|
||||
"Frame #%d dimension mismatched! "
|
||||
"Got %d x %d. Was expecting %d x %d.\n",
|
||||
fprintf(stderr, "Frame #%d dimension mismatched! "
|
||||
"Got %d x %d. Was expecting %d x %d.\n",
|
||||
pic_num, pic.width, pic.height, width, height);
|
||||
}
|
||||
}
|
||||
@@ -323,22 +284,13 @@ int main(int argc, const char* argv[]) {
|
||||
if (!ok) goto End;
|
||||
|
||||
if (verbose) {
|
||||
WFPRINTF(stderr, "Added frame #%3d at time %4d (file: %s)\n", pic_num,
|
||||
timestamp_ms, GET_WARGV_SHIFTED(argv, c));
|
||||
WFPRINTF(stderr, "Added frame #%3d at time %4d (file: %s)\n",
|
||||
pic_num, timestamp_ms, GET_WARGV_SHIFTED(argv, c));
|
||||
}
|
||||
timestamp_ms += duration;
|
||||
++pic_num;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -346,7 +298,7 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "Error during final animation assembly.\n");
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
// free resources
|
||||
WebPAnimEncoderDelete(enc);
|
||||
|
||||
@@ -364,10 +316,10 @@ End:
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
fprintf(stderr, "[%d frames, %u bytes].\n", pic_num,
|
||||
(unsigned int)webp_data.size);
|
||||
fprintf(stderr, "[%d frames, %u bytes].\n",
|
||||
pic_num, (unsigned int)webp_data.size);
|
||||
}
|
||||
WebPDataClear(&webp_data);
|
||||
ExUtilDeleteCommandLineArguments(&cmd_args);
|
||||
FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(ok ? 0 : 1);
|
||||
}
|
||||
|
||||
@@ -28,13 +28,17 @@ static WEBP_INLINE void StopwatchReset(Stopwatch* watch) {
|
||||
static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
|
||||
const LARGE_INTEGER old_value = *watch;
|
||||
LARGE_INTEGER freq;
|
||||
if (!QueryPerformanceCounter(watch)) return 0.0;
|
||||
if (!QueryPerformanceFrequency(&freq)) return 0.0;
|
||||
if (freq.QuadPart == 0) return 0.0;
|
||||
if (!QueryPerformanceCounter(watch))
|
||||
return 0.0;
|
||||
if (!QueryPerformanceFrequency(&freq))
|
||||
return 0.0;
|
||||
if (freq.QuadPart == 0)
|
||||
return 0.0;
|
||||
return (watch->QuadPart - old_value.QuadPart) / (double)freq.QuadPart;
|
||||
}
|
||||
|
||||
#else /* !_WIN32 */
|
||||
|
||||
#else /* !_WIN32 */
|
||||
#include <string.h> // memcpy
|
||||
#include <sys/time.h>
|
||||
|
||||
@@ -54,6 +58,6 @@ static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
|
||||
return delta_sec + delta_usec / 1000000.0;
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif // WEBP_EXAMPLES_STOPWATCH_H_
|
||||
|
||||
@@ -27,20 +27,18 @@
|
||||
#include <io.h>
|
||||
#include <wchar.h>
|
||||
#include <windows.h>
|
||||
|
||||
// shellapi.h must be included after windows.h.
|
||||
#include <shellapi.h>
|
||||
|
||||
// Create a wchar_t array containing Unicode parameters.
|
||||
#define INIT_WARGV(ARGC, ARGV) \
|
||||
int wargc; \
|
||||
const W_CHAR** const wargv = \
|
||||
(const W_CHAR**)CommandLineToArgvW(GetCommandLineW(), &wargc); \
|
||||
do { \
|
||||
if (wargv == NULL || wargc != (ARGC)) { \
|
||||
fprintf(stderr, "Error: Unable to get Unicode arguments.\n"); \
|
||||
FREE_WARGV_AND_RETURN(-1); \
|
||||
} \
|
||||
#define INIT_WARGV(ARGC, ARGV) \
|
||||
int wargc; \
|
||||
const W_CHAR** const wargv = \
|
||||
(const W_CHAR**)CommandLineToArgvW(GetCommandLineW(), &wargc); \
|
||||
do { \
|
||||
if (wargv == NULL || wargc != (ARGC)) { \
|
||||
fprintf(stderr, "Error: Unable to get Unicode arguments.\n"); \
|
||||
FREE_WARGV_AND_RETURN(-1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Use this to get a Unicode argument (e.g. file path).
|
||||
@@ -50,7 +48,7 @@
|
||||
#define GET_WARGV_OR_NULL() wargv
|
||||
|
||||
// Release resources. LocalFree() is needed after CommandLineToArgvW().
|
||||
#define FREE_WARGV() LOCAL_FREE((W_CHAR**)wargv)
|
||||
#define FREE_WARGV() LOCAL_FREE((W_CHAR** const)wargv)
|
||||
#define LOCAL_FREE(WARGV) \
|
||||
do { \
|
||||
if ((WARGV) != NULL) LocalFree(WARGV); \
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include <gif_lib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./gifdec.h"
|
||||
|
||||
#if !defined(STDIN_FILENO)
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#define _POSIX_C_SOURCE 200112L // for setenv
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -38,11 +37,12 @@
|
||||
#include <qcms.h>
|
||||
#endif
|
||||
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
|
||||
#include "../examples/example_util.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@@ -134,8 +134,9 @@ static int ApplyColorProfile(const WebPData* const profile,
|
||||
}
|
||||
|
||||
qcms_profile_precache_output_transform(output_profile);
|
||||
transform = qcms_transform_create(input_profile, input_type, output_profile,
|
||||
output_type, intent);
|
||||
transform = qcms_transform_create(input_profile, input_type,
|
||||
output_profile, output_type,
|
||||
intent);
|
||||
if (transform == NULL) {
|
||||
fprintf(stderr, "Error creating color transform!\n");
|
||||
goto Error;
|
||||
@@ -147,7 +148,7 @@ static int ApplyColorProfile(const WebPData* const profile,
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
if (input_profile != NULL) qcms_profile_release(input_profile);
|
||||
if (output_profile != NULL) qcms_profile_release(output_profile);
|
||||
if (transform != NULL) qcms_transform_release(transform);
|
||||
@@ -162,7 +163,7 @@ Error:
|
||||
//------------------------------------------------------------------------------
|
||||
// File decoding
|
||||
|
||||
static int Decode(void) { // Fills kParams.curr_frame
|
||||
static int Decode(void) { // Fills kParams.curr_frame
|
||||
const WebPIterator* const curr = &kParams.curr_frame;
|
||||
WebPDecoderConfig* const config = &kParams.config;
|
||||
WebPDecBuffer* const output_buffer = &config->output;
|
||||
@@ -170,8 +171,8 @@ static int Decode(void) { // Fills kParams.curr_frame
|
||||
|
||||
ClearPreviousPic();
|
||||
output_buffer->colorspace = MODE_RGBA;
|
||||
ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size, config) ==
|
||||
VP8_STATUS_OK);
|
||||
ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size,
|
||||
config) == VP8_STATUS_OK);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Decoding of frame #%d failed!\n", curr->frame_num);
|
||||
} else {
|
||||
@@ -339,7 +340,8 @@ static void DrawBackground(void) {
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glColor4f(GetColorf(kParams.bg_color, 16), // BGRA from spec
|
||||
GetColorf(kParams.bg_color, 8), GetColorf(kParams.bg_color, 0),
|
||||
GetColorf(kParams.bg_color, 8),
|
||||
GetColorf(kParams.bg_color, 0),
|
||||
GetColorf(kParams.bg_color, 24));
|
||||
glRecti(-1, -1, +1, +1);
|
||||
glPopMatrix();
|
||||
@@ -399,7 +401,8 @@ static void HandleDisplay(void) {
|
||||
|
||||
*prev = *curr;
|
||||
|
||||
glDrawPixels(pic->width, pic->height, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
glDrawPixels(pic->width, pic->height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
(GLvoid*)pic->u.RGBA.rgba);
|
||||
if (kParams.print_info) {
|
||||
char tmp[32];
|
||||
@@ -413,8 +416,8 @@ static void HandleDisplay(void) {
|
||||
glRasterPos2f(-0.95f, 0.80f);
|
||||
PrintString(tmp);
|
||||
if (curr->x_offset != 0 || curr->y_offset != 0) {
|
||||
snprintf(tmp, sizeof(tmp), " (offset:%d,%d)", curr->x_offset,
|
||||
curr->y_offset);
|
||||
snprintf(tmp, sizeof(tmp), " (offset:%d,%d)",
|
||||
curr->x_offset, curr->y_offset);
|
||||
glRasterPos2f(-0.95f, 0.70f);
|
||||
PrintString(tmp);
|
||||
}
|
||||
@@ -427,13 +430,10 @@ static void HandleDisplay(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void StartDisplay(const char* filename) {
|
||||
static void StartDisplay(void) {
|
||||
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)
|
||||
@@ -453,9 +453,8 @@ static void StartDisplay(const char* filename) {
|
||||
height = screen_height;
|
||||
}
|
||||
}
|
||||
snprintf(title, sizeof(title), "%s%s", filename, viewername);
|
||||
glutInitWindowSize(width, height);
|
||||
glutCreateWindow(title);
|
||||
glutCreateWindow("WebP viewer");
|
||||
glutDisplayFunc(HandleDisplay);
|
||||
glutReshapeFunc(HandleReshape);
|
||||
glutIdleFunc(NULL);
|
||||
@@ -494,7 +493,7 @@ static void Help(void) {
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int c, file_name_argv_index = 1;
|
||||
int c;
|
||||
WebPDecoderConfig* const config = &kParams.config;
|
||||
WebPIterator* const curr = &kParams.curr_frame;
|
||||
|
||||
@@ -502,7 +501,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
if (!WebPInitDecoderConfig(config)) {
|
||||
fprintf(stderr, "Library version mismatch!\n");
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
config->options.dithering_strength = 50;
|
||||
config->options.alpha_dithering_strength = 100;
|
||||
@@ -514,7 +513,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(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-noicc")) {
|
||||
kParams.use_color_profile = 0;
|
||||
} else if (!strcmp(argv[c], "-nofancy")) {
|
||||
@@ -537,38 +536,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(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-mt")) {
|
||||
config->options.use_threads = 1;
|
||||
} else if (!strcmp(argv[c], "--")) {
|
||||
if (c < argc - 1) {
|
||||
kParams.file_name = (const char*)GET_WARGV(argv, ++c);
|
||||
file_name_argv_index = c;
|
||||
}
|
||||
if (c < argc - 1) kParams.file_name = (const char*)GET_WARGV(argv, ++c);
|
||||
break;
|
||||
} else if (argv[c][0] == '-') {
|
||||
printf("Unknown option '%s'\n", argv[c]);
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
} else {
|
||||
kParams.file_name = (const char*)GET_WARGV(argv, c);
|
||||
file_name_argv_index = c;
|
||||
}
|
||||
|
||||
if (parse_error) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (kParams.file_name == NULL) {
|
||||
printf("missing input file!!\n");
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
}
|
||||
|
||||
if (!ImgIoUtilReadFile(kParams.file_name, &kParams.data.bytes,
|
||||
&kParams.data.size)) {
|
||||
if (!ImgIoUtilReadFile(kParams.file_name,
|
||||
&kParams.data.bytes, &kParams.data.size)) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
@@ -599,8 +594,7 @@ int main(int argc, char* argv[]) {
|
||||
if (!WebPDemuxGetChunk(kParams.dmux, "ICCP", 1, &kParams.iccp)) goto Error;
|
||||
printf("VP8X: Found color profile\n");
|
||||
#else
|
||||
fprintf(stderr,
|
||||
"Warning: color profile present, but qcms is unavailable!\n"
|
||||
fprintf(stderr, "Warning: color profile present, but qcms is unavailable!\n"
|
||||
"Build libqcms from Mozilla or Chromium and define WEBP_HAVE_QCMS "
|
||||
"before building.\n");
|
||||
#endif
|
||||
@@ -611,15 +605,15 @@ int main(int argc, char* argv[]) {
|
||||
kParams.has_animation = (curr->num_frames > 1);
|
||||
kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
|
||||
kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR);
|
||||
printf("VP8X: Found %d images in file (loop count = %d)\n", curr->num_frames,
|
||||
kParams.loop_count);
|
||||
printf("VP8X: Found %d images in file (loop count = %d)\n",
|
||||
curr->num_frames, kParams.loop_count);
|
||||
|
||||
// Decode first frame
|
||||
if (!Decode()) goto Error;
|
||||
|
||||
// Position iterator to last frame. Next call to HandleDisplay will wrap over.
|
||||
// We take this into account by bumping up loop_count.
|
||||
if (!WebPDemuxGetFrame(kParams.dmux, 0, curr)) goto Error;
|
||||
WebPDemuxGetFrame(kParams.dmux, 0, curr);
|
||||
if (kParams.loop_count) ++kParams.loop_count;
|
||||
|
||||
#if defined(__unix__) || defined(__CYGWIN__)
|
||||
@@ -633,26 +627,26 @@ int main(int argc, char* argv[]) {
|
||||
#ifdef FREEGLUT
|
||||
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
|
||||
#endif
|
||||
StartDisplay(argv[file_name_argv_index]);
|
||||
StartDisplay();
|
||||
|
||||
if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
|
||||
glutMainLoop();
|
||||
|
||||
// Should only be reached when using FREEGLUT:
|
||||
ClearParams();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
|
||||
Error:
|
||||
Error:
|
||||
ClearParams();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
#else // !WEBP_HAVE_GL
|
||||
#else // !WEBP_HAVE_GL
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
|
||||
(void)argc;
|
||||
return EXIT_FAILURE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
@@ -26,7 +24,6 @@
|
||||
#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
|
||||
@@ -34,26 +31,38 @@
|
||||
|
||||
#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] = {"Unknown", "Lossy", "Lossless"};
|
||||
static const char* const kFormats[3] = {
|
||||
"Unknown",
|
||||
"Lossy",
|
||||
"Lossless"
|
||||
};
|
||||
|
||||
static const char* const kLosslessTransforms[4] = {
|
||||
"Predictor", "Cross Color", "Subtract Green", "Color Indexing"};
|
||||
"Predictor",
|
||||
"Cross Color",
|
||||
"Subtract Green",
|
||||
"Color Indexing"
|
||||
};
|
||||
|
||||
static const char* const kAlphaFilterMethods[4] = {"None", "Horizontal",
|
||||
"Vertical", "Gradient"};
|
||||
static const char* const kAlphaFilterMethods[4] = {
|
||||
"None",
|
||||
"Horizontal",
|
||||
"Vertical",
|
||||
"Gradient"
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
WEBP_INFO_OK = 0,
|
||||
@@ -80,36 +89,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) {
|
||||
@@ -117,15 +126,15 @@ static void WebPInfoInit(WebPInfo* const webp_info) {
|
||||
}
|
||||
|
||||
static const uint32_t kWebPChunkTags[CHUNK_TYPES] = {
|
||||
MKFOURCC('V', 'P', '8', ' '), //
|
||||
MKFOURCC('V', 'P', '8', 'L'), //
|
||||
MKFOURCC('V', 'P', '8', 'X'), //
|
||||
MKFOURCC('A', 'L', 'P', 'H'), //
|
||||
MKFOURCC('A', 'N', 'I', 'M'), //
|
||||
MKFOURCC('A', 'N', 'M', 'F'), //
|
||||
MKFOURCC('I', 'C', 'C', 'P'), //
|
||||
MKFOURCC('E', 'X', 'I', 'F'), //
|
||||
MKFOURCC('X', 'M', 'P', ' '), //
|
||||
MKFOURCC('V', 'P', '8', ' '),
|
||||
MKFOURCC('V', 'P', '8', 'L'),
|
||||
MKFOURCC('V', 'P', '8', 'X'),
|
||||
MKFOURCC('A', 'L', 'P', 'H'),
|
||||
MKFOURCC('A', 'N', 'I', 'M'),
|
||||
MKFOURCC('A', 'N', 'M', 'F'),
|
||||
MKFOURCC('I', 'C', 'C', 'P'),
|
||||
MKFOURCC('E', 'X', 'I', 'F'),
|
||||
MKFOURCC('X', 'M', 'P', ' '),
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -175,23 +184,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; }
|
||||
static void Skip(MemBuffer* const mem, size_t size) {
|
||||
mem->start_ += size;
|
||||
}
|
||||
|
||||
static uint32_t ReadMemBufLE32(MemBuffer* const mem) {
|
||||
const uint8_t* const data = mem->buf + mem->start;
|
||||
const uint8_t* const data = mem->buf_ + mem->start_;
|
||||
const uint32_t val = GetLE32(data);
|
||||
assert(MemDataSize(mem) >= 4);
|
||||
Skip(mem, 4);
|
||||
@@ -252,10 +263,9 @@ static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info,
|
||||
int update_map, update_data;
|
||||
GET_BITS(update_map, 1);
|
||||
GET_BITS(update_data, 1);
|
||||
printf(
|
||||
" Update map: %d\n"
|
||||
" Update data: %d\n",
|
||||
update_map, update_data);
|
||||
printf(" Update map: %d\n"
|
||||
" Update data: %d\n",
|
||||
update_map, update_data);
|
||||
if (update_data) {
|
||||
int i, a_delta;
|
||||
int quantizer[4] = {0, 0, 0, 0};
|
||||
@@ -285,8 +295,8 @@ static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info,
|
||||
GET_BITS(bit, 1);
|
||||
if (bit) GET_BITS(prob_segment[i], 8);
|
||||
}
|
||||
printf(" Prob segment: %d %d %d\n", prob_segment[0], prob_segment[1],
|
||||
prob_segment[2]);
|
||||
printf(" Prob segment: %d %d %d\n",
|
||||
prob_segment[0], prob_segment[1], prob_segment[2]);
|
||||
}
|
||||
}
|
||||
return WEBP_INFO_OK;
|
||||
@@ -323,8 +333,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;
|
||||
@@ -336,7 +346,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;
|
||||
@@ -347,24 +357,23 @@ static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
|
||||
}
|
||||
data += 3;
|
||||
data_size -= 3;
|
||||
printf(
|
||||
" Key frame: %s\n"
|
||||
" Profile: %d\n"
|
||||
" Display: Yes\n"
|
||||
" Part. 0 length: %d\n",
|
||||
key_frame ? "Yes" : "No", profile, partition0_length);
|
||||
printf(" Key frame: %s\n"
|
||||
" Profile: %d\n"
|
||||
" Display: %s\n"
|
||||
" Part. 0 length: %d\n",
|
||||
key_frame ? "Yes" : "No", profile,
|
||||
display ? "Yes" : "No", partition0_length);
|
||||
if (key_frame) {
|
||||
if (!(data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a)) {
|
||||
LOG_ERROR("Invalid lossy bitstream signature.");
|
||||
return WEBP_INFO_BITSTREAM_ERROR;
|
||||
}
|
||||
printf(
|
||||
" Width: %d\n"
|
||||
" X scale: %d\n"
|
||||
" Height: %d\n"
|
||||
" Y scale: %d\n",
|
||||
((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6,
|
||||
((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6);
|
||||
printf(" Width: %d\n"
|
||||
" X scale: %d\n"
|
||||
" Height: %d\n"
|
||||
" Y scale: %d\n",
|
||||
((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6,
|
||||
((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6);
|
||||
data += 7;
|
||||
data_size -= 7;
|
||||
} else {
|
||||
@@ -467,7 +476,7 @@ static int LLGetBits(const uint8_t* const data, size_t data_size, size_t nb,
|
||||
static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
|
||||
const uint8_t* const data,
|
||||
size_t data_size,
|
||||
uint64_t* const bit_pos) {
|
||||
uint64_t* const bit_pos) {
|
||||
int use_transform, block_size, n_colors;
|
||||
LL_GET_BITS(use_transform, 1);
|
||||
printf(" Use transform: %s\n", use_transform ? "Yes" : "No");
|
||||
@@ -487,8 +496,7 @@ static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
|
||||
n_colors += 1;
|
||||
printf(" No. of colors: %d\n", n_colors);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return WEBP_INFO_OK;
|
||||
@@ -496,8 +504,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;
|
||||
@@ -532,8 +540,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;
|
||||
@@ -545,8 +553,8 @@ static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data,
|
||||
const int pre_processing = (data[0] >> 4) & 0x03;
|
||||
const int reserved_bits = (data[0] >> 6) & 0x03;
|
||||
printf(" Compression: %d\n", compression_method);
|
||||
printf(" Filter: %s (%d)\n", kAlphaFilterMethods[filter],
|
||||
filter);
|
||||
printf(" Filter: %s (%d)\n",
|
||||
kAlphaFilterMethods[filter], filter);
|
||||
printf(" Pre-processing: %d\n", pre_processing);
|
||||
if (compression_method > ALPHA_LOSSLESS_COMPRESSION) {
|
||||
LOG_ERROR("Invalid Alpha compression method.");
|
||||
@@ -598,14 +606,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;
|
||||
}
|
||||
@@ -621,7 +629,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);
|
||||
@@ -631,18 +639,18 @@ static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
|
||||
LOG_ERROR("Size of chunk payload is over limit.");
|
||||
return WEBP_INFO_INVALID_PARAM;
|
||||
}
|
||||
if (payload_size_padded > MemDataSize(mem)) {
|
||||
if (payload_size_padded > MemDataSize(mem)){
|
||||
LOG_ERROR("Truncated data detected when parsing chunk payload.");
|
||||
return WEBP_INFO_TRUNCATED_DATA;
|
||||
}
|
||||
for (i = 0; i < CHUNK_TYPES; ++i) {
|
||||
if (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;
|
||||
@@ -661,39 +669,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);
|
||||
printf(" Canvas size %d x %d\n", webp_info->canvas_width,
|
||||
webp_info->canvas_height);
|
||||
(webp_info->feature_flags_ & ICCP_FLAG) != 0,
|
||||
(webp_info->feature_flags_ & ALPHA_FLAG) != 0,
|
||||
(webp_info->feature_flags_ & EXIF_FLAG) != 0,
|
||||
(webp_info->feature_flags_ & XMP_FLAG) != 0,
|
||||
(webp_info->feature_flags_ & ANIMATION_FLAG) != 0);
|
||||
printf(" Canvas size %d x %d\n",
|
||||
webp_info->canvas_width_, webp_info->canvas_height_);
|
||||
}
|
||||
if (webp_info->canvas_width > MAX_CANVAS_SIZE) {
|
||||
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.");
|
||||
}
|
||||
@@ -702,25 +710,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;
|
||||
@@ -728,17 +738,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;
|
||||
}
|
||||
@@ -750,12 +760,11 @@ 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) {
|
||||
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);
|
||||
++webp_info->chunk_counts_[CHUNK_ANMF];
|
||||
if (!webp_info->quiet_) {
|
||||
printf(" Offset_X: %d\n Offset_Y: %d\n Width: %d\n Height: %d\n"
|
||||
" Duration: %d\n Dispose: %d\n Blend: %d\n",
|
||||
offset_x, offset_y, width, height, duration, dispose, blend);
|
||||
}
|
||||
if (duration > MAX_DURATION) {
|
||||
LOG_ERROR("Invalid duration parameter in ANMF chunk.");
|
||||
@@ -765,92 +774,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);
|
||||
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);
|
||||
@@ -861,42 +870,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) {
|
||||
LOG_ERROR(
|
||||
"ALPHA sub-chunk detected after VP8 sub-chunk "
|
||||
"in an ANMF chunk.");
|
||||
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;
|
||||
}
|
||||
@@ -906,40 +914,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);
|
||||
printf("Chunk %c%c%c%c at offset %6d, length %6d\n",
|
||||
tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset_,
|
||||
(int)chunk_data->size_);
|
||||
}
|
||||
}
|
||||
switch (id) {
|
||||
@@ -964,21 +973,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;
|
||||
@@ -988,55 +997,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;
|
||||
}
|
||||
@@ -1047,17 +1056,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(
|
||||
"Chunk type : VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP "
|
||||
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]);
|
||||
printf("%4d %4d %4d ",
|
||||
webp_info->anmf_subchunk_counts_[0],
|
||||
webp_info->anmf_subchunk_counts_[1],
|
||||
webp_info->anmf_subchunk_counts_[2]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
@@ -1080,39 +1089,37 @@ 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) {
|
||||
Error:
|
||||
if (!webp_info->quiet_) {
|
||||
if (webp_info_status == WEBP_INFO_OK) {
|
||||
printf("No error detected.\n");
|
||||
} else {
|
||||
printf("Errors detected.\n");
|
||||
}
|
||||
if (webp_info->num_warnings > 0) {
|
||||
printf("There were %d warning(s).\n", webp_info->num_warnings);
|
||||
if (webp_info->num_warnings_ > 0) {
|
||||
printf("There were %d warning(s).\n", webp_info->num_warnings_);
|
||||
}
|
||||
}
|
||||
return webp_info_status;
|
||||
}
|
||||
|
||||
static void Help(void) {
|
||||
printf(
|
||||
"Usage: webpinfo [options] in_files\n"
|
||||
"Note: there could be multiple input files;\n"
|
||||
" options must come before input files.\n"
|
||||
"Options:\n"
|
||||
" -version ........... Print version number and exit.\n"
|
||||
" -quiet ............. Do not show chunk parsing information.\n"
|
||||
" -diag .............. Show parsing error diagnosis.\n"
|
||||
" -summary ........... Show chunk stats summary.\n"
|
||||
" -bitstream_info .... Parse bitstream header.\n");
|
||||
printf("Usage: webpinfo [options] in_files\n"
|
||||
"Note: there could be multiple input files;\n"
|
||||
" options must come before input files.\n"
|
||||
"Options:\n"
|
||||
" -version ........... Print version number and exit.\n"
|
||||
" -quiet ............. Do not show chunk parsing information.\n"
|
||||
" -diag .............. Show parsing error diagnosis.\n"
|
||||
" -summary ........... Show chunk stats summary.\n"
|
||||
" -bitstream_info .... Parse bitstream header.\n");
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -1123,7 +1130,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (argc == 1) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
|
||||
}
|
||||
|
||||
// Parse command-line input.
|
||||
@@ -1131,7 +1138,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(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
|
||||
} else if (!strcmp(argv[c], "-quiet")) {
|
||||
quiet = 1;
|
||||
} else if (!strcmp(argv[c], "-diag")) {
|
||||
@@ -1142,9 +1149,9 @@ int main(int argc, const char* argv[]) {
|
||||
parse_bitstream = 1;
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
const int version = WebPGetDecoderVersion();
|
||||
printf("WebP Decoder version: %d.%d.%d\n", (version >> 16) & 0xff,
|
||||
(version >> 8) & 0xff, version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
printf("WebP Decoder version: %d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else { // Assume the remaining are all input files.
|
||||
break;
|
||||
}
|
||||
@@ -1152,7 +1159,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (c == argc) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(WEBP_INFO_INVALID_COMMAND);
|
||||
}
|
||||
|
||||
// Process input files one by one.
|
||||
@@ -1160,10 +1167,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)) {
|
||||
@@ -1171,10 +1178,9 @@ 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 == WEBP_INFO_OK) ? EXIT_SUCCESS
|
||||
: EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(webp_info_status);
|
||||
}
|
||||
|
||||
@@ -59,14 +59,11 @@
|
||||
#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.
|
||||
@@ -89,9 +86,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 {
|
||||
@@ -106,21 +103,24 @@ typedef enum {
|
||||
LAST_FEATURE
|
||||
} FeatureType;
|
||||
|
||||
static const char* const kFourccList[LAST_FEATURE] = {NULL, "EXIF", "XMP ",
|
||||
"ICCP", "ANMF"};
|
||||
static const char* const kFourccList[LAST_FEATURE] = {
|
||||
NULL, "EXIF", "XMP ", "ICCP", "ANMF"
|
||||
};
|
||||
|
||||
static const char* const kDescriptions[LAST_FEATURE] = {
|
||||
NULL, "EXIF metadata", "XMP metadata", "ICC profile", "Animation frame"};
|
||||
NULL, "EXIF metadata", "XMP metadata", "ICC profile",
|
||||
"Animation frame"
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
CommandLineArguments cmd_args;
|
||||
CommandLineArguments cmd_args_;
|
||||
|
||||
ActionType action_type;
|
||||
const char* input;
|
||||
const char* output;
|
||||
FeatureType type;
|
||||
FeatureArg* args;
|
||||
int arg_count;
|
||||
ActionType action_type_;
|
||||
const char* input_;
|
||||
const char* output_;
|
||||
FeatureType type_;
|
||||
FeatureArg* args_;
|
||||
int arg_count_;
|
||||
} Config;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -131,8 +131,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;
|
||||
}
|
||||
}
|
||||
@@ -140,49 +140,46 @@ static int CountOccurrences(const CommandLineArguments* const args,
|
||||
}
|
||||
|
||||
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
|
||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"};
|
||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
|
||||
};
|
||||
|
||||
static const char* ErrorString(WebPMuxError err) {
|
||||
assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
|
||||
return kErrorMessages[-err];
|
||||
}
|
||||
|
||||
#define RETURN_IF_ERROR(ERR_MSG) \
|
||||
do { \
|
||||
if (err != WEBP_MUX_OK) { \
|
||||
fprintf(stderr, ERR_MSG); \
|
||||
return err; \
|
||||
} \
|
||||
#define RETURN_IF_ERROR(ERR_MSG) \
|
||||
if (err != WEBP_MUX_OK) { \
|
||||
fprintf(stderr, ERR_MSG); \
|
||||
return err; \
|
||||
}
|
||||
|
||||
#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
|
||||
if (err != WEBP_MUX_OK) { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||
return err; \
|
||||
}
|
||||
|
||||
#define ERROR_GOTO1(ERR_MSG, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
|
||||
do { \
|
||||
if (err != WEBP_MUX_OK) { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||
return err; \
|
||||
} \
|
||||
#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
} while (0)
|
||||
|
||||
#define ERROR_GOTO1(ERR_MSG, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
} while (0)
|
||||
|
||||
#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
} while (0)
|
||||
|
||||
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
} while (0)
|
||||
|
||||
static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||
@@ -204,10 +201,10 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||
// Print the features present.
|
||||
printf("Features present:");
|
||||
if (flag & ANIMATION_FLAG) printf(" animation");
|
||||
if (flag & ICCP_FLAG) printf(" ICC profile");
|
||||
if (flag & EXIF_FLAG) printf(" EXIF metadata");
|
||||
if (flag & XMP_FLAG) printf(" XMP metadata");
|
||||
if (flag & ALPHA_FLAG) printf(" transparency");
|
||||
if (flag & ICCP_FLAG) printf(" ICC profile");
|
||||
if (flag & EXIF_FLAG) printf(" EXIF metadata");
|
||||
if (flag & XMP_FLAG) printf(" XMP metadata");
|
||||
if (flag & ALPHA_FLAG) printf(" transparency");
|
||||
printf("\n");
|
||||
|
||||
if (flag & ANIMATION_FLAG) {
|
||||
@@ -218,8 +215,8 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||
WebPMuxAnimParams params;
|
||||
err = WebPMuxGetAnimationParams(mux, ¶ms);
|
||||
assert(err == WEBP_MUX_OK);
|
||||
printf("Background color : 0x%.8X Loop Count : %d\n", params.bgcolor,
|
||||
params.loop_count);
|
||||
printf("Background color : 0x%.8X Loop Count : %d\n",
|
||||
params.bgcolor, params.loop_count);
|
||||
|
||||
err = WebPMuxNumChunks(mux, id, &nFrames);
|
||||
assert(err == WEBP_MUX_OK);
|
||||
@@ -251,9 +248,9 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||
printf("%8d %10s %5s ", frame.duration, dispose, blend);
|
||||
}
|
||||
printf("%10d %11s\n", (int)frame.bitstream.size,
|
||||
(features.format == 1) ? "lossy"
|
||||
: (features.format == 2) ? "lossless"
|
||||
: "undefined");
|
||||
(features.format == 1) ? "lossy" :
|
||||
(features.format == 2) ? "lossless" :
|
||||
"undefined");
|
||||
}
|
||||
WebPDataClear(&frame.bitstream);
|
||||
RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
|
||||
@@ -301,9 +298,8 @@ static void PrintHelp(void) {
|
||||
printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n");
|
||||
printf(" INPUT -o OUTPUT\n");
|
||||
printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
|
||||
printf(
|
||||
" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
|
||||
"\n");
|
||||
printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
|
||||
"\n");
|
||||
printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
|
||||
printf(" webpmux -info INPUT\n");
|
||||
printf(" webpmux [-h|-help]\n");
|
||||
@@ -357,9 +353,8 @@ static void PrintHelp(void) {
|
||||
printf(" 'di' is the pause duration before next frame,\n");
|
||||
printf(" 'xi','yi' specify the image offset for this frame,\n");
|
||||
printf(" 'mi' is the dispose method for this frame (0 or 1),\n");
|
||||
printf(
|
||||
" 'bi' is the blending method for this frame (+b or -b)"
|
||||
"\n");
|
||||
printf(" 'bi' is the blending method for this frame (+b or -b)"
|
||||
"\n");
|
||||
|
||||
printf("\n");
|
||||
printf("LOOP_COUNT:\n");
|
||||
@@ -370,33 +365,27 @@ static void PrintHelp(void) {
|
||||
printf("BACKGROUND_COLOR:\n");
|
||||
printf(" Background color of the canvas.\n");
|
||||
printf(" A,R,G,B\n");
|
||||
printf(
|
||||
" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
|
||||
"specifying\n");
|
||||
printf(
|
||||
" the Alpha, Red, Green and Blue component values "
|
||||
"respectively\n");
|
||||
printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
|
||||
"specifying\n");
|
||||
printf(" the Alpha, Red, Green and Blue component values "
|
||||
"respectively\n");
|
||||
printf(" [Default: 255,255,255,255]\n");
|
||||
|
||||
printf("\nINPUT & OUTPUT are in WebP format.\n");
|
||||
|
||||
printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
|
||||
printf(" and is assumed to be\nvalid.\n");
|
||||
printf(
|
||||
"\nNote: if a single file name is passed as the argument, the "
|
||||
"arguments will be\n");
|
||||
printf(
|
||||
"tokenized from this file. The file name must not start with "
|
||||
"the character '-'.\n");
|
||||
printf("\nNote: if a single file name is passed as the argument, the "
|
||||
"arguments will be\n");
|
||||
printf("tokenized from this file. The file name must not start with "
|
||||
"the character '-'.\n");
|
||||
}
|
||||
|
||||
static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
|
||||
if ((info->x_offset | info->y_offset) & 1) {
|
||||
fprintf(stderr,
|
||||
"Warning: odd offsets will be snapped to even values"
|
||||
" (%d, %d) -> (%d, %d)\n",
|
||||
info->x_offset, info->y_offset, info->x_offset & ~1,
|
||||
info->y_offset & ~1);
|
||||
fprintf(stderr, "Warning: odd offsets will be snapped to even values"
|
||||
" (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
|
||||
info->x_offset & ~1, info->y_offset & ~1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,8 +413,8 @@ static int WriteData(const char* filename, const WebPData* const webpdata) {
|
||||
if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
|
||||
WFPRINTF(stderr, "Error writing file %s!\n", (const W_CHAR*)filename);
|
||||
} else {
|
||||
WFPRINTF(stderr, "Saved file %s (%d bytes)\n", (const W_CHAR*)filename,
|
||||
(int)webpdata->size);
|
||||
WFPRINTF(stderr, "Saved file %s (%d bytes)\n",
|
||||
(const W_CHAR*)filename, (int)webpdata->size);
|
||||
ok = 1;
|
||||
}
|
||||
if (fout != stdout) fclose(fout);
|
||||
@@ -458,8 +447,8 @@ static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
|
||||
if (err == WEBP_MUX_OK) {
|
||||
err = WebPMuxSetAnimationParams(new_mux, &p);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO2("Error (%s) handling animation params.\n", ErrorString(err),
|
||||
End);
|
||||
ERROR_GOTO2("Error (%s) handling animation params.\n",
|
||||
ErrorString(err), End);
|
||||
}
|
||||
} else {
|
||||
/* it might not be an animation. Just keep moving. */
|
||||
@@ -477,7 +466,7 @@ static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
|
||||
}
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
if (!ok) {
|
||||
WebPMuxDelete(new_mux);
|
||||
new_mux = NULL;
|
||||
@@ -515,7 +504,8 @@ static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
|
||||
|
||||
if (blend_method != 'b') return 0;
|
||||
if (plus_minus != '-' && plus_minus != '+') return 0;
|
||||
info->blend_method = (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
|
||||
info->blend_method =
|
||||
(plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -532,8 +522,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));
|
||||
}
|
||||
}
|
||||
@@ -587,10 +577,8 @@ static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
|
||||
}
|
||||
|
||||
if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
|
||||
ERROR_GOTO1(
|
||||
"ERROR: Loop count and background color are relevant only in "
|
||||
"case of animation.\n",
|
||||
ErrValidate);
|
||||
ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
|
||||
"case of animation.\n", ErrValidate);
|
||||
}
|
||||
if (num_durations_args > 0 && num_frame_args != 0) {
|
||||
ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
|
||||
@@ -608,35 +596,29 @@ static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
|
||||
*num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
|
||||
}
|
||||
|
||||
ErrValidate:
|
||||
ErrValidate:
|
||||
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) \
|
||||
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_LEAST(NUM, LABEL) \
|
||||
if (argc < i + (NUM)) { \
|
||||
fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
|
||||
goto LABEL; \
|
||||
}
|
||||
|
||||
#define CHECK_NUM_ARGS_AT_MOST(NUM, LABEL) \
|
||||
do { \
|
||||
if (argc > i + (NUM)) { \
|
||||
fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
|
||||
goto LABEL; \
|
||||
} \
|
||||
} while (0)
|
||||
if (argc > i + (NUM)) { \
|
||||
fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
|
||||
goto LABEL; \
|
||||
}
|
||||
|
||||
#define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \
|
||||
do { \
|
||||
CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \
|
||||
CHECK_NUM_ARGS_AT_MOST(NUM, LABEL); \
|
||||
} while (0)
|
||||
#define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \
|
||||
CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \
|
||||
CHECK_NUM_ARGS_AT_MOST(NUM, LABEL);
|
||||
|
||||
// Parses command-line arguments to fill up config object. Also performs some
|
||||
// semantic checks. unicode_argv contains wchar_t arguments or is null.
|
||||
@@ -644,116 +626,117 @@ 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->action_type_ = ACTION_STRIP;
|
||||
config->arg_count_ = 0;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||
}
|
||||
++i;
|
||||
} else if (!strcmp(argv[i], "-frame")) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(3, ErrParse);
|
||||
if (ACTION_IS_NIL || config->action_type == ACTION_SET) {
|
||||
config->action_type = ACTION_SET;
|
||||
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 = wargv[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")) {
|
||||
PrintHelp();
|
||||
DeleteConfig(config);
|
||||
LOCAL_FREE((W_CHAR**)unicode_argv);
|
||||
LOCAL_FREE((W_CHAR** const)unicode_argv);
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[i], "-version")) {
|
||||
const int version = WebPGetMuxVersion();
|
||||
printf("%d.%d.%d\n", (version >> 16) & 0xff, (version >> 8) & 0xff,
|
||||
version & 0xff);
|
||||
printf("%d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
DeleteConfig(config);
|
||||
LOCAL_FREE((W_CHAR**)unicode_argv);
|
||||
LOCAL_FREE((W_CHAR** const)unicode_argv);
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
if (i < argc - 1) {
|
||||
++i;
|
||||
if (config->input == NULL) {
|
||||
config->input = wargv[i];
|
||||
if (config->input_ == NULL) {
|
||||
config->input_ = wargv[i];
|
||||
} else {
|
||||
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
|
||||
argv[i], ErrParse);
|
||||
@@ -764,66 +747,50 @@ 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 (!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)) {
|
||||
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);
|
||||
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];
|
||||
arg->filename_ = wargv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else {
|
||||
is_input = 1;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_input) {
|
||||
if (config->input == NULL) {
|
||||
config->input = wargv[i];
|
||||
} else if (!strcmp(argv[i], "frame") &&
|
||||
(config->action_type_ == ACTION_GET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->type_ = FEATURE_ANMF;
|
||||
arg->params_ = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "loop") &&
|
||||
(config->action_type_ == ACTION_SET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->type_ = FEATURE_LOOP;
|
||||
arg->params_ = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "bgcolor") &&
|
||||
(config->action_type_ == ACTION_SET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->type_ = FEATURE_BGCOLOR;
|
||||
arg->params_ = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else { // Assume input file.
|
||||
if (config->input_ == NULL) {
|
||||
config->input_ = wargv[i];
|
||||
} else {
|
||||
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
|
||||
argv[i], ErrParse);
|
||||
@@ -832,7 +799,7 @@ static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
|
||||
}
|
||||
}
|
||||
}
|
||||
ErrParse:
|
||||
ErrParse:
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -846,25 +813,25 @@ 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);
|
||||
}
|
||||
|
||||
ErrValidate2:
|
||||
ErrValidate2:
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -876,17 +843,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);
|
||||
}
|
||||
|
||||
@@ -895,7 +862,7 @@ static int InitializeConfig(int argc, const char* argv[], Config* const config,
|
||||
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
||||
}
|
||||
|
||||
Err1:
|
||||
Err1:
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -918,7 +885,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);
|
||||
}
|
||||
@@ -927,8 +894,8 @@ static int GetFrame(const WebPMux* mux, const Config* config) {
|
||||
err = WebPMuxGetFrame(mux, num, &info);
|
||||
if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n", ErrorString(err), num,
|
||||
ErrGet);
|
||||
ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n",
|
||||
ErrorString(err), num, ErrGet);
|
||||
}
|
||||
|
||||
mux_single = WebPMuxNew();
|
||||
@@ -943,9 +910,9 @@ 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:
|
||||
ErrGet:
|
||||
WebPDataClear(&info.bitstream);
|
||||
WebPMuxDelete(mux_single);
|
||||
return ok && !parse_error;
|
||||
@@ -958,11 +925,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;
|
||||
@@ -970,12 +937,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);
|
||||
ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
|
||||
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||
}
|
||||
ok = WriteData(config->output, &chunk);
|
||||
ok = WriteData(config->output_, &chunk);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -985,20 +952,20 @@ 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};
|
||||
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
|
||||
mux = WebPMuxNew();
|
||||
if (mux == NULL) {
|
||||
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
|
||||
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
|
||||
}
|
||||
for (i = 0; i < config->arg_count; ++i) {
|
||||
switch (config->args[i].subtype) {
|
||||
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);
|
||||
@@ -1009,15 +976,13 @@ 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
|
||||
// WebPMuxSetAnimationParams() method called later.
|
||||
ERROR_GOTO1(
|
||||
"ERROR: Loop count must be in the range 0 to "
|
||||
"65535.\n",
|
||||
Err2);
|
||||
ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
|
||||
"65535.\n", Err2);
|
||||
}
|
||||
ok = !parse_error;
|
||||
if (!ok) goto Err2;
|
||||
@@ -1027,10 +992,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",
|
||||
@@ -1039,10 +1004,8 @@ static int Process(const Config* config) {
|
||||
err = WebPMuxPushFrame(mux, &frame, 1);
|
||||
WebPDataClear(&frame.bitstream);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO3(
|
||||
"ERROR (%s): Could not add a frame at index %d."
|
||||
"\n",
|
||||
ErrorString(err), i, Err2);
|
||||
ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
|
||||
"\n", ErrorString(err), i, Err2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1063,28 +1026,28 @@ 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);
|
||||
ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
|
||||
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FEATURE_LOOP: {
|
||||
WebPMuxAnimParams params = {0xFFFFFFFF, 0};
|
||||
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) {
|
||||
@@ -1101,13 +1064,14 @@ static int Process(const Config* config) {
|
||||
break;
|
||||
}
|
||||
case FEATURE_BGCOLOR: {
|
||||
WebPMuxAnimParams params = {0xFFFFFFFF, 0};
|
||||
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);
|
||||
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) {
|
||||
@@ -1128,12 +1092,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);
|
||||
@@ -1141,10 +1105,9 @@ static int Process(const Config* config) {
|
||||
ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
|
||||
}
|
||||
if (num_frames == 0) {
|
||||
fprintf(stderr,
|
||||
"Doesn't look like the source is animated. "
|
||||
"Skipping duration setting.\n");
|
||||
ok = WriteWebP(mux, config->output);
|
||||
fprintf(stderr, "Doesn't look like the source is animated. "
|
||||
"Skipping duration setting.\n");
|
||||
ok = WriteWebP(mux, config->output_);
|
||||
if (!ok) goto Err2;
|
||||
} else {
|
||||
int i;
|
||||
@@ -1156,12 +1119,12 @@ 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, 10, 3, args);
|
||||
const int nb_args = ExUtilGetInts(config->args_[i].params_,
|
||||
10, 3, args);
|
||||
ok = (nb_args >= 1);
|
||||
if (!ok) goto Err3;
|
||||
duration = args[0];
|
||||
@@ -1169,7 +1132,7 @@ static int Process(const Config* config) {
|
||||
ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
|
||||
}
|
||||
|
||||
if (nb_args == 1) { // only duration is present -> use full interval
|
||||
if (nb_args == 1) { // only duration is present -> use full interval
|
||||
start = 1;
|
||||
end = num_frames;
|
||||
} else {
|
||||
@@ -1204,11 +1167,11 @@ 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;
|
||||
|
||||
Err3:
|
||||
Err3:
|
||||
WebPFree(durations);
|
||||
WebPMuxDelete(new_mux);
|
||||
if (!ok) goto Err2;
|
||||
@@ -1216,24 +1179,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);
|
||||
ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
|
||||
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||
}
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
|
||||
break;
|
||||
}
|
||||
ok = WriteWebP(mux, config->output);
|
||||
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;
|
||||
@@ -1244,7 +1207,7 @@ static int Process(const Config* config) {
|
||||
}
|
||||
}
|
||||
|
||||
Err2:
|
||||
Err2:
|
||||
WebPMuxDelete(mux);
|
||||
return ok;
|
||||
}
|
||||
@@ -1252,7 +1215,6 @@ Err2:
|
||||
//------------------------------------------------------------------------------
|
||||
// Main.
|
||||
|
||||
// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
|
||||
int main(int argc, const char* argv[]) {
|
||||
Config config;
|
||||
int ok;
|
||||
@@ -1266,7 +1228,7 @@ int main(int argc, const char* argv[]) {
|
||||
PrintHelp();
|
||||
}
|
||||
DeleteConfig(&config);
|
||||
FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(!ok);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -7,7 +7,6 @@ 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
|
||||
|
||||
171
extras/extras.c
171
extras/extras.c
@@ -11,21 +11,14 @@
|
||||
//
|
||||
|
||||
#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 6
|
||||
#define XTRA_MIN_VERSION 3
|
||||
#define XTRA_REV_VERSION 0
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -45,7 +38,7 @@ int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) {
|
||||
uv_width = (width + 1) >> 1;
|
||||
for (y = 0; y < pic->height; ++y) {
|
||||
memcpy(pic->y + y * pic->y_stride, gray_data, width);
|
||||
gray_data += width; // <- we could use some 'data_stride' here if needed
|
||||
gray_data += width; // <- we could use some 'data_stride' here if needed
|
||||
if ((y & 1) == 0) {
|
||||
memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width);
|
||||
memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width);
|
||||
@@ -167,159 +160,3 @@ 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);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -11,18 +11,15 @@
|
||||
#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 0x0003 // MAJOR(8b) + MINOR(8b)
|
||||
#define WEBP_EXTRAS_ABI_VERSION 0x0002 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -49,10 +46,10 @@ WEBP_EXTERN int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic);
|
||||
// MAX_PALETTE_SIZE. 'pic' must have been initialized. Its content, if any,
|
||||
// will be discarded. Returns 'false' in case of error, or if indexed[] contains
|
||||
// invalid indices.
|
||||
WEBP_EXTERN int WebPImportColorMappedARGB(const uint8_t* indexed,
|
||||
int indexed_stride,
|
||||
const uint32_t palette[],
|
||||
int palette_size, WebPPicture* pic);
|
||||
WEBP_EXTERN int
|
||||
WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
|
||||
const uint32_t palette[], int palette_size,
|
||||
WebPPicture* pic);
|
||||
|
||||
// Convert the ARGB content of 'pic' from associated to unassociated.
|
||||
// 'pic' can be for instance the result of calling of some WebPPictureImportXXX
|
||||
@@ -73,42 +70,8 @@ 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"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_EXTRAS_EXTRAS_H_
|
||||
|
||||
@@ -23,11 +23,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "webp/encode.h"
|
||||
#include "imageio/image_dec.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "src/webp/types.h"
|
||||
#include "webp/encode.h"
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
int keep_alpha) {
|
||||
@@ -48,7 +47,7 @@ static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
reader = WebPGuessImageReader(data, data_size);
|
||||
ok = reader(data, data_size, pic, keep_alpha, NULL);
|
||||
|
||||
End:
|
||||
End:
|
||||
if (!ok) {
|
||||
WFPRINTF(stderr, "Error! Could not process file %s\n",
|
||||
(const W_CHAR*)filename);
|
||||
@@ -57,8 +56,8 @@ End:
|
||||
return ok ? data_size : 0;
|
||||
}
|
||||
|
||||
static void RescalePlane(uint8_t* plane, int width, int height, int x_stride,
|
||||
int y_stride, int max) {
|
||||
static void RescalePlane(uint8_t* plane, int width, int height,
|
||||
int x_stride, int y_stride, int max) {
|
||||
const uint32_t factor = (max > 0) ? (255u << 16) / max : 0;
|
||||
int x, y;
|
||||
for (y = 0; y < height; ++y) {
|
||||
@@ -71,9 +70,9 @@ static void RescalePlane(uint8_t* plane, int width, int height, int x_stride,
|
||||
}
|
||||
|
||||
// Return the max absolute difference.
|
||||
static int DiffScaleChannel(uint8_t* src1, int stride1, const uint8_t* src2,
|
||||
int stride2, int x_stride, int w, int h,
|
||||
int do_scaling) {
|
||||
static int DiffScaleChannel(uint8_t* src1, int stride1,
|
||||
const uint8_t* src2, int stride2,
|
||||
int x_stride, int w, int h, int do_scaling) {
|
||||
int x, y;
|
||||
int max = 0;
|
||||
for (y = 0; y < h; ++y) {
|
||||
@@ -95,7 +94,7 @@ static int DiffScaleChannel(uint8_t* src1, int stride1, const uint8_t* src2,
|
||||
// breaking the library's hidden visibility. This code duplication avoids the
|
||||
// bigger annoyance of having to open up internal details of libdsp...
|
||||
|
||||
#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1
|
||||
#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1
|
||||
|
||||
// struct for accumulating statistical moments
|
||||
typedef struct {
|
||||
@@ -105,19 +104,19 @@ typedef struct {
|
||||
} DistoStats;
|
||||
|
||||
// hat-shaped filter. Sum of coefficients is equal to 16.
|
||||
static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = {1, 2, 3, 4, 3, 2, 1};
|
||||
static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = { 1, 2, 3, 4, 3, 2, 1 };
|
||||
|
||||
static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) {
|
||||
const uint32_t N = stats->w;
|
||||
const uint32_t w2 = N * N;
|
||||
const uint32_t w2 = N * N;
|
||||
const uint32_t C1 = 20 * w2;
|
||||
const uint32_t C2 = 60 * w2;
|
||||
const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
|
||||
const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
|
||||
const uint64_t xmxm = (uint64_t)stats->xm * stats->xm;
|
||||
const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
|
||||
if (xmxm + ymym >= C3) {
|
||||
const int64_t xmym = (int64_t)stats->xm * stats->ym;
|
||||
const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
|
||||
const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
|
||||
const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
|
||||
const uint64_t syy = (uint64_t)stats->yym * N - ymym;
|
||||
// we descale by 8 to prevent overflow during the fnum/fden multiply.
|
||||
@@ -129,13 +128,13 @@ static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) {
|
||||
assert(r >= 0. && r <= 1.0);
|
||||
return r;
|
||||
}
|
||||
return 1.; // area is too dark to contribute meaningfully
|
||||
return 1.; // area is too dark to contribute meaningfully
|
||||
}
|
||||
|
||||
static double SSIMGetClipped(const uint8_t* src1, int stride1,
|
||||
const uint8_t* src2, int stride2, int xo, int yo,
|
||||
int W, int H) {
|
||||
DistoStats stats = {0, 0, 0, 0, 0, 0};
|
||||
const uint8_t* src2, int stride2,
|
||||
int xo, int yo, int W, int H) {
|
||||
DistoStats stats = { 0, 0, 0, 0, 0, 0 };
|
||||
const int ymin = (yo - SSIM_KERNEL < 0) ? 0 : yo - SSIM_KERNEL;
|
||||
const int ymax = (yo + SSIM_KERNEL > H - 1) ? H - 1 : yo + SSIM_KERNEL;
|
||||
const int xmin = (xo - SSIM_KERNEL < 0) ? 0 : xo - SSIM_KERNEL;
|
||||
@@ -145,13 +144,13 @@ static double SSIMGetClipped(const uint8_t* src1, int stride1,
|
||||
src2 += ymin * stride2;
|
||||
for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
|
||||
for (x = xmin; x <= xmax; ++x) {
|
||||
const uint32_t w =
|
||||
kWeight[SSIM_KERNEL + x - xo] * kWeight[SSIM_KERNEL + y - yo];
|
||||
const uint32_t w = kWeight[SSIM_KERNEL + x - xo]
|
||||
* kWeight[SSIM_KERNEL + y - yo];
|
||||
const uint32_t s1 = src1[x];
|
||||
const uint32_t s2 = src2[x];
|
||||
stats.w += w;
|
||||
stats.xm += w * s1;
|
||||
stats.ym += w * s2;
|
||||
stats.w += w;
|
||||
stats.xm += w * s1;
|
||||
stats.ym += w * s2;
|
||||
stats.xxm += w * s1 * s1;
|
||||
stats.xym += w * s1 * s2;
|
||||
stats.yym += w * s2 * s2;
|
||||
@@ -161,9 +160,9 @@ static double SSIMGetClipped(const uint8_t* src1, int stride1,
|
||||
}
|
||||
|
||||
// Compute SSIM-score map. Return -1 in case of error, max diff otherwise.
|
||||
static int SSIMScaleChannel(uint8_t* src1, int stride1, const uint8_t* src2,
|
||||
int stride2, int x_stride, int w, int h,
|
||||
int do_scaling) {
|
||||
static int SSIMScaleChannel(uint8_t* src1, int stride1,
|
||||
const uint8_t* src2, int stride2,
|
||||
int x_stride, int w, int h, int do_scaling) {
|
||||
int x, y;
|
||||
int max = 0;
|
||||
uint8_t* const plane1 = (uint8_t*)malloc(2 * w * h * sizeof(*plane1));
|
||||
@@ -205,8 +204,8 @@ static void ConvertToGray(WebPPicture* const pic) {
|
||||
for (x = 0; x < pic->width; ++x) {
|
||||
const uint32_t argb = row[x];
|
||||
const uint32_t r = (argb >> 16) & 0xff;
|
||||
const uint32_t g = (argb >> 8) & 0xff;
|
||||
const uint32_t b = (argb >> 0) & 0xff;
|
||||
const uint32_t g = (argb >> 8) & 0xff;
|
||||
const uint32_t b = (argb >> 0) & 0xff;
|
||||
// We use BT.709 for converting to luminance.
|
||||
const uint32_t Y = (uint32_t)(0.2126 * r + 0.7152 * g + 0.0722 * b + .5);
|
||||
row[x] = (argb & 0xff000000u) | (Y * 0x010101u);
|
||||
@@ -228,11 +227,10 @@ static void Help(void) {
|
||||
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 = EXIT_FAILURE;
|
||||
int ret = 1;
|
||||
float disto[5];
|
||||
int type = 0;
|
||||
int c;
|
||||
@@ -248,7 +246,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) {
|
||||
fprintf(stderr, "Can't init pictures\n");
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(1);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
@@ -264,7 +262,7 @@ int main(int argc, const char* argv[]) {
|
||||
use_gray = 1;
|
||||
} else if (!strcmp(argv[c], "-h")) {
|
||||
help = 1;
|
||||
ret = EXIT_SUCCESS;
|
||||
ret = 0;
|
||||
} else if (!strcmp(argv[c], "-o")) {
|
||||
if (++c == argc) {
|
||||
fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]);
|
||||
@@ -297,7 +295,8 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "Error while computing the distortion.\n");
|
||||
goto End;
|
||||
}
|
||||
printf("%u %.2f %.2f %.2f %.2f %.2f [ %.2f bpp ]\n", (unsigned int)size1,
|
||||
printf("%u %.2f %.2f %.2f %.2f %.2f [ %.2f bpp ]\n",
|
||||
(unsigned int)size1,
|
||||
disto[4], disto[0], disto[1], disto[2], disto[3],
|
||||
8.f * size1 / pic1.width / pic1.height);
|
||||
|
||||
@@ -305,25 +304,21 @@ int main(int argc, const char* argv[]) {
|
||||
uint8_t* data = NULL;
|
||||
size_t data_size = 0;
|
||||
if (pic1.use_argb != pic2.use_argb) {
|
||||
fprintf(stderr,
|
||||
"Pictures are not in the same argb format. "
|
||||
"Can't save the difference map.\n");
|
||||
fprintf(stderr, "Pictures are not in the same argb format. "
|
||||
"Can't save the difference map.\n");
|
||||
goto End;
|
||||
}
|
||||
if (pic1.use_argb) {
|
||||
int n;
|
||||
fprintf(stderr, "max differences per channel: ");
|
||||
for (n = 0; n < 3; ++n) { // skip the alpha channel
|
||||
const int range =
|
||||
(type == 1)
|
||||
? SSIMScaleChannel(
|
||||
(uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4, 4,
|
||||
pic1.width, pic1.height, scale)
|
||||
: DiffScaleChannel(
|
||||
(uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4, 4,
|
||||
pic1.width, pic1.height, scale);
|
||||
for (n = 0; n < 3; ++n) { // skip the alpha channel
|
||||
const int range = (type == 1) ?
|
||||
SSIMScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
|
||||
4, pic1.width, pic1.height, scale) :
|
||||
DiffScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
|
||||
4, pic1.width, pic1.height, scale);
|
||||
if (range < 0) fprintf(stderr, "\nError computing diff map\n");
|
||||
fprintf(stderr, "[%d]", range);
|
||||
}
|
||||
@@ -334,29 +329,27 @@ int main(int argc, const char* argv[]) {
|
||||
goto End;
|
||||
}
|
||||
#if !defined(WEBP_REDUCE_CSP)
|
||||
data_size =
|
||||
WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb, pic1.width,
|
||||
pic1.height, pic1.argb_stride * 4, &data);
|
||||
data_size = WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb,
|
||||
pic1.width, pic1.height,
|
||||
pic1.argb_stride * 4,
|
||||
&data);
|
||||
if (data_size == 0) {
|
||||
fprintf(stderr, "Error during lossless encoding.\n");
|
||||
goto End;
|
||||
}
|
||||
ret = ImgIoUtilWriteFile(output, data, data_size) ? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
ret = ImgIoUtilWriteFile(output, data, data_size) ? 0 : 1;
|
||||
WebPFree(data);
|
||||
if (ret) goto End;
|
||||
#else
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
fprintf(stderr,
|
||||
"Cannot save the difference map. Please recompile "
|
||||
"without the WEBP_REDUCE_CSP flag.\n");
|
||||
goto End;
|
||||
fprintf(stderr, "Cannot save the difference map. Please recompile "
|
||||
"without the WEBP_REDUCE_CSP flag.\n");
|
||||
#endif // WEBP_REDUCE_CSP
|
||||
}
|
||||
ret = EXIT_SUCCESS;
|
||||
ret = 0;
|
||||
|
||||
End:
|
||||
End:
|
||||
WebPPictureFree(&pic1);
|
||||
WebPPictureFree(&pic2);
|
||||
FREE_WARGV_AND_RETURN(ret);
|
||||
|
||||
@@ -11,13 +11,11 @@
|
||||
//
|
||||
// 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)
|
||||
@@ -54,10 +52,10 @@ int VP8EstimateQuality(const uint8_t* const data, size_t size) {
|
||||
if (data == NULL) return -1;
|
||||
|
||||
if (WebPGetFeatures(data, size, &features) != VP8_STATUS_OK) {
|
||||
return -1; // invalid file
|
||||
return -1; // invalid file
|
||||
}
|
||||
if (features.format == 2) return 101; // lossless
|
||||
if (features.format == 0 || features.has_animation) return -1; // mixed
|
||||
if (features.format == 2) return 101; // lossless
|
||||
if (features.format == 0 || features.has_animation) return -1; // mixed
|
||||
|
||||
while (pos < size) {
|
||||
sig = (sig >> 8) | ((uint64_t)data[pos++] << 40);
|
||||
@@ -78,29 +76,29 @@ 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
|
||||
if (GET_BIT(1)) { // update data
|
||||
const int absolute_delta = GET_BIT(1);
|
||||
int q[4] = {0, 0, 0, 0};
|
||||
int q[4] = { 0, 0, 0, 0 };
|
||||
for (s = 0; s < 4; ++s) {
|
||||
if (GET_BIT(1)) {
|
||||
q[s] = GET_BIT(7);
|
||||
if (GET_BIT(1)) q[s] = -q[s]; // sign
|
||||
if (GET_BIT(1)) q[s] = -q[s]; // sign
|
||||
}
|
||||
}
|
||||
if (absolute_delta) Q = q[0]; // just use the first segment's quantizer
|
||||
for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength
|
||||
for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength
|
||||
}
|
||||
if (update_map) {
|
||||
for (s = 0; s < 3; ++s) CONDITIONAL_SKIP(8);
|
||||
}
|
||||
}
|
||||
// Filter header
|
||||
GET_BIT(1 + 6 + 3); // simple + level + sharpness
|
||||
if (GET_BIT(1)) { // use_lf_delta
|
||||
if (GET_BIT(1)) { // update lf_delta?
|
||||
GET_BIT(1 + 6 + 3); // simple + level + sharpness
|
||||
if (GET_BIT(1)) { // use_lf_delta
|
||||
if (GET_BIT(1)) { // update lf_delta?
|
||||
int n;
|
||||
for (n = 0; n < 4 + 4; ++n) CONDITIONAL_SKIP(6);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +0,0 @@
|
||||
// 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_
|
||||
@@ -15,7 +15,6 @@
|
||||
// Author: James Zern (jzern@google.com)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
@@ -23,15 +22,15 @@
|
||||
|
||||
#if defined(WEBP_HAVE_SDL)
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp_to_sdl.h"
|
||||
#include "webp/decode.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
#if defined(WEBP_HAVE_JUST_SDL_H)
|
||||
#include <SDL.h>
|
||||
#else
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL/SDL.h>
|
||||
#endif
|
||||
|
||||
static void ProcessEvents(void) {
|
||||
@@ -41,39 +40,28 @@ static void ProcessEvents(void) {
|
||||
switch (event.type) {
|
||||
case SDL_KEYUP:
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_q:
|
||||
done = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case SDLK_q: done = 1; break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
file = (const char*)GET_WARGV(argv, c);
|
||||
}
|
||||
@@ -97,9 +85,9 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
SDL_Quit();
|
||||
FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(ok ? 0 : 1);
|
||||
}
|
||||
|
||||
#else // !WEBP_HAVE_SDL
|
||||
|
||||
@@ -11,12 +11,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "extras/extras.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "src/webp/types.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;
|
||||
@@ -29,7 +27,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(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
const char* const filename = (const char*)GET_WARGV(argv, c);
|
||||
const uint8_t* data = NULL;
|
||||
@@ -46,11 +44,11 @@ int main(int argc, const char* argv[]) {
|
||||
if (!quiet) {
|
||||
printf("Estimated quality factor: %d\n", q);
|
||||
} else {
|
||||
printf("%d\n", q); // just print the number
|
||||
printf("%d\n", q); // just print the number
|
||||
}
|
||||
}
|
||||
free((void*)data);
|
||||
}
|
||||
}
|
||||
FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
FREE_WARGV_AND_RETURN(ok ? 0 : 1);
|
||||
}
|
||||
|
||||
@@ -17,77 +17,91 @@
|
||||
|
||||
#if defined(WEBP_HAVE_SDL)
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "src/webp/decode.h"
|
||||
#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 <SDL2/SDL.h>
|
||||
#include <SDL/SDL.h>
|
||||
#endif
|
||||
|
||||
static int init_ok = 0;
|
||||
int WebPToSDL(const char* data, unsigned int data_size) {
|
||||
int ok = 0;
|
||||
VP8StatusCode status;
|
||||
WebPBitstreamFeatures input;
|
||||
uint8_t* output = NULL;
|
||||
SDL_Window* window = NULL;
|
||||
SDL_Renderer* renderer = NULL;
|
||||
SDL_Texture* texture = NULL;
|
||||
int width, height;
|
||||
WebPDecoderConfig config;
|
||||
WebPBitstreamFeatures* const input = &config.input;
|
||||
WebPDecBuffer* const output = &config.output;
|
||||
SDL_Surface* screen = NULL;
|
||||
SDL_Surface* surface = NULL;
|
||||
|
||||
if (!WebPInitDecoderConfig(&config)) {
|
||||
fprintf(stderr, "Library version mismatch!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!init_ok) {
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
init_ok = 1;
|
||||
}
|
||||
|
||||
status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &input);
|
||||
status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input);
|
||||
if (status != VP8_STATUS_OK) goto Error;
|
||||
width = input.width;
|
||||
height = input.height;
|
||||
|
||||
SDL_CreateWindowAndRenderer(width, height, 0, &window, &renderer);
|
||||
if (window == NULL || renderer == NULL) {
|
||||
fprintf(stderr, "Unable to create window or renderer!\n");
|
||||
screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE);
|
||||
if (screen == NULL) {
|
||||
fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!\n",
|
||||
input->width, input->height);
|
||||
goto Error;
|
||||
}
|
||||
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);
|
||||
surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
|
||||
input->width, input->height, 32,
|
||||
0x000000ffu, // R mask
|
||||
0x0000ff00u, // G mask
|
||||
0x00ff0000u, // B mask
|
||||
0xff000000u); // A mask
|
||||
|
||||
if (surface == NULL) {
|
||||
fprintf(stderr, "Unable to create %dx%d RGBA surface!\n",
|
||||
input->width, input->height);
|
||||
goto Error;
|
||||
}
|
||||
if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
|
||||
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
output =
|
||||
WebPDecodeBGRA((const uint8_t*)data, (size_t)data_size, &width, &height);
|
||||
output->colorspace = MODE_BGRA;
|
||||
#else
|
||||
output =
|
||||
WebPDecodeRGBA((const uint8_t*)data, (size_t)data_size, &width, &height);
|
||||
output->colorspace = MODE_RGBA;
|
||||
#endif
|
||||
if (output == NULL) {
|
||||
output->width = surface->w;
|
||||
output->height = surface->h;
|
||||
output->u.RGBA.rgba = surface->pixels;
|
||||
output->u.RGBA.stride = surface->pitch;
|
||||
output->u.RGBA.size = surface->pitch * surface->h;
|
||||
output->is_external_memory = 1;
|
||||
|
||||
status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
fprintf(stderr, "Error decoding image (%d)\n", status);
|
||||
goto Error;
|
||||
}
|
||||
|
||||
SDL_UpdateTexture(texture, NULL, output, width * sizeof(uint32_t));
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
|
||||
if (SDL_BlitSurface(surface, NULL, screen, NULL) ||
|
||||
SDL_Flip(screen)) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
// We should call SDL_DestroyWindow(window) but that makes .js fail.
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyTexture(texture);
|
||||
WebPFree(output);
|
||||
Error:
|
||||
SDL_FreeSurface(surface);
|
||||
SDL_FreeSurface(screen);
|
||||
WebPFreeDecBuffer(output);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
|
||||
#include "./image_dec.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
const char* WebPGetEnabledInputFileFormats(void) {
|
||||
return "WebP"
|
||||
#ifdef WEBP_HAVE_JPEG
|
||||
@@ -63,8 +57,8 @@ WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
|
||||
}
|
||||
|
||||
static int FailReader(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)pic;
|
||||
@@ -75,18 +69,12 @@ static int FailReader(const uint8_t* const data, size_t data_size,
|
||||
|
||||
WebPImageReader WebPGetImageReader(WebPInputFileFormat format) {
|
||||
switch (format) {
|
||||
case WEBP_PNG_FORMAT:
|
||||
return ReadPNG;
|
||||
case WEBP_JPEG_FORMAT:
|
||||
return ReadJPEG;
|
||||
case WEBP_TIFF_FORMAT:
|
||||
return ReadTIFF;
|
||||
case WEBP_WEBP_FORMAT:
|
||||
return ReadWebP;
|
||||
case WEBP_PNM_FORMAT:
|
||||
return ReadPNM;
|
||||
default:
|
||||
return FailReader;
|
||||
case WEBP_PNG_FORMAT: return ReadPNG;
|
||||
case WEBP_JPEG_FORMAT: return ReadJPEG;
|
||||
case WEBP_TIFF_FORMAT: return ReadTIFF;
|
||||
case WEBP_WEBP_FORMAT: return ReadWebP;
|
||||
case WEBP_PNM_FORMAT: return ReadPNM;
|
||||
default: return FailReader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,14 @@
|
||||
#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||
#define WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "./jpegdec.h"
|
||||
#include "./metadata.h"
|
||||
#include "./jpegdec.h"
|
||||
#include "./pngdec.h"
|
||||
#include "./pnmdec.h"
|
||||
#include "./tiffdec.h"
|
||||
@@ -53,8 +51,8 @@ WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
|
||||
|
||||
// Signature for common image-reading functions (ReadPNG, ReadJPEG, ...)
|
||||
typedef int (*WebPImageReader)(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
|
||||
// Return the reader associated to a given file format.
|
||||
WebPImageReader WebPGetImageReader(WebPInputFileFormat format);
|
||||
@@ -66,7 +64,7 @@ WebPImageReader WebPGuessImageReader(const uint8_t* const data,
|
||||
size_t data_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||
|
||||
@@ -12,12 +12,11 @@
|
||||
#include "./image_enc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WEBP_HAVE_PNG
|
||||
#include <png.h>
|
||||
#include <setjmp.h> // note: this must be included *after* png.h
|
||||
#include <setjmp.h> // note: this must be included *after* png.h
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
@@ -26,32 +25,29 @@
|
||||
#endif
|
||||
#define CINTERFACE
|
||||
#define COBJMACROS
|
||||
#define _WIN32_IE \
|
||||
0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||
#include <shlwapi.h>
|
||||
#include <tchar.h>
|
||||
#include <wincodec.h>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
#endif
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PNG
|
||||
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -60,8 +56,8 @@
|
||||
#define MAKE_REFGUID(x) &(x)
|
||||
#endif
|
||||
|
||||
static HRESULT CreateOutputStream(const char* out_file_name, int write_to_mem,
|
||||
IStream** stream) {
|
||||
static HRESULT CreateOutputStream(const char* out_file_name,
|
||||
int write_to_mem, IStream** stream) {
|
||||
HRESULT hr = S_OK;
|
||||
if (write_to_mem) {
|
||||
// Output to a memory buffer. This is freed when 'stream' is released.
|
||||
@@ -78,22 +74,24 @@ static HRESULT CreateOutputStream(const char* out_file_name, int write_to_mem,
|
||||
}
|
||||
|
||||
static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
|
||||
REFGUID container_guid, uint8_t* rgb, int stride,
|
||||
REFGUID container_guid,
|
||||
uint8_t* rgb, int stride,
|
||||
uint32_t width, uint32_t height, int has_alpha) {
|
||||
HRESULT hr = S_OK;
|
||||
IWICImagingFactory* factory = NULL;
|
||||
IWICBitmapFrameEncode* frame = NULL;
|
||||
IWICBitmapEncoder* encoder = NULL;
|
||||
IStream* stream = NULL;
|
||||
WICPixelFormatGUID pixel_format =
|
||||
has_alpha ? GUID_WICPixelFormat32bppBGRA : GUID_WICPixelFormat24bppBGR;
|
||||
WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
|
||||
: GUID_WICPixelFormat24bppBGR;
|
||||
|
||||
if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG;
|
||||
|
||||
IFS(CoInitialize(NULL));
|
||||
IFS(CoCreateInstance(
|
||||
MAKE_REFGUID(CLSID_WICImagingFactory), NULL, CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory), (LPVOID*)&factory));
|
||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory),
|
||||
(LPVOID*)&factory));
|
||||
if (hr == REGDB_E_CLASSNOTREG) {
|
||||
fprintf(stderr,
|
||||
"Couldn't access Windows Imaging Component (are you running "
|
||||
@@ -103,13 +101,14 @@ static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
|
||||
IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
|
||||
IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
|
||||
&encoder));
|
||||
IFS(IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache));
|
||||
IFS(IWICBitmapEncoder_Initialize(encoder, stream,
|
||||
WICBitmapEncoderNoCache));
|
||||
IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
|
||||
IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
|
||||
IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
|
||||
IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
|
||||
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride, height * stride,
|
||||
rgb));
|
||||
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
|
||||
height * stride, rgb));
|
||||
IFS(IWICBitmapFrameEncode_Commit(frame));
|
||||
IFS(IWICBitmapEncoder_Commit(encoder));
|
||||
|
||||
@@ -151,11 +150,11 @@ int WebPWritePNG(const char* out_file_name, int use_stdout,
|
||||
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||
|
||||
return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
|
||||
MAKE_REFGUID(GUID_ContainerFormatPng), rgb,
|
||||
stride, width, height, has_alpha));
|
||||
MAKE_REFGUID(GUID_ContainerFormatPng),
|
||||
rgb, stride, width, height, has_alpha));
|
||||
}
|
||||
|
||||
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
|
||||
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
|
||||
static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp unused) {
|
||||
(void)unused; // remove variable-unused warning
|
||||
longjmp(png_jmpbuf(png), 1);
|
||||
@@ -167,8 +166,8 @@ int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
||||
|
||||
if (out_file == NULL || buffer == NULL) return 0;
|
||||
|
||||
png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, PNGErrorFunction,
|
||||
NULL);
|
||||
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, PNGErrorFunction, NULL);
|
||||
if (png == NULL) {
|
||||
return 0;
|
||||
}
|
||||
@@ -204,12 +203,11 @@ int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
||||
png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
|
||||
return 1;
|
||||
}
|
||||
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
|
||||
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
|
||||
int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
if (fout == NULL || buffer == NULL) return 0;
|
||||
|
||||
fprintf(stderr,
|
||||
"PNG support not compiled. Please install the libpng "
|
||||
fprintf(stderr, "PNG support not compiled. Please install the libpng "
|
||||
"development package before building.\n");
|
||||
fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
|
||||
return 0;
|
||||
@@ -234,10 +232,8 @@ static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer,
|
||||
if (row == NULL) return 0;
|
||||
|
||||
if (alpha) {
|
||||
fprintf(fout,
|
||||
"P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
|
||||
"TUPLTYPE RGB_ALPHA\nENDHDR\n",
|
||||
width, height);
|
||||
fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
|
||||
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
|
||||
} else {
|
||||
fprintf(fout, "P6\n%u %u\n255\n", width, height);
|
||||
}
|
||||
@@ -264,20 +260,14 @@ int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
|
||||
// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose.
|
||||
int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
uint32_t width, height;
|
||||
uint8_t* rgba;
|
||||
int stride;
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||
const int stride = buffer->u.RGBA.stride;
|
||||
const uint32_t bytes_per_px = 2;
|
||||
uint32_t y;
|
||||
|
||||
if (fout == NULL || buffer == NULL) return 0;
|
||||
|
||||
width = buffer->width;
|
||||
height = buffer->height;
|
||||
rgba = buffer->u.RGBA.rgba;
|
||||
stride = buffer->u.RGBA.stride;
|
||||
|
||||
if (rgba == NULL) return 0;
|
||||
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
|
||||
|
||||
fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height);
|
||||
for (y = 0; y < height; ++y) {
|
||||
@@ -298,59 +288,52 @@ static void PutLE16(uint8_t* const dst, uint32_t value) {
|
||||
}
|
||||
|
||||
static void PutLE32(uint8_t* const dst, uint32_t value) {
|
||||
PutLE16(dst + 0, (value >> 0) & 0xffff);
|
||||
PutLE16(dst + 0, (value >> 0) & 0xffff);
|
||||
PutLE16(dst + 2, (value >> 16) & 0xffff);
|
||||
}
|
||||
|
||||
#define BMP_HEADER_SIZE 54
|
||||
#define BMP_HEADER_ALPHA_EXTRA_SIZE 16 // for alpha info
|
||||
int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
int has_alpha, header_size;
|
||||
uint32_t width, height;
|
||||
uint8_t* rgba;
|
||||
int stride;
|
||||
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;
|
||||
uint32_t y;
|
||||
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};
|
||||
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;
|
||||
uint8_t bmp_header[BMP_HEADER_SIZE + BMP_HEADER_ALPHA_EXTRA_SIZE] = { 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;
|
||||
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
|
||||
|
||||
// bitmap file header
|
||||
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
|
||||
PutLE32(bmp_header + 2, total_size); // size including header
|
||||
PutLE32(bmp_header + 6, 0); // reserved
|
||||
PutLE32(bmp_header + 10, header_size); // offset to pixel array
|
||||
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
|
||||
PutLE32(bmp_header + 2, total_size); // size including header
|
||||
PutLE32(bmp_header + 6, 0); // reserved
|
||||
PutLE32(bmp_header + 10, header_size); // offset to pixel array
|
||||
// bitmap info header
|
||||
PutLE32(bmp_header + 14, header_size - 14); // DIB header size
|
||||
PutLE32(bmp_header + 18, width); // dimensions
|
||||
PutLE32(bmp_header + 22, height); // no vertical flip
|
||||
PutLE16(bmp_header + 26, 1); // number of planes
|
||||
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
|
||||
PutLE32(bmp_header + 30, has_alpha ? 3 : 0); // BI_BITFIELDS or BI_RGB
|
||||
PutLE32(bmp_header + 14, header_size - 14); // DIB header size
|
||||
PutLE32(bmp_header + 18, width); // dimensions
|
||||
PutLE32(bmp_header + 22, height); // no vertical flip
|
||||
PutLE16(bmp_header + 26, 1); // number of planes
|
||||
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
|
||||
PutLE32(bmp_header + 30, has_alpha ? 3 : 0); // BI_BITFIELDS or BI_RGB
|
||||
PutLE32(bmp_header + 34, image_size);
|
||||
PutLE32(bmp_header + 38, 2400); // x pixels/meter
|
||||
PutLE32(bmp_header + 42, 2400); // y pixels/meter
|
||||
PutLE32(bmp_header + 46, 0); // number of palette colors
|
||||
PutLE32(bmp_header + 50, 0); // important color count
|
||||
if (has_alpha) { // BITMAPV3INFOHEADER complement
|
||||
PutLE32(bmp_header + 54, 0x00ff0000); // red mask
|
||||
PutLE32(bmp_header + 58, 0x0000ff00); // green mask
|
||||
PutLE32(bmp_header + 62, 0x000000ff); // blue mask
|
||||
PutLE32(bmp_header + 66, 0xff000000); // alpha mask
|
||||
PutLE32(bmp_header + 38, 2400); // x pixels/meter
|
||||
PutLE32(bmp_header + 42, 2400); // y pixels/meter
|
||||
PutLE32(bmp_header + 46, 0); // number of palette colors
|
||||
PutLE32(bmp_header + 50, 0); // important color count
|
||||
if (has_alpha) { // BITMAPV3INFOHEADER complement
|
||||
PutLE32(bmp_header + 54, 0x00ff0000); // red mask
|
||||
PutLE32(bmp_header + 58, 0x0000ff00); // green mask
|
||||
PutLE32(bmp_header + 62, 0x000000ff); // blue mask
|
||||
PutLE32(bmp_header + 66, 0xff000000); // alpha mask
|
||||
}
|
||||
|
||||
// TODO(skal): color profile
|
||||
@@ -368,7 +351,7 @@ int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
}
|
||||
// write padding zeroes
|
||||
if (bmp_stride != line_size) {
|
||||
const uint8_t zeroes[3] = {0};
|
||||
const uint8_t zeroes[3] = { 0 };
|
||||
if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
|
||||
return 0;
|
||||
}
|
||||
@@ -389,61 +372,51 @@ 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) {
|
||||
int has_alpha;
|
||||
uint32_t width, height;
|
||||
uint8_t* rgba;
|
||||
int stride;
|
||||
uint8_t bytes_per_px = 0;
|
||||
const uint8_t assoc_alpha = 0;
|
||||
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||
const uint32_t width = buffer->width;
|
||||
const uint32_t height = buffer->height;
|
||||
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||
const int stride = buffer->u.RGBA.stride;
|
||||
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
|
||||
const uint8_t assoc_alpha =
|
||||
WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2;
|
||||
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
|
||||
const uint8_t num_ifd_entries = 0;
|
||||
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
|
||||
: NUM_IFD_ENTRIES - 1;
|
||||
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
|
||||
0x49, 0x49, 0x2a, 0x00, // little endian signature
|
||||
8, 0, 0, 0, // offset to the unique IFD that follows
|
||||
// IFD (offset = 8). Entries must be written in increasing tag order.
|
||||
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
|
||||
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
|
||||
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
|
||||
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
|
||||
EXTRA_DATA_OFFSET + 0, 0, 0, 0, 0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0,
|
||||
0, // 46: Compression: none
|
||||
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
|
||||
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
|
||||
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
|
||||
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
|
||||
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
|
||||
bytes_per_px, 0, 0, 0, 0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, // 106: Rows per strip (TBD)
|
||||
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
|
||||
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0, 0x1b, 0x01, 5, 0, 1, 0, 0,
|
||||
0, // 142: Y-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0, 0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0,
|
||||
0, // 154: PlanarConfiguration
|
||||
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
||||
0x52, 0x01, 3, 0, 1, 0, 0, 0, assoc_alpha, 0, 0,
|
||||
0, // 178: ExtraSamples: rgbA/RGBA
|
||||
0, 0, 0, 0, // 190: IFD terminator
|
||||
// EXTRA_DATA_OFFSET:
|
||||
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
||||
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
|
||||
0x49, 0x49, 0x2a, 0x00, // little endian signature
|
||||
8, 0, 0, 0, // offset to the unique IFD that follows
|
||||
// IFD (offset = 8). Entries must be written in increasing tag order.
|
||||
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
|
||||
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
|
||||
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
|
||||
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
|
||||
EXTRA_DATA_OFFSET + 0, 0, 0, 0,
|
||||
0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
|
||||
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
|
||||
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
|
||||
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
|
||||
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
|
||||
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
|
||||
bytes_per_px, 0, 0, 0,
|
||||
0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
|
||||
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
|
||||
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||
0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
|
||||
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
||||
0x52, 0x01, 3, 0, 1, 0, 0, 0,
|
||||
assoc_alpha, 0, 0, 0, // 178: ExtraSamples: rgbA/RGBA
|
||||
0, 0, 0, 0, // 190: IFD terminator
|
||||
// EXTRA_DATA_OFFSET:
|
||||
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
||||
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
|
||||
};
|
||||
uint32_t y;
|
||||
|
||||
if (fout == NULL || buffer == NULL) 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;
|
||||
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
|
||||
|
||||
// Fill placeholders in IFD:
|
||||
PutLE32(tiff_header + 10 + 8, width);
|
||||
@@ -518,11 +491,11 @@ int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
|
||||
if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
|
||||
|
||||
fprintf(fout, "P5\n%d %d\n255\n", (width + 1) & ~1,
|
||||
height + uv_height + a_height);
|
||||
fprintf(fout, "P5\n%d %d\n255\n",
|
||||
(width + 1) & ~1, height + uv_height + a_height);
|
||||
for (y = 0; ok && y < height; ++y) {
|
||||
ok &= (fwrite(src_y, width, 1, fout) == 1);
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
src_y += yuv->y_stride;
|
||||
}
|
||||
for (y = 0; ok && y < uv_height; ++y) {
|
||||
@@ -533,7 +506,7 @@ int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
}
|
||||
for (y = 0; ok && y < a_height; ++y) {
|
||||
ok &= (fwrite(src_a, width, 1, fout) == 1);
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
src_a += yuv->a_stride;
|
||||
}
|
||||
return ok;
|
||||
@@ -610,7 +583,8 @@ int WebPSaveImage(const WebPDecBuffer* const buffer,
|
||||
}
|
||||
}
|
||||
|
||||
if (format == PNG || format == RGBA || format == BGRA || format == ARGB ||
|
||||
if (format == PNG ||
|
||||
format == RGBA || format == BGRA || format == ARGB ||
|
||||
format == rgbA || format == bgrA || format == Argb) {
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
ok &= WebPWritePNG(out_file_name, use_stdout, buffer);
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
#include "webp/decode.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -38,19 +38,10 @@ typedef enum {
|
||||
RAW_YUV,
|
||||
ALPHA_PLANE_ONLY, // this is for experimenting only
|
||||
// forced colorspace output (for testing, mostly)
|
||||
RGB,
|
||||
RGBA,
|
||||
BGR,
|
||||
BGRA,
|
||||
ARGB,
|
||||
RGBA_4444,
|
||||
RGB_565,
|
||||
rgbA,
|
||||
bgrA,
|
||||
Argb,
|
||||
rgbA_4444,
|
||||
YUV,
|
||||
YUVA
|
||||
RGB, RGBA, BGR, BGRA, ARGB,
|
||||
RGBA_4444, RGB_565,
|
||||
rgbA, bgrA, Argb, rgbA_4444,
|
||||
YUV, YUVA
|
||||
} WebPOutputFileFormat;
|
||||
|
||||
// General all-purpose call.
|
||||
@@ -99,7 +90,7 @@ int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGE_ENC_H_
|
||||
|
||||
@@ -13,15 +13,12 @@
|
||||
#include "./imageio_util.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <fcntl.h> // for _O_BINARY
|
||||
#include <io.h> // for _setmode()
|
||||
#include <fcntl.h> // for _O_BINARY
|
||||
#include <io.h> // for _setmode()
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// File I/O
|
||||
@@ -65,14 +62,14 @@ int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
|
||||
*data_size = size;
|
||||
return 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
free(input);
|
||||
fprintf(stderr, "Could not read from stdin\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ImgIoUtilReadFile(const char* const file_name, const uint8_t** data,
|
||||
size_t* data_size) {
|
||||
int ImgIoUtilReadFile(const char* const file_name,
|
||||
const uint8_t** data, size_t* data_size) {
|
||||
int ok;
|
||||
uint8_t* file_data;
|
||||
size_t file_size;
|
||||
@@ -92,11 +89,6 @@ int ImgIoUtilReadFile(const char* const file_name, const uint8_t** data,
|
||||
}
|
||||
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);
|
||||
@@ -123,8 +115,8 @@ int ImgIoUtilReadFile(const char* const file_name, const uint8_t** data,
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int ImgIoUtilWriteFile(const char* const file_name, const uint8_t* data,
|
||||
size_t data_size) {
|
||||
int ImgIoUtilWriteFile(const char* const file_name,
|
||||
const uint8_t* data, size_t data_size) {
|
||||
int ok;
|
||||
FILE* out;
|
||||
const int to_stdout = (file_name == NULL) || !WSTRCMP(file_name, "-");
|
||||
@@ -145,8 +137,8 @@ int ImgIoUtilWriteFile(const char* const file_name, const uint8_t* data,
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
|
||||
int dst_stride, int width, int height) {
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height) {
|
||||
while (height-- > 0) {
|
||||
memcpy(dst, src, width * sizeof(*dst));
|
||||
src += src_stride;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -36,22 +35,22 @@ FILE* ImgIoUtilSetBinaryMode(FILE* file);
|
||||
// to be used as a C-string.
|
||||
// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
|
||||
// the function ImgIoUtilReadFromStdin().
|
||||
int ImgIoUtilReadFile(const char* const file_name, const uint8_t** data,
|
||||
size_t* data_size);
|
||||
int ImgIoUtilReadFile(const char* const file_name,
|
||||
const uint8_t** data, size_t* data_size);
|
||||
|
||||
// Same as ImgIoUtilReadFile(), but reads until EOF from stdin instead.
|
||||
int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size);
|
||||
|
||||
// Write a data segment into a file named 'file_name'. Returns true if ok.
|
||||
// If 'file_name' is NULL or equal to "-", output is written to stdout.
|
||||
int ImgIoUtilWriteFile(const char* const file_name, const uint8_t* data,
|
||||
size_t data_size);
|
||||
int ImgIoUtilWriteFile(const char* const file_name,
|
||||
const uint8_t* data, size_t data_size);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Copy width x height pixels from 'src' to 'dst' honoring the strides.
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
|
||||
int dst_stride, int width, int height);
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -59,7 +58,7 @@ void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
|
||||
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t stride, size_t height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||
|
||||
@@ -18,25 +18,24 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WEBP_HAVE_JPEG
|
||||
#include <jerror.h>
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
#include <setjmp.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"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Metadata processing
|
||||
|
||||
#ifndef JPEG_APP1
|
||||
#define JPEG_APP1 (JPEG_APP0 + 1)
|
||||
# define JPEG_APP1 (JPEG_APP0 + 1)
|
||||
#endif
|
||||
#ifndef JPEG_APP2
|
||||
#define JPEG_APP2 (JPEG_APP0 + 2)
|
||||
# define JPEG_APP2 (JPEG_APP0 + 2)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
@@ -64,7 +63,7 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
// ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
|
||||
static const char kICCPSignature[] = "ICC_PROFILE";
|
||||
static const size_t kICCPSignatureLength = 12; // signature includes '\0'
|
||||
static const size_t kICCPSkipLength = 14; // signature + seq & count
|
||||
static const size_t kICCPSkipLength = 14; // signature + seq & count
|
||||
int expected_count = 0;
|
||||
int actual_count = 0;
|
||||
int seq_max = 0;
|
||||
@@ -74,7 +73,8 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
|
||||
memset(iccp_segments, 0, sizeof(iccp_segments));
|
||||
for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
|
||||
if (marker->marker == JPEG_APP2 && marker->data_length > kICCPSkipLength &&
|
||||
if (marker->marker == JPEG_APP2 &&
|
||||
marker->data_length > kICCPSkipLength &&
|
||||
!memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
|
||||
// ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
|
||||
const int seq = marker->data[kICCPSignatureLength];
|
||||
@@ -83,9 +83,8 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
ICCPSegment* segment;
|
||||
|
||||
if (segment_size == 0 || count == 0 || seq == 0) {
|
||||
fprintf(stderr,
|
||||
"[ICCP] size (%d) / count (%d) / sequence number (%d)"
|
||||
" cannot be 0!\n",
|
||||
fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
|
||||
" cannot be 0!\n",
|
||||
(int)segment_size, seq, count);
|
||||
return 0;
|
||||
}
|
||||
@@ -100,7 +99,7 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
|
||||
segment = iccp_segments + seq - 1;
|
||||
if (segment->data_length != 0) {
|
||||
fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n", seq);
|
||||
fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -138,8 +137,8 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
int i;
|
||||
size_t offset = 0;
|
||||
for (i = 0; i < seq_max; ++i) {
|
||||
memcpy(iccp->bytes + offset, iccp_segments[i].data,
|
||||
iccp_segments[i].data_length);
|
||||
memcpy(iccp->bytes + offset,
|
||||
iccp_segments[i].data, iccp_segments[i].data_length);
|
||||
offset += iccp_segments[i].data_length;
|
||||
}
|
||||
}
|
||||
@@ -156,12 +155,12 @@ static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
|
||||
size_t signature_length;
|
||||
size_t storage_offset;
|
||||
} kJPEGMetadataMap[] = {
|
||||
// Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
|
||||
{JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif)},
|
||||
// XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
|
||||
// TODO(jzern) Add support for 'ExtendedXMP'
|
||||
{JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp)},
|
||||
{0, NULL, 0, 0},
|
||||
// Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
|
||||
{ JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif) },
|
||||
// XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
|
||||
// TODO(jzern) Add support for 'ExtendedXMP'
|
||||
{ JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
|
||||
{ 0, NULL, 0, 0 },
|
||||
};
|
||||
jpeg_saved_marker_ptr marker;
|
||||
// Treat ICC profiles separately as they may be segmented and out of order.
|
||||
@@ -179,8 +178,8 @@ static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
|
||||
kJPEGMetadataMap[i].storage_offset);
|
||||
|
||||
if (payload->bytes == NULL) {
|
||||
const char* marker_data =
|
||||
(const char*)marker->data + kJPEGMetadataMap[i].signature_length;
|
||||
const char* marker_data = (const char*)marker->data +
|
||||
kJPEGMetadataMap[i].signature_length;
|
||||
const size_t marker_data_length =
|
||||
marker->data_length - kJPEGMetadataMap[i].signature_length;
|
||||
if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
|
||||
@@ -207,18 +206,8 @@ 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);
|
||||
}
|
||||
|
||||
@@ -250,7 +239,9 @@ static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
|
||||
ctx->pub.next_input_byte += jump;
|
||||
}
|
||||
|
||||
static void ContextTerm(j_decompress_ptr cinfo) { (void)cinfo; }
|
||||
static void ContextTerm(j_decompress_ptr cinfo) {
|
||||
(void)cinfo;
|
||||
}
|
||||
|
||||
static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
|
||||
JPEGReadContext* const ctx) {
|
||||
@@ -265,7 +256,8 @@ static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
|
||||
}
|
||||
|
||||
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha, Metadata* const metadata) {
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
volatile int ok = 0;
|
||||
int width, height;
|
||||
int64_t stride;
|
||||
@@ -282,12 +274,12 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
ctx.data = data;
|
||||
ctx.data_size = data_size;
|
||||
|
||||
memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp safety
|
||||
memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp safety
|
||||
dinfo.err = jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = my_error_exit;
|
||||
|
||||
if (setjmp(jerr.setjmp_buffer)) {
|
||||
Error:
|
||||
Error:
|
||||
MetadataFree(metadata);
|
||||
jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
|
||||
goto End;
|
||||
@@ -350,11 +342,11 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
MetadataFree(metadata); // In case the caller forgets to free it on error.
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
free(rgb);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_JPEG
|
||||
#else // !WEBP_HAVE_JPEG
|
||||
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
@@ -363,8 +355,7 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr,
|
||||
"JPEG support not compiled. Please install the libjpeg "
|
||||
fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#ifndef WEBP_IMAGEIO_JPEGDEC_H_
|
||||
#define WEBP_IMAGEIO_JPEGDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -33,7 +31,7 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_JPEGDEC_H_
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
#ifndef WEBP_IMAGEIO_METADATA_H_
|
||||
#define WEBP_IMAGEIO_METADATA_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -43,7 +41,7 @@ int MetadataCopy(const char* metadata, size_t metadata_len,
|
||||
MetadataPayload* const payload);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_METADATA_H_
|
||||
|
||||
113
imageio/pngdec.c
113
imageio/pngdec.c
@@ -22,24 +22,24 @@
|
||||
#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 <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) (LOCAL_PNG_VERSION >= (((maj) << 8) | (min)))
|
||||
#define LOCAL_PNG_PREREQ(maj, min) \
|
||||
(LOCAL_PNG_VERSION >= (((maj) << 8) | (min)))
|
||||
|
||||
static void PNGAPI error_function(png_structp png, png_const_charp error) {
|
||||
if (error != NULL) fprintf(stderr, "libpng error: %s\n", error);
|
||||
longjmp(png_jmpbuf(png), 1);
|
||||
}
|
||||
|
||||
#if LOCAL_PNG_PREREQ(1, 4)
|
||||
#if LOCAL_PNG_PREREQ(1,4)
|
||||
typedef png_alloc_size_t LocalPngAllocSize;
|
||||
#else
|
||||
typedef png_size_t LocalPngAllocSize;
|
||||
@@ -111,8 +111,7 @@ static int ProcessRawProfile(const char* profile, size_t profile_len,
|
||||
}
|
||||
++src;
|
||||
// skip the profile name and extract the length.
|
||||
while (*src != '\0' && *src++ != '\n') {
|
||||
}
|
||||
while (*src != '\0' && *src++ != '\n') {}
|
||||
expected_length = (int)strtol(src, &end, 10);
|
||||
if (*end != '\n') {
|
||||
fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n",
|
||||
@@ -134,47 +133,32 @@ static const struct {
|
||||
MetadataPayload* const payload);
|
||||
size_t storage_offset;
|
||||
} kPNGMetadataMap[] = {
|
||||
// https://exiftool.org/TagNames/PNG.html#TextualData
|
||||
// See also: ExifTool on CPAN.
|
||||
{"Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif)},
|
||||
{"Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp)},
|
||||
// Exiftool puts exif data in APP1 chunk, too.
|
||||
{"Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif)},
|
||||
// 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},
|
||||
// https://exiftool.org/TagNames/PNG.html#TextualData
|
||||
// See also: ExifTool on CPAN.
|
||||
{ "Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif) },
|
||||
{ "Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp) },
|
||||
// Exiftool puts exif data in APP1 chunk, too.
|
||||
{ "Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif) },
|
||||
// XMP Specification Part 3, Section 3 #PNG
|
||||
{ "XML:com.adobe.xmp", MetadataCopy, METADATA_OFFSET(xmp) },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
// Looks for metadata at both the beginning and end of the PNG file, giving
|
||||
// preference to the head.
|
||||
// Returns true on success. The caller must use MetadataFree() on 'metadata' in
|
||||
// all cases.
|
||||
static int ExtractMetadataFromPNG(png_structp png, png_infop const head_info,
|
||||
static int ExtractMetadataFromPNG(png_structp png,
|
||||
png_infop const head_info,
|
||||
png_infop const end_info,
|
||||
Metadata* const metadata) {
|
||||
int p;
|
||||
|
||||
for (p = 0; p < 2; ++p) {
|
||||
for (p = 0; p < 2; ++p) {
|
||||
png_infop const info = (p == 0) ? head_info : end_info;
|
||||
png_textp text = NULL;
|
||||
const png_uint_32 num = png_get_text(png, info, &text, NULL);
|
||||
png_uint_32 i;
|
||||
|
||||
#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;
|
||||
@@ -208,24 +192,22 @@ static int ExtractMetadataFromPNG(png_structp png, png_infop const head_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef PNG_iCCP_SUPPORTED
|
||||
// Look for an ICC profile.
|
||||
{
|
||||
png_charp name;
|
||||
int comp_type;
|
||||
#if LOCAL_PNG_PREREQ(1, 5)
|
||||
#if LOCAL_PNG_PREREQ(1,5)
|
||||
png_bytep profile;
|
||||
#else
|
||||
png_charp profile;
|
||||
#endif
|
||||
png_uint_32 len;
|
||||
|
||||
if (png_get_iCCP(png, info, &name, &comp_type, &profile, &len) ==
|
||||
PNG_INFO_iCCP) {
|
||||
if (png_get_iCCP(png, info,
|
||||
&name, &comp_type, &profile, &len) == PNG_INFO_iCCP) {
|
||||
if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0;
|
||||
}
|
||||
}
|
||||
#endif // PNG_iCCP_SUPPORTED
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -246,14 +228,14 @@ static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
}
|
||||
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
volatile png_structp png = NULL;
|
||||
volatile png_infop info = NULL;
|
||||
volatile png_infop end_info = NULL;
|
||||
PNGReadContext context = {NULL, 0, 0};
|
||||
PNGReadContext context = { NULL, 0, 0 };
|
||||
int color_type, bit_depth, interlaced;
|
||||
int num_channels;
|
||||
int has_alpha;
|
||||
int num_passes;
|
||||
int p;
|
||||
volatile int ok = 0;
|
||||
@@ -266,19 +248,19 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
context.data = data;
|
||||
context.data_size = data_size;
|
||||
|
||||
png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL,
|
||||
MallocFunc, FreeFunc);
|
||||
png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
|
||||
NULL, MallocFunc, FreeFunc);
|
||||
if (png == NULL) goto End;
|
||||
|
||||
png_set_error_fn(png, 0, error_function, NULL);
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
Error:
|
||||
Error:
|
||||
MetadataFree(metadata);
|
||||
goto End;
|
||||
}
|
||||
|
||||
#if LOCAL_PNG_PREREQ(1, 5) || \
|
||||
(LOCAL_PNG_PREREQ(1, 4) && PNG_LIBPNG_VER_RELEASE >= 1)
|
||||
#if LOCAL_PNG_PREREQ(1,5) || \
|
||||
(LOCAL_PNG_PREREQ(1,4) && PNG_LIBPNG_VER_RELEASE >= 1)
|
||||
// If it looks like the bitstream is going to need more memory than libpng's
|
||||
// internal limit (default: 8M), try to (reasonably) raise it.
|
||||
if (data_size > png_get_chunk_malloc_max(png) && data_size < (1u << 24)) {
|
||||
@@ -293,9 +275,9 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
|
||||
png_set_read_fn(png, &context, ReadFunc);
|
||||
png_read_info(png, info);
|
||||
if (!png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type,
|
||||
&interlaced, NULL, NULL))
|
||||
goto Error;
|
||||
if (!png_get_IHDR(png, info,
|
||||
&width, &height, &bit_depth, &color_type, &interlaced,
|
||||
NULL, NULL)) goto Error;
|
||||
|
||||
png_set_strip_16(png);
|
||||
png_set_packing(png);
|
||||
@@ -311,6 +293,9 @@ 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.
|
||||
@@ -325,16 +310,13 @@ 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);
|
||||
|
||||
num_channels = png_get_channels(png, info);
|
||||
if (num_channels != 3 && num_channels != 4) {
|
||||
goto Error;
|
||||
}
|
||||
stride = (int64_t)num_channels * width * sizeof(*rgb);
|
||||
stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb);
|
||||
if (stride != (int)stride ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||
goto Error;
|
||||
@@ -359,32 +341,31 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
|
||||
pic->width = (int)width;
|
||||
pic->height = (int)height;
|
||||
ok = (num_channels == 4) ? WebPPictureImportRGBA(pic, rgb, (int)stride)
|
||||
: WebPPictureImportRGB(pic, rgb, (int)stride);
|
||||
ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, (int)stride)
|
||||
: WebPPictureImportRGB(pic, rgb, (int)stride);
|
||||
|
||||
if (!ok) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
if (png != NULL) {
|
||||
png_destroy_read_struct((png_structpp)&png, (png_infopp)&info,
|
||||
(png_infopp)&end_info);
|
||||
png_destroy_read_struct((png_structpp)&png,
|
||||
(png_infopp)&info, (png_infopp)&end_info);
|
||||
}
|
||||
free(rgb);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_PNG
|
||||
#else // !WEBP_HAVE_PNG
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr,
|
||||
"PNG support not compiled. Please install the libpng "
|
||||
fprintf(stderr, "PNG support not compiled. Please install the libpng "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#ifndef WEBP_IMAGEIO_PNGDEC_H_
|
||||
#define WEBP_IMAGEIO_PNGDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -29,11 +27,11 @@ struct WebPPicture;
|
||||
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||
// Returns true on success.
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_PNGDEC_H_
|
||||
|
||||
@@ -17,20 +17,19 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./imageio_util.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/types.h"
|
||||
#include "./imageio_util.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
WIDTH_FLAG = 1 << 0,
|
||||
HEIGHT_FLAG = 1 << 1,
|
||||
DEPTH_FLAG = 1 << 2,
|
||||
MAXVAL_FLAG = 1 << 3,
|
||||
TUPLE_FLAG = 1 << 4,
|
||||
WIDTH_FLAG = 1 << 0,
|
||||
HEIGHT_FLAG = 1 << 1,
|
||||
DEPTH_FLAG = 1 << 2,
|
||||
MAXVAL_FLAG = 1 << 3,
|
||||
TUPLE_FLAG = 1 << 4,
|
||||
ALL_NEEDED_FLAGS = WIDTH_FLAG | HEIGHT_FLAG | DEPTH_FLAG | MAXVAL_FLAG
|
||||
} PNMFlags;
|
||||
|
||||
@@ -39,9 +38,9 @@ typedef struct {
|
||||
size_t data_size;
|
||||
int width, height;
|
||||
int bytes_per_px;
|
||||
int depth; // 1 (grayscale), 2 (grayscale + alpha), 3 (rgb), 4 (rgba)
|
||||
int depth; // 1 (grayscale), 2 (grayscale + alpha), 3 (rgb), 4 (rgba)
|
||||
int max_value;
|
||||
int type; // 5, 6 or 7
|
||||
int type; // 5, 6 or 7
|
||||
int seen_flags;
|
||||
} PNMInfo;
|
||||
|
||||
@@ -55,7 +54,7 @@ static size_t ReadLine(const uint8_t* const data, size_t off, size_t data_size,
|
||||
char out[MAX_LINE_SIZE + 1], size_t* const out_size) {
|
||||
size_t i = 0;
|
||||
*out_size = 0;
|
||||
redo:
|
||||
redo:
|
||||
for (i = 0; i < MAX_LINE_SIZE && off < data_size; ++i) {
|
||||
out[i] = data[off++];
|
||||
if (out[i] == '\n') break;
|
||||
@@ -64,7 +63,7 @@ redo:
|
||||
if (i == 0) goto redo; // empty line
|
||||
if (out[0] == '#') goto redo; // skip comment
|
||||
}
|
||||
out[i] = 0; // safety sentinel
|
||||
out[i] = 0; // safety sentinel
|
||||
*out_size = i;
|
||||
return off;
|
||||
}
|
||||
@@ -173,8 +172,9 @@ static size_t ReadHeader(PNMInfo* const info) {
|
||||
info->depth = (info->type == 5) ? 1 : 3;
|
||||
}
|
||||
// perform some basic numerical validation
|
||||
if (info->width <= 0 || info->height <= 0 || info->type <= 0 ||
|
||||
info->type >= 9 || info->depth <= 0 || info->depth > 4 ||
|
||||
if (info->width <= 0 || info->height <= 0 ||
|
||||
info->type <= 0 || info->type >= 9 ||
|
||||
info->depth <= 0 || info->depth > 4 ||
|
||||
info->max_value <= 0 || info->max_value >= 65536) {
|
||||
return 0;
|
||||
}
|
||||
@@ -182,12 +182,13 @@ static size_t ReadHeader(PNMInfo* const info) {
|
||||
return off;
|
||||
}
|
||||
|
||||
int ReadPNM(const uint8_t* const data, size_t data_size, WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
int ok = 0;
|
||||
int i, j;
|
||||
uint64_t stride, pixel_bytes, sample_size, depth;
|
||||
uint8_t *rgb = NULL, *tmp_rgb;
|
||||
uint8_t* rgb = NULL, *tmp_rgb;
|
||||
size_t offset;
|
||||
PNMInfo info;
|
||||
|
||||
@@ -207,8 +208,8 @@ int ReadPNM(const uint8_t* const data, size_t data_size, WebPPicture* const pic,
|
||||
// Some basic validations.
|
||||
if (pic == NULL) goto End;
|
||||
if (info.width > WEBP_MAX_DIMENSION || info.height > WEBP_MAX_DIMENSION) {
|
||||
fprintf(stderr, "Invalid %dx%d dimension for PNM\n", info.width,
|
||||
info.height);
|
||||
fprintf(stderr, "Invalid %dx%d dimension for PNM\n",
|
||||
info.width, info.height);
|
||||
goto End;
|
||||
}
|
||||
|
||||
@@ -256,8 +257,8 @@ int ReadPNM(const uint8_t* const data, size_t data_size, WebPPicture* const pic,
|
||||
const uint32_t round = info.max_value / 2;
|
||||
int k = 0;
|
||||
for (i = 0; i < info.width * info.depth; ++i) {
|
||||
uint32_t v =
|
||||
(sample_size == 2) ? 256u * in[2 * i + 0] + in[2 * i + 1] : in[i];
|
||||
uint32_t v = (sample_size == 2) ? 256u * in[2 * i + 0] + in[2 * i + 1]
|
||||
: in[i];
|
||||
if (info.max_value != 255) v = (v * 255u + round) / info.max_value;
|
||||
if (v > 255u) v = 255u;
|
||||
if (info.depth > 2) {
|
||||
@@ -289,7 +290,7 @@ int ReadPNM(const uint8_t* const data, size_t data_size, WebPPicture* const pic,
|
||||
if (!ok) goto End;
|
||||
|
||||
ok = 1;
|
||||
End:
|
||||
End:
|
||||
free((void*)rgb);
|
||||
|
||||
(void)metadata;
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#ifndef WEBP_IMAGEIO_PNMDEC_H_
|
||||
#define WEBP_IMAGEIO_PNMDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -33,7 +31,7 @@ int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_PNMDEC_H_
|
||||
|
||||
@@ -22,18 +22,17 @@
|
||||
#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;
|
||||
size_t storage_offset;
|
||||
} kTIFFMetadataMap[] = {
|
||||
{TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp)},
|
||||
{TIFFTAG_XMLPACKET, METADATA_OFFSET(xmp)},
|
||||
{0, 0},
|
||||
{ TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp) },
|
||||
{ TIFFTAG_XMLPACKET, METADATA_OFFSET(xmp) },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
// Returns true on success. The caller must use MetadataFree() on 'metadata' in
|
||||
@@ -86,9 +85,9 @@ static toff_t MySize(thandle_t opaque) {
|
||||
|
||||
static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) {
|
||||
MyData* const my_data = (MyData*)opaque;
|
||||
offset += (whence == SEEK_CUR) ? my_data->pos
|
||||
: (whence == SEEK_SET) ? 0
|
||||
: my_data->size;
|
||||
offset += (whence == SEEK_CUR) ? my_data->pos
|
||||
: (whence == SEEK_SET) ? 0
|
||||
: my_data->size;
|
||||
if (offset > my_data->size) return (toff_t)-1;
|
||||
my_data->pos = offset;
|
||||
return offset;
|
||||
@@ -120,7 +119,7 @@ static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
|
||||
|
||||
// Unmultiply Argb data. Taken from dsp/alpha_processing
|
||||
// (we don't want to force a dependency to a libdspdec library).
|
||||
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||
#define HALF ((1u << MFIX) >> 1)
|
||||
|
||||
static uint32_t Unmult(uint8_t x, uint32_t mult) {
|
||||
@@ -128,7 +127,9 @@ static uint32_t Unmult(uint8_t x, uint32_t mult) {
|
||||
return (v > 255u) ? 255u : v;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t GetScale(uint32_t a) { return (255u << MFIX) / a; }
|
||||
static WEBP_INLINE uint32_t GetScale(uint32_t a) {
|
||||
return (255u << MFIX) / a;
|
||||
}
|
||||
|
||||
#undef MFIX
|
||||
#undef HALF
|
||||
@@ -138,7 +139,7 @@ static void MultARGBRow(uint8_t* ptr, int width) {
|
||||
for (x = 0; x < width; ++x, ptr += 4) {
|
||||
const uint32_t alpha = ptr[3];
|
||||
if (alpha < 255) {
|
||||
if (alpha == 0) { // alpha == 0
|
||||
if (alpha == 0) { // alpha == 0
|
||||
ptr[0] = ptr[1] = ptr[2] = 0;
|
||||
} else {
|
||||
const uint32_t scale = GetScale(alpha);
|
||||
@@ -151,8 +152,9 @@ static void MultARGBRow(uint8_t* ptr, int width) {
|
||||
}
|
||||
|
||||
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha, Metadata* const metadata) {
|
||||
MyData my_data = {data, (toff_t)data_size, 0};
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
MyData my_data = { data, (toff_t)data_size, 0 };
|
||||
TIFF* tif;
|
||||
uint32_t image_width, image_height, tile_width, tile_height;
|
||||
uint64_t stride;
|
||||
@@ -168,7 +170,8 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
tif = TIFFClientOpen("Memory", "r", &my_data, MyRead, MyRead, MySeek, MyClose,
|
||||
tif = TIFFClientOpen("Memory", "r", &my_data,
|
||||
MyRead, MyRead, MySeek, MyClose,
|
||||
MySize, MyMapFile, MyUnmapFile);
|
||||
if (tif == NULL) {
|
||||
fprintf(stderr, "Error! Cannot parse TIFF file\n");
|
||||
@@ -177,10 +180,9 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
|
||||
dircount = TIFFNumberOfDirectories(tif);
|
||||
if (dircount > 1) {
|
||||
fprintf(stderr,
|
||||
"Warning: multi-directory TIFF files are not supported.\n"
|
||||
"Only the first will be used, %d will be ignored.\n",
|
||||
dircount - 1);
|
||||
fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
|
||||
"Only the first will be used, %d will be ignored.\n",
|
||||
dircount - 1);
|
||||
}
|
||||
if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
|
||||
fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
|
||||
@@ -250,10 +252,9 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
tmp += stride;
|
||||
}
|
||||
}
|
||||
ok =
|
||||
keep_alpha
|
||||
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride)
|
||||
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride);
|
||||
ok = keep_alpha
|
||||
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride)
|
||||
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride);
|
||||
}
|
||||
_TIFFfree(raster);
|
||||
} else {
|
||||
@@ -270,11 +271,11 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
End:
|
||||
End:
|
||||
TIFFClose(tif);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_TIFF
|
||||
#else // !WEBP_HAVE_TIFF
|
||||
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
@@ -283,8 +284,7 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr,
|
||||
"TIFF support not compiled. Please install the libtiff "
|
||||
fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#ifndef WEBP_IMAGEIO_TIFFDEC_H_
|
||||
#define WEBP_IMAGEIO_TIFFDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -33,7 +31,7 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_TIFFDEC_H_
|
||||
|
||||
@@ -13,32 +13,26 @@
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "./webpdec.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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"
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebP decoding
|
||||
|
||||
static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
|
||||
"OK",
|
||||
"OUT_OF_MEMORY",
|
||||
"INVALID_PARAM",
|
||||
"BITSTREAM_ERROR",
|
||||
"UNSUPPORTED_FEATURE",
|
||||
"SUSPENDED",
|
||||
"USER_ABORT",
|
||||
"NOT_ENOUGH_DATA"};
|
||||
"OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
|
||||
"UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
|
||||
};
|
||||
|
||||
static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
|
||||
if (config->input.has_animation) {
|
||||
@@ -58,7 +52,8 @@ void PrintWebPError(const char* const in_file, int status) {
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
int LoadWebP(const char* const in_file, const uint8_t** data, size_t* data_size,
|
||||
int LoadWebP(const char* const in_file,
|
||||
const uint8_t** data, size_t* data_size,
|
||||
WebPBitstreamFeatures* bitstream) {
|
||||
VP8StatusCode status;
|
||||
WebPBitstreamFeatures local_features;
|
||||
@@ -88,8 +83,9 @@ VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
|
||||
return WebPDecode(data, data_size, config);
|
||||
}
|
||||
|
||||
VP8StatusCode DecodeWebPIncremental(const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config) {
|
||||
VP8StatusCode DecodeWebPIncremental(
|
||||
const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config) {
|
||||
VP8StatusCode status = VP8_STATUS_OK;
|
||||
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
|
||||
|
||||
@@ -114,7 +110,7 @@ VP8StatusCode DecodeWebPIncremental(const uint8_t* const data, size_t data_size,
|
||||
|
||||
static int ExtractMetadata(const uint8_t* const data, size_t data_size,
|
||||
Metadata* const metadata) {
|
||||
WebPData webp_data = {data, data_size};
|
||||
WebPData webp_data = { data, data_size };
|
||||
WebPDemuxer* const demux = WebPDemux(&webp_data);
|
||||
WebPChunkIterator chunk_iter;
|
||||
uint32_t flags;
|
||||
@@ -146,7 +142,8 @@ static int ExtractMetadata(const uint8_t* const data, size_t data_size,
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha, Metadata* const metadata) {
|
||||
WebPPicture* const pic,
|
||||
int keep_alpha, Metadata* const metadata) {
|
||||
int ok = 0;
|
||||
VP8StatusCode status = VP8_STATUS_OK;
|
||||
WebPDecoderConfig config;
|
||||
@@ -196,8 +193,7 @@ int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
#endif
|
||||
output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb;
|
||||
output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t);
|
||||
output_buffer->u.RGBA.size =
|
||||
(size_t)output_buffer->u.RGBA.stride * pic->height;
|
||||
output_buffer->u.RGBA.size = output_buffer->u.RGBA.stride * pic->height;
|
||||
} else {
|
||||
output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
|
||||
output_buffer->u.YUVA.y = pic->y;
|
||||
@@ -208,12 +204,10 @@ int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
output_buffer->u.YUVA.u_stride = pic->uv_stride;
|
||||
output_buffer->u.YUVA.v_stride = pic->uv_stride;
|
||||
output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0;
|
||||
output_buffer->u.YUVA.y_size = (size_t)pic->height * pic->y_stride;
|
||||
output_buffer->u.YUVA.u_size =
|
||||
(size_t)(pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.v_size =
|
||||
(size_t)(pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.a_size = (size_t)pic->height * pic->a_stride;
|
||||
output_buffer->u.YUVA.y_size = pic->height * pic->y_stride;
|
||||
output_buffer->u.YUVA.u_size = (pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.v_size = (pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.a_size = pic->height * pic->a_stride;
|
||||
}
|
||||
output_buffer->is_external_memory = 1;
|
||||
|
||||
@@ -228,7 +222,7 @@ int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
argb += pic->argb_stride;
|
||||
}
|
||||
}
|
||||
} while (0); // <- so we can 'break' out of the loop
|
||||
} while (0); // <- so we can 'break' out of the loop
|
||||
|
||||
if (status != VP8_STATUS_OK) {
|
||||
PrintWebPError("input data", status);
|
||||
|
||||
@@ -12,10 +12,7 @@
|
||||
#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" {
|
||||
@@ -35,7 +32,8 @@ void PrintWebPError(const char* const in_file, int status);
|
||||
// Reads a WebP from 'in_file', returning the contents and size in 'data' and
|
||||
// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures().
|
||||
// Returns true on success.
|
||||
int LoadWebP(const char* const in_file, const uint8_t** data, size_t* data_size,
|
||||
int LoadWebP(const char* const in_file,
|
||||
const uint8_t** data, size_t* data_size,
|
||||
WebPBitstreamFeatures* bitstream);
|
||||
|
||||
// Decodes the WebP contained in 'data'.
|
||||
@@ -47,8 +45,9 @@ VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config);
|
||||
|
||||
// Same as DecodeWebP(), but using the incremental decoder.
|
||||
VP8StatusCode DecodeWebPIncremental(const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config);
|
||||
VP8StatusCode DecodeWebPIncremental(
|
||||
const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -58,11 +57,11 @@ VP8StatusCode DecodeWebPIncremental(const uint8_t* const data, size_t data_size,
|
||||
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||
// Returns true on success.
|
||||
int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_WEBPDEC_H_
|
||||
|
||||
132
imageio/wicdec.c
132
imageio/wicdec.c
@@ -25,31 +25,31 @@
|
||||
#endif
|
||||
#define CINTERFACE
|
||||
#define COBJMACROS
|
||||
#define _WIN32_IE \
|
||||
0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||
#include <shlwapi.h>
|
||||
#include <tchar.h>
|
||||
#include <wincodec.h>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// modified version of DEFINE_GUID from guiddef.h.
|
||||
#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
static const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
|
||||
static const GUID name = \
|
||||
{ l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define MAKE_REFGUID(x) (x)
|
||||
@@ -66,17 +66,23 @@ typedef struct WICFormatImporter {
|
||||
// From Microsoft SDK 7.0a -- wincodec.h
|
||||
// Create local copies for compatibility when building against earlier
|
||||
// versions of the SDK.
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_, 0x6fddc324, 0x4e03, 0x4bfe, 0xb1,
|
||||
0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_, 0x6fddc324, 0x4e03, 0x4bfe, 0xb1,
|
||||
0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_, 0x6fddc324, 0x4e03, 0x4bfe,
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_, 0xf5c7ad2d, 0x6a8d, 0x43dd,
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
|
||||
0xf5c7ad2d, 0x6a8d, 0x43dd,
|
||||
0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_, 0x1562ff7c, 0xd352, 0x46f9,
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
|
||||
0x1562ff7c, 0xd352, 0x46f9,
|
||||
0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_, 0x6fddc324, 0x4e03, 0x4bfe,
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
|
||||
|
||||
static HRESULT OpenInputStream(const char* filename, IStream** stream) {
|
||||
@@ -141,7 +147,8 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
UINT num_color_contexts;
|
||||
IFS(IWICBitmapFrameDecode_GetColorContexts(frame, count, color_contexts,
|
||||
IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
|
||||
count, color_contexts,
|
||||
&num_color_contexts));
|
||||
assert(FAILED(hr) || num_color_contexts <= count);
|
||||
for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
|
||||
@@ -149,8 +156,8 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
||||
IFS(IWICColorContext_GetType(color_contexts[i], &type));
|
||||
if (SUCCEEDED(hr) && type == WICColorContextProfile) {
|
||||
UINT size;
|
||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i], 0, NULL,
|
||||
&size));
|
||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
||||
0, NULL, &size));
|
||||
if (SUCCEEDED(hr) && size > 0) {
|
||||
iccp->bytes = (uint8_t*)malloc(size);
|
||||
if (iccp->bytes == NULL) {
|
||||
@@ -158,8 +165,9 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
||||
break;
|
||||
}
|
||||
iccp->size = size;
|
||||
IFS(IWICColorContext_GetProfileBytes(
|
||||
color_contexts[i], (UINT)iccp->size, iccp->bytes, &size));
|
||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
||||
(UINT)iccp->size, iccp->bytes,
|
||||
&size));
|
||||
if (SUCCEEDED(hr) && size != iccp->size) {
|
||||
fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n",
|
||||
size, (uint32_t)iccp->size);
|
||||
@@ -201,7 +209,8 @@ static int HasPalette(GUID pixel_format) {
|
||||
|
||||
static int HasAlpha(IWICImagingFactory* const factory,
|
||||
IWICBitmapDecoder* const decoder,
|
||||
IWICBitmapFrameDecode* const frame, GUID pixel_format) {
|
||||
IWICBitmapFrameDecode* const frame,
|
||||
GUID pixel_format) {
|
||||
int has_alpha;
|
||||
if (HasPalette(pixel_format)) {
|
||||
IWICPalette* frame_palette = NULL;
|
||||
@@ -236,20 +245,21 @@ static int HasAlpha(IWICImagingFactory* const factory,
|
||||
return has_alpha;
|
||||
}
|
||||
|
||||
int ReadPictureWithWIC(const char* const filename, WebPPicture* const pic,
|
||||
int keep_alpha, Metadata* const metadata) {
|
||||
int ReadPictureWithWIC(const char* const filename,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
// From Microsoft SDK 6.0a -- ks.h
|
||||
// Define a local copy to avoid link errors under mingw.
|
||||
WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
static const WICFormatImporter kAlphaFormatImporters[] = {
|
||||
{&GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA},
|
||||
{&GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA},
|
||||
{NULL, 0, NULL},
|
||||
{ &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
|
||||
{ &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
|
||||
{ NULL, 0, NULL },
|
||||
};
|
||||
static const WICFormatImporter kNonAlphaFormatImporters[] = {
|
||||
{&GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR},
|
||||
{&GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB},
|
||||
{NULL, 0, NULL},
|
||||
{ &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
|
||||
{ &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
|
||||
{ NULL, 0, NULL },
|
||||
};
|
||||
HRESULT hr = S_OK;
|
||||
IWICBitmapFrameDecode* frame = NULL;
|
||||
@@ -264,20 +274,26 @@ int ReadPictureWithWIC(const char* const filename, WebPPicture* const pic,
|
||||
const WICFormatImporter* importer = NULL;
|
||||
GUID src_container_format = GUID_NULL_;
|
||||
// From Windows Kits\10\Include\10.0.19041.0\um\wincodec.h
|
||||
WEBP_DEFINE_GUID(GUID_ContainerFormatWebp_, 0xe094b0e2, 0x67f2, 0x45b3, 0xb0,
|
||||
0xea, 0x11, 0x53, 0x37, 0xca, 0x7c, 0xf3);
|
||||
WEBP_DEFINE_GUID(GUID_ContainerFormatWebp_,
|
||||
0xe094b0e2, 0x67f2, 0x45b3,
|
||||
0xb0, 0xea, 0x11, 0x53, 0x37, 0xca, 0x7c, 0xf3);
|
||||
static const GUID* kAlphaContainers[] = {
|
||||
&GUID_ContainerFormatBmp, &GUID_ContainerFormatPng,
|
||||
&GUID_ContainerFormatTiff, &GUID_ContainerFormatWebp_, NULL};
|
||||
&GUID_ContainerFormatBmp,
|
||||
&GUID_ContainerFormatPng,
|
||||
&GUID_ContainerFormatTiff,
|
||||
&GUID_ContainerFormatWebp_,
|
||||
NULL
|
||||
};
|
||||
int has_alpha = 0;
|
||||
int64_t stride;
|
||||
|
||||
if (filename == NULL || pic == NULL) return 0;
|
||||
|
||||
IFS(CoInitialize(NULL));
|
||||
IFS(CoCreateInstance(
|
||||
MAKE_REFGUID(CLSID_WICImagingFactory), NULL, CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory), (LPVOID*)&factory));
|
||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory),
|
||||
(LPVOID*)&factory));
|
||||
if (hr == REGDB_E_CLASSNOTREG) {
|
||||
fprintf(stderr,
|
||||
"Couldn't access Windows Imaging Component (are you running "
|
||||
@@ -287,7 +303,8 @@ int ReadPictureWithWIC(const char* const filename, WebPPicture* const pic,
|
||||
// Prepare for image decoding.
|
||||
IFS(OpenInputStream(filename, &stream));
|
||||
IFS(IWICImagingFactory_CreateDecoderFromStream(
|
||||
factory, stream, NULL, WICDecodeMetadataCacheOnDemand, &decoder));
|
||||
factory, stream, NULL,
|
||||
WICDecodeMetadataCacheOnDemand, &decoder));
|
||||
IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count));
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (frame_count == 0) {
|
||||
@@ -321,15 +338,18 @@ int ReadPictureWithWIC(const char* const filename, WebPPicture* const pic,
|
||||
hr == S_OK && importer->import != NULL; ++importer) {
|
||||
BOOL can_convert;
|
||||
const HRESULT cchr = IWICFormatConverter_CanConvert(
|
||||
converter, MAKE_REFGUID(src_pixel_format),
|
||||
MAKE_REFGUID(*importer->pixel_format), &can_convert);
|
||||
converter,
|
||||
MAKE_REFGUID(src_pixel_format),
|
||||
MAKE_REFGUID(*importer->pixel_format),
|
||||
&can_convert);
|
||||
if (SUCCEEDED(cchr) && can_convert) break;
|
||||
}
|
||||
if (importer->import == NULL) hr = E_FAIL;
|
||||
|
||||
IFS(IWICFormatConverter_Initialize(
|
||||
converter, (IWICBitmapSource*)frame, importer->pixel_format,
|
||||
WICBitmapDitherTypeNone, NULL, 0.0, WICBitmapPaletteTypeCustom));
|
||||
IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
|
||||
importer->pixel_format,
|
||||
WICBitmapDitherTypeNone,
|
||||
NULL, 0.0, WICBitmapPaletteTypeCustom));
|
||||
|
||||
// Decode.
|
||||
IFS(IWICFormatConverter_GetSize(converter, &width, &height));
|
||||
@@ -341,17 +361,18 @@ int ReadPictureWithWIC(const char* const filename, WebPPicture* const pic,
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
rgb = (BYTE*)malloc((size_t)stride * height);
|
||||
if (rgb == NULL) hr = E_OUTOFMEMORY;
|
||||
if (rgb == NULL)
|
||||
hr = E_OUTOFMEMORY;
|
||||
}
|
||||
IFS(IWICFormatConverter_CopyPixels(converter, NULL, (UINT)stride,
|
||||
(UINT)stride * height, rgb));
|
||||
IFS(IWICFormatConverter_CopyPixels(converter, NULL,
|
||||
(UINT)stride, (UINT)stride * height, rgb));
|
||||
|
||||
// WebP conversion.
|
||||
if (SUCCEEDED(hr)) {
|
||||
int ok;
|
||||
pic->width = width;
|
||||
pic->height = height;
|
||||
pic->use_argb = 1; // For WIC, we always force to argb
|
||||
pic->use_argb = 1; // For WIC, we always force to argb
|
||||
ok = importer->import(pic, rgb, (int)stride);
|
||||
if (!ok) hr = E_FAIL;
|
||||
}
|
||||
@@ -373,7 +394,7 @@ int ReadPictureWithWIC(const char* const filename, WebPPicture* const pic,
|
||||
free(rgb);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
#else // !HAVE_WINCODEC_H
|
||||
#else // !HAVE_WINCODEC_H
|
||||
int ReadPictureWithWIC(const char* const filename,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
@@ -381,11 +402,10 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr,
|
||||
"Windows Imaging Component (WIC) support not compiled. "
|
||||
"Visual Studio and mingw-w64 builds support WIC. Make sure "
|
||||
"wincodec.h detection is working correctly if using autoconf "
|
||||
"and HAVE_WINCODEC_H is defined before building.\n");
|
||||
fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. "
|
||||
"Visual Studio and mingw-w64 builds support WIC. Make sure "
|
||||
"wincodec.h detection is working correctly if using autoconf "
|
||||
"and HAVE_WINCODEC_H is defined before building.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif // HAVE_WINCODEC_H
|
||||
|
||||
@@ -28,7 +28,7 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_WICDEC_H_
|
||||
|
||||
26
iosbuild.sh
26
iosbuild.sh
@@ -31,8 +31,8 @@ readonly OLDPATH=${PATH}
|
||||
|
||||
# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
|
||||
# Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
|
||||
PLATFORMS="iPhoneSimulator64"
|
||||
PLATFORMS+=" iPhoneOS-arm64"
|
||||
PLATFORMS="iPhoneSimulator iPhoneSimulator64"
|
||||
PLATFORMS+=" iPhoneOS-V7 iPhoneOS-V7s iPhoneOS-V7-arm64"
|
||||
readonly PLATFORMS
|
||||
readonly SRCDIR=$(dirname $0)
|
||||
readonly TOPDIR=$(pwd)
|
||||
@@ -41,7 +41,6 @@ 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)
|
||||
@@ -53,7 +52,7 @@ DEMUXLIBLIST=''
|
||||
if [[ -z "${SDK}" ]]; then
|
||||
echo "iOS SDK not available"
|
||||
exit 1
|
||||
elif [[ ${SDK%%.*} -gt 8 && "${XCODE%%.*}" -lt 16 ]]; then
|
||||
elif [[ ${SDK%%.*} -gt 8 ]]; then
|
||||
EXTRA_CFLAGS="-fembed-bitcode"
|
||||
elif [[ ${SDK%%.*} -le 6 ]]; then
|
||||
echo "You need iOS SDK version 6.0 or above"
|
||||
@@ -64,8 +63,7 @@ echo "Xcode Version: ${XCODE}"
|
||||
echo "iOS SDK Version: ${SDK}"
|
||||
|
||||
if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \
|
||||
|| -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" \
|
||||
|| -e "${SHARPYUVTARGETDIR}" ]]; then
|
||||
|| -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" ]]; then
|
||||
cat << EOF
|
||||
WARNING: The following directories will be deleted:
|
||||
WARNING: ${BUILDDIR}
|
||||
@@ -73,16 +71,14 @@ 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} ${SHARPYUVTARGETDIR}
|
||||
${MUXTARGETDIR} ${DEMUXTARGETDIR}
|
||||
mkdir -p ${BUILDDIR} ${TARGETDIR}/Headers/ ${DECTARGETDIR}/Headers/ \
|
||||
${MUXTARGETDIR}/Headers/ ${DEMUXTARGETDIR}/Headers/ \
|
||||
${SHARPYUVTARGETDIR}/Headers/
|
||||
${MUXTARGETDIR}/Headers/ ${DEMUXTARGETDIR}/Headers/
|
||||
|
||||
if [[ ! -e ${SRCDIR}/configure ]]; then
|
||||
if ! (cd ${SRCDIR} && sh autogen.sh); then
|
||||
@@ -98,7 +94,7 @@ fi
|
||||
|
||||
for PLATFORM in ${PLATFORMS}; do
|
||||
ARCH2=""
|
||||
if [[ "${PLATFORM}" == "iPhoneOS-arm64" ]]; then
|
||||
if [[ "${PLATFORM}" == "iPhoneOS-V7-arm64" ]]; then
|
||||
PLATFORM="iPhoneOS"
|
||||
ARCH="aarch64"
|
||||
ARCH2="arm64"
|
||||
@@ -138,14 +134,13 @@ for PLATFORM in ${PLATFORMS}; do
|
||||
set +x
|
||||
|
||||
# Build only the libraries, skip the examples.
|
||||
make V=0 -C sharpyuv install
|
||||
make V=0 -C sharpyuv
|
||||
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
|
||||
|
||||
@@ -170,9 +165,4 @@ 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"
|
||||
|
||||
@@ -37,13 +37,13 @@ else
|
||||
endif
|
||||
|
||||
# SDL flags: use sdl-config if it exists
|
||||
SDL_CONFIG = $(shell sdl2-config --version 2> /dev/null)
|
||||
SDL_CONFIG = $(shell sdl-config --version 2> /dev/null)
|
||||
ifneq ($(SDL_CONFIG),)
|
||||
SDL_LIBS = $(shell sdl2-config --libs)
|
||||
SDL_FLAGS = $(shell sdl2-config --cflags)
|
||||
SDL_LIBS = $(shell sdl-config --libs)
|
||||
SDL_FLAGS = $(shell sdl-config --cflags)
|
||||
else
|
||||
# use best-guess
|
||||
SDL_LIBS = -lSDL2
|
||||
SDL_LIBS = -lSDL
|
||||
SDL_FLAGS =
|
||||
endif
|
||||
|
||||
@@ -276,7 +276,6 @@ 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 \
|
||||
@@ -291,7 +290,6 @@ 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) \
|
||||
@@ -345,7 +343,6 @@ 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 \
|
||||
|
||||
43
man/cwebp.1
43
man/cwebp.1
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH CWEBP 1 "April 10, 2025"
|
||||
.TH CWEBP 1 "March 17, 2022"
|
||||
.SH NAME
|
||||
cwebp \- compress an image file to a WebP file
|
||||
.SH SYNOPSIS
|
||||
@@ -102,14 +102,6 @@ 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.
|
||||
.TP
|
||||
@@ -143,9 +135,7 @@ 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. If \fB\-pass\fP is
|
||||
specified, but neither \fB-size\fP nor \fB-psnr\fP are, a target PSNR of 40dB
|
||||
will be used.
|
||||
specified, a default value of '6' passes will be used.
|
||||
.TP
|
||||
.BI \-qrange " int int
|
||||
Specifies the permissible interval for the quality factor. This is particularly
|
||||
@@ -188,8 +178,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. Note that this process is
|
||||
slower than the default 'fast' RGB->YUV conversion.
|
||||
Use more accurate and sharper RGB->YUV conversion if needed. Note that this
|
||||
process is slower than the default 'fast' RGB->YUV conversion.
|
||||
.TP
|
||||
.BI \-sns " int
|
||||
Specify the amplitude of the spatial noise shaping. Spatial noise shaping
|
||||
@@ -212,8 +202,7 @@ 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 for a few bits per 16x16
|
||||
macroblock.
|
||||
For a very large image, 512k only leaves room to few bits per 16x16 macroblock.
|
||||
The absolute minimum is 4 bits per macroblock. Skip, segment, and mode
|
||||
information can use up almost all these 4 bits (although the case is unlikely),
|
||||
which is problematic for very large images. The partition_limit factor controls
|
||||
@@ -222,8 +211,7 @@ 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. Note the \fB-m\fP and \fB-q\fP options also
|
||||
influence the encoder's decisions and ability to hit this limit.
|
||||
See the \fB\-segments\fP option.
|
||||
|
||||
.SS LOGGING OPTIONS
|
||||
These options control the level of output:
|
||||
@@ -307,12 +295,12 @@ Note: each input format may not support all combinations.
|
||||
.B \-noasm
|
||||
Disable all assembly optimizations.
|
||||
|
||||
.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 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 EXAMPLES
|
||||
cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
|
||||
@@ -332,13 +320,6 @@ 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)
|
||||
|
||||
21
man/dwebp.1
21
man/dwebp.1
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH DWEBP 1 "July 18, 2024"
|
||||
.TH DWEBP 1 "November 17, 2021"
|
||||
.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 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 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 EXAMPLES
|
||||
dwebp picture.webp \-o output.png
|
||||
@@ -133,13 +133,6 @@ 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),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH GIF2WEBP 1 "November 4, 2024"
|
||||
.TH GIF2WEBP 1 "November 17, 2021"
|
||||
.SH NAME
|
||||
gif2webp \- Convert a GIF image to WebP
|
||||
.SH SYNOPSIS
|
||||
@@ -39,18 +39,6 @@ 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.
|
||||
@@ -138,12 +126,12 @@ Print extra information.
|
||||
.B \-quiet
|
||||
Do not print anything.
|
||||
|
||||
.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 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 EXAMPLES
|
||||
gif2webp picture.gif \-o picture.webp
|
||||
@@ -167,13 +155,6 @@ 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),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH IMG2WEBP 1 "November 26, 2024"
|
||||
.TH IMG2WEBP 1 "January 5, 2022"
|
||||
.SH NAME
|
||||
img2webp \- create animated WebP file from a sequence of input images.
|
||||
.SH SYNOPSIS
|
||||
.B img2webp
|
||||
[file_options] [[frame_options] frame_file]... [\-o webp_file]
|
||||
[file_options] [[frame_options] frame_file]...
|
||||
.br
|
||||
.B img2webp argument_file_name
|
||||
.br
|
||||
@@ -44,18 +44,6 @@ 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'.
|
||||
@@ -88,27 +76,18 @@ 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
|
||||
@@ -118,13 +97,6 @@ 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)
|
||||
|
||||
21
man/vwebp.1
21
man/vwebp.1
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH VWEBP 1 "July 18, 2024"
|
||||
.TH VWEBP 1 "November 17, 2021"
|
||||
.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 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 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 EXAMPLES
|
||||
vwebp picture.webp
|
||||
@@ -94,13 +94,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH WEBPINFO 1 "July 18, 2024"
|
||||
.TH WEBPINFO 1 "November 17, 2021"
|
||||
.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 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 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 EXAMPLES
|
||||
.br
|
||||
@@ -73,13 +73,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH WEBPMUX 1 "July 18, 2024"
|
||||
.TH WEBPMUX 1 "November 17, 2021"
|
||||
.SH NAME
|
||||
webpmux \- create animated WebP files from non\-animated WebP images, extract
|
||||
frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
|
||||
@@ -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 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 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 EXAMPLES
|
||||
.P
|
||||
@@ -262,13 +262,6 @@ 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),
|
||||
|
||||
@@ -33,7 +33,7 @@ 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_LDFLAGS = -no-undefined -version-info 0:0:0 -lm
|
||||
libsharpyuv_la_LIBADD =
|
||||
libsharpyuv_la_LIBADD += libsharpyuv_sse2.la
|
||||
libsharpyuv_la_LIBADD += libsharpyuv_neon.la
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,0,4,2
|
||||
PRODUCTVERSION 0,0,4,2
|
||||
FILEVERSION 0,0,2,0
|
||||
PRODUCTVERSION 0,0,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -24,12 +24,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libsharpyuv DLL"
|
||||
VALUE "FileVersion", "0.4.2"
|
||||
VALUE "FileVersion", "0.2.0"
|
||||
VALUE "InternalName", "libsharpyuv.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2025"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2022"
|
||||
VALUE "OriginalFilename", "libsharpyuv.dll"
|
||||
VALUE "ProductName", "SharpYuv Library"
|
||||
VALUE "ProductVersion", "0.4.2"
|
||||
VALUE "ProductVersion", "0.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -19,14 +19,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/webp/types.h"
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
#include "sharpyuv/sharpyuv_gamma.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int SharpYuvGetVersion(void) { return SHARPYUV_VERSION; }
|
||||
int SharpYuvGetVersion(void) {
|
||||
return SHARPYUV_VERSION;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Sharp RGB->YUV conversion
|
||||
@@ -47,8 +49,8 @@ static int GetPrecisionShift(int rgb_bit_depth) {
|
||||
: (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
|
||||
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
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -73,62 +75,57 @@ static int RGBToGray(int64_t r, int64_t g, int64_t b) {
|
||||
}
|
||||
|
||||
static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
|
||||
int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
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);
|
||||
int rgb_bit_depth) {
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
const uint32_t A = SharpYuvGammaToLinear(a, bit_depth);
|
||||
const uint32_t B = SharpYuvGammaToLinear(b, bit_depth);
|
||||
const uint32_t C = SharpYuvGammaToLinear(c, bit_depth);
|
||||
const uint32_t D = SharpYuvGammaToLinear(d, bit_depth);
|
||||
return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
|
||||
int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
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);
|
||||
int rgb_bit_depth) {
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const uint32_t R = SharpYuvGammaToLinear(src[0 * w + i], bit_depth);
|
||||
const uint32_t G = SharpYuvGammaToLinear(src[1 * w + i], bit_depth);
|
||||
const uint32_t B = SharpYuvGammaToLinear(src[2 * w + i], bit_depth);
|
||||
const uint32_t Y = RGBToGray(R, G, B);
|
||||
dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth, transfer_type);
|
||||
} while (++i < w);
|
||||
dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
|
||||
fixed_t* dst, int uv_w, int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
int i = 0;
|
||||
do {
|
||||
fixed_t* dst, int uv_w, int rgb_bit_depth) {
|
||||
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], bit_depth, transfer_type);
|
||||
src2[0 * uv_w + 1], rgb_bit_depth);
|
||||
const int g =
|
||||
ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0],
|
||||
src2[2 * uv_w + 1], bit_depth, transfer_type);
|
||||
src2[2 * uv_w + 1], rgb_bit_depth);
|
||||
const int b =
|
||||
ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0],
|
||||
src2[4 * uv_w + 1], bit_depth, transfer_type);
|
||||
src2[4 * uv_w + 1], rgb_bit_depth);
|
||||
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;
|
||||
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;
|
||||
int i;
|
||||
assert(w > 0);
|
||||
do {
|
||||
for (i = 0; i < w; ++i) {
|
||||
y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
|
||||
} while (++i < w);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -144,16 +141,19 @@ 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,
|
||||
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;
|
||||
int i;
|
||||
const int w = (pic_width + 1) & ~1;
|
||||
do {
|
||||
for (i = 0; i < pic_width; ++i) {
|
||||
const int off = i * step;
|
||||
const int shift = GetPrecisionShift(rgb_bit_depth);
|
||||
if (rgb_bit_depth == 8) {
|
||||
@@ -165,7 +165,7 @@ static void ImportOneRow(const uint8_t* const r_ptr, const uint8_t* const g_ptr,
|
||||
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];
|
||||
@@ -174,13 +174,18 @@ static void ImportOneRow(const uint8_t* const r_ptr, const uint8_t* const g_ptr,
|
||||
}
|
||||
|
||||
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 bit_depth) {
|
||||
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
|
||||
const int len = (w - 1) >> 1; // length to filter
|
||||
int k = 3;
|
||||
while (k-- > 0) { // process each R/G/B segments in turn
|
||||
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);
|
||||
@@ -200,7 +205,7 @@ static void InterpolateTwoRows(const fixed_y_t* const best_y,
|
||||
out1 += w;
|
||||
out2 += w;
|
||||
prev_uv += uv_w;
|
||||
cur_uv += uv_w;
|
||||
cur_uv += uv_w;
|
||||
next_uv += uv_w;
|
||||
}
|
||||
}
|
||||
@@ -208,16 +213,16 @@ static void InterpolateTwoRows(const fixed_y_t* const best_y,
|
||||
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;
|
||||
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,
|
||||
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;
|
||||
@@ -228,11 +233,8 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
||||
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 {
|
||||
for (best_uv = best_uv_base, j = 0; j < height; ++j) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
const int off = (i >> 1);
|
||||
const int W = best_y[i];
|
||||
const int r = best_uv[off + 0 * uv_w] + W;
|
||||
@@ -244,22 +246,19 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
||||
} 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 {
|
||||
}
|
||||
for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
|
||||
for (i = 0; i < uv_w; ++i) {
|
||||
const int off = i;
|
||||
// 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 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];
|
||||
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) {
|
||||
@@ -269,11 +268,11 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
||||
((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;
|
||||
}
|
||||
|
||||
@@ -286,59 +285,48 @@ static void* SafeMalloc(uint64_t nmemb, size_t size) {
|
||||
return malloc((size_t)total_size);
|
||||
}
|
||||
|
||||
#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((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) {
|
||||
const SharpYuvConversionMatrix* yuv_matrix) {
|
||||
// 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;
|
||||
|
||||
const uint64_t tmp_buffer_size = (uint64_t)w * 3 * 2;
|
||||
const uint64_t best_y_base_size = (uint64_t)w * h;
|
||||
const uint64_t target_y_base_size = (uint64_t)w * h;
|
||||
const uint64_t best_rgb_y_size = (uint64_t)w * 2;
|
||||
const uint64_t best_uv_base_size = (uint64_t)uv_w * 3 * uv_h;
|
||||
const uint64_t target_uv_base_size = (uint64_t)uv_w * 3 * uv_h;
|
||||
const uint64_t best_rgb_uv_size = (uint64_t)uv_w * 3;
|
||||
fixed_y_t* const tmp_buffer = (fixed_y_t*)SafeMalloc(
|
||||
(tmp_buffer_size + best_y_base_size + target_y_base_size +
|
||||
best_rgb_y_size) +
|
||||
(best_uv_base_size + target_uv_base_size + best_rgb_uv_size),
|
||||
sizeof(*tmp_buffer));
|
||||
fixed_y_t *best_y_base, *target_y_base, *best_rgb_y;
|
||||
fixed_t *best_uv_base, *target_uv_base, *best_rgb_uv;
|
||||
fixed_y_t *best_y, *target_y;
|
||||
fixed_t *best_uv, *target_uv;
|
||||
// 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);
|
||||
assert(sizeof(fixed_y_t) == sizeof(fixed_t));
|
||||
|
||||
if (tmp_buffer == NULL) {
|
||||
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;
|
||||
}
|
||||
best_y_base = tmp_buffer + tmp_buffer_size;
|
||||
target_y_base = best_y_base + best_y_base_size;
|
||||
best_rgb_y = target_y_base + target_y_base_size;
|
||||
best_uv_base = (fixed_t*)(best_rgb_y + best_rgb_y_size);
|
||||
target_uv_base = best_uv_base + best_uv_base_size;
|
||||
best_rgb_uv = target_uv_base + target_uv_base_size;
|
||||
best_y = best_y_base;
|
||||
target_y = target_y_base;
|
||||
best_uv = best_uv_base;
|
||||
target_uv = target_uv_base;
|
||||
|
||||
// Import RGB samples to W/RGB representation.
|
||||
for (j = 0; j < height; j += 2) {
|
||||
@@ -347,7 +335,8 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
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);
|
||||
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);
|
||||
@@ -357,9 +346,9 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
StoreGray(src1, best_y + 0, w);
|
||||
StoreGray(src2, best_y + w, w);
|
||||
|
||||
UpdateW(src1, target_y, w, y_bit_depth, transfer_type);
|
||||
UpdateW(src2, target_y + w, w, y_bit_depth, transfer_type);
|
||||
UpdateChroma(src1, src2, target_uv, uv_w, y_bit_depth, transfer_type);
|
||||
UpdateW(src1, target_y, w, rgb_bit_depth);
|
||||
UpdateW(src2, target_y + w, w, rgb_bit_depth);
|
||||
UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth);
|
||||
memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
|
||||
best_y += 2 * w;
|
||||
best_uv += 3 * uv_w;
|
||||
@@ -380,33 +369,32 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
best_uv = best_uv_base;
|
||||
target_y = target_y_base;
|
||||
target_uv = target_uv_base;
|
||||
j = 0;
|
||||
do {
|
||||
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,
|
||||
y_bit_depth);
|
||||
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, y_bit_depth, transfer_type);
|
||||
UpdateW(src2, best_rgb_y + 1 * w, w, y_bit_depth, transfer_type);
|
||||
UpdateChroma(src1, src2, best_rgb_uv, uv_w, y_bit_depth, transfer_type);
|
||||
UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth);
|
||||
UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth);
|
||||
UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth);
|
||||
|
||||
// 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);
|
||||
SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w,
|
||||
rgb_bit_depth + GetPrecisionShift(rgb_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;
|
||||
@@ -420,26 +408,31 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
u_stride, v_ptr, v_stride, rgb_bit_depth, yuv_bit_depth,
|
||||
width, height, yuv_matrix);
|
||||
|
||||
End:
|
||||
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)
|
||||
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)
|
||||
|
||||
@@ -447,7 +440,6 @@ End:
|
||||
// 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 =
|
||||
@@ -469,42 +461,12 @@ void SharpYuvInit(VP8CPUInfo cpu_info_func) {
|
||||
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 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);
|
||||
@@ -523,7 +485,7 @@ int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr,
|
||||
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)) {
|
||||
if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride %2 != 0)) {
|
||||
// Step/stride should be even for uint16_t buffers.
|
||||
return 0;
|
||||
}
|
||||
@@ -555,11 +517,10 @@ int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr,
|
||||
scaled_matrix.rgb_to_u[3] = Shift(yuv_matrix->rgb_to_u[3], sfix);
|
||||
scaled_matrix.rgb_to_v[3] = Shift(yuv_matrix->rgb_to_v[3], sfix);
|
||||
|
||||
return DoSharpArgbToYuv(
|
||||
(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);
|
||||
return DoSharpArgbToYuv(r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride,
|
||||
rgb_bit_depth, y_ptr, y_stride, u_ptr, u_stride,
|
||||
v_ptr, v_stride, yuv_bit_depth, width, height,
|
||||
&scaled_matrix);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -22,37 +22,22 @@ extern "C" {
|
||||
#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
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
#define SHARPYUV_EXTERN extern __attribute__((visibility("default")))
|
||||
#else
|
||||
#if defined(_MSC_VER) && defined(WEBP_DLL)
|
||||
#define SHARPYUV_EXTERN __declspec(dllexport)
|
||||
#else
|
||||
#define SHARPYUV_EXTERN extern
|
||||
#endif /* defined(_WIN32) && defined(WEBP_DLL) */
|
||||
#endif /* _MSC_VER && WEBP_DLL */
|
||||
#endif /* __GNUC__ >= 4 */
|
||||
#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
|
||||
#define SHARPYUV_VERSION_MINOR 2
|
||||
#define SHARPYUV_VERSION_PATCH 0
|
||||
// Version as a uint32_t. The major number is the high 8 bits.
|
||||
// The minor number is the middle 8 bits. The patch number is the low 16 bits.
|
||||
#define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \
|
||||
@@ -66,50 +51,16 @@ extern "C" {
|
||||
SHARPYUV_EXTERN int SharpYuvGetVersion(void);
|
||||
|
||||
// RGB to YUV conversion matrix, in 16 bit fixed point.
|
||||
// y_ = rgb_to_y[0] * r + rgb_to_y[1] * g + rgb_to_y[2] * b + rgb_to_y[3]
|
||||
// u_ = rgb_to_u[0] * r + rgb_to_u[1] * g + rgb_to_u[2] * b + rgb_to_u[3]
|
||||
// v_ = rgb_to_v[0] * r + rgb_to_v[1] * g + rgb_to_v[2] * b + rgb_to_v[3]
|
||||
// Then 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.
|
||||
// y = rgb_to_y[0] * r + rgb_to_y[1] * g + rgb_to_y[2] * b + rgb_to_y[3]
|
||||
// u = rgb_to_u[0] * r + rgb_to_u[1] * g + rgb_to_u[2] * b + rgb_to_u[3]
|
||||
// v = rgb_to_v[0] * r + rgb_to_v[1] * g + rgb_to_v[2] * b + rgb_to_v[3]
|
||||
// Then y, u and v values are divided by 1<<16 and rounded.
|
||||
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).
|
||||
@@ -134,10 +85,6 @@ typedef enum SharpYuvTransferFunctionType {
|
||||
// adjacent pixels on the y, u and v channels. If yuv_bit_depth > 8, they
|
||||
// should be multiples of 2.
|
||||
// width, height: width and height of the image in pixels
|
||||
// yuv_matrix: RGB to YUV conversion matrix. The matrix values typically
|
||||
// depend on the input's rgb_bit_depth.
|
||||
// This function calls SharpYuvConvertWithOptions with a default transfer
|
||||
// function of kSharpYuvTransferFunctionSrgb.
|
||||
SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
||||
const void* b_ptr, int rgb_step,
|
||||
int rgb_stride, int rgb_bit_depth,
|
||||
@@ -146,31 +93,6 @@ SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
||||
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).
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#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,
|
||||
@@ -24,16 +22,16 @@ void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space,
|
||||
const float kr = yuv_color_space->kr;
|
||||
const float kb = yuv_color_space->kb;
|
||||
const float kg = 1.0f - kr - kb;
|
||||
const float cb = 0.5f / (1.0f - kb);
|
||||
const float cr = 0.5f / (1.0f - kr);
|
||||
const float cr = 0.5f / (1.0f - kb);
|
||||
const float cb = 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 scale_u = cr;
|
||||
float scale_v = cb;
|
||||
float add_uv = (float)(128 << shift);
|
||||
assert(yuv_color_space->bit_depth >= 8);
|
||||
|
||||
@@ -61,39 +59,35 @@ void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space,
|
||||
}
|
||||
|
||||
// Matrices are in YUV_FIX fixed point precision.
|
||||
// WebP's matrix, similar but not identical to kRec601LimitedMatrix
|
||||
// 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
|
||||
// WebP's matrix, similar but not identical to kRec601LimitedMatrix.
|
||||
static const SharpYuvConversionMatrix kWebpMatrix = {
|
||||
{16839, 33059, 6420, 16 << 16},
|
||||
{-9719, -19081, 28800, 128 << 16},
|
||||
{28800, -24116, -4684, 128 << 16},
|
||||
{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
|
||||
// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeLimited
|
||||
static const SharpYuvConversionMatrix kRec601LimitedMatrix = {
|
||||
{16829, 33039, 6416, 16 << 16},
|
||||
{-9714, -19071, 28784, 128 << 16},
|
||||
{28784, -24103, -4681, 128 << 16},
|
||||
{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
|
||||
// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeFull
|
||||
static const SharpYuvConversionMatrix kRec601FullMatrix = {
|
||||
{19595, 38470, 7471, 0},
|
||||
{-11058, -21710, 32768, 128 << 16},
|
||||
{32768, -27439, -5329, 128 << 16},
|
||||
{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
|
||||
// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeLimited
|
||||
static const SharpYuvConversionMatrix kRec709LimitedMatrix = {
|
||||
{11966, 40254, 4064, 16 << 16},
|
||||
{-6596, -22189, 28784, 128 << 16},
|
||||
{28784, -26145, -2639, 128 << 16},
|
||||
{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
|
||||
// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeFull
|
||||
static const SharpYuvConversionMatrix kRec709FullMatrix = {
|
||||
{13933, 46871, 4732, 0},
|
||||
{-7509, -25259, 32768, 128 << 16},
|
||||
{32768, -29763, -3005, 128 << 16},
|
||||
{13933, 46871, 4732, 0},
|
||||
{-7509, -25259, 32768, 128 << 16},
|
||||
{32768, -29763, -3005, 128 << 16},
|
||||
};
|
||||
|
||||
const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix(
|
||||
|
||||
@@ -20,8 +20,8 @@ extern "C" {
|
||||
|
||||
// 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)
|
||||
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.
|
||||
@@ -41,15 +41,10 @@ SHARPYUV_EXTERN void SharpYuvComputeConversionMatrix(
|
||||
|
||||
// Enums for precomputed conversion matrices.
|
||||
typedef enum {
|
||||
// WebP's matrix, similar but not identical to kSharpYuvMatrixRec601Limited
|
||||
kSharpYuvMatrixWebp = 0,
|
||||
// Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeLimited
|
||||
kSharpYuvMatrixRec601Limited,
|
||||
// Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeFull
|
||||
kSharpYuvMatrixRec601Full,
|
||||
// Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeLimited
|
||||
kSharpYuvMatrixRec709Limited,
|
||||
// Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeFull
|
||||
kSharpYuvMatrixRec709Full,
|
||||
kSharpYuvMatrixNum
|
||||
} SharpYuvMatrixType;
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -71,9 +69,9 @@ uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref,
|
||||
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);
|
||||
const uint16_t* best_y, uint16_t* out,
|
||||
int bit_depth);
|
||||
|
||||
extern VP8CPUInfo SharpYuvGetCPUInfo;
|
||||
extern void InitSharpYuvSSE2(void);
|
||||
extern void InitSharpYuvNEON(void);
|
||||
|
||||
|
||||
@@ -12,10 +12,8 @@
|
||||
#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.
|
||||
@@ -67,7 +65,8 @@ void SharpYuvInitGammaTables(void) {
|
||||
} else {
|
||||
value = (1. + a) * pow(g, 1. / kGammaF) - a;
|
||||
}
|
||||
kLinearToGammaTabS[v] = (uint32_t)(final_scale * value + 0.5);
|
||||
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] =
|
||||
@@ -98,7 +97,7 @@ static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t ToLinearSrgb(uint16_t v, int bit_depth) {
|
||||
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth) {
|
||||
const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;
|
||||
if (shift > 0) {
|
||||
return kGammaToLinearTabS[v << shift];
|
||||
@@ -106,315 +105,9 @@ static uint32_t ToLinearSrgb(uint16_t v, int bit_depth) {
|
||||
return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);
|
||||
}
|
||||
|
||||
static uint16_t FromLinearSrgb(uint32_t value, int bit_depth) {
|
||||
uint16_t SharpYuvLinearToGamma(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));
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#ifndef WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
|
||||
#define WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
|
||||
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -23,13 +22,11 @@ extern "C" {
|
||||
// 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 gamma color value on 'bit_depth' bits to a 16 bit linear value.
|
||||
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth);
|
||||
|
||||
// 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);
|
||||
// Converts a 16 bit linear color value to a gamma value on 'bit_depth' bits.
|
||||
uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#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;
|
||||
@@ -35,11 +35,11 @@ static uint64_t SharpYuvUpdateY_NEON(const uint16_t* ref, const uint16_t* src,
|
||||
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 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)
|
||||
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)));
|
||||
}
|
||||
@@ -60,8 +60,8 @@ static void SharpYuvUpdateRGB_NEON(const int16_t* ref, const int16_t* src,
|
||||
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
|
||||
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) {
|
||||
|
||||
@@ -14,11 +14,8 @@
|
||||
#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"
|
||||
#include <emmintrin.h>
|
||||
|
||||
static uint16_t clip_SSE2(int v, int max) {
|
||||
return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
|
||||
@@ -44,7 +41,7 @@ static uint64_t SharpYuvUpdateY_SSE2(const uint16_t* ref, const uint16_t* src,
|
||||
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(...))
|
||||
const __m128i I = _mm_madd_epi16(D, G); // sum(abs(...))
|
||||
_mm_storeu_si128((__m128i*)(dst + i), H);
|
||||
sum = _mm_add_epi32(sum, I);
|
||||
}
|
||||
@@ -66,8 +63,8 @@ static void SharpYuvUpdateRGB_SSE2(const int16_t* ref, const int16_t* src,
|
||||
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
|
||||
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) {
|
||||
@@ -93,8 +90,8 @@ static void SharpYuvFilterRow16_SSE2(const int16_t* A, const int16_t* B,
|
||||
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 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);
|
||||
|
||||
@@ -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 9:0:2
|
||||
libwebp_la_LDFLAGS = -no-undefined -version-info 8:6:1
|
||||
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 5:0:2
|
||||
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 4:6:1
|
||||
pkgconfig_DATA += libwebpdecoder.pc
|
||||
endif
|
||||
|
||||
|
||||
@@ -11,28 +11,20 @@
|
||||
//
|
||||
// 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"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ALPHDecoder object.
|
||||
|
||||
// Allocates a new alpha decoder instance.
|
||||
WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) {
|
||||
static ALPHDecoder* ALPHNew(void) {
|
||||
ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||
return dec;
|
||||
}
|
||||
@@ -40,8 +32,8 @@ WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) {
|
||||
// Clears and deallocates an alpha decoder instance.
|
||||
static void ALPHDelete(ALPHDecoder* const dec) {
|
||||
if (dec != NULL) {
|
||||
VP8LDelete(dec->vp8l_dec);
|
||||
dec->vp8l_dec = NULL;
|
||||
VP8LDelete(dec->vp8l_dec_);
|
||||
dec->vp8l_dec_ = NULL;
|
||||
WebPSafeFree(dec);
|
||||
}
|
||||
}
|
||||
@@ -53,42 +45,41 @@ 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).
|
||||
WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
size_t data_size, const VP8Io* const src_io,
|
||||
uint8_t* output) {
|
||||
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 || rsrv != 0) {
|
||||
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
|
||||
if (!VP8InitIo(io)) {
|
||||
return 0;
|
||||
}
|
||||
VP8InitIo(io);
|
||||
WebPInitCustomIo(NULL, io);
|
||||
io->opaque = dec;
|
||||
io->width = src_io->width;
|
||||
@@ -101,17 +92,12 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
io->crop_bottom = src_io->crop_bottom;
|
||||
// No need to copy the scaling parameters.
|
||||
|
||||
if (dec->method == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = dec->width * dec->height;
|
||||
if (dec->method_ == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = dec->width_ * dec->height_;
|
||||
ok = (alpha_data_size >= alpha_decoded_size);
|
||||
} else {
|
||||
assert(dec->method == ALPHA_LOSSLESS_COMPRESSION);
|
||||
{
|
||||
const uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha_data =
|
||||
WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, alpha_data,
|
||||
alpha_data_size);
|
||||
ok = VP8LDecodeAlphaHeader(dec, bounded_alpha_data, alpha_data_size);
|
||||
}
|
||||
assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION);
|
||||
ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size);
|
||||
}
|
||||
|
||||
return ok;
|
||||
@@ -121,70 +107,76 @@ WEBP_NODISCARD 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.
|
||||
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) {
|
||||
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]);
|
||||
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;
|
||||
const uint8_t* prev_line = dec->alpha_prev_line_;
|
||||
const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width;
|
||||
uint8_t* dst = dec->alpha_plane_ + row * width;
|
||||
assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]);
|
||||
if (alph_dec->filter_ != WEBP_FILTER_NONE) {
|
||||
assert(WebPUnfilters[alph_dec->filter_] != NULL);
|
||||
for (y = 0; y < num_rows; ++y) {
|
||||
WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width);
|
||||
prev_line = dst;
|
||||
dst += width;
|
||||
deltas += width;
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < num_rows; ++y) {
|
||||
memcpy(dst, deltas, width * sizeof(*dst));
|
||||
prev_line = dst;
|
||||
dst += width;
|
||||
deltas += width;
|
||||
}
|
||||
}
|
||||
dec->alpha_prev_line = prev_line;
|
||||
} else { // alph_dec->method == ALPHA_LOSSLESS_COMPRESSION
|
||||
assert(alph_dec->vp8l_dec != NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
WEBP_NODISCARD static int AllocateAlphaPlane(VP8Decoder* const dec,
|
||||
const VP8Io* const io) {
|
||||
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 VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"Alpha decoder initialization failed.");
|
||||
assert(dec->alpha_plane_mem_ == NULL);
|
||||
dec->alpha_plane_mem_ =
|
||||
(uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_));
|
||||
if (dec->alpha_plane_mem_ == NULL) {
|
||||
return 0;
|
||||
}
|
||||
dec->alpha_plane = dec->alpha_plane_mem;
|
||||
dec->alpha_prev_line = NULL;
|
||||
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.
|
||||
|
||||
WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
const VP8Io* const io,
|
||||
int row, int num_rows) {
|
||||
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;
|
||||
|
||||
@@ -194,49 +186,37 @@ WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dec->is_alpha_decoded) {
|
||||
if (dec->alph_dec == NULL) { // Initialize decoder.
|
||||
dec->alph_dec = ALPHNew();
|
||||
if (dec->alph_dec == NULL) {
|
||||
VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"Alpha decoder initialization failed.");
|
||||
return NULL;
|
||||
}
|
||||
if (!dec->is_alpha_decoded_) {
|
||||
if (dec->alph_dec_ == NULL) { // Initialize decoder.
|
||||
dec->alph_dec_ = ALPHNew();
|
||||
if (dec->alph_dec_ == NULL) return NULL;
|
||||
if (!AllocateAlphaPlane(dec, io)) goto Error;
|
||||
if (!ALPHInit(dec->alph_dec, dec->alpha_data, dec->alpha_data_size, io,
|
||||
dec->alpha_plane)) {
|
||||
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.");
|
||||
if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_,
|
||||
io, dec->alpha_plane_)) {
|
||||
goto Error;
|
||||
}
|
||||
// if we allowed use of alpha dithering, check whether it's needed at all
|
||||
if (dec->alph_dec->pre_processing != ALPHA_PREPROCESSED_LEVELS) {
|
||||
dec->alpha_dithering = 0; // disable dithering
|
||||
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
|
||||
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 + io->crop_left;
|
||||
uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha =
|
||||
WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(
|
||||
uint8_t*, alpha,
|
||||
(size_t)width*(io->crop_bottom - io->crop_top));
|
||||
if (!WebPDequantizeLevels(bounded_alpha, io->crop_right - io->crop_left,
|
||||
io->crop_bottom - io->crop_top, width,
|
||||
dec->alpha_dithering)) {
|
||||
if (dec->is_alpha_decoded_) { // finished?
|
||||
ALPHDelete(dec->alph_dec_);
|
||||
dec->alph_dec_ = NULL;
|
||||
if (dec->alpha_dithering_ > 0) {
|
||||
uint8_t* const alpha = dec->alpha_plane_ + io->crop_top * width
|
||||
+ io->crop_left;
|
||||
if (!WebPDequantizeLevels(alpha,
|
||||
io->crop_right - io->crop_left,
|
||||
io->crop_bottom - io->crop_top,
|
||||
width, dec->alpha_dithering_)) {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
@@ -244,9 +224,9 @@ WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
}
|
||||
|
||||
// Return a pointer to the current decoded row.
|
||||
return dec->alpha_plane + row * width;
|
||||
return dec->alpha_plane_ + row * width;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
WebPDeallocateAlphaMemory(dec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -14,13 +14,8 @@
|
||||
#ifndef WEBP_DEC_ALPHAI_DEC_H_
|
||||
#define WEBP_DEC_ALPHAI_DEC_H_
|
||||
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/filters_utils.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -30,30 +25,30 @@ struct VP8LDecoder; // Defined in dec/vp8li.h.
|
||||
|
||||
typedef struct ALPHDecoder ALPHDecoder;
|
||||
struct ALPHDecoder {
|
||||
int width;
|
||||
int height;
|
||||
int method;
|
||||
WEBP_FILTER_TYPE filter;
|
||||
int pre_processing;
|
||||
struct VP8LDecoder* vp8l_dec;
|
||||
VP8Io io;
|
||||
int use_8b_decode; // Although alpha channel requires only 1 byte per
|
||||
// pixel, sometimes VP8LDecoder may need to allocate
|
||||
// 4 bytes per pixel internally during decode.
|
||||
uint8_t* output;
|
||||
const uint8_t* prev_line; // last output row (or NULL)
|
||||
int width_;
|
||||
int height_;
|
||||
int method_;
|
||||
WEBP_FILTER_TYPE filter_;
|
||||
int pre_processing_;
|
||||
struct VP8LDecoder* vp8l_dec_;
|
||||
VP8Io io_;
|
||||
int use_8b_decode_; // Although alpha channel requires only 1 byte per
|
||||
// pixel, sometimes VP8LDecoder may need to allocate
|
||||
// 4 bytes per pixel internally during decode.
|
||||
uint8_t* output_;
|
||||
const uint8_t* prev_line_; // last output row (or NULL)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
// Deallocate memory associated to dec->alpha_plane decoding
|
||||
// Deallocate memory associated to dec->alpha_plane_ decoding
|
||||
void WebPDeallocateAlphaMemory(VP8Decoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_DEC_ALPHAI_DEC_H_
|
||||
|
||||
@@ -11,37 +11,32 @@
|
||||
//
|
||||
// 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"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer
|
||||
|
||||
// Number of bytes per pixel for the different color-spaces.
|
||||
static const uint8_t kModeBpp[MODE_LAST] = {3, 4, 3, 4, 4, 2, 2, //
|
||||
4, 4, 4, 2, // pre-multiplied modes
|
||||
1, 1};
|
||||
static const uint8_t kModeBpp[MODE_LAST] = {
|
||||
3, 4, 3, 4, 4, 2, 2,
|
||||
4, 4, 4, 2, // pre-multiplied modes
|
||||
1, 1 };
|
||||
|
||||
// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
|
||||
// Convert to an integer to handle both the unsigned/signed enum cases
|
||||
// without the need for casting to remove type limit warnings.
|
||||
int IsValidColorspace(int webp_csp_mode) {
|
||||
static int IsValidColorspace(int webp_csp_mode) {
|
||||
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
|
||||
}
|
||||
|
||||
// strictly speaking, the very last (or first, if flipped) row
|
||||
// doesn't require padding.
|
||||
#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
|
||||
((uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH))
|
||||
#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
|
||||
((uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH))
|
||||
|
||||
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
int ok = 1;
|
||||
@@ -50,9 +45,9 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
const int height = buffer->height;
|
||||
if (!IsValidColorspace(mode)) {
|
||||
ok = 0;
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
const WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_height = (height + 1) / 2;
|
||||
const int y_stride = abs(buf->y_stride);
|
||||
const int u_stride = abs(buf->u_stride);
|
||||
@@ -76,11 +71,11 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
ok &= (a_size <= buf->a_size);
|
||||
ok &= (buf->a != NULL);
|
||||
}
|
||||
} else { // RGB checks
|
||||
} else { // RGB checks
|
||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
const int stride = abs(buf->stride);
|
||||
const uint64_t size =
|
||||
MIN_BUFFER_SIZE((uint64_t)width * kModeBpp[mode], height, stride);
|
||||
MIN_BUFFER_SIZE(width * kModeBpp[mode], height, stride);
|
||||
ok &= (size <= buf->size);
|
||||
ok &= (stride >= width * kModeBpp[mode]);
|
||||
ok &= (buf->rgba != NULL);
|
||||
@@ -128,7 +123,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
}
|
||||
buffer->private_memory = output;
|
||||
|
||||
if (!WebPIsRGBMode(mode)) { // YUVA initialization
|
||||
if (!WebPIsRGBMode(mode)) { // YUVA initialization
|
||||
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
buf->y = output;
|
||||
buf->y_stride = stride;
|
||||
@@ -186,14 +181,14 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
||||
if (buffer == NULL || width <= 0 || height <= 0) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
if (options != NULL) { // First, apply options if there is any.
|
||||
if (options != NULL) { // First, apply options if there is any.
|
||||
if (options->use_cropping) {
|
||||
const int cw = options->crop_width;
|
||||
const int ch = options->crop_height;
|
||||
const int x = options->crop_left & ~1;
|
||||
const int y = options->crop_top & ~1;
|
||||
if (!WebPCheckCropDimensions(width, height, x, y, cw, ch)) {
|
||||
return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
|
||||
return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
|
||||
}
|
||||
width = cw;
|
||||
height = ch;
|
||||
@@ -203,14 +198,14 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
int scaled_width = options->scaled_width;
|
||||
int scaled_height = options->scaled_height;
|
||||
if (!WebPRescalerGetScaledDimensions(width, height, &scaled_width,
|
||||
&scaled_height)) {
|
||||
if (!WebPRescalerGetScaledDimensions(
|
||||
width, height, &scaled_width, &scaled_height)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
width = scaled_width;
|
||||
height = scaled_height;
|
||||
#else
|
||||
return VP8_STATUS_INVALID_PARAM; // rescaling not supported
|
||||
return VP8_STATUS_INVALID_PARAM; // rescaling not supported
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -236,7 +231,7 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
|
||||
return 0; // version mismatch
|
||||
}
|
||||
if (buffer == NULL) return 0;
|
||||
WEBP_UNSAFE_MEMSET(buffer, 0, sizeof(*buffer));
|
||||
memset(buffer, 0, sizeof(*buffer));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -254,7 +249,7 @@ void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
||||
if (src != NULL && dst != NULL) {
|
||||
*dst = *src;
|
||||
if (src->private_memory != NULL) {
|
||||
dst->is_external_memory = 1; // dst buffer doesn't own the memory.
|
||||
dst->is_external_memory = 1; // dst buffer doesn't own the memory.
|
||||
dst->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
@@ -265,7 +260,7 @@ void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
|
||||
if (src != NULL && dst != NULL) {
|
||||
*dst = *src;
|
||||
if (src->private_memory != NULL) {
|
||||
src->is_external_memory = 1; // src relinquishes ownership
|
||||
src->is_external_memory = 1; // src relinquishes ownership
|
||||
src->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
@@ -290,8 +285,8 @@ VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf,
|
||||
} else {
|
||||
const WebPYUVABuffer* const src = &src_buf->u.YUVA;
|
||||
const WebPYUVABuffer* const dst = &dst_buf->u.YUVA;
|
||||
WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride, src_buf->width,
|
||||
src_buf->height);
|
||||
WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride,
|
||||
src_buf->width, src_buf->height);
|
||||
WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride,
|
||||
(src_buf->width + 1) / 2, (src_buf->height + 1) / 2);
|
||||
WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride,
|
||||
|
||||
@@ -15,48 +15,40 @@
|
||||
#define WEBP_DEC_COMMON_DEC_H_
|
||||
|
||||
// intra prediction modes
|
||||
enum {
|
||||
B_DC_PRED = 0, // 4x4 modes
|
||||
B_TM_PRED = 1,
|
||||
B_VE_PRED = 2,
|
||||
B_HE_PRED = 3,
|
||||
B_RD_PRED = 4,
|
||||
B_VR_PRED = 5,
|
||||
B_LD_PRED = 6,
|
||||
B_VL_PRED = 7,
|
||||
B_HD_PRED = 8,
|
||||
B_HU_PRED = 9,
|
||||
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
|
||||
enum { B_DC_PRED = 0, // 4x4 modes
|
||||
B_TM_PRED = 1,
|
||||
B_VE_PRED = 2,
|
||||
B_HE_PRED = 3,
|
||||
B_RD_PRED = 4,
|
||||
B_VR_PRED = 5,
|
||||
B_LD_PRED = 6,
|
||||
B_VL_PRED = 7,
|
||||
B_HD_PRED = 8,
|
||||
B_HU_PRED = 9,
|
||||
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
|
||||
|
||||
// Luma16 or UV modes
|
||||
DC_PRED = B_DC_PRED,
|
||||
V_PRED = B_VE_PRED,
|
||||
H_PRED = B_HE_PRED,
|
||||
TM_PRED = B_TM_PRED,
|
||||
B_PRED = NUM_BMODES, // refined I4x4 mode
|
||||
NUM_PRED_MODES = 4,
|
||||
// Luma16 or UV modes
|
||||
DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
|
||||
H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
|
||||
B_PRED = NUM_BMODES, // refined I4x4 mode
|
||||
NUM_PRED_MODES = 4,
|
||||
|
||||
// special modes
|
||||
B_DC_PRED_NOTOP = 4,
|
||||
B_DC_PRED_NOLEFT = 5,
|
||||
B_DC_PRED_NOTOPLEFT = 6,
|
||||
NUM_B_DC_MODES = 7
|
||||
};
|
||||
// special modes
|
||||
B_DC_PRED_NOTOP = 4,
|
||||
B_DC_PRED_NOLEFT = 5,
|
||||
B_DC_PRED_NOTOPLEFT = 6,
|
||||
NUM_B_DC_MODES = 7 };
|
||||
|
||||
enum {
|
||||
MB_FEATURE_TREE_PROBS = 3,
|
||||
NUM_MB_SEGMENTS = 4,
|
||||
NUM_REF_LF_DELTAS = 4,
|
||||
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
|
||||
MAX_NUM_PARTITIONS = 8,
|
||||
// Probabilities
|
||||
NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
|
||||
NUM_BANDS = 8,
|
||||
NUM_CTX = 3,
|
||||
NUM_PROBAS = 11
|
||||
};
|
||||
|
||||
// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
|
||||
int IsValidColorspace(int webp_csp_mode);
|
||||
enum { MB_FEATURE_TREE_PROBS = 3,
|
||||
NUM_MB_SEGMENTS = 4,
|
||||
NUM_REF_LF_DELTAS = 4,
|
||||
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
|
||||
MAX_NUM_PARTITIONS = 8,
|
||||
// Probabilities
|
||||
NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
|
||||
NUM_BANDS = 8,
|
||||
NUM_CTX = 3,
|
||||
NUM_PROBAS = 11
|
||||
};
|
||||
|
||||
#endif // WEBP_DEC_COMMON_DEC_H_
|
||||
|
||||
@@ -11,31 +11,19 @@
|
||||
//
|
||||
// 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"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main reconstruction function.
|
||||
|
||||
static const uint16_t kScan[16] = {
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
|
||||
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
|
||||
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS};
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
|
||||
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
|
||||
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
|
||||
};
|
||||
|
||||
static int CheckMode(int mb_x, int mb_y, int mode) {
|
||||
if (mode == B_DC_PRED) {
|
||||
@@ -49,7 +37,7 @@ static int CheckMode(int mb_x, int mb_y, int mode) {
|
||||
}
|
||||
|
||||
static void Copy32b(uint8_t* const dst, const uint8_t* const src) {
|
||||
WEBP_UNSAFE_MEMCPY(dst, src, 4);
|
||||
memcpy(dst, src, 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
|
||||
@@ -71,9 +59,9 @@ static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
|
||||
|
||||
static void DoUVTransform(uint32_t bits, const int16_t* const src,
|
||||
uint8_t* const dst) {
|
||||
if (bits & 0xff) { // any non-zero coeff at all?
|
||||
if (bits & 0xaa) { // any non-zero AC coefficient?
|
||||
VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V
|
||||
if (bits & 0xff) { // any non-zero coeff at all?
|
||||
if (bits & 0xaa) { // any non-zero AC coefficient?
|
||||
VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V
|
||||
} else {
|
||||
VP8TransformDCUV(src, dst);
|
||||
}
|
||||
@@ -84,11 +72,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) {
|
||||
@@ -105,14 +93,14 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
} else {
|
||||
// we only need to do this init once at block (0,0).
|
||||
// Afterward, it remains valid for the whole topmost row.
|
||||
WEBP_UNSAFE_MEMSET(y_dst - BPS - 1, 127, 16 + 4 + 1);
|
||||
WEBP_UNSAFE_MEMSET(u_dst - BPS - 1, 127, 8 + 1);
|
||||
WEBP_UNSAFE_MEMSET(v_dst - BPS - 1, 127, 8 + 1);
|
||||
memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
|
||||
memset(u_dst - BPS - 1, 127, 8 + 1);
|
||||
memset(v_dst - BPS - 1, 127, 8 + 1);
|
||||
}
|
||||
|
||||
// Reconstruct one row.
|
||||
for (mb_x = 0; mb_x < dec->mb_w; ++mb_x) {
|
||||
const VP8MBData* const block = ctx->mb_data + mb_x;
|
||||
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.
|
||||
@@ -127,26 +115,26 @@ 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) {
|
||||
WEBP_UNSAFE_MEMCPY(y_dst - BPS, top_yuv[0].y, 16);
|
||||
WEBP_UNSAFE_MEMCPY(u_dst - BPS, top_yuv[0].u, 8);
|
||||
WEBP_UNSAFE_MEMCPY(v_dst - BPS, top_yuv[0].v, 8);
|
||||
memcpy(y_dst - BPS, top_yuv[0].y, 16);
|
||||
memcpy(u_dst - BPS, top_yuv[0].u, 8);
|
||||
memcpy(v_dst - BPS, top_yuv[0].v, 8);
|
||||
}
|
||||
|
||||
// predict and add residuals
|
||||
if (block->is_i4x4) { // 4x4
|
||||
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
|
||||
WEBP_UNSAFE_MEMSET(top_right, top_yuv[0].y[15], sizeof(*top_right));
|
||||
if (mb_x >= dec->mb_w_ - 1) { // on rightmost border
|
||||
memset(top_right, top_yuv[0].y[15], sizeof(*top_right));
|
||||
} else {
|
||||
WEBP_UNSAFE_MEMCPY(top_right, top_yuv[1].y, sizeof(*top_right));
|
||||
memcpy(top_right, top_yuv[1].y, sizeof(*top_right));
|
||||
}
|
||||
}
|
||||
// replicate the top-right pixels below
|
||||
@@ -155,11 +143,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]);
|
||||
} else { // 16x16
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]);
|
||||
VP8PredLuma16[pred_func](y_dst);
|
||||
if (bits != 0) {
|
||||
for (n = 0; n < 16; ++n, bits <<= 2) {
|
||||
@@ -169,8 +157,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);
|
||||
@@ -178,28 +166,25 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
}
|
||||
|
||||
// stash away top samples for next block
|
||||
if (mb_y < dec->mb_h - 1) {
|
||||
WEBP_UNSAFE_MEMCPY(top_yuv[0].y, y_dst + 15 * BPS, 16);
|
||||
WEBP_UNSAFE_MEMCPY(top_yuv[0].u, u_dst + 7 * BPS, 8);
|
||||
WEBP_UNSAFE_MEMCPY(top_yuv[0].v, v_dst + 7 * BPS, 8);
|
||||
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) {
|
||||
WEBP_UNSAFE_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) {
|
||||
WEBP_UNSAFE_MEMCPY(u_out + j * dec->cache_uv_stride, u_dst + j * BPS,
|
||||
8);
|
||||
WEBP_UNSAFE_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,43 +198,43 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
// Simple filter: up to 2 luma samples are read and 1 is written.
|
||||
// Complex filter: up to 4 luma samples are read and 3 are written. Same for
|
||||
// U/V, so it's 8 samples total (because of the 2x upsampling).
|
||||
static const uint8_t kFilterExtraRows[3] = {0, 2, 8};
|
||||
static 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;
|
||||
} else { // complex
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh = f_info->hev_thresh_;
|
||||
if (mb_x > 0) {
|
||||
VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
}
|
||||
if (f_info->f_inner) {
|
||||
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);
|
||||
}
|
||||
@@ -257,7 +242,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);
|
||||
}
|
||||
@@ -267,9 +252,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);
|
||||
}
|
||||
}
|
||||
@@ -278,51 +263,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,8 +321,9 @@ 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]
|
||||
8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1};
|
||||
// roughly, it's dqm->uv_mat_[1]
|
||||
8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1
|
||||
};
|
||||
|
||||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec) {
|
||||
@@ -350,24 +336,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,17 +370,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_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -410,36 +396,36 @@ static void DitherRow(VP8Decoder* const dec) {
|
||||
// * we must clip the remaining pixels against the cropping area. The VP8Io
|
||||
// struct must have the following fields set correctly before calling put():
|
||||
|
||||
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
|
||||
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
|
||||
|
||||
// Finalize and transmit a complete row. Return false in case of user-abort.
|
||||
static int FinishRow(void* arg1, void* arg2) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)arg1;
|
||||
VP8Io* const io = (VP8Io*)arg2;
|
||||
int ok = 1;
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx;
|
||||
const int cache_id = ctx->id;
|
||||
const int extra_y_rows = kFilterExtraRows[dec->filter_type];
|
||||
const int ysize = extra_y_rows * dec->cache_y_stride;
|
||||
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride;
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride;
|
||||
uint8_t* const ydst = dec->cache_y - ysize + y_offset;
|
||||
uint8_t* const udst = dec->cache_u - uvsize + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v - uvsize + uv_offset;
|
||||
const int mb_y = ctx->mb_y;
|
||||
const 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);
|
||||
}
|
||||
|
||||
@@ -452,20 +438,20 @@ 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) {
|
||||
y_end -= extra_y_rows;
|
||||
}
|
||||
if (y_end > io->crop_bottom) {
|
||||
y_end = io->crop_bottom; // make sure we don't overflow on last row.
|
||||
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,
|
||||
@@ -476,9 +462,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;
|
||||
}
|
||||
@@ -497,14 +483,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) {
|
||||
WEBP_UNSAFE_MEMCPY(dec->cache_y - ysize, ydst + 16 * dec->cache_y_stride,
|
||||
ysize);
|
||||
WEBP_UNSAFE_MEMCPY(dec->cache_u - uvsize, udst + 8 * dec->cache_uv_stride,
|
||||
uvsize);
|
||||
WEBP_UNSAFE_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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,43 +500,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;
|
||||
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;
|
||||
VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int filter_row =
|
||||
(dec->filter_type_ > 0) &&
|
||||
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
||||
if (dec->mt_method_ == 0) {
|
||||
// ctx->id_ and ctx->f_info_ are already set
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = filter_row;
|
||||
ReconstructRow(dec, ctx);
|
||||
ok = FinishRow(dec, io);
|
||||
} else {
|
||||
WebPWorker* const worker = &dec->worker;
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
// Finish previous job *before* updating context
|
||||
ok &= WebPGetWorkerInterface()->Sync(worker);
|
||||
assert(worker->status == OK);
|
||||
if (ok) { // spawn a new deblocking/output job
|
||||
ctx->io = *io;
|
||||
ctx->id = dec->cache_id;
|
||||
ctx->mb_y = dec->mb_y;
|
||||
ctx->filter_row = filter_row;
|
||||
if (dec->mt_method == 2) { // swap macroblock data
|
||||
VP8MBData* const tmp = ctx->mb_data;
|
||||
ctx->mb_data = dec->mb_data;
|
||||
dec->mb_data = tmp;
|
||||
assert(worker->status_ == OK);
|
||||
if (ok) { // spawn a new deblocking/output job
|
||||
ctx->io_ = *io;
|
||||
ctx->id_ = dec->cache_id_;
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = filter_row;
|
||||
if (dec->mt_method_ == 2) { // swap macroblock data
|
||||
VP8MBData* const tmp = ctx->mb_data_;
|
||||
ctx->mb_data_ = dec->mb_data_;
|
||||
dec->mb_data_ = tmp;
|
||||
} else {
|
||||
// perform reconstruction directly in main thread
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
if (filter_row) { // swap filter info
|
||||
VP8FInfo* const tmp = ctx->f_info;
|
||||
ctx->f_info = dec->f_info;
|
||||
dec->f_info = tmp;
|
||||
if (filter_row) { // swap filter info
|
||||
VP8FInfo* const tmp = ctx->f_info_;
|
||||
ctx->f_info_ = dec->f_info_;
|
||||
dec->f_info_ = tmp;
|
||||
}
|
||||
// (reconstruct)+filter in parallel
|
||||
WebPGetWorkerInterface()->Launch(worker);
|
||||
if (++dec->cache_id == dec->num_caches) {
|
||||
dec->cache_id = 0;
|
||||
if (++dec->cache_id_ == dec->num_caches_) {
|
||||
dec->cache_id_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -567,13 +550,13 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// Call setup() first. This may trigger additional decoding features on 'io'.
|
||||
// Note: Afterward, we must call teardown() no matter what.
|
||||
if (io->setup != NULL && !io->setup(io)) {
|
||||
VP8SetError(dec, VP8_STATUS_INVALID_PARAM, "Frame setup failed");
|
||||
return dec->status;
|
||||
VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
|
||||
return dec->status_;
|
||||
}
|
||||
|
||||
// Disable filtering per user request
|
||||
if (io->bypass_filtering) {
|
||||
dec->filter_type = 0;
|
||||
dec->filter_type_ = 0;
|
||||
}
|
||||
|
||||
// Define the area where we can skip in-loop filtering, in case of cropping.
|
||||
@@ -586,29 +569,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);
|
||||
@@ -617,8 +600,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) {
|
||||
@@ -652,31 +635,31 @@ int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// io->put: [ 0..15][16..31][ 0..15][...
|
||||
|
||||
#define MT_CACHE_LINES 3
|
||||
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
|
||||
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
|
||||
|
||||
// Initialize multi/single-thread worker
|
||||
static int InitThreadContext(VP8Decoder* const dec) {
|
||||
dec->cache_id = 0;
|
||||
if (dec->mt_method > 0) {
|
||||
WebPWorker* const worker = &dec->worker;
|
||||
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;
|
||||
}
|
||||
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
const WebPHeaderStructure* const headers, int width,
|
||||
int height) {
|
||||
const WebPHeaderStructure* const headers,
|
||||
int width, int height) {
|
||||
if (options == NULL || options->use_threads == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -697,104 +680,103 @@ 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)
|
||||
: 0;
|
||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b);
|
||||
(dec->filter_type_ > 0) ?
|
||||
mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo)
|
||||
: 0;
|
||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
|
||||
const size_t mb_data_size =
|
||||
(dec->mt_method == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data);
|
||||
const size_t cache_height =
|
||||
(16 * num_caches + kFilterExtraRows[dec->filter_type]) * 3 / 2;
|
||||
(dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_);
|
||||
const size_t cache_height = (16 * num_caches
|
||||
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
||||
const size_t cache_size = top_size * cache_height;
|
||||
// alpha_size is the only one that scales as width x height.
|
||||
const uint64_t alpha_size =
|
||||
(dec->alpha_data != NULL)
|
||||
? (uint64_t)dec->pic_hdr.width * dec->pic_hdr.height
|
||||
: 0ULL;
|
||||
const uint64_t needed = (uint64_t)intra_pred_mode_size + top_size +
|
||||
mb_info_size + f_info_size + yuv_size + mb_data_size +
|
||||
cache_size + alpha_size + WEBP_ALIGN_CST;
|
||||
const uint64_t alpha_size = (dec->alpha_data_ != NULL) ?
|
||||
(uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
|
||||
const uint64_t needed = (uint64_t)intra_pred_mode_size
|
||||
+ top_size + mb_info_size + f_info_size
|
||||
+ yuv_size + mb_data_size
|
||||
+ cache_size + alpha_size + WEBP_ALIGN_CST;
|
||||
uint8_t* mem;
|
||||
|
||||
if (!CheckSizeOverflow(needed)) return 0; // check for overflow
|
||||
if (needed > dec->mem_size) {
|
||||
WebPSafeFree(dec->mem);
|
||||
dec->mem_size = 0;
|
||||
dec->mem = WebPSafeMalloc(needed, sizeof(uint8_t));
|
||||
if (dec->mem == NULL) {
|
||||
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.
|
||||
WEBP_UNSAFE_MEMSET(dec->mb_info - 1, 0, mb_info_size);
|
||||
VP8InitScanline(dec); // initialize left too.
|
||||
memset(dec->mb_info_ - 1, 0, mb_info_size);
|
||||
VP8InitScanline(dec); // initialize left too.
|
||||
|
||||
// initialize top
|
||||
WEBP_UNSAFE_MEMSET(dec->intra_t, B_DC_PRED, intra_pred_mode_size);
|
||||
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -802,16 +784,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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user