Compare commits

..

59 Commits

Author SHA1 Message Date
e1f1bce9dc Fix invalid incremental decoding check.
(cherry picked from commit 95ea5226c8)

Change-Id: I80c2165aa9fdf43077db155d2d00e0e99db73eab
2023-10-06 11:53:43 +02:00
90d47113bc Fix OOB write in BuildHuffmanTable.
First, BuildHuffmanTable is called to check if the data is valid.
If it is and the table is not big enough, more memory is allocated.

This will make sure that valid (but unoptimized because of unbalanced
codes) streams are still decodable.
(cherry picked from commit 902bc91)

Change-Id: I3abe4db460dcac62c14a84832284c0b530630af2
2023-10-06 11:53:43 +02:00
bc99ef0699 Limit memory allocation when reading invalid Huffman codes.
This is a backported fix for: CVE-2020-36332

This is a merge of:
dce5d76431
39cb9aad85
067031eaed

Change-Id: Iab84d2ca459327cdcee1038499842d30370fe486
2023-10-06 11:53:40 +02:00
ece9684f52 update ChangeLog
Change-Id: I0d8730f3f5fb0b54404666bc0088fe29a3e55fc4
2016-12-15 15:44:21 -08:00
aa7744ca38 anim_util: quiet implicit conv warnings in 32-bit
the sizes are already validated by CheckSizeForOverflow(), add casts to
size_t to avoid -Wshorten-64-to-32

Change-Id: Ida9102c2104f4a334a0ad16d6e01a12bedfd4eec
(cherry picked from commit 1e2e25b0d4)
2016-12-15 11:51:44 -08:00
d9120271e7 jpegdec: correct ContextFill signature
fill_input_buffer returns a boolean

Change-Id: I208a1a862fa6c57cb5b73568b84055f734c1c36f
(cherry picked from commit cec7201447)
2016-12-15 11:51:28 -08:00
24eb39401b Remove some errors when compiling the code as C++.
This fixes some cases from
https://bugs.chromium.org/p/webp/issues/detail?id=137

Change-Id: I58f3a617bf973dbe4c5794004a01e2aea39ba53a
(cherry picked from commit 28ce304344)
2016-12-15 11:50:44 -08:00
a4a8e5f32c vwebp: clear canvas during resize w/o animation
this corrects the checkboard pattern displayed with transparent images

Change-Id: I5f46dbc9fa3893d61f5f1d4fda643ac030238f94
(cherry picked from commit 67c25ad5b4)
2016-12-14 11:26:24 -08:00
31ca2a8098 tiffdec: restore libtiff 3.9.x compatibility
use tsize_t which is equivalent (for now) to tmsize_t added in version 4

Change-Id: I7489ef7b39d9a3692a4b952de22476b245240ed1
2016-12-13 22:50:28 -08:00
b2f77b57a9 update NEWS
Change-Id: Ie74e9331378a254cc2265eaa1c94101db767e411
2016-12-13 18:51:20 -08:00
5ab6d9de1f AnimEncoder: avoid freeing uninitialized memory pointer.
In GenerateCandidates(), when candidate_ll->evaluate_ and
candidate_lossy->evaluate_ are both true, if lossless encoding
exits on error, candidate_ll->evaluate_ would not be correctly
reset. This will cause freeing uninitialized memory pointer in
SetFrame().

BUG=webp:322

Change-Id: I481b49a186e4fa3607ce71b4543a481083edf444
(cherry picked from commit 3ebe1c0003)
2016-12-13 18:18:57 -08:00
f29bf582df WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless.
This improves compression by ~5% at default quality.

