mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-15 13:29:54 +02:00
Compare commits
59 Commits
v0.5.1-rc5
...
0.5.2
Author | SHA1 | Date | |
---|---|---|---|
e1f1bce9dc | |||
90d47113bc | |||
bc99ef0699 | |||
ece9684f52 | |||
aa7744ca38 | |||
d9120271e7 | |||
24eb39401b | |||
a4a8e5f32c | |||
31ca2a8098 | |||
b2f77b57a9 | |||
5ab6d9de1f | |||
f29bf582df | |||
df780e0eac | |||
218460cdd7 | |||
de7d654d0d | |||
74a12b10d9 | |||
be7dcc088c | |||
408858308a | |||
8f38c72e11 | |||
33ca93f909 | |||
76e190735b | |||
f91ba96306 | |||
25d74e652e | |||
03f1c00877 | |||
58410cd6dc | |||
e168af8c6c | |||
ed9dec41a5 | |||
3c49178f7d | |||
9595f29010 | |||
7ec9552c22 | |||
9871335fc8 | |||
0ae32226ce | |||
ab4c8056e0 | |||
eec5fa3a95 | |||
004d569086 | |||
4fe5d588bf | |||
bd63a31aab | |||
363a568131 | |||
a0d2753fcb | |||
31fe11a57a | |||
532215dd29 | |||
9c75dbd39c | |||
af2e05cbdf | |||
26ffa2962b | |||
7416280d75 | |||
13cf1d2e41 | |||
eb9a4b97c5 | |||
42ebe3b783 | |||
83cbfa09a1 | |||
a1ade40ed8 | |||
fd4d090fd1 | |||
9daad4598b | |||
c284780f0a | |||
e375080d8f | |||
c222a053af | |||
bb2336170b | |||
883d41fb40 | |||
3d97bb7514 | |||
deb54d915a |
1
.mailmap
1
.mailmap
@ -9,3 +9,4 @@ Vikas Arora <vikasa@google.com>
|
||||
<vrabaud@google.com> <vincent.rabaud@gmail.com>
|
||||
Tamar Levy <tamar.levy@intel.com>
|
||||
<qrczak@google.com> <qrczak>
|
||||
Hui Su <huisu@google.com>
|
||||
|
1
AUTHORS
1
AUTHORS
@ -2,6 +2,7 @@ Contributors:
|
||||
- Charles Munger (clm at google dot com)
|
||||
- Christian Duvivier (cduvivier at google dot com)
|
||||
- Djordje Pesut (djordje dot pesut at imgtec dot com)
|
||||
- Hui Su (huisu at google dot com)
|
||||
- James Zern (jzern at google dot com)
|
||||
- Jan Engelhardt (jengelh at medozas dot de)
|
||||
- Johann (johann dot koenig at duck dot com)
|
||||
|
@ -1,6 +1,7 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD
|
||||
WEBP_CFLAGS += -fvisibility=hidden
|
||||
|
||||
ifeq ($(APP_OPTIM),release)
|
||||
WEBP_CFLAGS += -finline-functions -ffast-math \
|
||||
|
218
CMakeLists.txt
218
CMakeLists.txt
@ -7,6 +7,7 @@ option(WEBP_BUILD_CWEBP "Build the cwebp command line tool." OFF)
|
||||
option(WEBP_BUILD_DWEBP "Build the dwebp command line tool." OFF)
|
||||
option(WEBP_EXPERIMENTAL_FEATURES "Build with experimental features." OFF)
|
||||
option(WEBP_FORCE_ALIGNED "Force aligned memory operations." OFF)
|
||||
option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces." OFF)
|
||||
|
||||
set(WEBP_DEP_LIBRARIES)
|
||||
set(WEBP_DEP_INCLUDE_DIRS)
|
||||
@ -17,210 +18,27 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
)
|
||||
endif()
|
||||
|
||||
include(cmake/config.h.cmake)
|
||||
|
||||
################################################################################
|
||||
# Generate the config.h to compile with specific intrinsics / libs.
|
||||
|
||||
## Check for compiler options.
|
||||
include(CheckCSourceCompiles)
|
||||
check_c_source_compiles("
|
||||
int main(void) {
|
||||
(void)__builtin_bswap16(0);
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
HAVE_BUILTIN_BSWAP16
|
||||
)
|
||||
check_c_source_compiles("
|
||||
int main(void) {
|
||||
(void)__builtin_bswap32(0);
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
HAVE_BUILTIN_BSWAP32
|
||||
)
|
||||
check_c_source_compiles("
|
||||
int main(void) {
|
||||
(void)__builtin_bswap64(0);
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
HAVE_BUILTIN_BSWAP64
|
||||
)
|
||||
|
||||
## Check for libraries.
|
||||
find_package(Threads)
|
||||
if(Threads_FOUND)
|
||||
if(CMAKE_USE_PTHREADS_INIT)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||
endif()
|
||||
foreach(PTHREAD_TEST HAVE_PTHREAD_PRIO_INHERIT PTHREAD_CREATE_UNDETACHED)
|
||||
check_c_source_compiles("
|
||||
#include <pthread.h>
|
||||
int main (void) {
|
||||
int attr = ${PTHREAD_TEST};
|
||||
return attr;
|
||||
}
|
||||
" ${PTHREAD_TEST}
|
||||
)
|
||||
endforeach()
|
||||
list(APPEND WEBP_DEP_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
set(WEBP_USE_THREAD ${Threads_FOUND})
|
||||
|
||||
# TODO: this seems unused, check with autotools.
|
||||
set(LT_OBJDIR ".libs/")
|
||||
|
||||
# Only useful for vwebp, so useless for now.
|
||||
# find_package(OpenGL)
|
||||
# set(WEBP_HAVE_GL ${OPENGL_FOUND})
|
||||
# set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIRS})
|
||||
# set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} ${OPENGL_LIBRARIES})
|
||||
|
||||
# Find the standard C math library.
|
||||
find_library(MATH_LIBRARY NAMES m)
|
||||
if(MATH_LIBRARY)
|
||||
list(APPEND WEBP_DEP_LIBRARIES ${MATH_LIBRARY})
|
||||
# Options.
|
||||
if(WEBP_ENABLE_SWAP_16BIT_CSP)
|
||||
add_definitions(-DWEBP_SWAP_16BIT_CSP)
|
||||
endif()
|
||||
|
||||
# Find the standard image libraries.
|
||||
set(WEBP_DEP_IMG_LIBRARIES)
|
||||
set(WEBP_DEP_IMG_INCLUDE_DIRS)
|
||||
foreach(I_LIB PNG JPEG TIFF GIF)
|
||||
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_DIRS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
## Check for specific headers.
|
||||
include(CheckIncludeFiles)
|
||||
check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
|
||||
check_include_files(dlfcn.h HAVE_DLFCN_H)
|
||||
check_include_files(GLUT/glut.h HAVE_GLUT_GLUT_H)
|
||||
check_include_files(GL/glut.h HAVE_GL_GLUT_H)
|
||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files(memory.h HAVE_MEMORY_H)
|
||||
check_include_files(OpenGL/glut.h HAVE_OPENGL_GLUT_H)
|
||||
check_include_files(shlwapi.h HAVE_SHLWAPI_H)
|
||||
check_include_files(stdint.h HAVE_STDINT_H)
|
||||
check_include_files(stdlib.h HAVE_STDLIB_H)
|
||||
check_include_files(strings.h HAVE_STRINGS_H)
|
||||
check_include_files(string.h HAVE_STRING_H)
|
||||
check_include_files(sys/stat.h HAVE_SYS_STAT_H)
|
||||
check_include_files(sys/types.h HAVE_SYS_TYPES_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(wincodec.h HAVE_WINCODEC_H)
|
||||
check_include_files(windows.h HAVE_WINDOWS_H)
|
||||
|
||||
# Windows specifics
|
||||
if(HAVE_WINCODEC_H)
|
||||
list(APPEND WEBP_DEP_LIBRARIES shlwapi ole32 windowscodecs)
|
||||
endif()
|
||||
|
||||
## Check for SIMD extensions.
|
||||
set(WEBP_SIMD_FLAGS "SSE2;SSE41;AVX2")
|
||||
set(WEBP_SIMD_FILE_EXTENSIONS "_sse2.c;_sse41.c;_avx2.c")
|
||||
if(MSVC)
|
||||
# MSVC does not have a SSE4 flag but AVX2 support implies
|
||||
# SSE4 support.
|
||||
set(SIMD_ENABLE_FLAGS "/arch:SSE2;/arch:AVX2;/arch:AVX2")
|
||||
set(SIMD_DISABLE_FLAGS)
|
||||
else()
|
||||
set(SIMD_ENABLE_FLAGS "-msse2;-msse4.1;-mavx2")
|
||||
set(SIMD_DISABLE_FLAGS "-mno-sse2;-mno-sse4.1;-mno-avx2")
|
||||
endif()
|
||||
|
||||
set(WEBP_SIMD_FILES_TO_NOT_INCLUDE)
|
||||
set(WEBP_SIMD_FILES_TO_INCLUDE)
|
||||
set(WEBP_SIMD_FLAGS_TO_INCLUDE)
|
||||
|
||||
list(LENGTH WEBP_SIMD_FLAGS WEBP_SIMD_FLAGS_LENGTH)
|
||||
math(EXPR WEBP_SIMD_FLAGS_RANGE "${WEBP_SIMD_FLAGS_LENGTH} - 1")
|
||||
|
||||
foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
|
||||
list(GET WEBP_SIMD_FLAGS ${I_SIMD} WEBP_SIMD_FLAG)
|
||||
list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SIMD_COMPILE_FLAG})
|
||||
check_c_source_compiles("
|
||||
#include \"${CMAKE_CURRENT_LIST_DIR}/src/dsp/dsp.h\"
|
||||
int main(void) {
|
||||
#if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
|
||||
this is not valid code
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
WEBP_HAVE_${WEBP_SIMD_FLAG}
|
||||
################################################################################
|
||||
# Android only.
|
||||
if(ANDROID)
|
||||
include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
|
||||
add_library(cpufeatures STATIC
|
||||
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
|
||||
)
|
||||
|
||||
# Check which files we should include or not.
|
||||
list(GET WEBP_SIMD_FILE_EXTENSIONS ${I_SIMD} WEBP_SIMD_FILE_EXTENSION)
|
||||
file(GLOB SIMD_FILES RELATIVE ${CMAKE_CURRENT_LIST_DIR}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/dsp/*${WEBP_SIMD_FILE_EXTENSION}"
|
||||
target_link_libraries(cpufeatures dl)
|
||||
set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} cpufeatures)
|
||||
set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS}
|
||||
${ANDROID_NDK}/sources/android/cpufeatures
|
||||
)
|
||||
if(WEBP_HAVE_${WEBP_SIMD_FLAG})
|
||||
# Memorize the file and flags.
|
||||
foreach(FILE ${SIMD_FILES})
|
||||
list(APPEND WEBP_SIMD_FILES_TO_INCLUDE ${FILE})
|
||||
list(APPEND WEBP_SIMD_FLAGS_TO_INCLUDE ${SIMD_COMPILE_FLAG})
|
||||
endforeach()
|
||||
else()
|
||||
# Remove the file from the list.
|
||||
foreach(FILE ${SIMD_FILES})
|
||||
list(APPEND WEBP_SIMD_FILES_NOT_TO_INCLUDE ${FILE})
|
||||
endforeach()
|
||||
# Explicitly disable SIMD.
|
||||
if(SIMD_DISABLE_FLAGS)
|
||||
list(GET SIMD_DISABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
|
||||
include(CheckCCompilerFlag)
|
||||
check_c_compiler_flag(${SIMD_COMPILE_FLAG} HAS_COMPILE_FLAG)
|
||||
if(HAS_COMPILE_FLAG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILE_FLAG}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
## Define extra info.
|
||||
set(PACKAGE ${PROJECT_NAME})
|
||||
set(PACKAGE_NAME ${PROJECT_NAME})
|
||||
|
||||
# Read from configure.ac.
|
||||
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_AC)
|
||||
string(REGEX MATCHALL "\\[([0-9a-z\\.:/]*)\\]"
|
||||
CONFIGURE_AC_PACKAGE_INFO ${CONFIGURE_AC}
|
||||
)
|
||||
function(strip_bracket VAR)
|
||||
string(LENGTH ${${VAR}} TMP_LEN)
|
||||
math(EXPR TMP_LEN ${TMP_LEN}-2)
|
||||
string(SUBSTRING ${${VAR}} 1 ${TMP_LEN} TMP_SUB)
|
||||
set(${VAR} ${TMP_SUB} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
list(GET CONFIGURE_AC_PACKAGE_INFO 1 PACKAGE_VERSION)
|
||||
strip_bracket(PACKAGE_VERSION)
|
||||
list(GET CONFIGURE_AC_PACKAGE_INFO 2 PACKAGE_BUGREPORT)
|
||||
strip_bracket(PACKAGE_BUGREPORT)
|
||||
list(GET CONFIGURE_AC_PACKAGE_INFO 3 PACKAGE_URL)
|
||||
strip_bracket(PACKAGE_URL)
|
||||
|
||||
# Build more info.
|
||||
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
||||
set(PACKAGE_TARNAME ${PACKAGE_NAME})
|
||||
set(VERSION ${PACKAGE_VERSION})
|
||||
|
||||
## Generate the config.h header.
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/webp/config.h)
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
# The webp folder is included as we reference config.h as
|
||||
# ../webp/config.h or webp/config.h
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/webp
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# WebP source files.
|
||||
@ -251,7 +69,7 @@ parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/dsp "${WEBP_SRCS}")
|
||||
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/enc "${WEBP_SRCS}")
|
||||
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/utils "${WEBP_SRCS}")
|
||||
|
||||
# Remove the files specific to SIMD we don't user.
|
||||
# Remove the files specific to SIMD we don't use.
|
||||
foreach(FILE ${WEBP_SIMD_FILES_NOT_TO_INCLUDE})
|
||||
list(REMOVE_ITEM WEBP_SRCS ${FILE})
|
||||
endforeach()
|
||||
|
56
ChangeLog
56
ChangeLog
@ -1,3 +1,59 @@
|
||||
aa7744c anim_util: quiet implicit conv warnings in 32-bit
|
||||
d912027 jpegdec: correct ContextFill signature
|
||||
24eb394 Remove some errors when compiling the code as C++.
|
||||
a4a8e5f vwebp: clear canvas during resize w/o animation
|
||||
31ca2a8 tiffdec: restore libtiff 3.9.x compatibility
|
||||
b2f77b5 update NEWS
|
||||
5ab6d9d AnimEncoder: avoid freeing uninitialized memory pointer.
|
||||
f29bf58 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless.
|
||||
df780e0 fix a potential overflow with MALLOC_LIMIT
|
||||
218460c bump version to 0.5.2
|
||||
de7d654 update AUTHORS & .mailmap
|
||||
74a12b1 iosbuild.sh: add WebPDecoder.framework + encoder
|
||||
be7dcc0 AnimEncoder: Correctly skip a frame when sub-rectangle is empty.
|
||||
4088583 Fix assertions in WebPRescalerExportRow()
|
||||
8f38c72 fix a typo in WebPPictureYUVAToARGB's doc
|
||||
33ca93f systematically call WebPDemuxReleaseIterator() on dec->prev_iter_
|
||||
76e1907 doc: use two's complement explicitly for uint8->int8 conversion
|
||||
f91ba96 Anim_encoder: correctly handle enc->prev_candidate_undecided_
|
||||
25d74e6 WebPPictureDistortion(): free() -> WebPSafeFree()
|
||||
03f1c00 mux/Makefile.am: add missing -lm
|
||||
58410cd fix bug in RefineUsingDistortion()
|
||||
e168af8 fix filtering auto-adjustment
|
||||
ed9dec4 fix doc and code snippet for WebPINewDecoder() doc
|
||||
3c49178 prevent 32b overflow for very large canvas_width / height
|
||||
9595f29 fix anim_util.c compilation when HAVE_GIF is not defined.
|
||||
7ec9552 Make gif transparent color to be transparent black
|
||||
9871335 Add a CMake option for WEBP_SWAP_16BIT_CSP.
|
||||
0ae3222 Fix missing cpu-features for Android.
|
||||
ab4c805 cpu.cmake: improve webp_check_compiler_flag output
|
||||
eec5fa3 Provide support for CMake on Android studio 2.2.
|
||||
004d569 Split the main CMake file.
|
||||
4fe5d58 Android.mk: use -fvisibility=hidden
|
||||
bd63a31 vwebp: ensure setenv() is available in stdlib.h
|
||||
363a568 vwebp: handle window resizing properly
|
||||
a0d2753 lower WEBP_MAX_ALLOCABLE_MEMORY default
|
||||
31fe11a fix infinite loop in case of PARTITION0 overflow
|
||||
532215d Change the rule of picking UV mode in MBAnalyzeBestUVMode()
|
||||
9c75dbd cwebp.1: improve some grammar
|
||||
af2e05c vwebp: Clear previous frame when a key triggers a redraw
|
||||
26ffa29 Add descriptions of default configuration in help info.
|
||||
7416280 Fix an unsigned integer overflow error in enc/cost.h
|
||||
13cf1d2 Do token recording and counting in a single loop
|
||||
eb9a4b9 Reset segment id if we decide not to update segment map
|
||||
42ebe3b configure: fix NEON flag detection under gcc 6
|
||||
83cbfa0 Import: use relative pointer offsets
|
||||
a1ade40 PreprocessARGB: use relative pointer offsets
|
||||
fd4d090 ConvertWRGBToYUV: use relative pointer offsets
|
||||
9daad45 ImportYUVAFromRGBA: use relative pointer offsets
|
||||
c284780 imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow
|
||||
e375080 gifdec,Remap: avoid out of bounds colormap read
|
||||
c222a05 additional fix for stride type as size_t
|
||||
bb23361 fix potential overflow when width * height * 4 >= (1<<32)
|
||||
883d41f gif2webp: fix crash with NULL extension data
|
||||
3d97bb7 update ChangeLog (tag: v0.5.1, origin/0.5.1, 0.5.1)
|
||||
deb54d9 Clarify the expected 'config' lifespan in WebPIDecode()
|
||||
c7e2d24 update ChangeLog (tag: v0.5.1-rc5)
|
||||
c7eb06f Fix corner case in CostManagerInit.
|
||||
ab7937a gif2webp: normalize the number of .'s in the help message
|
||||
3cdec84 vwebp: normalize the number of .'s in the help message
|
||||
|
13
NEWS
13
NEWS
@ -1,3 +1,16 @@
|
||||
- 12/13/2016: version 0.5.2
|
||||
This is a binary compatible release.
|
||||
This release covers CVE-2016-8888 and CVE-2016-9085.
|
||||
* further security related hardening in the tools; fixes to
|
||||
gif2webp/AnimEncoder (issues #310, #314, #316, #322), cwebp/libwebp (issue
|
||||
#312)
|
||||
* full libwebp (encoder & decoder) iOS framework; libwebpdecoder
|
||||
WebP.framework renamed to WebPDecoder.framework (issue #307)
|
||||
* CMake support for Android Studio (2.2)
|
||||
* miscellaneous build related fixes (issue #306, #313)
|
||||
* miscellaneous documentation improvements (issue #225)
|
||||
* minor lossy encoder fixes and improvements
|
||||
|
||||
- 6/14/2016: version 0.5.1
|
||||
This is a binary compatible release.
|
||||
* miscellaneous bug fixes (issues #280, #289)
|
||||
|
25
README
25
README
@ -4,7 +4,7 @@
|
||||
\__\__/\____/\_____/__/ ____ ___
|
||||
/ _/ / \ \ / _ \/ _/
|
||||
/ \_/ / / \ \ __/ \__
|
||||
\____/____/\_____/_____/____/v0.5.1
|
||||
\____/____/\_____/_____/____/v0.5.2
|
||||
|
||||
Description:
|
||||
============
|
||||
@ -220,8 +220,9 @@ assumed to be a PNG, JPEG, TIFF or WebP file.
|
||||
Options:
|
||||
-h / -help ............. short help
|
||||
-H / -longhelp ......... long help
|
||||
-q <float> ............. quality factor (0:small..100:big)
|
||||
-alpha_q <int> ......... transparency-compression quality (0..100)
|
||||
-q <float> ............. quality factor (0:small..100:big), default=75
|
||||
-alpha_q <int> ......... transparency-compression quality (0..100),
|
||||
default=100
|
||||
-preset <string> ....... preset setting, one of:
|
||||
default, photo, picture,
|
||||
drawing, icon, text
|
||||
@ -229,15 +230,15 @@ Options:
|
||||
-z <int> ............... activates lossless preset with given
|
||||
level in [0:fast, ..., 9:slowest]
|
||||
|
||||
-m <int> ............... compression method (0=fast, 6=slowest)
|
||||
-segments <int> ........ number of segments to use (1..4)
|
||||
-m <int> ............... compression method (0=fast, 6=slowest), default=4
|
||||
-segments <int> ........ number of segments to use (1..4), default=4
|
||||
-size <int> ............ target size (in bytes)
|
||||
-psnr <float> .......... target PSNR (in dB. typically: 42)
|
||||
|
||||
-s <int> <int> ......... input size (width x height) for YUV
|
||||
-sns <int> ............. spatial noise shaping (0:off, 100:max)
|
||||
-f <int> ............... filter strength (0=off..100)
|
||||
-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp)
|
||||
-sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
|
||||
-f <int> ............... filter strength (0=off..100), default=60
|
||||
-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
|
||||
-strong ................ use strong filter instead of simple (default)
|
||||
-nostrong .............. use simple filter instead of strong
|
||||
-partition_limit <int> . limit quality to fit the 512k limit on
|
||||
@ -252,18 +253,18 @@ Options:
|
||||
-print_ssim ............ prints averaged SSIM distortion
|
||||
-print_lsim ............ prints local-similarity distortion
|
||||
-d <file.pgm> .......... dump the compressed output (PGM file)
|
||||
-alpha_method <int> .... transparency-compression method (0..1)
|
||||
-alpha_method <int> .... transparency-compression method (0..1), default=1
|
||||
-alpha_filter <string> . predictive filtering for alpha plane,
|
||||
one of: none, fast (default) or best
|
||||
-exact ................. preserve RGB values in transparent area
|
||||
-exact ................. preserve RGB values in transparent area, default=off
|
||||
-blend_alpha <hex> ..... blend colors against background color
|
||||
expressed as RGB values written in
|
||||
hexadecimal, e.g. 0xc0e0d0 for red=0xc0
|
||||
green=0xe0 and blue=0xd0
|
||||
-noalpha ............... discard any transparency information
|
||||
-lossless .............. encode image losslessly
|
||||
-lossless .............. encode image losslessly, default=off
|
||||
-near_lossless <int> ... use near-lossless image
|
||||
preprocessing (0..100=off)
|
||||
preprocessing (0..100=off), default=100
|
||||
-hint <string> ......... specify image characteristics hint,
|
||||
one of: photo, picture or graph
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
__ __ ____ ____ ____ __ __ _ __ __
|
||||
/ \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
|
||||
\ / __/ _ \ __/ / / (_/ /__
|
||||
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v0.3.1
|
||||
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v0.3.2
|
||||
|
||||
|
||||
Description:
|
||||
|
141
cmake/config.h.cmake
Normal file
141
cmake/config.h.cmake
Normal file
@ -0,0 +1,141 @@
|
||||
# Generate the config.h to compile with specific intrinsics / libs.
|
||||
|
||||
## Check for compiler options.
|
||||
include(CheckCSourceCompiles)
|
||||
check_c_source_compiles("
|
||||
int main(void) {
|
||||
(void)__builtin_bswap16(0);
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
HAVE_BUILTIN_BSWAP16
|
||||
)
|
||||
check_c_source_compiles("
|
||||
int main(void) {
|
||||
(void)__builtin_bswap32(0);
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
HAVE_BUILTIN_BSWAP32
|
||||
)
|
||||
check_c_source_compiles("
|
||||
int main(void) {
|
||||
(void)__builtin_bswap64(0);
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
HAVE_BUILTIN_BSWAP64
|
||||
)
|
||||
|
||||
## Check for libraries.
|
||||
find_package(Threads)
|
||||
if(Threads_FOUND)
|
||||
if(CMAKE_USE_PTHREADS_INIT)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||
endif()
|
||||
foreach(PTHREAD_TEST HAVE_PTHREAD_PRIO_INHERIT PTHREAD_CREATE_UNDETACHED)
|
||||
check_c_source_compiles("
|
||||
#include <pthread.h>
|
||||
int main (void) {
|
||||
int attr = ${PTHREAD_TEST};
|
||||
return attr;
|
||||
}
|
||||
" ${PTHREAD_TEST}
|
||||
)
|
||||
endforeach()
|
||||
list(APPEND WEBP_DEP_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
set(WEBP_USE_THREAD ${Threads_FOUND})
|
||||
|
||||
# TODO: this seems unused, check with autotools.
|
||||
set(LT_OBJDIR ".libs/")
|
||||
|
||||
# Only useful for vwebp, so useless for now.
|
||||
# find_package(OpenGL)
|
||||
# set(WEBP_HAVE_GL ${OPENGL_FOUND})
|
||||
# set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIRS})
|
||||
# set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} ${OPENGL_LIBRARIES})
|
||||
|
||||
# Find the standard C math library.
|
||||
find_library(MATH_LIBRARY NAMES m)
|
||||
if(MATH_LIBRARY)
|
||||
list(APPEND WEBP_DEP_LIBRARIES ${MATH_LIBRARY})
|
||||
endif()
|
||||
|
||||
# Find the standard image libraries.
|
||||
set(WEBP_DEP_IMG_LIBRARIES)
|
||||
set(WEBP_DEP_IMG_INCLUDE_DIRS)
|
||||
foreach(I_LIB PNG JPEG TIFF GIF)
|
||||
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_DIRS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
## Check for specific headers.
|
||||
include(CheckIncludeFiles)
|
||||
check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
|
||||
check_include_files(dlfcn.h HAVE_DLFCN_H)
|
||||
check_include_files(GLUT/glut.h HAVE_GLUT_GLUT_H)
|
||||
check_include_files(GL/glut.h HAVE_GL_GLUT_H)
|
||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files(memory.h HAVE_MEMORY_H)
|
||||
check_include_files(OpenGL/glut.h HAVE_OPENGL_GLUT_H)
|
||||
check_include_files(shlwapi.h HAVE_SHLWAPI_H)
|
||||
check_include_files(stdint.h HAVE_STDINT_H)
|
||||
check_include_files(stdlib.h HAVE_STDLIB_H)
|
||||
check_include_files(strings.h HAVE_STRINGS_H)
|
||||
check_include_files(string.h HAVE_STRING_H)
|
||||
check_include_files(sys/stat.h HAVE_SYS_STAT_H)
|
||||
check_include_files(sys/types.h HAVE_SYS_TYPES_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(wincodec.h HAVE_WINCODEC_H)
|
||||
check_include_files(windows.h HAVE_WINDOWS_H)
|
||||
|
||||
# Windows specifics
|
||||
if(HAVE_WINCODEC_H)
|
||||
list(APPEND WEBP_DEP_LIBRARIES shlwapi ole32 windowscodecs)
|
||||
endif()
|
||||
|
||||
## Check for SIMD extensions.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cpu.cmake)
|
||||
|
||||
## Define extra info.
|
||||
set(PACKAGE ${PROJECT_NAME})
|
||||
set(PACKAGE_NAME ${PROJECT_NAME})
|
||||
|
||||
# Read from configure.ac.
|
||||
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_AC)
|
||||
string(REGEX MATCHALL "\\[([0-9a-z\\.:/]*)\\]"
|
||||
CONFIGURE_AC_PACKAGE_INFO ${CONFIGURE_AC}
|
||||
)
|
||||
function(strip_bracket VAR)
|
||||
string(LENGTH ${${VAR}} TMP_LEN)
|
||||
math(EXPR TMP_LEN ${TMP_LEN}-2)
|
||||
string(SUBSTRING ${${VAR}} 1 ${TMP_LEN} TMP_SUB)
|
||||
set(${VAR} ${TMP_SUB} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
list(GET CONFIGURE_AC_PACKAGE_INFO 1 PACKAGE_VERSION)
|
||||
strip_bracket(PACKAGE_VERSION)
|
||||
list(GET CONFIGURE_AC_PACKAGE_INFO 2 PACKAGE_BUGREPORT)
|
||||
strip_bracket(PACKAGE_BUGREPORT)
|
||||
list(GET CONFIGURE_AC_PACKAGE_INFO 3 PACKAGE_URL)
|
||||
strip_bracket(PACKAGE_URL)
|
||||
|
||||
# Build more info.
|
||||
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
||||
set(PACKAGE_TARNAME ${PACKAGE_NAME})
|
||||
set(VERSION ${PACKAGE_VERSION})
|
||||
|
||||
## Generate the config.h header.
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/webp/config.h)
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
# The webp folder is included as we reference config.h as
|
||||
# ../webp/config.h or webp/config.h
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/webp
|
||||
)
|
96
cmake/cpu.cmake
Normal file
96
cmake/cpu.cmake
Normal file
@ -0,0 +1,96 @@
|
||||
## Check for SIMD extensions.
|
||||
|
||||
function(webp_check_compiler_flag WEBP_SIMD_FLAG)
|
||||
unset(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} CACHE)
|
||||
check_c_source_compiles("
|
||||
#include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\"
|
||||
int main(void) {
|
||||
#if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
|
||||
this is not valid code
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
" WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG}
|
||||
)
|
||||
if(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
|
||||
set(WEBP_HAVE_${WEBP_SIMD_FLAG} 1 PARENT_SCOPE)
|
||||
else()
|
||||
set(WEBP_HAVE_${WEBP_SIMD_FLAG} 0 PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# those are included in the names of WEBP_USE_* in c++ code.
|
||||
set(WEBP_SIMD_FLAGS "SSE2;SSE41;AVX2;MIPS32;MIPS_DSP_R2;NEON;MSA")
|
||||
set(WEBP_SIMD_FILE_EXTENSIONS "_sse2.c;_sse41.c;_avx2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
|
||||
if(MSVC)
|
||||
# MSVC does not have a SSE4 flag but AVX2 support implies
|
||||
# SSE4 support.
|
||||
set(SIMD_ENABLE_FLAGS "/arch:SSE2;/arch:AVX2;/arch:AVX2;;;;")
|
||||
set(SIMD_DISABLE_FLAGS)
|
||||
else()
|
||||
set(SIMD_ENABLE_FLAGS "-msse2;-msse4.1;-mavx2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
|
||||
set(SIMD_DISABLE_FLAGS "-mno-sse2;-mno-sse4.1;-mno-avx2;;-mno-dspr2;;-mno-msa")
|
||||
endif()
|
||||
|
||||
set(WEBP_SIMD_FILES_TO_NOT_INCLUDE)
|
||||
set(WEBP_SIMD_FILES_TO_INCLUDE)
|
||||
set(WEBP_SIMD_FLAGS_TO_INCLUDE)
|
||||
|
||||
if(${ANDROID})
|
||||
if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
|
||||
# This is because Android studio uses the configuration
|
||||
# "-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16"
|
||||
# that does not trigger neon optimizations but should
|
||||
# (as this configuration does not exist anymore).
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon ")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(LENGTH WEBP_SIMD_FLAGS WEBP_SIMD_FLAGS_LENGTH)
|
||||
math(EXPR WEBP_SIMD_FLAGS_RANGE "${WEBP_SIMD_FLAGS_LENGTH} - 1")
|
||||
|
||||
foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
|
||||
list(GET WEBP_SIMD_FLAGS ${I_SIMD} WEBP_SIMD_FLAG)
|
||||
|
||||
# First try with no extra flag added as the compiler might have default flags
|
||||
# (especially on Android).
|
||||
unset(WEBP_HAVE_${WEBP_SIMD_FLAG} CACHE)
|
||||
set(CMAKE_REQUIRED_FLAGS)
|
||||
webp_check_compiler_flag(${WEBP_SIMD_FLAG})
|
||||
if(NOT WEBP_HAVE_${WEBP_SIMD_FLAG})
|
||||
list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SIMD_COMPILE_FLAG})
|
||||
webp_check_compiler_flag(${WEBP_SIMD_FLAG})
|
||||
else()
|
||||
set(SIMD_COMPILE_FLAG " ")
|
||||
endif()
|
||||
# Check which files we should include or not.
|
||||
list(GET WEBP_SIMD_FILE_EXTENSIONS ${I_SIMD} WEBP_SIMD_FILE_EXTENSION)
|
||||
file(GLOB SIMD_FILES "${CMAKE_CURRENT_LIST_DIR}/../"
|
||||
"src/dsp/*${WEBP_SIMD_FILE_EXTENSION}"
|
||||
)
|
||||
if(WEBP_HAVE_${WEBP_SIMD_FLAG})
|
||||
# Memorize the file and flags.
|
||||
foreach(FILE ${SIMD_FILES})
|
||||
list(APPEND WEBP_SIMD_FILES_TO_INCLUDE ${FILE})
|
||||
list(APPEND WEBP_SIMD_FLAGS_TO_INCLUDE ${SIMD_COMPILE_FLAG})
|
||||
endforeach()
|
||||
else()
|
||||
# Remove the file from the list.
|
||||
foreach(FILE ${SIMD_FILES})
|
||||
list(APPEND WEBP_SIMD_FILES_NOT_TO_INCLUDE ${FILE})
|
||||
endforeach()
|
||||
# Explicitly disable SIMD.
|
||||
if(SIMD_DISABLE_FLAGS)
|
||||
list(GET SIMD_DISABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
|
||||
include(CheckCCompilerFlag)
|
||||
if(SIMD_COMPILE_FLAG)
|
||||
unset(HAS_COMPILE_FLAG CACHE)
|
||||
check_c_compiler_flag(${SIMD_COMPILE_FLAG} HAS_COMPILE_FLAG)
|
||||
if(HAS_COMPILE_FLAG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILE_FLAG}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
82
configure.ac
82
configure.ac
@ -1,4 +1,4 @@
|
||||
AC_INIT([libwebp], [0.5.1],
|
||||
AC_INIT([libwebp], [0.5.2],
|
||||
[https://bugs.chromium.org/p/webp],,
|
||||
[http://developers.google.com/speed/webp])
|
||||
AC_CANONICAL_HOST
|
||||
@ -10,6 +10,7 @@ dnl === it must occur before LT_INIT (AC_PROG_LIBTOOL).
|
||||
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
|
||||
|
||||
AC_PROG_LIBTOOL
|
||||
AC_PROG_SED
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
dnl === Enable less verbose output when building.
|
||||
@ -195,40 +196,51 @@ AC_ARG_ENABLE([neon_rtcd],
|
||||
AS_IF([test "x$enable_neon" != "xno"], [
|
||||
case "$host_cpu" in
|
||||
arm|armv7*)
|
||||
dnl Test for NEON support with no flags.
|
||||
AC_CHECK_HEADER([arm_neon.h],
|
||||
[AC_DEFINE(WEBP_HAVE_NEON, [1],
|
||||
[Set to 1 if NEON is supported])],
|
||||
dnl Test for NEON support using -mfpu=neon
|
||||
[unset ac_cv_header_arm_neon_h
|
||||
NEON_FLAGS="$INTRINSICS_CFLAGS $NEON_FLAGS"
|
||||
TEST_AND_ADD_CFLAGS([NEON_FLAGS], [-mfpu=neon])
|
||||
AS_IF([test -n "$NEON_FLAGS"], [
|
||||
SAVED_CFLAGS=$CFLAGS
|
||||
CFLAGS="$CFLAGS $NEON_FLAGS"
|
||||
AC_CHECK_HEADER([arm_neon.h],
|
||||
[AS_IF([test "${host_os%%-*}" = "linux" -o \
|
||||
"x$enable_neon_rtcd" = "xno"], [
|
||||
AC_DEFINE(WEBP_HAVE_NEON, [1],
|
||||
[Set to 1 if NEON is supported])],
|
||||
[AC_MSG_WARN(m4_normalize([NEON runtime
|
||||
cpu-detection is unavailble for
|
||||
${host_os%%-*}. Force with
|
||||
CFLAGS=-mfpu=neon or
|
||||
--disable-neon-rtcd.]))
|
||||
enable_neon_rtcd=no
|
||||
NEON_FLAGS=""])],
|
||||
[NEON_FLAGS=""])
|
||||
CFLAGS=$SAVED_CFLAGS
|
||||
AS_IF([test -n "$NEON_FLAGS"], [
|
||||
dnl If NEON is available and rtcd is disabled apply
|
||||
dnl NEON_FLAGS globally.
|
||||
AS_IF([test "x$enable_neon_rtcd" = "xno"], [
|
||||
AM_CFLAGS="$AM_CFLAGS $NEON_FLAGS"
|
||||
NEON_FLAGS=""],
|
||||
[AC_DEFINE(WEBP_HAVE_NEON_RTCD, [1],
|
||||
[Set to 1 if runtime detection of NEON
|
||||
is enabled])])])])])
|
||||
# Test for NEON support without flags before falling back to -mfpu=neon
|
||||
for flag in '' '-mfpu=neon'; do
|
||||
LOCAL_NEON_FLAGS="$INTRINSICS_CFLAGS $NEON_FLAGS"
|
||||
TEST_AND_ADD_CFLAGS([LOCAL_NEON_FLAGS], [$flag])
|
||||
SAVED_CFLAGS=$CFLAGS
|
||||
CFLAGS="$CFLAGS $LOCAL_NEON_FLAGS"
|
||||
|
||||
dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition.
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <arm_neon.h>
|
||||
int main(void) {
|
||||
int8x8_t v = vdup_n_s8(0);
|
||||
(void)v;
|
||||
return 0;
|
||||
}])],
|
||||
[NEON_FLAGS="$(echo $LOCAL_NEON_FLAGS | $SED 's/^ *//')"
|
||||
AS_IF([test -n "$NEON_FLAGS"], [
|
||||
AS_IF([test "${host_os%%-*}" = "linux" -o \
|
||||
"x$enable_neon_rtcd" = "xno"], [
|
||||
CFLAGS=$SAVED_CFLAGS
|
||||
AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
|
||||
break
|
||||
],[
|
||||
AC_MSG_WARN(m4_normalize([NEON runtime cpu-detection is
|
||||
unavailable for ${host_os%%-*}. Force
|
||||
with CFLAGS=-mfpu=neon or
|
||||
--disable-neon-rtcd.]))
|
||||
enable_neon_rtcd=no
|
||||
NEON_FLAGS=""
|
||||
])
|
||||
],[
|
||||
CFLAGS=$SAVED_CFLAGS
|
||||
AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
|
||||
break
|
||||
])])
|
||||
CFLAGS=$SAVED_CFLAGS
|
||||
done
|
||||
|
||||
AS_IF([test -n "$NEON_FLAGS"], [
|
||||
# If NEON is available and rtcd is disabled apply NEON_FLAGS globally.
|
||||
AS_IF([test "x$enable_neon_rtcd" = "xno"], [
|
||||
AM_CFLAGS="$AM_CFLAGS $NEON_FLAGS"
|
||||
NEON_FLAGS=""],
|
||||
[AC_DEFINE(WEBP_HAVE_NEON_RTCD, [1],
|
||||
[Set to 1 if runtime detection of NEON is enabled])])])
|
||||
;;
|
||||
esac
|
||||
AC_SUBST([NEON_FLAGS])])
|
||||
|
@ -429,6 +429,11 @@ int8 ColorTransformDelta(int8 t, int8 c) {
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A conversion from the 8-bit unsigned representation (uint8) to the 8-bit
|
||||
signed one (int8) is required before calling ColorTransformDelta().
|
||||
It should be performed using 8-bit two's complement (that is: uint8 range
|
||||
\[128-255\] is mapped to the \[-128, -1\] range of its converted int8 value).
|
||||
|
||||
The multiplication is to be done using more precision (with at least
|
||||
16-bit dynamics). The sign extension property of the shift operation
|
||||
does not matter here: only the lowest 8 bits are used from the result,
|
||||
|
@ -110,6 +110,20 @@ static int CompareValues(uint32_t a, uint32_t b, const char* output_str) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) {
|
||||
if (premultiply) {
|
||||
const int alpha1 = (bg1 >> 24) & 0xff;
|
||||
const int alpha2 = (bg2 >> 24) & 0xff;
|
||||
if (alpha1 == 0 && alpha2 == 0) return 1;
|
||||
}
|
||||
if (bg1 != bg2) {
|
||||
fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n",
|
||||
bg1, bg2);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Note: As long as frame durations and reconstructed frames are identical, it
|
||||
// is OK for other aspects like offsets, dispose/blend method to vary.
|
||||
static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||
@ -131,8 +145,8 @@ static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||
if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
|
||||
ok = CompareValues(img1->loop_count, img2->loop_count,
|
||||
"Loop count mismatch") && ok;
|
||||
ok = CompareValues(img1->bgcolor, img2->bgcolor,
|
||||
"Background color mismatch") && ok;
|
||||
ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
|
||||
premultiply) && ok;
|
||||
}
|
||||
|
||||
for (i = 0; i < img1->num_frames; ++i) {
|
||||
|
@ -39,13 +39,24 @@ static int IsFullFrame(int width, int height,
|
||||
return (width == canvas_width && height == canvas_height);
|
||||
}
|
||||
|
||||
static int CheckSizeForOverflow(uint64_t size) {
|
||||
return (size == (size_t)size);
|
||||
}
|
||||
|
||||
static int AllocateFrames(AnimatedImage* const image, uint32_t num_frames) {
|
||||
uint32_t i;
|
||||
const size_t rgba_size =
|
||||
image->canvas_width * kNumChannels * image->canvas_height;
|
||||
uint8_t* const mem = (uint8_t*)malloc(num_frames * rgba_size * sizeof(*mem));
|
||||
DecodedFrame* const frames =
|
||||
(DecodedFrame*)malloc(num_frames * sizeof(*frames));
|
||||
uint8_t* mem = NULL;
|
||||
DecodedFrame* frames = NULL;
|
||||
const uint64_t rgba_size =
|
||||
(uint64_t)image->canvas_width * kNumChannels * image->canvas_height;
|
||||
const uint64_t total_size = (uint64_t)num_frames * rgba_size * sizeof(*mem);
|
||||
const uint64_t total_frame_size = (uint64_t)num_frames * sizeof(*frames);
|
||||
if (!CheckSizeForOverflow(total_size) ||
|
||||
!CheckSizeForOverflow(total_frame_size)) {
|
||||
return 0;
|
||||
}
|
||||
mem = (uint8_t*)malloc((size_t)total_size);
|
||||
frames = (DecodedFrame*)malloc((size_t)total_frame_size);
|
||||
|
||||
if (mem == NULL || frames == NULL) {
|
||||
free(mem);
|
||||
@ -265,6 +276,8 @@ static int ReadAnimatedWebP(const char filename[],
|
||||
// -----------------------------------------------------------------------------
|
||||
// GIF Decoding.
|
||||
|
||||
#ifdef WEBP_HAVE_GIF
|
||||
|
||||
// Returns true if this is a valid GIF bitstream.
|
||||
static int IsGIF(const WebPData* const data) {
|
||||
return data->size > GIF_STAMP_LEN &&
|
||||
@ -273,8 +286,6 @@ static int IsGIF(const WebPData* const data) {
|
||||
!memcmp(GIF89_STAMP, data->bytes, GIF_STAMP_LEN));
|
||||
}
|
||||
|
||||
#ifdef WEBP_HAVE_GIF
|
||||
|
||||
// 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)
|
||||
@ -396,7 +407,7 @@ static uint32_t GetBackgroundColorGIF(GifFileType* gif) {
|
||||
const ColorMapObject* const color_map = gif->SColorMap;
|
||||
if (transparent_index != NO_TRANSPARENT_COLOR &&
|
||||
gif->SBackGroundColor == transparent_index) {
|
||||
return 0x00ffffff; // Special case: transparent white.
|
||||
return 0x00000000; // Special case: transparent black.
|
||||
} else if (color_map == NULL || color_map->Colors == NULL
|
||||
|| gif->SBackGroundColor >= color_map->ColorCount) {
|
||||
return 0xffffffff; // Invalid: assume white.
|
||||
@ -665,6 +676,11 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
|
||||
#else
|
||||
|
||||
static int IsGIF(const WebPData* const data) {
|
||||
(void)data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
int dump_frames, const char dump_folder[]) {
|
||||
(void)filename;
|
||||
|
@ -516,9 +516,10 @@ 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)\n");
|
||||
printf(" -alpha_q <int> ......... transparency-compression quality "
|
||||
"(0..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");
|
||||
@ -526,16 +527,20 @@ static void HelpLong(void) {
|
||||
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)\n");
|
||||
printf(" -segments <int> ........ number of segments to use (1..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)\n");
|
||||
printf(" -f <int> ............... filter strength (0=off..100)\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)\n");
|
||||
"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");
|
||||
@ -552,19 +557,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)\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"
|
||||
"\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\n");
|
||||
printf(" -lossless .............. encode image losslessly, default=off\n");
|
||||
printf(" -near_lossless <int> ... use near-lossless image\n"
|
||||
" preprocessing (0..100=off)\n");
|
||||
" preprocessing (0..100=off), "
|
||||
"default=100\n");
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
printf(" -delta_palettization ... use delta palettization\n");
|
||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||
|
@ -279,3 +279,10 @@ void ExUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
|
||||
const uint64_t total_size = nmemb * size;
|
||||
return (total_size == (size_t)total_size);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -59,6 +59,11 @@ int ExUtilWriteFile(const char* const file_name,
|
||||
void ExUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns 0 in case of overflow of nmemb * size.
|
||||
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t nmemb, size_t size);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebP decoding
|
||||
|
||||
|
@ -359,6 +359,8 @@ int main(int argc, const char *argv[]) {
|
||||
if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) {
|
||||
goto End;
|
||||
}
|
||||
if (data == NULL) continue;
|
||||
|
||||
switch (extension) {
|
||||
case COMMENT_EXT_FUNC_CODE: {
|
||||
break; // Do nothing for now.
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux_types.h"
|
||||
|
||||
#define GIF_TRANSPARENT_COLOR 0x00ffffff
|
||||
#define GIF_TRANSPARENT_COLOR 0x00000000
|
||||
#define GIF_WHITE_COLOR 0xffffffff
|
||||
#define GIF_TRANSPARENT_MASK 0x01
|
||||
#define GIF_DISPOSE_MASK 0x07
|
||||
@ -81,20 +81,27 @@ int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void Remap(const GifFileType* const gif, const uint8_t* const src,
|
||||
int len, int transparent_index, uint32_t* dst) {
|
||||
static int Remap(const GifFileType* const gif, const uint8_t* const src,
|
||||
int len, int transparent_index, uint32_t* dst) {
|
||||
int i;
|
||||
const GifColorType* colors;
|
||||
const ColorMapObject* const cmap =
|
||||
gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
|
||||
if (cmap == NULL) return;
|
||||
if (cmap == NULL) return 1;
|
||||
if (cmap->Colors == NULL || cmap->ColorCount <= 0) return 0;
|
||||
colors = cmap->Colors;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
const GifColorType c = colors[src[i]];
|
||||
dst[i] = (src[i] == transparent_index) ? GIF_TRANSPARENT_COLOR
|
||||
: c.Blue | (c.Green << 8) | (c.Red << 16) | (0xff << 24);
|
||||
if (src[i] == transparent_index) {
|
||||
dst[i] = GIF_TRANSPARENT_COLOR;
|
||||
} else if (src[i] < cmap->ColorCount) {
|
||||
const GifColorType c = colors[src[i]];
|
||||
dst[i] = c.Blue | (c.Green << 8) | (c.Red << 16) | (0xffu << 24);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
@ -103,12 +110,18 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
const GifImageDesc* const image_desc = &gif->Image;
|
||||
uint32_t* dst = NULL;
|
||||
uint8_t* tmp = NULL;
|
||||
int ok = 0;
|
||||
GIFFrameRect rect = {
|
||||
const GIFFrameRect rect = {
|
||||
image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
|
||||
};
|
||||
const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height;
|
||||
int ok = 0;
|
||||
*gif_rect = rect;
|
||||
|
||||
if (memory_needed != (size_t)memory_needed || memory_needed > (4ULL << 32)) {
|
||||
fprintf(stderr, "Image is too large (%d x %d).", rect.width, rect.height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Use a view for the sub-picture:
|
||||
if (!WebPPictureView(picture, rect.x_offset, rect.y_offset,
|
||||
rect.width, rect.height, &sub_image)) {
|
||||
@ -127,20 +140,21 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
const int interlace_jumps[] = { 8, 8, 4, 2 };
|
||||
int pass;
|
||||
for (pass = 0; pass < 4; ++pass) {
|
||||
int y;
|
||||
for (y = interlace_offsets[pass]; y < rect.height;
|
||||
y += interlace_jumps[pass]) {
|
||||
const size_t stride = (size_t)sub_image.argb_stride;
|
||||
int y = interlace_offsets[pass];
|
||||
uint32_t* row = dst + y * stride;
|
||||
const size_t jump = interlace_jumps[pass] * stride;
|
||||
for (; y < rect.height; y += interlace_jumps[pass], row += jump) {
|
||||
if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
|
||||
Remap(gif, tmp, rect.width, transparent_index,
|
||||
dst + y * sub_image.argb_stride);
|
||||
if (!Remap(gif, tmp, rect.width, transparent_index, row)) goto End;
|
||||
}
|
||||
}
|
||||
} else { // Non-interlaced image.
|
||||
int y;
|
||||
for (y = 0; y < rect.height; ++y) {
|
||||
uint32_t* ptr = dst;
|
||||
for (y = 0; y < rect.height; ++y, ptr += sub_image.argb_stride) {
|
||||
if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
|
||||
Remap(gif, tmp, rect.width, transparent_index,
|
||||
dst + y * sub_image.argb_stride);
|
||||
if (!Remap(gif, tmp, rect.width, transparent_index, ptr)) goto End;
|
||||
}
|
||||
}
|
||||
ok = 1;
|
||||
@ -216,13 +230,11 @@ int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
|
||||
|
||||
static void ClearRectangle(WebPPicture* const picture,
|
||||
int left, int top, int width, int height) {
|
||||
int j;
|
||||
for (j = top; j < top + height; ++j) {
|
||||
uint32_t* const dst = picture->argb + j * picture->argb_stride;
|
||||
int i;
|
||||
for (i = left; i < left + width; ++i) {
|
||||
dst[i] = GIF_TRANSPARENT_COLOR;
|
||||
}
|
||||
int i, j;
|
||||
const size_t stride = picture->argb_stride;
|
||||
uint32_t* dst = picture->argb + top * stride + left;
|
||||
for (j = 0; j < height; ++j, dst += stride) {
|
||||
for (i = 0; i < width; ++i) dst[i] = GIF_TRANSPARENT_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,29 +258,31 @@ void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
|
||||
if (dispose == GIF_DISPOSE_BACKGROUND) {
|
||||
GIFClearPic(curr_canvas, rect);
|
||||
} else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
|
||||
const int src_stride = prev_canvas->argb_stride;
|
||||
const uint32_t* const src =
|
||||
prev_canvas->argb + rect->x_offset + rect->y_offset * src_stride;
|
||||
const int dst_stride = curr_canvas->argb_stride;
|
||||
uint32_t* const dst =
|
||||
curr_canvas->argb + rect->x_offset + rect->y_offset * dst_stride;
|
||||
const size_t src_stride = prev_canvas->argb_stride;
|
||||
const uint32_t* const src = prev_canvas->argb + rect->x_offset
|
||||
+ rect->y_offset * src_stride;
|
||||
const size_t dst_stride = curr_canvas->argb_stride;
|
||||
uint32_t* const dst = curr_canvas->argb + rect->x_offset
|
||||
+ rect->y_offset * dst_stride;
|
||||
assert(prev_canvas != NULL);
|
||||
WebPCopyPlane((uint8_t*)src, 4 * src_stride, (uint8_t*)dst, 4 * dst_stride,
|
||||
WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride),
|
||||
(uint8_t*)dst, (int)(4 * dst_stride),
|
||||
4 * rect->width, rect->height);
|
||||
}
|
||||
}
|
||||
|
||||
void GIFBlendFrames(const WebPPicture* const src,
|
||||
const GIFFrameRect* const rect, WebPPicture* const dst) {
|
||||
int j;
|
||||
int i, j;
|
||||
const size_t src_stride = src->argb_stride;
|
||||
const size_t dst_stride = dst->argb_stride;
|
||||
assert(src->width == dst->width && src->height == dst->height);
|
||||
for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
|
||||
int i;
|
||||
for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
|
||||
const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
|
||||
const uint32_t src_pixel = src->argb[j * src_stride + i];
|
||||
const int src_alpha = src_pixel >> 24;
|
||||
if (src_alpha != 0) {
|
||||
dst->argb[j * dst->argb_stride + i] = src_pixel;
|
||||
dst->argb[j * dst_stride + i] = src_pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -222,10 +222,10 @@ static void ContextInit(j_decompress_ptr cinfo) {
|
||||
ctx->pub.bytes_in_buffer = ctx->data_size;
|
||||
}
|
||||
|
||||
static int ContextFill(j_decompress_ptr cinfo) {
|
||||
static boolean ContextFill(j_decompress_ptr cinfo) {
|
||||
// we shouldn't get here.
|
||||
ERREXIT(cinfo, JERR_FILE_READ);
|
||||
return 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
|
||||
@ -258,7 +258,8 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
volatile int ok = 0;
|
||||
int stride, width, height;
|
||||
int width, height;
|
||||
int64_t stride;
|
||||
volatile struct jpeg_decompress_struct dinfo;
|
||||
struct my_error_mgr jerr;
|
||||
uint8_t* volatile rgb = NULL;
|
||||
@ -297,9 +298,14 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
|
||||
width = dinfo.output_width;
|
||||
height = dinfo.output_height;
|
||||
stride = dinfo.output_width * dinfo.output_components * sizeof(*rgb);
|
||||
stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb);
|
||||
|
||||
rgb = (uint8_t*)malloc(stride * height);
|
||||
if (stride != (int)stride ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||
goto End;
|
||||
}
|
||||
|
||||
rgb = (uint8_t*)malloc((size_t)stride * height);
|
||||
if (rgb == NULL) {
|
||||
goto End;
|
||||
}
|
||||
@ -326,7 +332,7 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
// WebP conversion.
|
||||
pic->width = width;
|
||||
pic->height = height;
|
||||
ok = WebPPictureImportRGB(pic, rgb, stride);
|
||||
ok = WebPPictureImportRGB(pic, rgb, (int)stride);
|
||||
if (!ok) goto Error;
|
||||
|
||||
End:
|
||||
|
@ -217,7 +217,7 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
int p;
|
||||
volatile int ok = 0;
|
||||
png_uint_32 width, height, y;
|
||||
png_uint_32 stride;
|
||||
int64_t stride;
|
||||
uint8_t* volatile rgb = NULL;
|
||||
|
||||
context.data = data;
|
||||
@ -270,8 +270,14 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
|
||||
num_passes = png_set_interlace_handling(png);
|
||||
png_read_update_info(png, info);
|
||||
stride = (has_alpha ? 4 : 3) * width * sizeof(*rgb);
|
||||
rgb = (uint8_t*)malloc(stride * height);
|
||||
|
||||
stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb);
|
||||
if (stride != (int)stride ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
rgb = (uint8_t*)malloc((size_t)stride * height);
|
||||
if (rgb == NULL) goto Error;
|
||||
for (p = 0; p < num_passes; ++p) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <tiffio.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./example_util.h"
|
||||
#include "./metadata.h"
|
||||
|
||||
static const struct {
|
||||
@ -124,6 +125,7 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
MySize, MyMapFile, MyUnmapFile);
|
||||
uint32 width, height;
|
||||
uint32* raster;
|
||||
int64_t alloc_size;
|
||||
int ok = 0;
|
||||
tdir_t dircount;
|
||||
|
||||
@ -144,7 +146,16 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
|
||||
goto End;
|
||||
}
|
||||
raster = (uint32*)_TIFFmalloc(width * height * sizeof(*raster));
|
||||
|
||||
if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)width * height,
|
||||
sizeof(*raster))) {
|
||||
goto End;
|
||||
}
|
||||
// _Tiffmalloc uses a signed type for size.
|
||||
alloc_size = (int64_t)((uint64_t)width * height * sizeof(*raster));
|
||||
if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
|
||||
|
||||
raster = (uint32*)_TIFFmalloc((tsize_t)alloc_size);
|
||||
if (raster != NULL) {
|
||||
if (TIFFReadRGBAImageOriented(tif, width, height, raster,
|
||||
ORIENTATION_TOPLEFT, 1)) {
|
||||
|
@ -14,6 +14,10 @@
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#if defined(__unix__) || defined(__CYGWIN__)
|
||||
#define _POSIX_C_SOURCE 200112L // for setenv
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -63,6 +67,7 @@ static struct {
|
||||
WebPIterator curr_frame;
|
||||
WebPIterator prev_frame;
|
||||
WebPChunkIterator iccp;
|
||||
int viewport_width, viewport_height;
|
||||
} kParams;
|
||||
|
||||
static void ClearPreviousPic(void) {
|
||||
@ -242,18 +247,24 @@ static void HandleKey(unsigned char key, int pos_x, int pos_y) {
|
||||
}
|
||||
} else if (key == 'i') {
|
||||
kParams.print_info = 1 - kParams.print_info;
|
||||
// TODO(skal): handle refresh of animation's last-frame too. It's quite
|
||||
// more involved though (need to save the previous frame).
|
||||
if (!kParams.has_animation) ClearPreviousFrame();
|
||||
glutPostRedisplay();
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleReshape(int width, int height) {
|
||||
// TODO(skal): proper handling of resize, esp. for large pictures.
|
||||
// + key control of the zoom.
|
||||
// TODO(skal): should we preserve aspect ratio?
|
||||
// Also: handle larger-than-screen pictures correctly.
|
||||
glViewport(0, 0, width, height);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
kParams.viewport_width = width;
|
||||
kParams.viewport_height = height;
|
||||
if (!kParams.has_animation) ClearPreviousFrame();
|
||||
}
|
||||
|
||||
static void PrintString(const char* const text) {
|
||||
@ -295,7 +306,8 @@ static void HandleDisplay(void) {
|
||||
GLfloat xoff, yoff;
|
||||
if (pic == NULL) return;
|
||||
glPushMatrix();
|
||||
glPixelZoom(1, -1);
|
||||
glPixelZoom((GLfloat)(+1. / kParams.canvas_width * kParams.viewport_width),
|
||||
(GLfloat)(-1. / kParams.canvas_height * kParams.viewport_height));
|
||||
xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width);
|
||||
yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
|
||||
glRasterPos2f(-1.f + xoff, 1.f - yoff);
|
||||
@ -304,8 +316,6 @@ static void HandleDisplay(void) {
|
||||
|
||||
if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ||
|
||||
curr->blend_method == WEBP_MUX_NO_BLEND) {
|
||||
// TODO(later): these offsets and those above should factor in window size.
|
||||
// they will be incorrect if the window is resized.
|
||||
// glScissor() takes window coordinates (0,0 at bottom left).
|
||||
int window_x, window_y;
|
||||
int frame_w, frame_h;
|
||||
@ -325,6 +335,10 @@ static void HandleDisplay(void) {
|
||||
}
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
// Only update the requested area, not the whole canvas.
|
||||
window_x = window_x * kParams.viewport_width / kParams.canvas_width;
|
||||
window_y = window_y * kParams.viewport_height / kParams.canvas_height;
|
||||
frame_w = frame_w * kParams.viewport_width / kParams.canvas_width;
|
||||
frame_h = frame_h * kParams.viewport_height / kParams.canvas_height;
|
||||
glScissor(window_x, window_y, frame_w, frame_h);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT); // use clear color
|
||||
@ -367,6 +381,7 @@ static void StartDisplay(void) {
|
||||
glutInitWindowSize(width, height);
|
||||
glutCreateWindow("WebP viewer");
|
||||
glutDisplayFunc(HandleDisplay);
|
||||
glutReshapeFunc(HandleReshape);
|
||||
glutIdleFunc(NULL);
|
||||
glutKeyboardFunc(HandleKey);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
@ -375,7 +390,6 @@ static void StartDisplay(void) {
|
||||
GetColorf(kParams.bg_color, 8),
|
||||
GetColorf(kParams.bg_color, 16),
|
||||
GetColorf(kParams.bg_color, 24));
|
||||
HandleReshape(width, height);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
DrawCheckerBoard();
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
NULL
|
||||
};
|
||||
int has_alpha = 0;
|
||||
int stride;
|
||||
int64_t stride;
|
||||
|
||||
IFS(CoInitialize(NULL));
|
||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||
@ -334,14 +334,19 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
|
||||
// Decode.
|
||||
IFS(IWICFormatConverter_GetSize(converter, &width, &height));
|
||||
stride = importer->bytes_per_pixel * width * sizeof(*rgb);
|
||||
stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb);
|
||||
if (stride != (int)stride ||
|
||||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
rgb = (BYTE*)malloc(stride * height);
|
||||
rgb = (BYTE*)malloc((size_t)stride * height);
|
||||
if (rgb == NULL)
|
||||
hr = E_OUTOFMEMORY;
|
||||
}
|
||||
IFS(IWICFormatConverter_CopyPixels(converter, NULL,
|
||||
stride, stride * height, rgb));
|
||||
(UINT)stride, (UINT)stride * height, rgb));
|
||||
|
||||
// WebP conversion.
|
||||
if (SUCCEEDED(hr)) {
|
||||
@ -349,7 +354,7 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
pic->width = width;
|
||||
pic->height = height;
|
||||
pic->use_argb = 1; // For WIC, we always force to argb
|
||||
ok = importer->import(pic, rgb, stride);
|
||||
ok = importer->import(pic, rgb, (int)stride);
|
||||
if (!ok) hr = E_FAIL;
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
|
25
iosbuild.sh
25
iosbuild.sh
@ -1,10 +1,11 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This script generates 'WebP.framework'. An iOS app can decode WebP images
|
||||
# by including 'WebP.framework'.
|
||||
# This script generates 'WebP.framework' and 'WebPDecoder.framework'. An iOS
|
||||
# app can decode WebP images by including 'WebPDecoder.framework' and both
|
||||
# encode and decode WebP images by including 'WebP.framework'.
|
||||
#
|
||||
# Run ./iosbuild.sh to generate 'WebP.framework' under the current directory
|
||||
# (previous build will be erased if it exists).
|
||||
# Run ./iosbuild.sh to generate the frameworks under the current directory
|
||||
# (the previous build will be erased if it exists).
|
||||
#
|
||||
# This script is inspired by the build script written by Carson McDonald.
|
||||
# (http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/).
|
||||
@ -33,10 +34,12 @@ readonly SRCDIR=$(dirname $0)
|
||||
readonly TOPDIR=$(pwd)
|
||||
readonly BUILDDIR="${TOPDIR}/iosbuild"
|
||||
readonly TARGETDIR="${TOPDIR}/WebP.framework"
|
||||
readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.framework"
|
||||
readonly DEVELOPER=$(xcode-select --print-path)
|
||||
readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
|
||||
readonly LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
|
||||
LIBLIST=''
|
||||
DECLIBLIST=''
|
||||
|
||||
if [[ -z "${SDK}" ]]; then
|
||||
echo "iOS SDK not available"
|
||||
@ -50,10 +53,8 @@ else
|
||||
echo "iOS SDK Version ${SDK}"
|
||||
fi
|
||||
|
||||
rm -rf ${BUILDDIR}
|
||||
rm -rf ${TARGETDIR}
|
||||
mkdir -p ${BUILDDIR}
|
||||
mkdir -p ${TARGETDIR}/Headers/
|
||||
rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR}
|
||||
mkdir -p ${BUILDDIR} ${TARGETDIR}/Headers/ ${DECTARGETDIR}/Headers/
|
||||
|
||||
if [[ ! -e ${SRCDIR}/configure ]]; then
|
||||
if ! (cd ${SRCDIR} && sh autogen.sh); then
|
||||
@ -107,12 +108,13 @@ for PLATFORM in ${PLATFORMS}; do
|
||||
CFLAGS="${CFLAGS}"
|
||||
set +x
|
||||
|
||||
# run make only in the src/ directory to create libwebpdecoder.a
|
||||
# run make only in the src/ directory to create libwebp.a/libwebpdecoder.a
|
||||
cd src/
|
||||
make V=0
|
||||
make install
|
||||
|
||||
LIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
|
||||
LIBLIST+=" ${ROOTDIR}/lib/libwebp.a"
|
||||
DECLIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
|
||||
|
||||
make clean
|
||||
cd ..
|
||||
@ -122,3 +124,6 @@ done
|
||||
|
||||
cp -a ${SRCDIR}/src/webp/*.h ${TARGETDIR}/Headers/
|
||||
${LIPO} -create ${LIBLIST} -output ${TARGETDIR}/WebP
|
||||
|
||||
cp -a ${SRCDIR}/src/webp/*.h ${DECTARGETDIR}/Headers/
|
||||
${LIPO} -create ${DECLIBLIST} -output ${DECTARGETDIR}/WebPDecoder
|
||||
|
14
man/cwebp.1
14
man/cwebp.1
@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH CWEBP 1 "June 23, 2016"
|
||||
.TH CWEBP 1 "September 02, 2016"
|
||||
.SH NAME
|
||||
cwebp \- compress an image file to a WebP file
|
||||
.SH SYNOPSIS
|
||||
@ -117,15 +117,15 @@ or without alpha).
|
||||
.TP
|
||||
.BI \-size " int
|
||||
Specify a target size (in bytes) to try and reach for the compressed output.
|
||||
Compressor will make several pass of partial encoding in order to get as
|
||||
The compressor will make several passes of partial encoding in order to get as
|
||||
close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP
|
||||
as used, \fB\-size\fP value will prevail.
|
||||
are used, \fB\-size\fP value will prevail.
|
||||
.TP
|
||||
.BI \-psnr " float
|
||||
Specify a target PSNR (in dB) to try and reach for the compressed output.
|
||||
Compressor will make several pass of partial encoding in order to get as
|
||||
The compressor will make several passes of partial encoding in order to get as
|
||||
close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP
|
||||
as used, \fB\-size\fP value will prevail.
|
||||
are used, \fB\-size\fP value will prevail.
|
||||
.TP
|
||||
.BI \-pass " int
|
||||
Set a maximum number of passes to use during the dichotomy used by
|
||||
@ -171,7 +171,7 @@ Specify the amplitude of the spatial noise shaping. Spatial noise shaping
|
||||
(or \fBsns\fP for short) refers to a general collection of built\-in algorithms
|
||||
used to decide which area of the picture should use relatively less bits,
|
||||
and where else to better transfer these bits. The possible range goes from
|
||||
0 (algorithm is off) to 100 (the maximal effect). The default value is 80.
|
||||
0 (algorithm is off) to 100 (the maximal effect). The default value is 50.
|
||||
.TP
|
||||
.BI \-segments " int
|
||||
Change the number of partitions to use during the segmentation of the
|
||||
@ -222,7 +222,7 @@ Report encoding progress in percent.
|
||||
Do not print anything.
|
||||
.TP
|
||||
.B \-short
|
||||
Only print brief information (output file size and PSNR) for testing purpose.
|
||||
Only print brief information (output file size and PSNR) for testing purposes.
|
||||
.TP
|
||||
.BI \-map " int
|
||||
Output additional ASCII\-map of encoding information. Possible map values
|
||||
|
@ -38,7 +38,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 6:1:0
|
||||
libwebp_la_LDFLAGS = -no-undefined -version-info 6:2:0
|
||||
libwebpincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebp.pc
|
||||
|
||||
@ -50,7 +50,7 @@ if BUILD_LIBWEBPDECODER
|
||||
libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
|
||||
libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
|
||||
|
||||
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 2:1:0
|
||||
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 2:2:0
|
||||
pkgconfig_DATA += libwebpdecoder.pc
|
||||
endif
|
||||
|
||||
|
@ -67,7 +67,7 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
}
|
||||
|
||||
dec->method_ = (data[0] >> 0) & 0x03;
|
||||
dec->filter_ = (data[0] >> 2) & 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 ||
|
||||
|
@ -32,7 +32,7 @@ extern "C" {
|
||||
// version numbers
|
||||
#define DEC_MAJ_VERSION 0
|
||||
#define DEC_MIN_VERSION 5
|
||||
#define DEC_REV_VERSION 1
|
||||
#define DEC_REV_VERSION 2
|
||||
|
||||
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||
// Constraints are: We need to store one 16x16 block of luma samples (y),
|
||||
|
204
src/dec/vp8l.c
204
src/dec/vp8l.c
@ -252,11 +252,11 @@ static int ReadHuffmanCodeLengths(
|
||||
int symbol;
|
||||
int max_symbol;
|
||||
int prev_code_len = DEFAULT_CODE_LENGTH;
|
||||
HuffmanCode table[1 << LENGTHS_TABLE_BITS];
|
||||
HuffmanTables tables;
|
||||
|
||||
if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS,
|
||||
code_length_code_lengths,
|
||||
NUM_CODE_LENGTH_CODES)) {
|
||||
if (!VP8LHuffmanTablesAllocate(1 << LENGTHS_TABLE_BITS, &tables) ||
|
||||
!VP8LBuildHuffmanTable(&tables, LENGTHS_TABLE_BITS,
|
||||
code_length_code_lengths, NUM_CODE_LENGTH_CODES)) {
|
||||
goto End;
|
||||
}
|
||||
|
||||
@ -276,7 +276,7 @@ static int ReadHuffmanCodeLengths(
|
||||
int code_len;
|
||||
if (max_symbol-- == 0) break;
|
||||
VP8LFillBitWindow(br);
|
||||
p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
|
||||
p = &tables.curr_segment->start[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
|
||||
VP8LSetBitPos(br, br->bit_pos_ + p->bits);
|
||||
code_len = p->value;
|
||||
if (code_len < kCodeLengthLiterals) {
|
||||
@ -299,6 +299,7 @@ static int ReadHuffmanCodeLengths(
|
||||
ok = 1;
|
||||
|
||||
End:
|
||||
VP8LHuffmanTablesDeallocate(&tables);
|
||||
if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
return ok;
|
||||
}
|
||||
@ -306,7 +307,8 @@ static int ReadHuffmanCodeLengths(
|
||||
// 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman
|
||||
// tree.
|
||||
static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
|
||||
int* const code_lengths, HuffmanCode* const table) {
|
||||
int* const code_lengths,
|
||||
HuffmanTables* const table) {
|
||||
int ok = 0;
|
||||
int size = 0;
|
||||
VP8LBitReader* const br = &dec->br_;
|
||||
@ -361,12 +363,18 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
VP8LMetadata* const hdr = &dec->hdr_;
|
||||
uint32_t* huffman_image = NULL;
|
||||
HTreeGroup* htree_groups = NULL;
|
||||
HuffmanCode* huffman_tables = NULL;
|
||||
HuffmanCode* next = NULL;
|
||||
HuffmanTables* huffman_tables = &hdr->huffman_tables_;
|
||||
int num_htree_groups = 1;
|
||||
int num_htree_groups_max = 1;
|
||||
int max_alphabet_size = 0;
|
||||
int* code_lengths = NULL;
|
||||
const int table_size = kTableSize[color_cache_bits];
|
||||
int* mapping = NULL;
|
||||
int ok = 0;
|
||||
|
||||
// Check the table has been 0 initialized (through InitMetadata).
|
||||
assert(huffman_tables->root.start == NULL);
|
||||
assert(huffman_tables->curr_segment == NULL);
|
||||
|
||||
if (allow_recursion && VP8LReadBits(br, 1)) {
|
||||
// use meta Huffman codes.
|
||||
@ -383,10 +391,36 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
// The huffman data is stored in red and green bytes.
|
||||
const int group = (huffman_image[i] >> 8) & 0xffff;
|
||||
huffman_image[i] = group;
|
||||
if (group >= num_htree_groups) {
|
||||
num_htree_groups = group + 1;
|
||||
if (group >= num_htree_groups_max) {
|
||||
num_htree_groups_max = group + 1;
|
||||
}
|
||||
}
|
||||
// Check the validity of num_htree_groups_max. If it seems too big, use a
|
||||
// smaller value for later. This will prevent big memory allocations to end
|
||||
// up with a bad bitstream anyway.
|
||||
// The value of 1000 is totally arbitrary. We know that num_htree_groups_max
|
||||
// is smaller than (1 << 16) and should be smaller than the number of pixels
|
||||
// (though the format allows it to be bigger).
|
||||
if (num_htree_groups_max > 1000 || num_htree_groups_max > xsize * ysize) {
|
||||
// Create a mapping from the used indices to the minimal set of used
|
||||
// values [0, num_htree_groups)
|
||||
mapping = (int*)WebPSafeMalloc(num_htree_groups_max, sizeof(*mapping));
|
||||
if (mapping == NULL) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
goto Error;
|
||||
}
|
||||
// -1 means a value is unmapped, and therefore unused in the Huffman
|
||||
// image.
|
||||
memset(mapping, 0xff, num_htree_groups_max * sizeof(*mapping));
|
||||
for (num_htree_groups = 0, i = 0; i < huffman_pixs; ++i) {
|
||||
// Get the current mapping for the group and remap the Huffman image.
|
||||
int* const mapped_group = &mapping[huffman_image[i]];
|
||||
if (*mapped_group == -1) *mapped_group = num_htree_groups++;
|
||||
huffman_image[i] = *mapped_group;
|
||||
}
|
||||
} else {
|
||||
num_htree_groups = num_htree_groups_max;
|
||||
}
|
||||
}
|
||||
|
||||
if (br->eos_) goto Error;
|
||||
@ -402,83 +436,99 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
}
|
||||
}
|
||||
|
||||
huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size,
|
||||
sizeof(*huffman_tables));
|
||||
htree_groups = VP8LHtreeGroupsNew(num_htree_groups);
|
||||
code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size,
|
||||
sizeof(*code_lengths));
|
||||
|
||||
if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) {
|
||||
if (htree_groups == NULL || code_lengths == NULL ||
|
||||
!VP8LHuffmanTablesAllocate(num_htree_groups * table_size,
|
||||
huffman_tables)) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
next = huffman_tables;
|
||||
for (i = 0; i < num_htree_groups; ++i) {
|
||||
HTreeGroup* const htree_group = &htree_groups[i];
|
||||
HuffmanCode** const htrees = htree_group->htrees;
|
||||
int size;
|
||||
int total_size = 0;
|
||||
int is_trivial_literal = 1;
|
||||
int max_bits = 0;
|
||||
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
|
||||
int alphabet_size = kAlphabetSize[j];
|
||||
htrees[j] = next;
|
||||
if (j == 0 && color_cache_bits > 0) {
|
||||
alphabet_size += 1 << color_cache_bits;
|
||||
}
|
||||
size = ReadHuffmanCode(alphabet_size, dec, code_lengths, next);
|
||||
if (size == 0) {
|
||||
goto Error;
|
||||
}
|
||||
if (is_trivial_literal && kLiteralMap[j] == 1) {
|
||||
is_trivial_literal = (next->bits == 0);
|
||||
}
|
||||
total_size += next->bits;
|
||||
next += size;
|
||||
if (j <= ALPHA) {
|
||||
int local_max_bits = code_lengths[0];
|
||||
int k;
|
||||
for (k = 1; k < alphabet_size; ++k) {
|
||||
if (code_lengths[k] > local_max_bits) {
|
||||
local_max_bits = code_lengths[k];
|
||||
}
|
||||
for (i = 0; i < num_htree_groups_max; ++i) {
|
||||
// If the index "i" is unused in the Huffman image, just make sure the
|
||||
// coefficients are valid but do not store them.
|
||||
if (mapping != NULL && mapping[i] == -1) {
|
||||
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
|
||||
int alphabet_size = kAlphabetSize[j];
|
||||
if (j == 0 && color_cache_bits > 0) {
|
||||
alphabet_size += (1 << color_cache_bits);
|
||||
}
|
||||
// Passing in NULL so that nothing gets filled.
|
||||
if (!ReadHuffmanCode(alphabet_size, dec, code_lengths, NULL)) {
|
||||
goto Error;
|
||||
}
|
||||
max_bits += local_max_bits;
|
||||
}
|
||||
}
|
||||
htree_group->is_trivial_literal = is_trivial_literal;
|
||||
htree_group->is_trivial_code = 0;
|
||||
if (is_trivial_literal) {
|
||||
const int red = htrees[RED][0].value;
|
||||
const int blue = htrees[BLUE][0].value;
|
||||
const int alpha = htrees[ALPHA][0].value;
|
||||
htree_group->literal_arb =
|
||||
((uint32_t)alpha << 24) | (red << 16) | blue;
|
||||
if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) {
|
||||
htree_group->is_trivial_code = 1;
|
||||
htree_group->literal_arb |= htrees[GREEN][0].value << 8;
|
||||
} else {
|
||||
HTreeGroup* const htree_group =
|
||||
&htree_groups[(mapping == NULL) ? i : mapping[i]];
|
||||
HuffmanCode** const htrees = htree_group->htrees;
|
||||
int size;
|
||||
int total_size = 0;
|
||||
int is_trivial_literal = 1;
|
||||
int max_bits = 0;
|
||||
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
|
||||
int alphabet_size = kAlphabetSize[j];
|
||||
if (j == 0 && color_cache_bits > 0) {
|
||||
alphabet_size += (1 << color_cache_bits);
|
||||
}
|
||||
size =
|
||||
ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_tables);
|
||||
htrees[j] = huffman_tables->curr_segment->curr_table;
|
||||
if (size == 0) {
|
||||
goto Error;
|
||||
}
|
||||
if (is_trivial_literal && kLiteralMap[j] == 1) {
|
||||
is_trivial_literal = (htrees[j]->bits == 0);
|
||||
}
|
||||
total_size += htrees[j]->bits;
|
||||
huffman_tables->curr_segment->curr_table += size;
|
||||
if (j <= ALPHA) {
|
||||
int local_max_bits = code_lengths[0];
|
||||
int k;
|
||||
for (k = 1; k < alphabet_size; ++k) {
|
||||
if (code_lengths[k] > local_max_bits) {
|
||||
local_max_bits = code_lengths[k];
|
||||
}
|
||||
}
|
||||
max_bits += local_max_bits;
|
||||
}
|
||||
}
|
||||
htree_group->is_trivial_literal = is_trivial_literal;
|
||||
htree_group->is_trivial_code = 0;
|
||||
if (is_trivial_literal) {
|
||||
const int red = htrees[RED][0].value;
|
||||
const int blue = htrees[BLUE][0].value;
|
||||
const int alpha = htrees[ALPHA][0].value;
|
||||
htree_group->literal_arb = ((uint32_t)alpha << 24) | (red << 16) | blue;
|
||||
if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) {
|
||||
htree_group->is_trivial_code = 1;
|
||||
htree_group->literal_arb |= htrees[GREEN][0].value << 8;
|
||||
}
|
||||
}
|
||||
htree_group->use_packed_table =
|
||||
!htree_group->is_trivial_code && (max_bits < HUFFMAN_PACKED_BITS);
|
||||
if (htree_group->use_packed_table) BuildPackedTable(htree_group);
|
||||
}
|
||||
htree_group->use_packed_table = !htree_group->is_trivial_code &&
|
||||
(max_bits < HUFFMAN_PACKED_BITS);
|
||||
if (htree_group->use_packed_table) BuildPackedTable(htree_group);
|
||||
}
|
||||
WebPSafeFree(code_lengths);
|
||||
ok = 1;
|
||||
|
||||
// All OK. Finalize pointers and return.
|
||||
// All OK. Finalize pointers.
|
||||
hdr->huffman_image_ = huffman_image;
|
||||
hdr->num_htree_groups_ = num_htree_groups;
|
||||
hdr->htree_groups_ = htree_groups;
|
||||
hdr->huffman_tables_ = huffman_tables;
|
||||
return 1;
|
||||
|
||||
Error:
|
||||
WebPSafeFree(code_lengths);
|
||||
WebPSafeFree(huffman_image);
|
||||
WebPSafeFree(huffman_tables);
|
||||
VP8LHtreeGroupsFree(htree_groups);
|
||||
return 0;
|
||||
WebPSafeFree(mapping);
|
||||
if (!ok) {
|
||||
WebPSafeFree(huffman_image);
|
||||
VP8LHuffmanTablesDeallocate(huffman_tables);
|
||||
VP8LHtreeGroupsFree(htree_groups);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -1163,9 +1213,21 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
|
||||
assert(br->eos_ == VP8LIsEndOfStream(br));
|
||||
}
|
||||
|
||||
if (dec->incremental_ && br->eos_ && src < src_end) {
|
||||
br->eos_ = VP8LIsEndOfStream(br);
|
||||
// In incremental decoding:
|
||||
// br->eos_ && src < src_last: if 'br' reached the end of the buffer and
|
||||
// 'src_last' has not been reached yet, there is not enough data. 'dec' has to
|
||||
// be reset until there is more data.
|
||||
// !br->eos_ && src < src_last: this cannot happen as either the buffer is
|
||||
// fully read, either enough has been read to reach 'src_last'.
|
||||
// src >= src_last: 'src_last' is reached, all is fine. 'src' can actually go
|
||||
// beyond 'src_last' in case the image is cropped and an LZ77 goes further.
|
||||
// The buffer might have been enough or there is some left. 'br->eos_' does
|
||||
// not matter.
|
||||
assert(!dec->incremental_ || (br->eos_ && src < src_last) || src >= src_last);
|
||||
if (dec->incremental_ && br->eos_ && src < src_last) {
|
||||
RestoreState(dec);
|
||||
} else if (!br->eos_) {
|
||||
} else if ((dec->incremental_ && src >= src_last) || !br->eos_) {
|
||||
// Process the remaining rows corresponding to last row-block.
|
||||
if (process_func != NULL) {
|
||||
process_func(dec, row > last_row ? last_row : row);
|
||||
@ -1283,7 +1345,7 @@ static void ClearMetadata(VP8LMetadata* const hdr) {
|
||||
assert(hdr != NULL);
|
||||
|
||||
WebPSafeFree(hdr->huffman_image_);
|
||||
WebPSafeFree(hdr->huffman_tables_);
|
||||
VP8LHuffmanTablesDeallocate(&hdr->huffman_tables_);
|
||||
VP8LHtreeGroupsFree(hdr->htree_groups_);
|
||||
VP8LColorCacheClear(&hdr->color_cache_);
|
||||
VP8LColorCacheClear(&hdr->saved_color_cache_);
|
||||
@ -1598,7 +1660,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
|
||||
// Sanity checks.
|
||||
if (dec == NULL) return 0;
|
||||
|
||||
assert(dec->hdr_.huffman_tables_ != NULL);
|
||||
assert(dec->hdr_.huffman_tables_.root.start != NULL);
|
||||
assert(dec->hdr_.htree_groups_ != NULL);
|
||||
assert(dec->hdr_.num_htree_groups_ > 0);
|
||||
|
||||
|
@ -51,7 +51,7 @@ typedef struct {
|
||||
uint32_t *huffman_image_;
|
||||
int num_htree_groups_;
|
||||
HTreeGroup *htree_groups_;
|
||||
HuffmanCode *huffman_tables_;
|
||||
HuffmanTables huffman_tables_;
|
||||
} VP8LMetadata;
|
||||
|
||||
typedef struct VP8LDecoder VP8LDecoder;
|
||||
|
@ -9,6 +9,6 @@ libwebpdemuxinclude_HEADERS += ../webp/mux_types.h
|
||||
libwebpdemuxinclude_HEADERS += ../webp/types.h
|
||||
|
||||
libwebpdemux_la_LIBADD = ../libwebp.la
|
||||
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:0:0
|
||||
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:1:0
|
||||
libwebpdemuxincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebpdemux.pc
|
||||
|
@ -112,18 +112,15 @@ WebPAnimDecoder* WebPAnimDecoderNewInternal(
|
||||
dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR);
|
||||
dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT);
|
||||
|
||||
{
|
||||
const int canvas_bytes =
|
||||
dec->info_.canvas_width * NUM_CHANNELS * dec->info_.canvas_height;
|
||||
// Note: calloc() because we fill frame with zeroes as well.
|
||||
dec->curr_frame_ = WebPSafeCalloc(1ULL, canvas_bytes);
|
||||
if (dec->curr_frame_ == NULL) goto Error;
|
||||
dec->prev_frame_disposed_ = WebPSafeCalloc(1ULL, canvas_bytes);
|
||||
if (dec->prev_frame_disposed_ == NULL) goto Error;
|
||||
}
|
||||
// Note: calloc() because we fill frame with zeroes as well.
|
||||
dec->curr_frame_ = (uint8_t*)WebPSafeCalloc(
|
||||
dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
|
||||
if (dec->curr_frame_ == NULL) goto Error;
|
||||
dec->prev_frame_disposed_ = (uint8_t*)WebPSafeCalloc(
|
||||
dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
|
||||
if (dec->prev_frame_disposed_ == NULL) goto Error;
|
||||
|
||||
WebPAnimDecoderReset(dec);
|
||||
|
||||
return dec;
|
||||
|
||||
Error:
|
||||
@ -144,9 +141,13 @@ static int IsFullFrame(int width, int height, int canvas_width,
|
||||
}
|
||||
|
||||
// Clear the canvas to transparent.
|
||||
static void ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
|
||||
uint32_t canvas_height) {
|
||||
memset(buf, 0, canvas_width * NUM_CHANNELS * canvas_height);
|
||||
static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
|
||||
uint32_t canvas_height) {
|
||||
const uint64_t size =
|
||||
(uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf);
|
||||
if (size != (size_t)size) return 0;
|
||||
memset(buf, 0, (size_t)size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Clear given frame rectangle to transparent.
|
||||
@ -162,10 +163,13 @@ static void ZeroFillFrameRect(uint8_t* buf, int buf_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 int CopyCanvas(const uint8_t* src, uint8_t* dst,
|
||||
uint32_t width, uint32_t height) {
|
||||
const uint64_t size = (uint64_t)width * height * NUM_CHANNELS;
|
||||
if (size != (size_t)size) return 0;
|
||||
assert(src != NULL && dst != NULL);
|
||||
memcpy(dst, src, width * NUM_CHANNELS * height);
|
||||
memcpy(dst, src, (size_t)size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns true if the current frame is a key-frame.
|
||||
@ -328,9 +332,14 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_,
|
||||
dec->prev_frame_was_keyframe_, width, height);
|
||||
if (is_key_frame) {
|
||||
ZeroFillCanvas(dec->curr_frame_, width, height);
|
||||
if (!ZeroFillCanvas(dec->curr_frame_, width, height)) {
|
||||
goto Error;
|
||||
}
|
||||
} else {
|
||||
CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_, width, height);
|
||||
if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_,
|
||||
width, height)) {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode.
|
||||
@ -393,6 +402,7 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
|
||||
// Update info of the previous frame and dispose it for the next iteration.
|
||||
dec->prev_frame_timestamp_ = timestamp;
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||
dec->prev_iter_ = iter;
|
||||
dec->prev_frame_was_keyframe_ = is_key_frame;
|
||||
CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height);
|
||||
@ -421,6 +431,7 @@ int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) {
|
||||
void WebPAnimDecoderReset(WebPAnimDecoder* dec) {
|
||||
if (dec != NULL) {
|
||||
dec->prev_frame_timestamp_ = 0;
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||
memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_));
|
||||
dec->prev_frame_was_keyframe_ = 0;
|
||||
dec->next_frame_ = 1;
|
||||
@ -434,6 +445,7 @@ const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) {
|
||||
|
||||
void WebPAnimDecoderDelete(WebPAnimDecoder* dec) {
|
||||
if (dec != NULL) {
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||
WebPDemuxDelete(dec->demux_);
|
||||
WebPSafeFree(dec->curr_frame_);
|
||||
WebPSafeFree(dec->prev_frame_disposed_);
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#define DMUX_MAJ_VERSION 0
|
||||
#define DMUX_MIN_VERSION 3
|
||||
#define DMUX_REV_VERSION 0
|
||||
#define DMUX_REV_VERSION 1
|
||||
|
||||
typedef struct {
|
||||
size_t start_; // start location of the data
|
||||
|
@ -239,7 +239,7 @@ VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES];
|
||||
//------------------------------------------------------------------------------
|
||||
// 4x4
|
||||
|
||||
#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
|
||||
#define AVG3(a, b, c) ((uint8_t)(((a) + 2 * (b) + (c) + 2) >> 2))
|
||||
#define AVG2(a, b) (((a) + (b) + 1) >> 1)
|
||||
|
||||
static void VE4(uint8_t* dst) { // vertical
|
||||
|
@ -335,7 +335,7 @@ static void Intra16Preds(uint8_t* dst,
|
||||
// luma 4x4 prediction
|
||||
|
||||
#define DST(x, y) dst[(x) + (y) * BPS]
|
||||
#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
|
||||
#define AVG3(a, b, c) ((uint8_t)(((a) + 2 * (b) + (c) + 2) >> 2))
|
||||
#define AVG2(a, b) (((a) + (b) + 1) >> 1)
|
||||
|
||||
static void VE4(uint8_t* dst, const uint8_t* top) { // vertical
|
||||
|
@ -173,10 +173,10 @@ void WebPRescalerExportRow(WebPRescaler* const wrk) {
|
||||
WebPRescalerExportRowExpand(wrk);
|
||||
} else if (wrk->fxy_scale) {
|
||||
WebPRescalerExportRowShrink(wrk);
|
||||
} else { // very special case for src = dst = 1x1
|
||||
} else { // special case
|
||||
int i;
|
||||
assert(wrk->src_height == wrk->dst_height && wrk->x_add == 1);
|
||||
assert(wrk->src_width == 1 && wrk->dst_width <= 2);
|
||||
assert(wrk->src_height == 1 && wrk->dst_height == 1);
|
||||
for (i = 0; i < wrk->num_channels * wrk->dst_width; ++i) {
|
||||
wrk->dst[i] = wrk->irow[i];
|
||||
wrk->irow[i] = 0;
|
||||
|
@ -307,6 +307,7 @@ static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it,
|
||||
|
||||
static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
|
||||
int best_alpha = DEFAULT_ALPHA;
|
||||
int smallest_alpha = 0;
|
||||
int best_mode = 0;
|
||||
const int max_mode = MAX_UV_MODE;
|
||||
int mode;
|
||||
@ -322,6 +323,10 @@ static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
|
||||
alpha = GetAlpha(&histo);
|
||||
if (IS_BETTER_ALPHA(alpha, best_alpha)) {
|
||||
best_alpha = alpha;
|
||||
}
|
||||
// The best prediction mode tends to be the one with the smallest alpha.
|
||||
if (mode == 0 || alpha < smallest_alpha) {
|
||||
smallest_alpha = alpha;
|
||||
best_mode = mode;
|
||||
}
|
||||
}
|
||||
|
@ -281,18 +281,6 @@ int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) {
|
||||
//------------------------------------------------------------------------------
|
||||
// Recording of token probabilities.
|
||||
|
||||
// Record proba context used
|
||||
static int Record(int bit, proba_t* const stats) {
|
||||
proba_t p = *stats;
|
||||
if (p >= 0xffff0000u) { // an overflow is inbound.
|
||||
p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
|
||||
}
|
||||
// record bit count (lower 16 bits) and increment total count (upper 16 bits).
|
||||
p += 0x00010000u + bit;
|
||||
*stats = p;
|
||||
return bit;
|
||||
}
|
||||
|
||||
// We keep the table-free variant around for reference, in case.
|
||||
#define USE_LEVEL_CODE_TABLE
|
||||
|
||||
@ -303,31 +291,31 @@ int VP8RecordCoeffs(int ctx, const VP8Residual* const res) {
|
||||
// should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1
|
||||
proba_t* s = res->stats[n][ctx];
|
||||
if (res->last < 0) {
|
||||
Record(0, s + 0);
|
||||
VP8RecordStats(0, s + 0);
|
||||
return 0;
|
||||
}
|
||||
while (n <= res->last) {
|
||||
int v;
|
||||
Record(1, s + 0); // order of record doesn't matter
|
||||
VP8RecordStats(1, s + 0); // order of record doesn't matter
|
||||
while ((v = res->coeffs[n++]) == 0) {
|
||||
Record(0, s + 1);
|
||||
VP8RecordStats(0, s + 1);
|
||||
s = res->stats[VP8EncBands[n]][0];
|
||||
}
|
||||
Record(1, s + 1);
|
||||
if (!Record(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1
|
||||
VP8RecordStats(1, s + 1);
|
||||
if (!VP8RecordStats(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1
|
||||
s = res->stats[VP8EncBands[n]][1];
|
||||
} else {
|
||||
v = abs(v);
|
||||
#if !defined(USE_LEVEL_CODE_TABLE)
|
||||
if (!Record(v > 4, s + 3)) {
|
||||
if (Record(v != 2, s + 4))
|
||||
Record(v == 4, s + 5);
|
||||
} else if (!Record(v > 10, s + 6)) {
|
||||
Record(v > 6, s + 7);
|
||||
} else if (!Record((v >= 3 + (8 << 2)), s + 8)) {
|
||||
Record((v >= 3 + (8 << 1)), s + 9);
|
||||
if (!VP8RecordStats(v > 4, s + 3)) {
|
||||
if (VP8RecordStats(v != 2, s + 4))
|
||||
VP8RecordStats(v == 4, s + 5);
|
||||
} else if (!VP8RecordStats(v > 10, s + 6)) {
|
||||
VP8RecordStats(v > 6, s + 7);
|
||||
} else if (!VP8RecordStats((v >= 3 + (8 << 2)), s + 8)) {
|
||||
VP8RecordStats((v >= 3 + (8 << 1)), s + 9);
|
||||
} else {
|
||||
Record((v >= 3 + (8 << 3)), s + 10);
|
||||
VP8RecordStats((v >= 3 + (8 << 3)), s + 10);
|
||||
}
|
||||
#else
|
||||
if (v > MAX_VARIABLE_LEVEL) {
|
||||
@ -340,14 +328,14 @@ int VP8RecordCoeffs(int ctx, const VP8Residual* const res) {
|
||||
int i;
|
||||
for (i = 0; (pattern >>= 1) != 0; ++i) {
|
||||
const int mask = 2 << i;
|
||||
if (pattern & 1) Record(!!(bits & mask), s + 3 + i);
|
||||
if (pattern & 1) VP8RecordStats(!!(bits & mask), s + 3 + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
s = res->stats[VP8EncBands[n]][2];
|
||||
}
|
||||
}
|
||||
if (n < 16) Record(0, s + 0);
|
||||
if (n < 16) VP8RecordStats(0, s + 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,20 @@ void VP8InitResidual(int first, int coeff_type,
|
||||
|
||||
int VP8RecordCoeffs(int ctx, const VP8Residual* const res);
|
||||
|
||||
// Record proba context used.
|
||||
static WEBP_INLINE int VP8RecordStats(int bit, proba_t* const stats) {
|
||||
proba_t p = *stats;
|
||||
// An overflow is inbound. Note we handle this at 0xfffe0000u instead of
|
||||
// 0xffff0000u to make sure p + 1u does not overflow.
|
||||
if (p >= 0xfffe0000u) {
|
||||
p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
|
||||
}
|
||||
// record bit count (lower 16 bits) and increment total count (upper 16 bits).
|
||||
p += 0x00010000u + bit;
|
||||
*stats = p;
|
||||
return bit;
|
||||
}
|
||||
|
||||
// Cost of coding one event with probability 'proba'.
|
||||
static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) {
|
||||
return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba];
|
||||
|
@ -185,6 +185,13 @@ static int GetProba(int a, int b) {
|
||||
: (255 * a + total / 2) / total; // rounded proba
|
||||
}
|
||||
|
||||
static void ResetSegments(VP8Encoder* const enc) {
|
||||
int n;
|
||||
for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
|
||||
enc->mb_info_[n].segment_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetSegmentProbas(VP8Encoder* const enc) {
|
||||
int p[NUM_MB_SEGMENTS] = { 0 };
|
||||
int n;
|
||||
@ -206,6 +213,7 @@ static void SetSegmentProbas(VP8Encoder* const enc) {
|
||||
|
||||
enc->segment_hdr_.update_map_ =
|
||||
(probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255);
|
||||
if (!enc->segment_hdr_.update_map_) ResetSegments(enc);
|
||||
enc->segment_hdr_.size_ =
|
||||
p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) +
|
||||
p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) +
|
||||
@ -406,9 +414,7 @@ static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd,
|
||||
VP8InitResidual(0, 1, enc, &res);
|
||||
VP8SetResidualCoeffs(rd->y_dc_levels, &res);
|
||||
it->top_nz_[8] = it->left_nz_[8] =
|
||||
VP8RecordCoeffTokens(ctx, 1,
|
||||
res.first, res.last, res.coeffs, tokens);
|
||||
VP8RecordCoeffs(ctx, &res);
|
||||
VP8RecordCoeffTokens(ctx, &res, tokens);
|
||||
VP8InitResidual(1, 0, enc, &res);
|
||||
} else {
|
||||
VP8InitResidual(0, 3, enc, &res);
|
||||
@ -420,9 +426,7 @@ static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd,
|
||||
const int ctx = it->top_nz_[x] + it->left_nz_[y];
|
||||
VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
|
||||
it->top_nz_[x] = it->left_nz_[y] =
|
||||
VP8RecordCoeffTokens(ctx, res.coeff_type,
|
||||
res.first, res.last, res.coeffs, tokens);
|
||||
VP8RecordCoeffs(ctx, &res);
|
||||
VP8RecordCoeffTokens(ctx, &res, tokens);
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,9 +438,7 @@ static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd,
|
||||
const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
|
||||
VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
|
||||
it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
|
||||
VP8RecordCoeffTokens(ctx, 2,
|
||||
res.first, res.last, res.coeffs, tokens);
|
||||
VP8RecordCoeffs(ctx, &res);
|
||||
VP8RecordCoeffTokens(ctx, &res, tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -814,7 +816,7 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
|
||||
num_pass_left, stats.last_value, stats.value,
|
||||
stats.last_q, stats.q, stats.dq);
|
||||
#endif
|
||||
if (size_p0 > PARTITION0_SIZE_LIMIT) {
|
||||
if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
|
||||
++num_pass_left;
|
||||
enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation...
|
||||
continue; // ...and start over
|
||||
|
@ -592,8 +592,8 @@ static int HistoQueueInit(HistoQueue* const histo_queue, const int max_index) {
|
||||
histo_queue->max_size = max_index * max_index;
|
||||
// We allocate max_size + 1 because the last element at index "size" is
|
||||
// used as temporary data (and it could be up to max_size).
|
||||
histo_queue->queue = WebPSafeMalloc(histo_queue->max_size + 1,
|
||||
sizeof(*histo_queue->queue));
|
||||
histo_queue->queue = (HistogramPair*)WebPSafeMalloc(
|
||||
histo_queue->max_size + 1, sizeof(*histo_queue->queue));
|
||||
return histo_queue->queue != NULL;
|
||||
}
|
||||
|
||||
@ -659,7 +659,8 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) {
|
||||
int i, j;
|
||||
VP8LHistogram** const histograms = image_histo->histograms;
|
||||
// Indexes of remaining histograms.
|
||||
int* const clusters = WebPSafeMalloc(image_histo_size, sizeof(*clusters));
|
||||
int* const clusters =
|
||||
(int*)WebPSafeMalloc(image_histo_size, sizeof(*clusters));
|
||||
// Priority queue of histogram pairs.
|
||||
HistoQueue histo_queue;
|
||||
|
||||
|
@ -88,8 +88,9 @@ int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height) {
|
||||
}
|
||||
|
||||
int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height) {
|
||||
const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
|
||||
const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT;
|
||||
const WebPEncCSP uv_csp =
|
||||
(WebPEncCSP)((int)picture->colorspace & WEBP_CSP_UV_MASK);
|
||||
const int has_alpha = (int)picture->colorspace & WEBP_CSP_ALPHA_BIT;
|
||||
const int y_stride = width;
|
||||
const int uv_width = (width + 1) >> 1;
|
||||
const int uv_height = (height + 1) >> 1;
|
||||
|
@ -381,36 +381,42 @@ static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) {
|
||||
return clip_8b(128 + (v >> (YUV_FIX + SFIX)));
|
||||
}
|
||||
|
||||
static int ConvertWRGBToYUV(const fixed_y_t* const best_y,
|
||||
const fixed_t* const best_uv,
|
||||
static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
||||
WebPPicture* const picture) {
|
||||
int i, j;
|
||||
uint8_t* dst_y = picture->y;
|
||||
uint8_t* dst_u = picture->u;
|
||||
uint8_t* dst_v = picture->v;
|
||||
const fixed_t* const best_uv_base = best_uv;
|
||||
const int w = (picture->width + 1) & ~1;
|
||||
const int h = (picture->height + 1) & ~1;
|
||||
const int uv_w = w >> 1;
|
||||
const int uv_h = h >> 1;
|
||||
for (j = 0; j < picture->height; ++j) {
|
||||
for (best_uv = best_uv_base, j = 0; j < picture->height; ++j) {
|
||||
for (i = 0; i < picture->width; ++i) {
|
||||
const int off = 3 * ((i >> 1) + (j >> 1) * uv_w);
|
||||
const int off2 = i + j * picture->y_stride;
|
||||
const int W = best_y[i + j * w];
|
||||
const int off = 3 * (i >> 1);
|
||||
const int W = best_y[i];
|
||||
const int r = best_uv[off + 0] + W;
|
||||
const int g = best_uv[off + 1] + W;
|
||||
const int b = best_uv[off + 2] + W;
|
||||
picture->y[off2] = ConvertRGBToY(r, g, b);
|
||||
dst_y[i] = ConvertRGBToY(r, g, b);
|
||||
}
|
||||
best_y += w;
|
||||
best_uv += (j & 1) * 3 * uv_w;
|
||||
dst_y += picture->y_stride;
|
||||
}
|
||||
for (j = 0; j < uv_h; ++j) {
|
||||
uint8_t* const dst_u = picture->u + j * picture->uv_stride;
|
||||
uint8_t* const dst_v = picture->v + j * picture->uv_stride;
|
||||
for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
|
||||
for (i = 0; i < uv_w; ++i) {
|
||||
const int off = 3 * (i + j * uv_w);
|
||||
const int off = 3 * i;
|
||||
const int r = best_uv[off + 0];
|
||||
const int g = best_uv[off + 1];
|
||||
const int b = best_uv[off + 2];
|
||||
dst_u[i] = ConvertRGBToU(r, g, b);
|
||||
dst_v[i] = ConvertRGBToV(r, g, b);
|
||||
}
|
||||
best_uv += 3 * uv_w;
|
||||
dst_u += picture->uv_stride;
|
||||
dst_v += picture->uv_stride;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -420,9 +426,9 @@ static int ConvertWRGBToYUV(const fixed_y_t* const best_y,
|
||||
|
||||
#define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((W) * (H), sizeof(T)))
|
||||
|
||||
static int PreprocessARGB(const uint8_t* const r_ptr,
|
||||
const uint8_t* const g_ptr,
|
||||
const uint8_t* const b_ptr,
|
||||
static int PreprocessARGB(const uint8_t* r_ptr,
|
||||
const uint8_t* g_ptr,
|
||||
const uint8_t* b_ptr,
|
||||
int step, int rgb_stride,
|
||||
WebPPicture* const picture) {
|
||||
// we expand the right/bottom border if needed
|
||||
@ -435,20 +441,24 @@ static int PreprocessARGB(const uint8_t* const r_ptr,
|
||||
// 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 = SAFE_ALLOC(w, h, fixed_y_t);
|
||||
fixed_y_t* const target_y = SAFE_ALLOC(w, h, fixed_y_t);
|
||||
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 = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
|
||||
fixed_t* const target_uv = SAFE_ALLOC(uv_w * 3, uv_h, fixed_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;
|
||||
int ok;
|
||||
int diff_sum = 0;
|
||||
const int first_diff_threshold = (int)(2.5 * w * h);
|
||||
const int min_improvement = 5; // stop if improvement is below this %
|
||||
const int min_first_improvement = 80;
|
||||
|
||||
if (best_y == NULL || best_uv == NULL ||
|
||||
target_y == NULL || target_uv == 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 = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||
@ -462,41 +472,47 @@ static int PreprocessARGB(const uint8_t* const r_ptr,
|
||||
const int is_last_row = (j == picture->height - 1);
|
||||
fixed_y_t* const src1 = tmp_buffer;
|
||||
fixed_y_t* const src2 = tmp_buffer + 3 * w;
|
||||
const int off1 = j * rgb_stride;
|
||||
const int off2 = off1 + rgb_stride;
|
||||
const int uv_off = (j >> 1) * 3 * uv_w;
|
||||
fixed_y_t* const dst_y = best_y + j * w;
|
||||
|
||||
// prepare two rows of input
|
||||
ImportOneRow(r_ptr + off1, g_ptr + off1, b_ptr + off1,
|
||||
step, picture->width, src1);
|
||||
ImportOneRow(r_ptr, g_ptr, b_ptr, step, picture->width, src1);
|
||||
if (!is_last_row) {
|
||||
ImportOneRow(r_ptr + off2, g_ptr + off2, b_ptr + off2,
|
||||
ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
|
||||
step, picture->width, src2);
|
||||
} else {
|
||||
memcpy(src2, src1, 3 * w * sizeof(*src2));
|
||||
}
|
||||
UpdateW(src1, target_y + (j + 0) * w, w);
|
||||
UpdateW(src2, target_y + (j + 1) * w, w);
|
||||
diff_sum += UpdateChroma(src1, src2, target_uv + uv_off, dst_y, uv_w);
|
||||
memcpy(best_uv + uv_off, target_uv + uv_off, 3 * uv_w * sizeof(*best_uv));
|
||||
memcpy(dst_y + w, dst_y, w * sizeof(*dst_y));
|
||||
UpdateW(src1, target_y, w);
|
||||
UpdateW(src2, target_y + w, w);
|
||||
diff_sum += UpdateChroma(src1, src2, target_uv, best_y, uv_w);
|
||||
memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
|
||||
memcpy(best_y + w, best_y, w * sizeof(*best_y));
|
||||
best_y += 2 * w;
|
||||
best_uv += 3 * uv_w;
|
||||
target_y += 2 * w;
|
||||
target_uv += 3 * uv_w;
|
||||
r_ptr += 2 * rgb_stride;
|
||||
g_ptr += 2 * rgb_stride;
|
||||
b_ptr += 2 * rgb_stride;
|
||||
}
|
||||
|
||||
// Iterate and resolve clipping conflicts.
|
||||
for (iter = 0; iter < kNumIterations; ++iter) {
|
||||
int k;
|
||||
const fixed_t* cur_uv = best_uv;
|
||||
const fixed_t* prev_uv = best_uv;
|
||||
const fixed_t* cur_uv = best_uv_base;
|
||||
const fixed_t* prev_uv = best_uv_base;
|
||||
const int old_diff_sum = diff_sum;
|
||||
diff_sum = 0;
|
||||
|
||||
best_y = best_y_base;
|
||||
best_uv = best_uv_base;
|
||||
target_y = target_y_base;
|
||||
target_uv = target_uv_base;
|
||||
for (j = 0; j < h; j += 2) {
|
||||
fixed_y_t* const src1 = tmp_buffer;
|
||||
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 + j * w, prev_uv, cur_uv, next_uv,
|
||||
w, src1, src2);
|
||||
InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w, src1, src2);
|
||||
prev_uv = cur_uv;
|
||||
cur_uv = next_uv;
|
||||
}
|
||||
@ -507,16 +523,15 @@ static int PreprocessARGB(const uint8_t* const r_ptr,
|
||||
|
||||
// update two rows of Y and one row of RGB
|
||||
for (i = 0; i < 2 * w; ++i) {
|
||||
const int off = i + j * w;
|
||||
const int diff_y = target_y[off] - best_rgb_y[i];
|
||||
const int new_y = (int)best_y[off] + diff_y;
|
||||
best_y[off] = clip_y(new_y);
|
||||
const int diff_y = target_y[i] - best_rgb_y[i];
|
||||
const int new_y = (int)best_y[i] + diff_y;
|
||||
best_y[i] = clip_y(new_y);
|
||||
}
|
||||
for (i = 0; i < uv_w; ++i) {
|
||||
const int off = 3 * (i + (j >> 1) * uv_w);
|
||||
const int off = 3 * i;
|
||||
int W;
|
||||
for (k = 0; k <= 2; ++k) {
|
||||
const int diff_uv = (int)target_uv[off + k] - best_rgb_uv[3 * i + k];
|
||||
const int diff_uv = (int)target_uv[off + k] - best_rgb_uv[off + k];
|
||||
best_uv[off + k] += diff_uv;
|
||||
}
|
||||
W = RGBToGray(best_uv[off + 0], best_uv[off + 1], best_uv[off + 2]);
|
||||
@ -524,6 +539,10 @@ static int PreprocessARGB(const uint8_t* const r_ptr,
|
||||
best_uv[off + k] -= W;
|
||||
}
|
||||
}
|
||||
best_y += 2 * w;
|
||||
best_uv += 3 * uv_w;
|
||||
target_y += 2 * w;
|
||||
target_uv += 3 * uv_w;
|
||||
}
|
||||
// test exit condition
|
||||
if (diff_sum > 0) {
|
||||
@ -545,13 +564,13 @@ static int PreprocessARGB(const uint8_t* const r_ptr,
|
||||
}
|
||||
|
||||
// final reconstruction
|
||||
ok = ConvertWRGBToYUV(best_y, best_uv, picture);
|
||||
ok = ConvertWRGBToYUV(best_y_base, best_uv_base, picture);
|
||||
|
||||
End:
|
||||
WebPSafeFree(best_y);
|
||||
WebPSafeFree(best_uv);
|
||||
WebPSafeFree(target_y);
|
||||
WebPSafeFree(target_uv);
|
||||
WebPSafeFree(best_y_base);
|
||||
WebPSafeFree(best_uv_base);
|
||||
WebPSafeFree(target_y_base);
|
||||
WebPSafeFree(target_uv_base);
|
||||
WebPSafeFree(best_rgb_y);
|
||||
WebPSafeFree(best_rgb_uv);
|
||||
WebPSafeFree(tmp_buffer);
|
||||
@ -830,10 +849,10 @@ static WEBP_INLINE void ConvertRowsToUV(const uint16_t* rgb,
|
||||
}
|
||||
}
|
||||
|
||||
static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
|
||||
const uint8_t* const g_ptr,
|
||||
const uint8_t* const b_ptr,
|
||||
const uint8_t* const a_ptr,
|
||||
static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
|
||||
const uint8_t* g_ptr,
|
||||
const uint8_t* b_ptr,
|
||||
const uint8_t* a_ptr,
|
||||
int step, // bytes per pixel
|
||||
int rgb_stride, // bytes per scanline
|
||||
float dithering,
|
||||
@ -900,36 +919,34 @@ static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
|
||||
// Downsample Y/U/V planes, two rows at a time
|
||||
for (y = 0; y < (height >> 1); ++y) {
|
||||
int rows_have_alpha = has_alpha;
|
||||
const int off1 = (2 * y + 0) * rgb_stride;
|
||||
const int off2 = (2 * y + 1) * rgb_stride;
|
||||
if (use_dsp) {
|
||||
if (is_rgb) {
|
||||
WebPConvertRGB24ToY(r_ptr + off1, dst_y, width);
|
||||
WebPConvertRGB24ToY(r_ptr + off2, dst_y + picture->y_stride, width);
|
||||
WebPConvertRGB24ToY(r_ptr, dst_y, width);
|
||||
WebPConvertRGB24ToY(r_ptr + rgb_stride,
|
||||
dst_y + picture->y_stride, width);
|
||||
} else {
|
||||
WebPConvertBGR24ToY(b_ptr + off1, dst_y, width);
|
||||
WebPConvertBGR24ToY(b_ptr + off2, dst_y + picture->y_stride, width);
|
||||
WebPConvertBGR24ToY(b_ptr, dst_y, width);
|
||||
WebPConvertBGR24ToY(b_ptr + rgb_stride,
|
||||
dst_y + picture->y_stride, width);
|
||||
}
|
||||
} else {
|
||||
ConvertRowToY(r_ptr + off1, g_ptr + off1, b_ptr + off1, step,
|
||||
dst_y, width, rg);
|
||||
ConvertRowToY(r_ptr + off2, g_ptr + off2, b_ptr + off2, step,
|
||||
ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg);
|
||||
ConvertRowToY(r_ptr + rgb_stride,
|
||||
g_ptr + rgb_stride,
|
||||
b_ptr + rgb_stride, step,
|
||||
dst_y + picture->y_stride, width, rg);
|
||||
}
|
||||
dst_y += 2 * picture->y_stride;
|
||||
if (has_alpha) {
|
||||
rows_have_alpha &= !WebPExtractAlpha(a_ptr + off1, rgb_stride,
|
||||
width, 2,
|
||||
rows_have_alpha &= !WebPExtractAlpha(a_ptr, rgb_stride, width, 2,
|
||||
dst_a, picture->a_stride);
|
||||
dst_a += 2 * picture->a_stride;
|
||||
}
|
||||
// Collect averaged R/G/B(/A)
|
||||
if (!rows_have_alpha) {
|
||||
AccumulateRGB(r_ptr + off1, g_ptr + off1, b_ptr + off1,
|
||||
step, rgb_stride, tmp_rgb, width);
|
||||
AccumulateRGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, tmp_rgb, width);
|
||||
} else {
|
||||
AccumulateRGBA(r_ptr + off1, g_ptr + off1, b_ptr + off1, a_ptr + off1,
|
||||
rgb_stride, tmp_rgb, width);
|
||||
AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, rgb_stride, tmp_rgb, width);
|
||||
}
|
||||
// Convert to U/V
|
||||
if (rg == NULL) {
|
||||
@ -939,31 +956,33 @@ static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
|
||||
}
|
||||
dst_u += picture->uv_stride;
|
||||
dst_v += picture->uv_stride;
|
||||
r_ptr += 2 * rgb_stride;
|
||||
b_ptr += 2 * rgb_stride;
|
||||
g_ptr += 2 * rgb_stride;
|
||||
if (has_alpha) a_ptr += 2 * rgb_stride;
|
||||
}
|
||||
if (height & 1) { // extra last row
|
||||
const int off = 2 * y * rgb_stride;
|
||||
int row_has_alpha = has_alpha;
|
||||
if (use_dsp) {
|
||||
if (r_ptr < b_ptr) {
|
||||
WebPConvertRGB24ToY(r_ptr + off, dst_y, width);
|
||||
WebPConvertRGB24ToY(r_ptr, dst_y, width);
|
||||
} else {
|
||||
WebPConvertBGR24ToY(b_ptr + off, dst_y, width);
|
||||
WebPConvertBGR24ToY(b_ptr, dst_y, width);
|
||||
}
|
||||
} else {
|
||||
ConvertRowToY(r_ptr + off, g_ptr + off, b_ptr + off, step,
|
||||
dst_y, width, rg);
|
||||
ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg);
|
||||
}
|
||||
if (row_has_alpha) {
|
||||
row_has_alpha &= !WebPExtractAlpha(a_ptr + off, 0, width, 1, dst_a, 0);
|
||||
row_has_alpha &= !WebPExtractAlpha(a_ptr, 0, width, 1, dst_a, 0);
|
||||
}
|
||||
// Collect averaged R/G/B(/A)
|
||||
if (!row_has_alpha) {
|
||||
// Collect averaged R/G/B
|
||||
AccumulateRGB(r_ptr + off, g_ptr + off, b_ptr + off,
|
||||
step, /* rgb_stride = */ 0, tmp_rgb, width);
|
||||
AccumulateRGB(r_ptr, g_ptr, b_ptr, step, /* rgb_stride = */ 0,
|
||||
tmp_rgb, width);
|
||||
} else {
|
||||
AccumulateRGBA(r_ptr + off, g_ptr + off, b_ptr + off, a_ptr + off,
|
||||
/* rgb_stride = */ 0, tmp_rgb, width);
|
||||
AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, /* rgb_stride = */ 0,
|
||||
tmp_rgb, width);
|
||||
}
|
||||
if (rg == NULL) {
|
||||
WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width);
|
||||
@ -1086,10 +1105,10 @@ static int Import(WebPPicture* const picture,
|
||||
const uint8_t* const rgb, int rgb_stride,
|
||||
int step, int swap_rb, int import_alpha) {
|
||||
int y;
|
||||
const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0);
|
||||
const uint8_t* const g_ptr = rgb + 1;
|
||||
const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2);
|
||||
const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL;
|
||||
const uint8_t* r_ptr = rgb + (swap_rb ? 2 : 0);
|
||||
const uint8_t* g_ptr = rgb + 1;
|
||||
const uint8_t* b_ptr = rgb + (swap_rb ? 0 : 2);
|
||||
const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL;
|
||||
const int width = picture->width;
|
||||
const int height = picture->height;
|
||||
|
||||
@ -1102,20 +1121,25 @@ static int Import(WebPPicture* const picture,
|
||||
VP8EncDspARGBInit();
|
||||
|
||||
if (import_alpha) {
|
||||
uint32_t* dst = picture->argb;
|
||||
assert(step == 4);
|
||||
for (y = 0; y < height; ++y) {
|
||||
uint32_t* const dst = &picture->argb[y * picture->argb_stride];
|
||||
const int offset = y * rgb_stride;
|
||||
VP8PackARGB(a_ptr + offset, r_ptr + offset, g_ptr + offset,
|
||||
b_ptr + offset, width, dst);
|
||||
VP8PackARGB(a_ptr, r_ptr, g_ptr, b_ptr, width, dst);
|
||||
a_ptr += rgb_stride;
|
||||
r_ptr += rgb_stride;
|
||||
g_ptr += rgb_stride;
|
||||
b_ptr += rgb_stride;
|
||||
dst += picture->argb_stride;
|
||||
}
|
||||
} else {
|
||||
uint32_t* dst = picture->argb;
|
||||
assert(step >= 3);
|
||||
for (y = 0; y < height; ++y) {
|
||||
uint32_t* const dst = &picture->argb[y * picture->argb_stride];
|
||||
const int offset = y * rgb_stride;
|
||||
VP8PackRGB(r_ptr + offset, g_ptr + offset, b_ptr + offset,
|
||||
width, step, dst);
|
||||
VP8PackRGB(r_ptr, g_ptr, b_ptr, width, step, dst);
|
||||
r_ptr += rgb_stride;
|
||||
g_ptr += rgb_stride;
|
||||
b_ptr += rgb_stride;
|
||||
dst += picture->argb_stride;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
@ -110,7 +110,7 @@ int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
|
||||
VP8SSIMAccumulatePlane(tmp1, w, tmp2, w, w, h, &stats[c]);
|
||||
}
|
||||
}
|
||||
free(tmp_plane);
|
||||
WebPSafeFree(tmp_plane);
|
||||
}
|
||||
} else {
|
||||
int has_alpha, uv_w, uv_h;
|
||||
|
@ -278,7 +278,7 @@ static void SetupMatrices(VP8Encoder* enc) {
|
||||
CheckLambdaValue(&m->lambda_trellis_uv_);
|
||||
CheckLambdaValue(&m->tlambda_);
|
||||
|
||||
m->min_disto_ = 10 * m->y1_.q_[0]; // quantization-aware min disto
|
||||
m->min_disto_ = 20 * m->y1_.q_[0]; // quantization-aware min disto
|
||||
m->max_edge_ = 0;
|
||||
|
||||
m->i4_penalty_ = 1000 * q_i4 * q_i4;
|
||||
@ -874,9 +874,9 @@ static void StoreMaxDelta(VP8SegmentInfo* const dqm, const int16_t DCs[16]) {
|
||||
// We look at the first three AC coefficients to determine what is the average
|
||||
// delta between each sub-4x4 block.
|
||||
const int v0 = abs(DCs[1]);
|
||||
const int v1 = abs(DCs[4]);
|
||||
const int v2 = abs(DCs[5]);
|
||||
int max_v = (v0 > v1) ? v1 : v0;
|
||||
const int v1 = abs(DCs[2]);
|
||||
const int v2 = abs(DCs[4]);
|
||||
int max_v = (v1 > v0) ? v1 : v0;
|
||||
max_v = (v2 > max_v) ? v2 : max_v;
|
||||
if (max_v > dqm->max_edge_) dqm->max_edge_ = max_v;
|
||||
}
|
||||
@ -957,7 +957,7 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
|
||||
// we have a blocky macroblock (only DCs are non-zero) with fairly high
|
||||
// distortion, record max delta so we can later adjust the minimal filtering
|
||||
// strength needed to smooth these blocks out.
|
||||
if ((rd->nz & 0xffff) == 0 && rd->D > dqm->min_disto_) {
|
||||
if ((rd->nz & 0x100ffff) == 0x1000000 && rd->D > dqm->min_disto_) {
|
||||
StoreMaxDelta(dqm, rd->y_dc_levels);
|
||||
}
|
||||
}
|
||||
@ -1155,7 +1155,8 @@ static void RefineUsingDistortion(VP8EncIterator* const it,
|
||||
const int lambda_d_uv = 120;
|
||||
score_t score_i4 = dqm->i4_penalty_;
|
||||
score_t i4_bit_sum = 0;
|
||||
const score_t bit_limit = it->enc_->mb_header_limit_;
|
||||
const score_t bit_limit = try_both_modes ? it->enc_->mb_header_limit_
|
||||
: MAX_COST; // no early-out allowed
|
||||
|
||||
if (is_i16) { // First, evaluate Intra16 distortion
|
||||
int best_mode = -1;
|
||||
|
@ -87,14 +87,16 @@ static int TBufferNewPage(VP8TBuffer* const b) {
|
||||
#define TOKEN_ID(t, b, ctx) \
|
||||
(NUM_PROBAS * ((ctx) + NUM_CTX * ((b) + NUM_BANDS * (t))))
|
||||
|
||||
static WEBP_INLINE uint32_t AddToken(VP8TBuffer* const b,
|
||||
uint32_t bit, uint32_t proba_idx) {
|
||||
static WEBP_INLINE uint32_t AddToken(VP8TBuffer* const b, uint32_t bit,
|
||||
uint32_t proba_idx,
|
||||
proba_t* const stats) {
|
||||
assert(proba_idx < FIXED_PROBA_BIT);
|
||||
assert(bit <= 1);
|
||||
if (b->left_ > 0 || TBufferNewPage(b)) {
|
||||
const int slot = --b->left_;
|
||||
b->tokens_[slot] = (bit << 15) | proba_idx;
|
||||
}
|
||||
VP8RecordStats(bit, stats);
|
||||
return bit;
|
||||
}
|
||||
|
||||
@ -108,13 +110,16 @@ static WEBP_INLINE void AddConstantToken(VP8TBuffer* const b,
|
||||
}
|
||||
}
|
||||
|
||||
int VP8RecordCoeffTokens(const int ctx, const int coeff_type,
|
||||
int first, int last,
|
||||
const int16_t* const coeffs,
|
||||
int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res,
|
||||
VP8TBuffer* const tokens) {
|
||||
int n = first;
|
||||
const int16_t* const coeffs = res->coeffs;
|
||||
const int coeff_type = res->coeff_type;
|
||||
const int last = res->last;
|
||||
int n = res->first;
|
||||
uint32_t base_id = TOKEN_ID(coeff_type, n, ctx);
|
||||
if (!AddToken(tokens, last >= 0, base_id + 0)) {
|
||||
// should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1
|
||||
proba_t* s = res->stats[n][ctx];
|
||||
if (!AddToken(tokens, last >= 0, base_id + 0, s + 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -122,18 +127,20 @@ int VP8RecordCoeffTokens(const int ctx, const int coeff_type,
|
||||
const int c = coeffs[n++];
|
||||
const int sign = c < 0;
|
||||
const uint32_t v = sign ? -c : c;
|
||||
if (!AddToken(tokens, v != 0, base_id + 1)) {
|
||||
if (!AddToken(tokens, v != 0, base_id + 1, s + 1)) {
|
||||
base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 0); // ctx=0
|
||||
s = res->stats[VP8EncBands[n]][0];
|
||||
continue;
|
||||
}
|
||||
if (!AddToken(tokens, v > 1, base_id + 2)) {
|
||||
if (!AddToken(tokens, v > 1, base_id + 2, s + 2)) {
|
||||
base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 1); // ctx=1
|
||||
s = res->stats[VP8EncBands[n]][1];
|
||||
} else {
|
||||
if (!AddToken(tokens, v > 4, base_id + 3)) {
|
||||
if (AddToken(tokens, v != 2, base_id + 4))
|
||||
AddToken(tokens, v == 4, base_id + 5);
|
||||
} else if (!AddToken(tokens, v > 10, base_id + 6)) {
|
||||
if (!AddToken(tokens, v > 6, base_id + 7)) {
|
||||
if (!AddToken(tokens, v > 4, base_id + 3, s + 3)) {
|
||||
if (AddToken(tokens, v != 2, base_id + 4, s + 4))
|
||||
AddToken(tokens, v == 4, base_id + 5, s + 5);
|
||||
} else if (!AddToken(tokens, v > 10, base_id + 6, s + 6)) {
|
||||
if (!AddToken(tokens, v > 6, base_id + 7, s + 7)) {
|
||||
AddConstantToken(tokens, v == 6, 159);
|
||||
} else {
|
||||
AddConstantToken(tokens, v >= 9, 165);
|
||||
@ -144,26 +151,26 @@ int VP8RecordCoeffTokens(const int ctx, const int coeff_type,
|
||||
const uint8_t* tab;
|
||||
uint32_t residue = v - 3;
|
||||
if (residue < (8 << 1)) { // VP8Cat3 (3b)
|
||||
AddToken(tokens, 0, base_id + 8);
|
||||
AddToken(tokens, 0, base_id + 9);
|
||||
AddToken(tokens, 0, base_id + 8, s + 8);
|
||||
AddToken(tokens, 0, base_id + 9, s + 9);
|
||||
residue -= (8 << 0);
|
||||
mask = 1 << 2;
|
||||
tab = VP8Cat3;
|
||||
} else if (residue < (8 << 2)) { // VP8Cat4 (4b)
|
||||
AddToken(tokens, 0, base_id + 8);
|
||||
AddToken(tokens, 1, base_id + 9);
|
||||
AddToken(tokens, 0, base_id + 8, s + 8);
|
||||
AddToken(tokens, 1, base_id + 9, s + 9);
|
||||
residue -= (8 << 1);
|
||||
mask = 1 << 3;
|
||||
tab = VP8Cat4;
|
||||
} else if (residue < (8 << 3)) { // VP8Cat5 (5b)
|
||||
AddToken(tokens, 1, base_id + 8);
|
||||
AddToken(tokens, 0, base_id + 10);
|
||||
AddToken(tokens, 1, base_id + 8, s + 8);
|
||||
AddToken(tokens, 0, base_id + 10, s + 9);
|
||||
residue -= (8 << 2);
|
||||
mask = 1 << 4;
|
||||
tab = VP8Cat5;
|
||||
} else { // VP8Cat6 (11b)
|
||||
AddToken(tokens, 1, base_id + 8);
|
||||
AddToken(tokens, 1, base_id + 10);
|
||||
AddToken(tokens, 1, base_id + 8, s + 8);
|
||||
AddToken(tokens, 1, base_id + 10, s + 9);
|
||||
residue -= (8 << 3);
|
||||
mask = 1 << 10;
|
||||
tab = VP8Cat6;
|
||||
@ -174,9 +181,10 @@ int VP8RecordCoeffTokens(const int ctx, const int coeff_type,
|
||||
}
|
||||
}
|
||||
base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 2); // ctx=2
|
||||
s = res->stats[VP8EncBands[n]][2];
|
||||
}
|
||||
AddConstantToken(tokens, sign, 128);
|
||||
if (n == 16 || !AddToken(tokens, n <= last, base_id + 0)) {
|
||||
if (n == 16 || !AddToken(tokens, n <= last, base_id + 0, s + 0)) {
|
||||
return 1; // EOB
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ extern "C" {
|
||||
// version numbers
|
||||
#define ENC_MAJ_VERSION 0
|
||||
#define ENC_MIN_VERSION 5
|
||||
#define ENC_REV_VERSION 1
|
||||
#define ENC_REV_VERSION 2
|
||||
|
||||
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
|
||||
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
|
||||
@ -325,9 +325,7 @@ int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw,
|
||||
const uint8_t* const probas, int final_pass);
|
||||
|
||||
// record the coding of coefficients without knowing the probabilities yet
|
||||
int VP8RecordCoeffTokens(const int ctx, const int coeff_type,
|
||||
int first, int last,
|
||||
const int16_t* const coeffs,
|
||||
int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res,
|
||||
VP8TBuffer* const tokens);
|
||||
|
||||
// Estimate the final coded size given a set of 'probas'.
|
||||
|
@ -34,8 +34,8 @@
|
||||
// Palette reordering for smaller sum of deltas (and for smaller storage).
|
||||
|
||||
static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
|
||||
const uint32_t a = WebPMemToUint32(p1);
|
||||
const uint32_t b = WebPMemToUint32(p2);
|
||||
const uint32_t a = WebPMemToUint32((uint8_t*)p1);
|
||||
const uint32_t b = WebPMemToUint32((uint8_t*)p2);
|
||||
assert(a != b);
|
||||
return (a < b) ? -1 : 1;
|
||||
}
|
||||
@ -224,9 +224,8 @@ static int AnalyzeEntropy(const uint32_t* argb,
|
||||
{
|
||||
double entropy_comp[kHistoTotal];
|
||||
double entropy[kNumEntropyIx];
|
||||
EntropyIx k;
|
||||
EntropyIx last_mode_to_analyze =
|
||||
use_palette ? kPalette : kSpatialSubGreen;
|
||||
int k;
|
||||
int last_mode_to_analyze = use_palette ? kPalette : kSpatialSubGreen;
|
||||
int j;
|
||||
// Let's add one zero to the predicted histograms. The zeros are removed
|
||||
// too efficiently by the pix_diff == 0 comparison, at least one of the
|
||||
@ -263,7 +262,7 @@ static int AnalyzeEntropy(const uint32_t* argb,
|
||||
*min_entropy_ix = kDirect;
|
||||
for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) {
|
||||
if (entropy[*min_entropy_ix] > entropy[k]) {
|
||||
*min_entropy_ix = k;
|
||||
*min_entropy_ix = (EntropyIx)k;
|
||||
}
|
||||
}
|
||||
*red_and_blue_always_zero = 1;
|
||||
|
@ -13,6 +13,6 @@ libwebpmuxinclude_HEADERS += ../webp/mux_types.h
|
||||
libwebpmuxinclude_HEADERS += ../webp/types.h
|
||||
|
||||
libwebpmux_la_LIBADD = ../libwebp.la
|
||||
libwebpmux_la_LDFLAGS = -no-undefined -version-info 2:1:0
|
||||
libwebpmux_la_LDFLAGS = -no-undefined -version-info 2:2:0 -lm
|
||||
libwebpmuxincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebpmux.pc
|
||||
|
@ -829,8 +829,8 @@ static WebPEncodingError GenerateCandidates(
|
||||
WebPPicture* const curr_canvas = &enc->curr_canvas_copy_;
|
||||
const WebPPicture* const prev_canvas =
|
||||
is_dispose_none ? &enc->prev_canvas_ : &enc->prev_canvas_disposed_;
|
||||
int use_blending_ll;
|
||||
int use_blending_lossy;
|
||||
int use_blending_ll, use_blending_lossy;
|
||||
int evaluate_ll, evaluate_lossy;
|
||||
|
||||
CopyCurrentCanvas(enc);
|
||||
use_blending_ll =
|
||||
@ -843,16 +843,19 @@ static WebPEncodingError GenerateCandidates(
|
||||
|
||||
// Pick candidates to be tried.
|
||||
if (!enc->options_.allow_mixed) {
|
||||
candidate_ll->evaluate_ = is_lossless;
|
||||
candidate_lossy->evaluate_ = !is_lossless;
|
||||
evaluate_ll = is_lossless;
|
||||
evaluate_lossy = !is_lossless;
|
||||
} else if (enc->options_.minimize_size) {
|
||||
evaluate_ll = 1;
|
||||
evaluate_lossy = 1;
|
||||
} else { // Use a heuristic for trying lossless and/or lossy compression.
|
||||
const int num_colors = WebPGetColorPalette(¶ms->sub_frame_ll_, NULL);
|
||||
candidate_ll->evaluate_ = (num_colors < MAX_COLORS_LOSSLESS);
|
||||
candidate_lossy->evaluate_ = (num_colors >= MIN_COLORS_LOSSY);
|
||||
evaluate_ll = (num_colors < MAX_COLORS_LOSSLESS);
|
||||
evaluate_lossy = (num_colors >= MIN_COLORS_LOSSY);
|
||||
}
|
||||
|
||||
// Generate candidates.
|
||||
if (candidate_ll->evaluate_) {
|
||||
if (evaluate_ll) {
|
||||
CopyCurrentCanvas(enc);
|
||||
if (use_blending_ll) {
|
||||
enc->curr_canvas_copy_modified_ =
|
||||
@ -862,7 +865,7 @@ static WebPEncodingError GenerateCandidates(
|
||||
config_ll, use_blending_ll, candidate_ll);
|
||||
if (error_code != VP8_ENC_OK) return error_code;
|
||||
}
|
||||
if (candidate_lossy->evaluate_) {
|
||||
if (evaluate_lossy) {
|
||||
CopyCurrentCanvas(enc);
|
||||
if (use_blending_lossy) {
|
||||
enc->curr_canvas_copy_modified_ =
|
||||
@ -1029,6 +1032,8 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc,
|
||||
const WebPPicture* const prev_canvas = &enc->prev_canvas_;
|
||||
Candidate candidates[CANDIDATE_COUNT];
|
||||
const int is_lossless = config->lossless;
|
||||
const int consider_lossless = is_lossless || enc->options_.allow_mixed;
|
||||
const int consider_lossy = !is_lossless || enc->options_.allow_mixed;
|
||||
const int is_first_frame = enc->is_first_frame_;
|
||||
|
||||
// First frame cannot be skipped as there is no 'previous frame' to merge it
|
||||
@ -1066,9 +1071,7 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc,
|
||||
return VP8_ENC_ERROR_INVALID_CONFIGURATION;
|
||||
}
|
||||
|
||||
for (i = 0; i < CANDIDATE_COUNT; ++i) {
|
||||
candidates[i].evaluate_ = 0;
|
||||
}
|
||||
memset(candidates, 0, sizeof(candidates));
|
||||
|
||||
// Change-rectangle assuming previous frame was DISPOSE_NONE.
|
||||
if (!GetSubRects(prev_canvas, curr_canvas, is_key_frame, is_first_frame,
|
||||
@ -1077,8 +1080,8 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc,
|
||||
goto Err;
|
||||
}
|
||||
|
||||
if ((is_lossless && IsEmptyRect(&dispose_none_params.rect_ll_)) ||
|
||||
(!is_lossless && IsEmptyRect(&dispose_none_params.rect_lossy_))) {
|
||||
if ((consider_lossless && IsEmptyRect(&dispose_none_params.rect_ll_)) ||
|
||||
(consider_lossy && IsEmptyRect(&dispose_none_params.rect_lossy_))) {
|
||||
// Don't encode the frame at all. Instead, the duration of the previous
|
||||
// frame will be increased later.
|
||||
assert(empty_rect_allowed_none);
|
||||
@ -1187,16 +1190,20 @@ static int CacheFrame(WebPAnimEncoder* const enc,
|
||||
enc->prev_candidate_undecided_ = 0;
|
||||
} else {
|
||||
int64_t curr_delta;
|
||||
FrameRect prev_rect_key, prev_rect_sub;
|
||||
|
||||
// Add this as a frame rectangle to enc.
|
||||
error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped);
|
||||
if (error_code != VP8_ENC_OK) goto End;
|
||||
if (frame_skipped) goto Skip;
|
||||
prev_rect_sub = enc->prev_rect_;
|
||||
|
||||
|
||||
// Add this as a key-frame to enc, too.
|
||||
error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped);
|
||||
if (error_code != VP8_ENC_OK) goto End;
|
||||
assert(frame_skipped == 0); // Key-frame cannot be an empty rectangle.
|
||||
prev_rect_key = enc->prev_rect_;
|
||||
|
||||
// Analyze size difference of the two variants.
|
||||
curr_delta = KeyFramePenalty(encoded_frame);
|
||||
@ -1207,11 +1214,13 @@ static int CacheFrame(WebPAnimEncoder* const enc,
|
||||
old_keyframe->is_key_frame_ = 0;
|
||||
}
|
||||
encoded_frame->is_key_frame_ = 1;
|
||||
enc->prev_candidate_undecided_ = 1;
|
||||
enc->keyframe_ = (int)position;
|
||||
enc->best_delta_ = curr_delta;
|
||||
enc->flush_count_ = enc->count_ - 1; // We can flush previous frames.
|
||||
} else {
|
||||
encoded_frame->is_key_frame_ = 0;
|
||||
enc->prev_candidate_undecided_ = 0;
|
||||
}
|
||||
// Note: We need '>=' below because when kmin and kmax are both zero,
|
||||
// count_since_key_frame will always be > kmax.
|
||||
@ -1221,7 +1230,10 @@ static int CacheFrame(WebPAnimEncoder* const enc,
|
||||
enc->keyframe_ = KEYFRAME_NONE;
|
||||
enc->best_delta_ = DELTA_INFINITY;
|
||||
}
|
||||
enc->prev_candidate_undecided_ = 1;
|
||||
if (!enc->prev_candidate_undecided_) {
|
||||
enc->prev_rect_ =
|
||||
encoded_frame->is_key_frame_ ? prev_rect_key : prev_rect_sub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,3 +9,4 @@ Version: @PACKAGE_VERSION@
|
||||
Requires: libwebp >= 0.2.0
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -lwebpmux
|
||||
Libs.private: -lm
|
||||
|
@ -28,7 +28,7 @@ extern "C" {
|
||||
|
||||
#define MUX_MAJ_VERSION 0
|
||||
#define MUX_MIN_VERSION 3
|
||||
#define MUX_REV_VERSION 1
|
||||
#define MUX_REV_VERSION 2
|
||||
|
||||
// Chunk object.
|
||||
typedef struct WebPChunk WebPChunk;
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "./muxi.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#define UNDEFINED_CHUNK_SIZE (-1)
|
||||
#define UNDEFINED_CHUNK_SIZE ((uint32_t)(-1))
|
||||
|
||||
const ChunkInfo kChunks[] = {
|
||||
{ MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE },
|
||||
@ -439,7 +439,7 @@ static int IsNotCompatible(int feature, int num_items) {
|
||||
return (feature != 0) != (num_items > 0);
|
||||
}
|
||||
|
||||
#define NO_FLAG 0
|
||||
#define NO_FLAG ((WebPFeatureFlags)0)
|
||||
|
||||
// Test basic constraints:
|
||||
// retrieval, maximum number of chunks by index (use -1 to skip)
|
||||
|
@ -75,11 +75,13 @@ static WEBP_INLINE int NextTableBitSize(const int* const count,
|
||||
return len - root_bits;
|
||||
}
|
||||
|
||||
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size) {
|
||||
// sorted[code_lengths_size] is a pre-allocated array for sorting symbols
|
||||
// by code length.
|
||||
static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size,
|
||||
uint16_t sorted[]) {
|
||||
HuffmanCode* table = root_table; // next available space in table
|
||||
int total_size = 1 << root_bits; // total size root table + 2nd level table
|
||||
int* sorted = NULL; // symbols sorted by code length
|
||||
int len; // current code length
|
||||
int symbol; // symbol index in original or sorted table
|
||||
// number of codes of each length:
|
||||
@ -89,7 +91,8 @@ int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
|
||||
assert(code_lengths_size != 0);
|
||||
assert(code_lengths != NULL);
|
||||
assert(root_table != NULL);
|
||||
assert((root_table != NULL && sorted != NULL) ||
|
||||
(root_table == NULL && sorted == NULL));
|
||||
assert(root_bits > 0);
|
||||
|
||||
// Build histogram of code lengths.
|
||||
@ -114,26 +117,26 @@ int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
offset[len + 1] = offset[len] + count[len];
|
||||
}
|
||||
|
||||
sorted = (int*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
|
||||
if (sorted == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sort symbols by length, by symbol order within each length.
|
||||
for (symbol = 0; symbol < code_lengths_size; ++symbol) {
|
||||
const int symbol_code_length = code_lengths[symbol];
|
||||
if (code_lengths[symbol] > 0) {
|
||||
sorted[offset[symbol_code_length]++] = symbol;
|
||||
if (sorted != NULL) {
|
||||
sorted[offset[symbol_code_length]++] = symbol;
|
||||
} else {
|
||||
offset[symbol_code_length]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special case code with only one value.
|
||||
if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) {
|
||||
HuffmanCode code;
|
||||
code.bits = 0;
|
||||
code.value = (uint16_t)sorted[0];
|
||||
ReplicateValue(table, 1, total_size, code);
|
||||
WebPSafeFree(sorted);
|
||||
if (sorted != NULL) {
|
||||
HuffmanCode code;
|
||||
code.bits = 0;
|
||||
code.value = (uint16_t)sorted[0];
|
||||
ReplicateValue(table, 1, total_size, code);
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
@ -153,9 +156,9 @@ int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
num_nodes += num_open;
|
||||
num_open -= count[len];
|
||||
if (num_open < 0) {
|
||||
WebPSafeFree(sorted);
|
||||
return 0;
|
||||
}
|
||||
if (root_table == NULL) continue;
|
||||
for (; count[len] > 0; --count[len]) {
|
||||
HuffmanCode code;
|
||||
code.bits = (uint8_t)len;
|
||||
@ -172,34 +175,122 @@ int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
num_nodes += num_open;
|
||||
num_open -= count[len];
|
||||
if (num_open < 0) {
|
||||
WebPSafeFree(sorted);
|
||||
return 0;
|
||||
}
|
||||
for (; count[len] > 0; --count[len]) {
|
||||
HuffmanCode code;
|
||||
if ((key & mask) != low) {
|
||||
table += table_size;
|
||||
if (root_table != NULL) table += table_size;
|
||||
table_bits = NextTableBitSize(count, len, root_bits);
|
||||
table_size = 1 << table_bits;
|
||||
total_size += table_size;
|
||||
low = key & mask;
|
||||
root_table[low].bits = (uint8_t)(table_bits + root_bits);
|
||||
root_table[low].value = (uint16_t)((table - root_table) - low);
|
||||
if (root_table != NULL) {
|
||||
root_table[low].bits = (uint8_t)(table_bits + root_bits);
|
||||
root_table[low].value = (uint16_t)((table - root_table) - low);
|
||||
}
|
||||
}
|
||||
if (root_table != NULL) {
|
||||
code.bits = (uint8_t)(len - root_bits);
|
||||
code.value = (uint16_t)sorted[symbol++];
|
||||
ReplicateValue(&table[key >> root_bits], step, table_size, code);
|
||||
}
|
||||
code.bits = (uint8_t)(len - root_bits);
|
||||
code.value = (uint16_t)sorted[symbol++];
|
||||
ReplicateValue(&table[key >> root_bits], step, table_size, code);
|
||||
key = GetNextKey(key, len);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if tree is full.
|
||||
if (num_nodes != 2 * offset[MAX_ALLOWED_CODE_LENGTH] - 1) {
|
||||
WebPSafeFree(sorted);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
WebPSafeFree(sorted);
|
||||
return total_size;
|
||||
}
|
||||
|
||||
// Maximum code_lengths_size is 2328 (reached for 11-bit color_cache_bits).
|
||||
// More commonly, the value is around ~280.
|
||||
#define MAX_CODE_LENGTHS_SIZE \
|
||||
((1 << MAX_CACHE_BITS) + NUM_LITERAL_CODES + NUM_LENGTH_CODES)
|
||||
// Cut-off value for switching between heap and stack allocation.
|
||||
#define SORTED_SIZE_CUTOFF 512
|
||||
int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size) {
|
||||
const int total_size =
|
||||
BuildHuffmanTable(NULL, root_bits, code_lengths, code_lengths_size, NULL);
|
||||
assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
|
||||
if (total_size == 0 || root_table == NULL) return total_size;
|
||||
|
||||
if (root_table->curr_segment->curr_table + total_size >=
|
||||
root_table->curr_segment->start + root_table->curr_segment->size) {
|
||||
// If 'root_table' does not have enough memory, allocate a new segment.
|
||||
// The available part of root_table->curr_segment is left unused because we
|
||||
// need a contiguous buffer.
|
||||
const int segment_size = root_table->curr_segment->size;
|
||||
struct HuffmanTablesSegment* next =
|
||||
(HuffmanTablesSegment*)WebPSafeMalloc(1, sizeof(*next));
|
||||
if (next == NULL) return 0;
|
||||
// Fill the new segment.
|
||||
// We need at least 'total_size' but if that value is small, it is better to
|
||||
// allocate a big chunk to prevent more allocations later. 'segment_size' is
|
||||
// therefore chosen (any other arbitrary value could be chosen).
|
||||
next->size = total_size > segment_size ? total_size : segment_size;
|
||||
next->start =
|
||||
(HuffmanCode*)WebPSafeMalloc(next->size, sizeof(*next->start));
|
||||
if (next->start == NULL) {
|
||||
WebPSafeFree(next);
|
||||
return 0;
|
||||
}
|
||||
next->curr_table = next->start;
|
||||
next->next = NULL;
|
||||
// Point to the new segment.
|
||||
root_table->curr_segment->next = next;
|
||||
root_table->curr_segment = next;
|
||||
}
|
||||
if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
|
||||
// use local stack-allocated array.
|
||||
uint16_t sorted[SORTED_SIZE_CUTOFF];
|
||||
BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
|
||||
code_lengths, code_lengths_size, sorted);
|
||||
} else { // rare case. Use heap allocation.
|
||||
uint16_t* const sorted =
|
||||
(uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
|
||||
if (sorted == NULL) return 0;
|
||||
BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
|
||||
code_lengths, code_lengths_size, sorted);
|
||||
WebPSafeFree(sorted);
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables) {
|
||||
// Have 'segment' point to the first segment for now, 'root'.
|
||||
HuffmanTablesSegment* const root = &huffman_tables->root;
|
||||
huffman_tables->curr_segment = root;
|
||||
// Allocate root.
|
||||
root->start = (HuffmanCode*)WebPSafeMalloc(size, sizeof(*root->start));
|
||||
if (root->start == NULL) return 0;
|
||||
root->curr_table = root->start;
|
||||
root->next = NULL;
|
||||
root->size = size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables) {
|
||||
HuffmanTablesSegment *current, *next;
|
||||
if (huffman_tables == NULL) return;
|
||||
// Free the root node.
|
||||
current = &huffman_tables->root;
|
||||
next = current->next;
|
||||
WebPSafeFree(current->start);
|
||||
current->start = NULL;
|
||||
current->next = NULL;
|
||||
current = next;
|
||||
// Free the following nodes.
|
||||
while (current != NULL) {
|
||||
next = current->next;
|
||||
WebPSafeFree(current->start);
|
||||
WebPSafeFree(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,29 @@ typedef struct {
|
||||
// or non-literal symbol otherwise
|
||||
} HuffmanCode32;
|
||||
|
||||
// Contiguous memory segment of HuffmanCodes.
|
||||
typedef struct HuffmanTablesSegment {
|
||||
HuffmanCode* start;
|
||||
// Pointer to where we are writing into the segment. Starts at 'start' and
|
||||
// cannot go beyond 'start' + 'size'.
|
||||
HuffmanCode* curr_table;
|
||||
// Pointer to the next segment in the chain.
|
||||
struct HuffmanTablesSegment* next;
|
||||
int size;
|
||||
} HuffmanTablesSegment;
|
||||
|
||||
// Chained memory segments of HuffmanCodes.
|
||||
typedef struct HuffmanTables {
|
||||
HuffmanTablesSegment root;
|
||||
// Currently processed segment. At first, this is 'root'.
|
||||
HuffmanTablesSegment* curr_segment;
|
||||
} HuffmanTables;
|
||||
|
||||
// Allocates a HuffmanTables with 'size' contiguous HuffmanCodes. Returns 0 on
|
||||
// memory allocation error, 1 otherwise.
|
||||
int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables);
|
||||
void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables);
|
||||
|
||||
#define HUFFMAN_PACKED_BITS 6
|
||||
#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS)
|
||||
|
||||
@ -78,7 +101,7 @@ void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
|
||||
// the huffman table.
|
||||
// Returns built table size or 0 in case of error (invalid tree or
|
||||
// memory error).
|
||||
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -48,11 +48,15 @@ void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
|
||||
wrk->y_sub = wrk->y_expand ? y_sub - 1 : y_sub;
|
||||
wrk->y_accum = wrk->y_expand ? wrk->y_sub : wrk->y_add;
|
||||
if (!wrk->y_expand) {
|
||||
// this is WEBP_RESCALER_FRAC(dst_height, x_add * y_add) without the cast.
|
||||
// This is WEBP_RESCALER_FRAC(dst_height, x_add * y_add) without the cast.
|
||||
// Its value is <= WEBP_RESCALER_ONE, because dst_height <= wrk->y_add, and
|
||||
// wrk->x_add >= 1;
|
||||
const uint64_t ratio =
|
||||
(uint64_t)dst_height * WEBP_RESCALER_ONE / (wrk->x_add * wrk->y_add);
|
||||
if (ratio != (uint32_t)ratio) {
|
||||
// We can't represent the ratio with the current fixed-point precision.
|
||||
// When ratio == WEBP_RESCALER_ONE, we can't represent the ratio with the
|
||||
// current fixed-point precision. This happens when src_height ==
|
||||
// wrk->y_add (which == src_height), and wrk->x_add == 1.
|
||||
// => We special-case fxy_scale = 0, in WebPRescalerExportRow().
|
||||
wrk->fxy_scale = 0;
|
||||
} else {
|
||||
|
@ -175,8 +175,12 @@ static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
|
||||
}
|
||||
#endif
|
||||
#if defined(MALLOC_LIMIT)
|
||||
if (mem_limit > 0 && total_mem + total_size >= mem_limit) {
|
||||
return 0; // fake fail!
|
||||
if (mem_limit > 0) {
|
||||
const uint64_t new_total_mem = (uint64_t)total_mem + total_size;
|
||||
if (new_total_mem != (size_t)new_total_mem ||
|
||||
new_total_mem > mem_limit) {
|
||||
return 0; // fake fail!
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "../dsp/dsp.h"
|
||||
#include "../webp/types.h"
|
||||
@ -32,7 +33,14 @@ extern "C" {
|
||||
// Memory allocation
|
||||
|
||||
// This is the maximum memory amount that libwebp will ever try to allocate.
|
||||
#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 40)
|
||||
#ifndef WEBP_MAX_ALLOCABLE_MEMORY
|
||||
#if SIZE_MAX > (1ULL << 34)
|
||||
#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 34)
|
||||
#else
|
||||
// For 32-bit targets keep this below INT_MAX to avoid valgrind warnings.
|
||||
#define WEBP_MAX_ALLOCABLE_MEMORY ((1ULL << 31) - (1 << 16))
|
||||
#endif
|
||||
#endif // WEBP_MAX_ALLOCABLE_MEMORY
|
||||
|
||||
// size-checking safe malloc/calloc: verify that the requested size is not too
|
||||
// large, or return NULL. You don't need to call these for constructs like
|
||||
|
@ -248,19 +248,19 @@ typedef enum VP8StatusCode {
|
||||
// picture is only partially decoded, pending additional input.
|
||||
// Code example:
|
||||
//
|
||||
// WebPInitDecBuffer(&buffer);
|
||||
// buffer.colorspace = mode;
|
||||
// WebPInitDecBuffer(&output_buffer);
|
||||
// output_buffer.colorspace = mode;
|
||||
// ...
|
||||
// WebPIDecoder* idec = WebPINewDecoder(&buffer);
|
||||
// while (has_more_data) {
|
||||
// // ... (get additional data)
|
||||
// WebPIDecoder* idec = WebPINewDecoder(&output_buffer);
|
||||
// while (additional_data_is_available) {
|
||||
// // ... (get additional data in some new_data[] buffer)
|
||||
// status = WebPIAppend(idec, new_data, new_data_size);
|
||||
// if (status != VP8_STATUS_SUSPENDED ||
|
||||
// break;
|
||||
// if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
|
||||
// break; // an error occurred.
|
||||
// }
|
||||
//
|
||||
// // The above call decodes the current available buffer.
|
||||
// // Part of the image can now be refreshed by calling to
|
||||
// // Part of the image can now be refreshed by calling
|
||||
// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
|
||||
// }
|
||||
// WebPIDelete(idec);
|
||||
@ -471,16 +471,18 @@ static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
|
||||
// parameter, in which case the features will be parsed and stored into
|
||||
// config->input. Otherwise, 'data' can be NULL and no parsing will occur.
|
||||
// Note that 'config' can be NULL too, in which case a default configuration
|
||||
// is used.
|
||||
// is used. If 'config' is not NULL, it must outlive the WebPIDecoder object
|
||||
// as some references to its fields will be used. No internal copy of 'config'
|
||||
// is made.
|
||||
// The return WebPIDecoder object must always be deleted calling WebPIDelete().
|
||||
// Returns NULL in case of error (and config->status will then reflect
|
||||
// the error condition).
|
||||
// the error condition, if available).
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPIDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config);
|
||||
|
||||
// Non-incremental version. This version decodes the full data at once, taking
|
||||
// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK
|
||||
// if the decoding was successful).
|
||||
// if the decoding was successful). Note that 'config' cannot be NULL.
|
||||
WEBP_EXTERN(VP8StatusCode) WebPDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config);
|
||||
|
||||
|
@ -481,10 +481,10 @@ WEBP_EXTERN(int) WebPPictureARGBToYUVADithered(
|
||||
WEBP_EXTERN(int) WebPPictureSmartARGBToYUVA(WebPPicture* picture);
|
||||
|
||||
// Converts picture->yuv to picture->argb and sets picture->use_argb to true.
|
||||
// The input format must be YUV_420 or YUV_420A.
|
||||
// Note that the use of this method is discouraged if one has access to the
|
||||
// raw ARGB samples, since using YUV420 is comparatively lossy. Also, the
|
||||
// conversion from YUV420 to ARGB incurs a small loss too.
|
||||
// The input format must be YUV_420 or YUV_420A. The conversion from YUV420 to
|
||||
// ARGB incurs a small loss too.
|
||||
// Note that the use of this colorspace is discouraged if one has access to the
|
||||
// raw ARGB samples, since using YUV420 is comparatively lossy.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN(int) WebPPictureYUVAToARGB(WebPPicture* picture);
|
||||
|
||||
|
Reference in New Issue
Block a user