If only 'allow_mixed' is on (but 'minimize_size' isn't), we continue to
use a heuristic to try one of the two or both.

Change-Id: Ia573a73ea26ad25f9debff759eed69d2b0449e82
(cherry picked from commit 3f4042b52a)
2016-12-13 18:18:48 -08:00
df780e0eac fix a potential overflow with MALLOC_LIMIT
BUG=webp:321

Change-Id: Iab89dfe167fb394fcdffd3b2732d4ac9bef764b0
(cherry picked from commit 76bbcf2ed6)
2016-12-13 16:15:28 -08:00
218460cdd7 bump version to 0.5.2
libwebp{,decoder} - 0.5.2
libwebp libtool - 6.2.0
libwebpdecoder libtool - 2.2.0

mux - 0.3.2
libtool - 2.2.0

demux - 0.3.1
libtool - 2.1.0

Change-Id: Idf199415c325e6e9d157459a4e016ebba88c3f34
2016-12-12 17:36:12 -08:00
de7d654d0d update AUTHORS & .mailmap
Change-Id: If4d71ede51f91959e4990f3455c781a8bcf4d373
2016-12-12 17:36:09 -08:00
74a12b10d9 iosbuild.sh: add WebPDecoder.framework + encoder
WebPDecoder.framework replaces WebP.framework as the decode-only
framework.
WebP.framework now includes the full library allowing for use of the
encoder.

BUG=webp:307

Change-Id: Ic8139f201576bf94b0d4a31cb7cad0655cd8ba97
(cherry picked from commit 1d5046d1f9)
2016-12-12 12:50:49 -08:00
be7dcc088c AnimEncoder: Correctly skip a frame when sub-rectangle is empty.
Change-Id: I0d288bd9561b48cf5a1eae92a1b7106ba44c664e
(cherry picked from commit 1cc79e92ac)
2016-12-09 20:22:31 -08:00
408858308a Fix assertions in WebPRescalerExportRow()
Change-Id: I25711dd54e71c90a25f7b18e0ef9155e8151a15e
(cherry picked from commit 27b5d991e2)
2016-12-09 20:22:25 -08:00
8f38c72e11 fix a typo in WebPPictureYUVAToARGB's doc
method -> colorspace

Change-Id: I5c9a2ccc909c967a936758dde2cfce92eb95462a
(cherry picked from commit dc789ada44)
2016-12-09 17:27:59 -08:00
33ca93f909 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_
Change-Id: I4a767134dcc52a7ee7c3bc5deb91012eaf7b6512
(cherry picked from commit aaf2a6a698)
2016-12-09 17:27:54 -08:00
76e190735b doc: use two's complement explicitly for uint8->int8 conversion
BUG=webp:225

Change-Id: I6bad131e275dbd992484e95a1b834010121281b8
(cherry picked from commit 13ae011e4c)
2016-12-09 17:27:50 -08:00
f91ba96306 Anim_encoder: correctly handle enc->prev_candidate_undecided_
Set enc->prev_candidate_undecided_ as 0 when a frame is not chosen
as a possible keyframe, so that the dispose method can be
dispose-to-background.

Change-Id: If2899f5dbc06fb53705fb8240072ab6440a6de12
(cherry picked from commit 29fedbf58b)
2016-12-09 16:58:28 -08:00
25d74e652e WebPPictureDistortion(): free() -> WebPSafeFree()
missed one!

Change-Id: I643170451b3ac07c748b70a9abfe8af17a716b24
(cherry picked from commit 32dead4ee3)
2016-12-09 16:58:19 -08:00
03f1c00877 mux/Makefile.am: add missing -lm
+ libwebpmux.pc

anim_encode.c relies on functions from math.h

BUG=webp:306

Change-Id: I3a8eb48febfd52bfbeb04f4dc615ccbed72926f7
(cherry picked from commit aaf2530cc3)
2016-12-09 15:03:08 -08:00
58410cd6dc fix bug in RefineUsingDistortion()
When try_both_modes=0 (that is: -m 0 or -m 1), and the mode is i4,
we were still sometimes falling back to (unexplored, uninitialized) i16 mode,
which resulted in a enc/dec mismatch.
This was mainly occurring for large images (when bit_limit is low enough)

We disable the fall-back by disabling bit_limit using a large MAX_COST threshold.

Change-Id: I0c60257595812bd813b239ff4c86703ddf63cbf8
(cherry picked from commit 0a3838ca77)
2016-12-08 15:48:16 -08:00
e168af8c6c fix filtering auto-adjustment
the min-distortion was quite too low. And we were also
considering the fully skipped macroblocks (nz=0) in the stats.
We need to have at least *some* non-zero dc coeffs (nz=0x100XXXX).

Fix also two typos in StoreMaxDelta: the v0/v1 comparison was wrong,
and the DCs[] coeffs are actually already in ZigZag order.

Change-Id: I602aaa74b36f7ce80017e506212c7d6fd9deba1f
(cherry picked from commit e4cd4daf74)
2016-12-08 15:48:08 -08:00
ed9dec41a5 fix doc and code snippet for WebPINewDecoder() doc
Change-Id: I1a75fdf60f0b9f1816be28f22613438bfe21752b
(cherry picked from commit e715285611)
2016-12-08 15:48:04 -08:00
3c49178f7d prevent 32b overflow for very large canvas_width / height
some multiplies here and there needed some extra checks
and error reporting. Even if width * height is guaranteed
to be < 2**32, we were multiplying by num_channels and
triggering a 32b overflow.
Some multiplies were not using size_t or uint64_t, additionally.

Change-Id: If2a35b94c8af204135f4b88a7fd63850aa381bbf
(cherry picked from commit 1c36440094)
2016-12-08 15:27:51 -08:00
9595f29010 fix anim_util.c compilation when HAVE_GIF is not defined.
Change-Id: I1a4f13a5799c020fb4b3ade3c7336f049443ab62
(cherry picked from commit 1effde7bcd)
2016-12-08 15:27:46 -08:00
7ec9552c22 Make gif transparent color to be transparent black
Change it from transparent white to transparent black, which matches
the transparent color assumed in Webp dispose-to-background method.

Also pre-multiply background colors before comparison in anim_diff,
just as what is done with regular pixel values.

Change-Id: I5a790522df21619c666ce499f73e42294ed276f2
(cherry picked from commit 43bd895879)
2016-12-08 15:27:40 -08:00
9871335fc8 Add a CMake option for WEBP_SWAP_16BIT_CSP.
Change-Id: I79ed6cbce9563bf7ca97fc2e10ec3e75b6b8ab5b
(cherry picked from commit 7b54e26bac)
2016-12-07 23:41:36 -08:00
0ae32226ce Fix missing cpu-features for Android.
Change-Id: Ief13b7c63b96194970b00a5732aa080f794e7ae6
(cherry picked from commit d2223d8d6c)
2016-12-07 23:41:28 -08:00
ab4c8056e0 cpu.cmake: improve webp_check_compiler_flag output
WEBP_HAVE_FLAG_LOCAL -> WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} this will
include the flag being tested in the output:
-- Performing Test WEBP_HAVE_FLAG_NEON

Change-Id: I1c0a143a857b16e4eb1fcf8b23c176380a5fef29
(cherry picked from commit ee1057e3b1)
2016-12-07 23:41:23 -08:00
eec5fa3a95 Provide support for CMake on Android studio 2.2.
Change-Id: Ib3508c20a220b29d42f963cf154cd9ab5236ed6e
(cherry picked from commit a80e8cfdf2)
2016-12-07 23:41:17 -08:00
004d569086 Split the main CMake file.
This will well isolate contributions for original code,
generated code and SIMD (especially for Android).

Change-Id: Ie47664decc7f43c2f57260a72cab951c347281a7
(cherry picked from commit 6c62841076)
2016-12-07 23:41:08 -08:00
4fe5d588bf Android.mk: use -fvisibility=hidden
brings the final libwebp.so size down 16/20K with arm64/armv7 builds
using ndk-r13

Change-Id: I20d8aba61d6b692b0fc32f4b271e2f9872f03c28
(cherry picked from commit de568abfdb)
2016-12-07 18:34:46 -08:00
bd63a31aab vwebp: ensure setenv() is available in stdlib.h
quiets a -Wimplicit-function-declaration with some configurations of gcc
(-std=c99).
_POSIX_C_SOURCE is preferred over _BSD_SOURCE with newer versions of
glibc

Change-Id: I378bffb13ba52ff5c4bad1433090dcc387e5d507
(cherry picked from commit bfab894739)
2016-12-07 18:30:54 -08:00
363a568131 vwebp: handle window resizing properly
The image is scaled to fit the whole viewport.
Avoid some oddities with offsets, etc.

removes some TODO.

Change-Id: I52fae9ca80a2feed234f32261c7f6358d7594e21
(cherry picked from commit 9310d19258)
2016-12-07 18:30:49 -08:00
a0d2753fcb lower WEBP_MAX_ALLOCABLE_MEMORY default
restrict to 2^34 for 64-bit targets, < 2^32 for 32-bit

Change-Id: Iff4ce40ae2c3c7fc119f018c2128dbe8f744341f
(cherry picked from commit b8384b53d6)
2016-12-07 18:30:44 -08:00
31fe11a57a fix infinite loop in case of PARTITION0 overflow
max_i4_header_bits_ could drop to zero for difficult image and trigger
a loop. Surprisingly, StatLoop() didn't have this bug.

Change-Id: Idc0f9eadef30a2b2f02041b994f25def30901e36
(cherry picked from commit 21e7537abe)
2016-12-07 18:30:39 -08:00
532215dd29 Change the rule of picking UV mode in MBAnalyzeBestUVMode()
Pick the mode with the smallest alpha.
It only affects m0, in which case the mode decision is not re-examined
later in VP8Decimate(). Tests on some natural content png images show
PSNR increase as well as visual quality improvement.

Change-Id: Iea997e718cd7477160fa05eb7cfb35f4cec2fa9a
(cherry picked from commit 1377ac2ec1)
2016-12-07 18:30:33 -08:00
9c75dbd39c cwebp.1: improve some grammar
Change-Id: Id849d7e0d7573f5b8d3b2e807d95e9c628f03b1e
(cherry picked from commit 5b46f7fc80)
2016-12-07 18:30:28 -08:00
af2e05cbdf vwebp: Clear previous frame when a key triggers a redraw
otherwise, transparent areas were accumulating.

Change-Id: I066a96a2bcf0cac750b3df0c02229542b1ed3473
(cherry picked from commit c0a27fd2af)
2016-12-07 18:30:23 -08:00
26ffa2962b Add descriptions of default configuration in help info.
Change-Id: I43188fab5f57bd45aa3e564df52e36cc37b1bb2f
(cherry picked from commit 74f6f9e793)
2016-12-07 18:30:17 -08:00
7416280d75 Fix an unsigned integer overflow error in enc/cost.h
Change-Id: I9774b59c417c185f09a61a115364b9642976a100
(cherry picked from commit 0b2c58a91c)
2016-12-07 18:29:51 -08:00
13cf1d2e41 Do token recording and counting in a single loop
Change-Id: I8afd3c486b210bd67888de03e91dde7f78276f89
(cherry picked from commit 0c0fb83211)
2016-12-07 18:29:44 -08:00
eb9a4b97c5 Reset segment id if we decide not to update segment map
This avoids potential encoder and decoder mismatch.

Change-Id: I5282d3e168afc6193033ad3fce8fbc35618ab2f5
(cherry picked from commit 386e4ba2f0)
2016-12-07 18:25:06 -08:00
42ebe3b783 configure: fix NEON flag detection under gcc 6
use a compile check on a separate file to avoid assuming using
arm_neon.h is safe to use without flags when just the file itself is
self-contained with GCC target pragmas.

BUG=webp:313

Change-Id: I48f92ae3e6e4a9468ea5b937c80a89ee40b2dcfd
(cherry picked from commit 0104d730bf)
2016-12-07 18:23:55 -08:00
83cbfa09a1 Import: use relative pointer offsets
avoids int rollover when working with large input

BUG=webp:312

Change-Id: I6ad9f93b6c4b665c559bff87716a7b847f66a20d
(cherry picked from commit 342e15f0ce)
2016-11-09 15:50:57 -08:00
a1ade40ed8 PreprocessARGB: use relative pointer offsets
avoids int rollover when working with large input

BUG=webp:312

Change-Id: I2881bec2884b550c966108beeff1bf0d8ef9f76b
(cherry picked from commit 1147ab4ee7)
2016-11-09 15:24:16 -08:00
fd4d090fd1 ConvertWRGBToYUV: use relative pointer offsets
avoids int rollover when working with large input

BUG=webp:312

Change-Id: I693cbb295df9cf94aa89294b19c0496bdbe84d18
(cherry picked from commit de9fa5074e)
2016-11-09 12:57:03 -08:00
9daad4598b ImportYUVAFromRGBA: use relative pointer offsets
avoids int rollover when working with large input

BUG=webp:312

Change-Id: I3d7b689be8d5751248a82d1021243d80d3f67203
(cherry picked from commit deb1b83199)
2016-11-09 12:56:49 -08:00
c284780f0a imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow
and use it to validate decoder allocations. fixes a crash in jpegdec at
least.

BUG=webp:312

Change-Id: Ia940590098f29510add6aad10a8dfe9e9ea46bf4
(cherry picked from commit bc86b7a8a1)
2016-11-02 23:36:28 -07:00
e375080d8f gifdec,Remap: avoid out of bounds colormap read
make this function return success/failure.
an empty map or out of bounds read is treated as an error.

BUG=webp:316

Change-Id: Ic8651836915ea4dd8e0dc81ca8d5d3f247be1ff8
(cherry picked from commit cac9a36a23)
2016-11-02 23:10:22 -07:00
c222a053af additional fix for stride type as size_t
- remove the inclusion of format_constants.h
 - use incremental update of pointer, instead of arithmetic

(follow-up to e2affacc35)

Change-Id: I48420c8defc8d47339f54bc00e9da9617f08ab32
(cherry picked from commit 9f5c8eca79)
2016-11-02 23:09:49 -07:00
bb2336170b fix potential overflow when width * height * 4 >= (1<<32)
Mostly: avoid doing calculation like: ptr + j * stride
when stride is 'int'. Rather use size_t, or pointer increments (ptr += stride)
when possible.

BUG=webp:314

Change-Id: I81c684b515dd1ec4f601f32d50a6e821c4e46e20
(cherry picked from commit e2affacc35)
2016-11-02 23:09:49 -07:00
883d41fb40 gif2webp: fix crash with NULL extension data
DGifGetExtension() may successfully return, but the data pointer should
still be validated

BUG=webp:310

Change-Id: I6cfe617871fef2fe07887e5f48bb20f7ab7cfb35
(cherry picked from commit 806f6279ae)
2016-11-02 23:09:49 -07:00
3d97bb7514 update ChangeLog
Change-Id: I05b9ac2cf8fdd26e3f2e68780dca3e613bc2f732
2016-07-06 18:00:16 -07:00
deb54d915a Clarify the expected 'config' lifespan in WebPIDecode()
Change-Id: I08c880adc34d651a63fa2d07cfb07b1468a6df1b
2016-07-06 17:17:15 -07:00
61 changed files with 1195 additions and 678 deletions

View File

@ -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>

View File

@ -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)

View File

@ -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 \

View File

@ -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()

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -1,7 +1,7 @@
 __ __ ____ ____ ____ __ __ _ __ __
/ \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
\ / __/ _ \ __/ / / (_/ /__
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v0.3.1
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v0.3.2
Description:

141
cmake/config.h.cmake Normal file
View 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
View 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()

View File

@ -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])])

View File

@ -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,

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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);
}
// -----------------------------------------------------------------------------

View File

@ -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

View File

@ -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.

View File

@ -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;
}
}
}

View File

@ -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:

View File

@ -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) {

View File

@ -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)) {

View File

@ -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();
}

View File

@ -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)) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ||

View File

@ -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),

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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_);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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];

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
}
}

View File

@ -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'.

View File

@ -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;

View File

@ -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

View File

@ -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(&params->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;
}
}
}

View File

@ -9,3 +9,4 @@ Version: @PACKAGE_VERSION@
Requires: libwebp >= 0.2.0
Cflags: -I${includedir}
Libs: -L${libdir} -lwebpmux
Libs.private: -lm

View File

@ -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;

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);