Compare commits

..

2 Commits

Author SHA1 Message Date
3b44c5eb71 libwebp.jar: build w/Java 1.6 for Android compat
broken since:
 a5c297c swig/java: reduce wrapper function code duplication

this was a part of v0.3.1, but not v0.3.0.

Change-Id: I001d4bd0a7a1aa1b2d267bc63bc1d8226bff00c1
(cherry picked from commit de899516c7)
2013-10-16 19:17:16 +02:00
c28544a420 fix memleak in WebPIDelete()
happens when decoding is partial (past Partition0), without error and
interrupted by calling WebPIDelete()

WebPIDelete() needs to call VP8ExitCritical() to free in-flight resources

Change-Id: Id4faef1b92f7edd8c17d642c58860e70dd570506
(cherry picked from commit 40ae3520b1)
2013-10-16 19:16:53 +02:00
269 changed files with 18448 additions and 60869 deletions

1
.gitattributes vendored
View File

@ -2,4 +2,3 @@
.gitignore export-ignore .gitignore export-ignore
.mailmap export-ignore .mailmap export-ignore
*.pdf -text -diff *.pdf -text -diff
*.ppm -text -diff

13
.gitignore vendored
View File

@ -1,6 +1,5 @@
*.l[ao] *.l[ao]
*.[ao] *.[ao]
*.pc
.deps .deps
.libs .libs
/aclocal.m4 /aclocal.m4
@ -15,27 +14,15 @@
/libtool /libtool
/ltmain.sh /ltmain.sh
/missing /missing
/mkinstalldirs
/stamp-h1 /stamp-h1
Makefile Makefile
Makefile.in Makefile.in
examples/anim_diff
examples/[cdv]webp examples/[cdv]webp
examples/gif2webp examples/gif2webp
examples/img2webp
examples/webpmux examples/webpmux
src/webp/config.h*
src/webp/stamp-h1
/output /output
/doc/output /doc/output
*.idb *.idb
*.pdb *.pdb
/iosbuild /iosbuild
/WebP.framework /WebP.framework
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
.gradle
/build
extras/get_disto
extras/webp_quality

View File

@ -1,12 +1,6 @@
<johann.koenig@duck.com> <johannkoenig@google.com> <johann.koenig@duck.com> <johannkoenig@google.com>
Mikołaj Zalewski <mikolajz@google.com> Mikołaj Zalewski <mikolajz@google.com>
Pascal Massimino <pascal.massimino@gmail.com> Pascal Massimino <pascal.massimino@gmail.com>
<pascal.massimino@gmail.com> <skal@google.com>
Vikas Arora <vikasa@google.com> Vikas Arora <vikasa@google.com>
<vikasa@google.com> <vikasa@gmail.com> <vikasa@google.com> <vikasa@gmail.com>
<vikasa@google.com> <vikaas.arora@gmail.com> <vikasa@google.com> <vikaas.arora@gmail.com>
<slobodan.prijic@imgtec.com> <Slobodan.Prijic@imgtec.com>
<vrabaud@google.com> <vincent.rabaud@gmail.com>
Tamar Levy <tamar.levy@intel.com>
<qrczak@google.com> <qrczak>
Hui Su <huisu@google.com>

21
AUTHORS
View File

@ -1,38 +1,17 @@
Contributors: Contributors:
- Charles Munger (clm at google dot com)
- Christian Duvivier (cduvivier 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) - James Zern (jzern at google dot com)
- Jan Engelhardt (jengelh at medozas dot de) - Jan Engelhardt (jengelh at medozas dot de)
- Jehan (jehan at girinstud dot io)
- Johann (johann dot koenig at duck dot com) - Johann (johann dot koenig at duck dot com)
- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
- Jyrki Alakuijala (jyrki at google dot com) - Jyrki Alakuijala (jyrki at google dot com)
- Lode Vandevenne (lode at google dot com)
- Lou Quillio (louquillio at google dot com) - Lou Quillio (louquillio at google dot com)
- Mans Rullgard (mans at mansr dot com) - Mans Rullgard (mans at mansr dot com)
- Marcin Kowalczyk (qrczak at google dot com)
- Martin Olsson (mnemo at minimum dot se) - Martin Olsson (mnemo at minimum dot se)
- Mikołaj Zalewski (mikolajz at google dot com) - Mikołaj Zalewski (mikolajz at google dot com)
- Mislav Bradac (mislavm at google dot com)
- Nico Weber (thakis at chromium dot org)
- Noel Chromium (noel at chromium dot org) - Noel Chromium (noel at chromium dot org)
- Owen Rodley (orodley at google dot com)
- Parag Salasakar (img dot mips1 at gmail dot com)
- Pascal Massimino (pascal dot massimino at gmail dot com) - Pascal Massimino (pascal dot massimino at gmail dot com)
- Paweł Hajdan, Jr (phajdan dot jr at chromium dot org)
- Pierre Joye (pierre dot php at gmail dot com) - Pierre Joye (pierre dot php at gmail dot com)
- Sam Clegg (sbc at chromium dot org)
- Scott Hancher (seh at google dot com)
- Scott LaVarnway (slavarnway at google dot com) - Scott LaVarnway (slavarnway at google dot com)
- Scott Talbot (s at chikachow dot org)
- Slobodan Prijic (slobodan dot prijic at imgtec dot com)
- Somnath Banerjee (somnath dot banerjee at gmail dot com) - Somnath Banerjee (somnath dot banerjee at gmail dot com)
- Sriraman Tallam (tmsriram at google dot com)
- Tamar Levy (tamar dot levy at intel dot com)
- Timothy Gu (timothygu99 at gmail dot com)
- Urvang Joshi (urvang at google dot com) - Urvang Joshi (urvang at google dot com)
- Vikas Arora (vikasa at google dot com) - Vikas Arora (vikasa at google dot com)
- Vincent Rabaud (vrabaud at google dot com)
- Yang Zhang (yang dot zhang at arm dot com)

View File

@ -1,266 +1,75 @@
LOCAL_PATH := $(call my-dir) LOCAL_PATH:= $(call my-dir)
WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD include $(CLEAR_VARS)
WEBP_CFLAGS += -fvisibility=hidden LOCAL_SRC_FILES := \
src/dec/alpha.c \
src/dec/buffer.c \
src/dec/frame.c \
src/dec/idec.c \
src/dec/io.c \
src/dec/layer.c \
src/dec/quant.c \
src/dec/tree.c \
src/dec/vp8.c \
src/dec/vp8l.c \
src/dec/webp.c \
src/dsp/cpu.c \
src/dsp/dec.c \
src/dsp/dec_sse2.c \
src/dsp/enc.c \
src/dsp/enc_sse2.c \
src/dsp/lossless.c \
src/dsp/upsampling.c \
src/dsp/upsampling_sse2.c \
src/dsp/yuv.c \
src/enc/alpha.c \
src/enc/analysis.c \
src/enc/backward_references.c \
src/enc/config.c \
src/enc/cost.c \
src/enc/filter.c \
src/enc/frame.c \
src/enc/histogram.c \
src/enc/iterator.c \
src/enc/layer.c \
src/enc/picture.c \
src/enc/quant.c \
src/enc/syntax.c \
src/enc/token.c \
src/enc/tree.c \
src/enc/vp8l.c \
src/enc/webpenc.c \
src/utils/bit_reader.c \
src/utils/bit_writer.c \
src/utils/color_cache.c \
src/utils/filters.c \
src/utils/huffman.c \
src/utils/huffman_encode.c \
src/utils/quant_levels.c \
src/utils/quant_levels_dec.c \
src/utils/rescaler.c \
src/utils/thread.c \
src/utils/utils.c \
ifeq ($(APP_OPTIM),release) LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD \
WEBP_CFLAGS += -finline-functions -ffast-math \ -DWEBP_USE_THREAD \
-ffunction-sections -fdata-sections -finline-functions -frename-registers -ffast-math \
ifeq ($(findstring clang,$(NDK_TOOLCHAIN_VERSION)),) -s -fomit-frame-pointer -Isrc/webp
WEBP_CFLAGS += -frename-registers -s
endif
endif
ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),) LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
# Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal # Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
# instructions to be generated for armv7a code. Instead target the neon code # instructions to be generated for armv7a code. Instead target the neon code
# specifically. # specifically.
NEON := c.neon LOCAL_SRC_FILES += src/dsp/dec_neon.c.neon
USE_CPUFEATURES := yes LOCAL_SRC_FILES += src/dsp/upsampling_neon.c.neon
else LOCAL_SRC_FILES += src/dsp/enc_neon.c.neon
NEON := c
endif endif
LOCAL_STATIC_LIBRARIES := cpufeatures
dec_srcs := \ LOCAL_MODULE:= webp
src/dec/alpha_dec.c \
src/dec/buffer_dec.c \
src/dec/frame_dec.c \
src/dec/idec_dec.c \
src/dec/io_dec.c \
src/dec/quant_dec.c \
src/dec/tree_dec.c \
src/dec/vp8_dec.c \
src/dec/vp8l_dec.c \
src/dec/webp_dec.c \
demux_srcs := \
src/demux/anim_decode.c \
src/demux/demux.c \
dsp_dec_srcs := \
src/dsp/alpha_processing.c \
src/dsp/alpha_processing_mips_dsp_r2.c \
src/dsp/alpha_processing_neon.$(NEON) \
src/dsp/alpha_processing_sse2.c \
src/dsp/alpha_processing_sse41.c \
src/dsp/argb.c \
src/dsp/argb_mips_dsp_r2.c \
src/dsp/argb_sse2.c \
src/dsp/cpu.c \
src/dsp/dec.c \
src/dsp/dec_clip_tables.c \
src/dsp/dec_mips32.c \
src/dsp/dec_mips_dsp_r2.c \
src/dsp/dec_msa.c \
src/dsp/dec_neon.$(NEON) \
src/dsp/dec_sse2.c \
src/dsp/dec_sse41.c \
src/dsp/filters.c \
src/dsp/filters_mips_dsp_r2.c \
src/dsp/filters_msa.c \
src/dsp/filters_neon.$(NEON) \
src/dsp/filters_sse2.c \
src/dsp/lossless.c \
src/dsp/lossless_mips_dsp_r2.c \
src/dsp/lossless_msa.c \
src/dsp/lossless_neon.$(NEON) \
src/dsp/lossless_sse2.c \
src/dsp/rescaler.c \
src/dsp/rescaler_mips32.c \
src/dsp/rescaler_mips_dsp_r2.c \
src/dsp/rescaler_msa.c \
src/dsp/rescaler_neon.$(NEON) \
src/dsp/rescaler_sse2.c \
src/dsp/upsampling.c \
src/dsp/upsampling_mips_dsp_r2.c \
src/dsp/upsampling_msa.c \
src/dsp/upsampling_neon.$(NEON) \
src/dsp/upsampling_sse2.c \
src/dsp/yuv.c \
src/dsp/yuv_mips32.c \
src/dsp/yuv_mips_dsp_r2.c \
src/dsp/yuv_sse2.c \
dsp_enc_srcs := \
src/dsp/cost.c \
src/dsp/cost_mips32.c \
src/dsp/cost_mips_dsp_r2.c \
src/dsp/cost_sse2.c \
src/dsp/enc.c \
src/dsp/enc_avx2.c \
src/dsp/enc_mips32.c \
src/dsp/enc_mips_dsp_r2.c \
src/dsp/enc_msa.c \
src/dsp/enc_neon.$(NEON) \
src/dsp/enc_sse2.c \
src/dsp/enc_sse41.c \
src/dsp/lossless_enc.c \
src/dsp/lossless_enc_mips32.c \
src/dsp/lossless_enc_mips_dsp_r2.c \
src/dsp/lossless_enc_msa.c \
src/dsp/lossless_enc_neon.$(NEON) \
src/dsp/lossless_enc_sse2.c \
src/dsp/lossless_enc_sse41.c \
enc_srcs := \
src/enc/alpha_enc.c \
src/enc/analysis_enc.c \
src/enc/backward_references_enc.c \
src/enc/config_enc.c \
src/enc/cost_enc.c \
src/enc/delta_palettization_enc.c \
src/enc/filter_enc.c \
src/enc/frame_enc.c \
src/enc/histogram_enc.c \
src/enc/iterator_enc.c \
src/enc/near_lossless_enc.c \
src/enc/picture_enc.c \
src/enc/picture_csp_enc.c \
src/enc/picture_psnr_enc.c \
src/enc/picture_rescale_enc.c \
src/enc/picture_tools_enc.c \
src/enc/predictor_enc.c \
src/enc/quant_enc.c \
src/enc/syntax_enc.c \
src/enc/token_enc.c \
src/enc/tree_enc.c \
src/enc/vp8l_enc.c \
src/enc/webp_enc.c \
mux_srcs := \
src/mux/anim_encode.c \
src/mux/muxedit.c \
src/mux/muxinternal.c \
src/mux/muxread.c \
utils_dec_srcs := \
src/utils/bit_reader_utils.c \
src/utils/color_cache_utils.c \
src/utils/filters_utils.c \
src/utils/huffman_utils.c \
src/utils/quant_levels_dec_utils.c \
src/utils/random_utils.c \
src/utils/rescaler_utils.c \
src/utils/thread_utils.c \
src/utils/utils.c \
utils_enc_srcs := \
src/utils/bit_writer_utils.c \
src/utils/huffman_encode_utils.c \
src/utils/quant_levels_utils.c \
################################################################################
# libwebpdecoder
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(dec_srcs) \
$(dsp_dec_srcs) \
$(utils_dec_srcs) \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
# prefer arm over thumb mode for performance gains
LOCAL_ARM_MODE := arm
ifeq ($(USE_CPUFEATURES),yes)
LOCAL_STATIC_LIBRARIES := cpufeatures
endif
LOCAL_MODULE := webpdecoder_static
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
ifeq ($(ENABLE_SHARED),1) $(call import-module,android/cpufeatures)
include $(CLEAR_VARS)
LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
LOCAL_MODULE := webpdecoder
include $(BUILD_SHARED_LIBRARY)
endif # ENABLE_SHARED=1
################################################################################
# libwebp
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(dsp_enc_srcs) \
$(enc_srcs) \
$(utils_enc_srcs) \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
# prefer arm over thumb mode for performance gains
LOCAL_ARM_MODE := arm
LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
LOCAL_MODULE := webp
ifeq ($(ENABLE_SHARED),1)
include $(BUILD_SHARED_LIBRARY)
else
include $(BUILD_STATIC_LIBRARY)
endif
################################################################################
# libwebpdemux
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(demux_srcs)
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
# prefer arm over thumb mode for performance gains
LOCAL_ARM_MODE := arm
LOCAL_MODULE := webpdemux
ifeq ($(ENABLE_SHARED),1)
LOCAL_SHARED_LIBRARIES := webp
include $(BUILD_SHARED_LIBRARY)
else
LOCAL_STATIC_LIBRARIES := webp
include $(BUILD_STATIC_LIBRARY)
endif
################################################################################
# libwebpmux
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(mux_srcs)
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
# prefer arm over thumb mode for performance gains
LOCAL_ARM_MODE := arm
LOCAL_MODULE := webpmux
ifeq ($(ENABLE_SHARED),1)
LOCAL_SHARED_LIBRARIES := webp
include $(BUILD_SHARED_LIBRARY)
else
LOCAL_STATIC_LIBRARIES := webp
include $(BUILD_STATIC_LIBRARY)
endif
################################################################################
WEBP_SRC_PATH := $(LOCAL_PATH)
include $(WEBP_SRC_PATH)/imageio/Android.mk
include $(WEBP_SRC_PATH)/examples/Android.mk
ifeq ($(USE_CPUFEATURES),yes)
$(call import-module,android/cpufeatures)
endif

View File

@ -1,189 +0,0 @@
cmake_minimum_required(VERSION 2.8.7)
project(libwebp C)
# Options for coder / decoder executables.
option(WEBP_BUILD_CWEBP "Build the cwebp command line tool." OFF)
option(WEBP_BUILD_DWEBP "Build the dwebp command line tool." OFF)
option(WEBP_BUILD_GIF2WEBP "Build the gif2webp conversion tool." OFF)
option(WEBP_BUILD_IMG2WEBP "Build the img2webp animation tool." OFF)
option(WEBP_EXPERIMENTAL_FEATURES "Build with experimental features." OFF)
option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces." OFF)
set(WEBP_DEP_LIBRARIES)
set(WEBP_DEP_INCLUDE_DIRS)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE
"Build type: Release, Debug or RelWithDebInfo" STRING FORCE
)
endif()
include(cmake/config.h.cmake)
################################################################################
# Options.
if(WEBP_ENABLE_SWAP_16BIT_CSP)
add_definitions(-DWEBP_SWAP_16BIT_CSP)
endif()
################################################################################
# Android only.
if(ANDROID)
include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
add_library(cpufeatures STATIC
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
)
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
)
endif()
################################################################################
# WebP source files.
# Read the Makefile.am to get the source files.
function(parse_Makefile_am FOLDER VAR)
file(READ ${FOLDER}/Makefile.am MAKEFILE_AM)
string(REGEX MATCHALL "_SOURCES \\+= [^\n]*"
FILES_PER_LINE ${MAKEFILE_AM}
)
set(SRCS ${${VAR}})
foreach(FILES ${FILES_PER_LINE})
string(SUBSTRING ${FILES} 12 -1 FILES)
string(REGEX MATCHALL "[0-9a-z\\._]+"
FILES ${FILES}
)
foreach(FILE ${FILES})
list(APPEND SRCS ${FOLDER}/${FILE})
endforeach()
endforeach()
set(${VAR} ${SRCS} PARENT_SCOPE)
endfunction()
set(WEBP_SRCS)
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/dec "WEBP_SRCS")
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/demux "WEBP_SRCS")
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 use.
foreach(FILE ${WEBP_SIMD_FILES_NOT_TO_INCLUDE})
list(REMOVE_ITEM WEBP_SRCS ${FILE})
endforeach()
# Build the library.
add_definitions(-Wall)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/ ${WEBP_DEP_INCLUDE_DIRS})
add_library(webp ${WEBP_SRCS})
target_link_libraries(webp ${WEBP_DEP_LIBRARIES})
# Change the compile flags for SIMD files we use.
list(LENGTH WEBP_SIMD_FILES_TO_INCLUDE WEBP_SIMD_FILES_TO_INCLUDE_LENGTH)
math(EXPR WEBP_SIMD_FILES_TO_INCLUDE_RANGE
"${WEBP_SIMD_FILES_TO_INCLUDE_LENGTH}-1"
)
foreach(I_FILE RANGE ${WEBP_SIMD_FILES_TO_INCLUDE_RANGE})
list(GET WEBP_SIMD_FILES_TO_INCLUDE ${I_FILE} FILE)
list(GET WEBP_SIMD_FLAGS_TO_INCLUDE ${I_FILE} SIMD_COMPILE_FLAG)
set_source_files_properties(${FILE} PROPERTIES
COMPILE_FLAGS ${SIMD_COMPILE_FLAG}
)
endforeach()
# Build the executables if asked for.
if(WEBP_BUILD_CWEBP OR WEBP_BUILD_DWEBP OR
WEBP_BUILD_GIF2WEBP OR WEBP_BUILD_IMG2WEBP)
# Example utility library.
set(exampleutil_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h
${CMAKE_CURRENT_SOURCE_DIR}/examples/example_util.c
${CMAKE_CURRENT_SOURCE_DIR}/examples/example_util.h)
add_library(exampleutil ${exampleutil_SRCS})
target_link_libraries(exampleutil webp ${WEBP_DEP_LIBRARIES})
set(imageioutil_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/imageio/imageio_util.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/imageio_util.h)
add_library(imageioutil ${imageioutil_SRCS})
target_link_libraries(imageioutil ${WEBP_DEP_LIBRARIES})
# Image-decoding utility library.
set(imagedec_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/examples/gifdec.c
${CMAKE_CURRENT_SOURCE_DIR}/examples/gifdec.h
${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_dec.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_dec.h
${CMAKE_CURRENT_SOURCE_DIR}/imageio/jpegdec.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/jpegdec.h
${CMAKE_CURRENT_SOURCE_DIR}/imageio/metadata.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/metadata.h
${CMAKE_CURRENT_SOURCE_DIR}/imageio/pngdec.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/pngdec.h
${CMAKE_CURRENT_SOURCE_DIR}/imageio/tiffdec.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/tiffdec.h
${CMAKE_CURRENT_SOURCE_DIR}/imageio/webpdec.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/webpdec.h
${CMAKE_CURRENT_SOURCE_DIR}/imageio/wicdec.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/wicdec.h)
add_library(imagedec ${imagedec_SRCS})
target_link_libraries(imagedec webp ${WEBP_DEP_LIBRARIES}
${WEBP_DEP_IMG_LIBRARIES})
# Image-encoding utility library.
set(imageenc_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_enc.c
${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_enc.h)
add_library(imageenc ${imageenc_SRCS})
target_link_libraries(imageenc webp imageioutil
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES})
endif()
if(WEBP_BUILD_DWEBP)
# dwebp
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
add_executable(dwebp
${CMAKE_CURRENT_SOURCE_DIR}/examples/dwebp.c
${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h)
target_link_libraries(dwebp imagedec imageenc webp
exampleutil imageioutil
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}
)
endif()
if(WEBP_BUILD_CWEBP)
# cwebp
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
add_executable(cwebp
${CMAKE_CURRENT_SOURCE_DIR}/examples/cwebp.c
${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h)
target_link_libraries(cwebp imagedec webp exampleutil imageioutil
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}
)
endif()
if(WEBP_BUILD_GIF2WEBP)
# gif2webp
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
set(GIF2WEBP_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/examples/gif2webp.c)
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "GIF2WEBP_SRCS")
add_executable(gif2webp ${GIF2WEBP_SRCS})
target_link_libraries(gif2webp imagedec webp exampleutil imageioutil
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}
)
endif()
if(WEBP_BUILD_IMG2WEBP)
# img2webp
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
set(IMG2WEBP_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/examples/img2webp.c)
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "IMG2WEBP_SRCS")
add_executable(img2webp ${IMG2WEBP_SRCS})
target_link_libraries(img2webp imagedec webp exampleutil imageioutil
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}
)
endif()

4850
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,3 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src imageio man SUBDIRS = src examples man
EXTRA_DIST = COPYING autogen.sh EXTRA_DIST = COPYING autogen.sh
if WANT_EXTRAS
SUBDIRS += extras
endif
SUBDIRS += examples

View File

@ -11,8 +11,6 @@ LIBWEBPDEMUX_BASENAME = libwebpdemux
ARCH = x86 ARCH = x86
!ELSE IF ! [ cl 2>&1 | find "x64" > NUL ] !ELSE IF ! [ cl 2>&1 | find "x64" > NUL ]
ARCH = x64 ARCH = x64
!ELSE IF ! [ cl 2>&1 | find "ARM" > NUL ]
ARCH = ARM
!ELSE !ELSE
!ERROR Unable to auto-detect toolchain architecture! \ !ERROR Unable to auto-detect toolchain architecture! \
If cl.exe is in your PATH rerun nmake with ARCH=<arch>. If cl.exe is in your PATH rerun nmake with ARCH=<arch>.
@ -29,22 +27,15 @@ PLATFORM_LDFLAGS = /SAFESEH
NOLOGO = /nologo NOLOGO = /nologo
CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG
CCDEBUG = cl.exe $(NOLOGO) /Od /Gm /Zi /D_DEBUG /RTC1 CCDEBUG = cl.exe $(NOLOGO) /Od /Gm /Zi /D_DEBUG /RTC1
CFLAGS = /Isrc $(NOLOGO) /W3 /EHsc /c CFLAGS = /Isrc $(NOLOGO) /W3 /EHsc /c /GS
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS) LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
LNKDLL = link.exe /DLL $(NOLOGO) LNKDLL = link.exe /DLL $(NOLOGO)
LNKEXE = link.exe $(NOLOGO) LNKEXE = link.exe $(NOLOGO)
LNKLIB = lib.exe $(NOLOGO) LNKLIB = lib.exe $(NOLOGO)
MT = mt.exe $(NOLOGO) MT = mt.exe $(NOLOGO)
RCNODBG = rc.exe $(NOLOGO) /l"0x0409" # 0x409 = U.S. English
RCDEBUG = $(RCNODBG) /D_DEBUG
!IF "$(ARCH)" == "ARM"
CFLAGS = $(CFLAGS) /DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP /DWEBP_USE_THREAD
!ELSE
CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
!ENDIF
CFGSET = FALSE CFGSET = FALSE
!IF "$(OBJDIR)" == "" !IF "$(OBJDIR)" == ""
@ -53,21 +44,11 @@ OUTDIR = ..\obj\
OUTDIR = $(OBJDIR) OUTDIR = $(OBJDIR)
!ENDIF !ENDIF
!IF "$(HAVE_AVX2)" == "1"
CFLAGS = $(CFLAGS) /DWEBP_HAVE_AVX2
AVX2_FLAGS = /arch:AVX2
!ENDIF
############################################################## ##############################################################
# Runtime library configuration # Runtime library configuration
!IF "$(RTLIBCFG)" == "static" !IF "$(RTLIBCFG)" == "static"
RTLIB = /MT RTLIB = /MT
RTLIBD = /MTd RTLIBD = /MTd
!ELSE IF "$(RTLIBCFG)" == "legacy"
RTLIBCFG = static
RTLIB = /MT
RTLIBD = /MTd
CFLAGS = $(CFLAGS) /GS- /arch:IA32
!ELSE !ELSE
RTLIB = /MD RTLIB = /MD
RTLIBD = /MDd RTLIBD = /MDd
@ -84,8 +65,6 @@ OUTPUT_DIRS = $(DIRBIN) $(DIRINC) $(DIRLIB) \
$(DIROBJ)\dsp \ $(DIROBJ)\dsp \
$(DIROBJ)\enc \ $(DIROBJ)\enc \
$(DIROBJ)\examples \ $(DIROBJ)\examples \
$(DIROBJ)\extras \
$(DIROBJ)\imageio \
$(DIROBJ)\mux \ $(DIROBJ)\mux \
$(DIROBJ)\utils \ $(DIROBJ)\utils \
@ -103,11 +82,9 @@ LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
!ELSE IF "$(CFG)" == "release-dynamic" !ELSE IF "$(CFG)" == "release-dynamic"
CC = $(CCNODBG) CC = $(CCNODBG)
RC = $(RCNODBG)
DLLBUILD = TRUE DLLBUILD = TRUE
!ELSE IF "$(CFG)" == "debug-dynamic" !ELSE IF "$(CFG)" == "debug-dynamic"
CC = $(CCDEBUG) CC = $(CCDEBUG)
RC = $(RCDEBUG)
RTLIB = $(RTLIBD) RTLIB = $(RTLIBD)
DLLBUILD = TRUE DLLBUILD = TRUE
LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
@ -124,7 +101,9 @@ LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME).lib
LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib
LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib
!ELSE IF "$(DLLBUILD)" == "TRUE" !ELSE IF "$(DLLBUILD)" == "TRUE"
DLLC = webp_dll.c
DLLINC = webp_dll.h DLLINC = webp_dll.h
DLL_OBJS = $(DIROBJ)\$(DLLC:.c=.obj)
CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL
LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME)_dll.lib LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME)_dll.lib
LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib
@ -153,11 +132,8 @@ CFGSET = TRUE
!MESSAGE . features enabled. !MESSAGE . features enabled.
!MESSAGE - (empty) - build libwebp-based targets for CFG !MESSAGE - (empty) - build libwebp-based targets for CFG
!MESSAGE - all - build (de)mux-based targets for CFG !MESSAGE - all - build (de)mux-based targets for CFG
!MESSAGE - gif2webp - requires libgif & >= VS2013
!MESSAGE - anim_diff - requires libgif & >= VS2013
!MESSAGE !MESSAGE
!MESSAGE RTLIBCFG controls the runtime library linkage - 'static' or 'dynamic'. !MESSAGE RTLIBCFG controls the runtime library linkage - 'static' or 'dynamic'.
!MESSAGE 'legacy' will produce a Windows 2000 compatible library.
!MESSAGE OBJDIR is the path where you like to build (obj, bins, etc.), !MESSAGE OBJDIR is the path where you like to build (obj, bins, etc.),
!MESSAGE defaults to ..\obj !MESSAGE defaults to ..\obj
@ -175,160 +151,85 @@ CFGSET = TRUE
# #
DEC_OBJS = \ DEC_OBJS = \
$(DIROBJ)\dec\alpha_dec.obj \ $(DIROBJ)\dec\alpha.obj \
$(DIROBJ)\dec\buffer_dec.obj \ $(DIROBJ)\dec\buffer.obj \
$(DIROBJ)\dec\frame_dec.obj \ $(DIROBJ)\dec\frame.obj \
$(DIROBJ)\dec\idec_dec.obj \ $(DIROBJ)\dec\idec.obj \
$(DIROBJ)\dec\io_dec.obj \ $(DIROBJ)\dec\io.obj \
$(DIROBJ)\dec\quant_dec.obj \ $(DIROBJ)\dec\layer.obj \
$(DIROBJ)\dec\tree_dec.obj \ $(DIROBJ)\dec\quant.obj \
$(DIROBJ)\dec\vp8_dec.obj \ $(DIROBJ)\dec\tree.obj \
$(DIROBJ)\dec\vp8l_dec.obj \ $(DIROBJ)\dec\vp8.obj \
$(DIROBJ)\dec\webp_dec.obj \ $(DIROBJ)\dec\vp8l.obj \
$(DIROBJ)\dec\webp.obj \
DEMUX_OBJS = \ DEMUX_OBJS = \
$(DIROBJ)\demux\anim_decode.obj \
$(DIROBJ)\demux\demux.obj \ $(DIROBJ)\demux\demux.obj \
DSP_DEC_OBJS = \ DSP_DEC_OBJS = \
$(DIROBJ)\dsp\alpha_processing.obj \
$(DIROBJ)\dsp\alpha_processing_mips_dsp_r2.obj \
$(DIROBJ)\dsp\alpha_processing_neon.obj \
$(DIROBJ)\dsp\alpha_processing_sse2.obj \
$(DIROBJ)\dsp\alpha_processing_sse41.obj \
$(DIROBJ)\dsp\cpu.obj \ $(DIROBJ)\dsp\cpu.obj \
$(DIROBJ)\dsp\dec.obj \ $(DIROBJ)\dsp\dec.obj \
$(DIROBJ)\dsp\dec_clip_tables.obj \
$(DIROBJ)\dsp\dec_mips32.obj \
$(DIROBJ)\dsp\dec_mips_dsp_r2.obj \
$(DIROBJ)\dsp\dec_msa.obj \
$(DIROBJ)\dsp\dec_neon.obj \ $(DIROBJ)\dsp\dec_neon.obj \
$(DIROBJ)\dsp\dec_sse2.obj \ $(DIROBJ)\dsp\dec_sse2.obj \
$(DIROBJ)\dsp\dec_sse41.obj \
$(DIROBJ)\dsp\filters.obj \
$(DIROBJ)\dsp\filters_mips_dsp_r2.obj \
$(DIROBJ)\dsp\filters_msa.obj \
$(DIROBJ)\dsp\filters_neon.obj \
$(DIROBJ)\dsp\filters_sse2.obj \
$(DIROBJ)\dsp\lossless.obj \ $(DIROBJ)\dsp\lossless.obj \
$(DIROBJ)\dsp\lossless_mips_dsp_r2.obj \
$(DIROBJ)\dsp\lossless_msa.obj \
$(DIROBJ)\dsp\lossless_neon.obj \
$(DIROBJ)\dsp\lossless_sse2.obj \
$(DIROBJ)\dsp\rescaler.obj \
$(DIROBJ)\dsp\rescaler_mips32.obj \
$(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
$(DIROBJ)\dsp\rescaler_msa.obj \
$(DIROBJ)\dsp\rescaler_neon.obj \
$(DIROBJ)\dsp\rescaler_sse2.obj \
$(DIROBJ)\dsp\upsampling.obj \ $(DIROBJ)\dsp\upsampling.obj \
$(DIROBJ)\dsp\upsampling_mips_dsp_r2.obj \
$(DIROBJ)\dsp\upsampling_msa.obj \
$(DIROBJ)\dsp\upsampling_neon.obj \ $(DIROBJ)\dsp\upsampling_neon.obj \
$(DIROBJ)\dsp\upsampling_sse2.obj \ $(DIROBJ)\dsp\upsampling_sse2.obj \
$(DIROBJ)\dsp\yuv.obj \ $(DIROBJ)\dsp\yuv.obj \
$(DIROBJ)\dsp\yuv_mips32.obj \
$(DIROBJ)\dsp\yuv_mips_dsp_r2.obj \
$(DIROBJ)\dsp\yuv_sse2.obj \
DSP_ENC_OBJS = \ DSP_ENC_OBJS = \
$(DIROBJ)\dsp\argb.obj \
$(DIROBJ)\dsp\argb_mips_dsp_r2.obj \
$(DIROBJ)\dsp\argb_sse2.obj \
$(DIROBJ)\dsp\cost.obj \
$(DIROBJ)\dsp\cost_mips32.obj \
$(DIROBJ)\dsp\cost_mips_dsp_r2.obj \
$(DIROBJ)\dsp\cost_sse2.obj \
$(DIROBJ)\dsp\enc.obj \ $(DIROBJ)\dsp\enc.obj \
$(DIROBJ)\dsp\enc_avx2.obj \
$(DIROBJ)\dsp\enc_mips32.obj \
$(DIROBJ)\dsp\enc_mips_dsp_r2.obj \
$(DIROBJ)\dsp\enc_msa.obj \
$(DIROBJ)\dsp\enc_neon.obj \ $(DIROBJ)\dsp\enc_neon.obj \
$(DIROBJ)\dsp\enc_sse2.obj \ $(DIROBJ)\dsp\enc_sse2.obj \
$(DIROBJ)\dsp\enc_sse41.obj \
$(DIROBJ)\dsp\lossless_enc.obj \
$(DIROBJ)\dsp\lossless_enc_mips32.obj \
$(DIROBJ)\dsp\lossless_enc_mips_dsp_r2.obj \
$(DIROBJ)\dsp\lossless_enc_msa.obj \
$(DIROBJ)\dsp\lossless_enc_neon.obj \
$(DIROBJ)\dsp\lossless_enc_sse2.obj \
$(DIROBJ)\dsp\lossless_enc_sse41.obj \
EX_ANIM_UTIL_OBJS = \ EX_FORMAT_DEC_OBJS = \
$(DIROBJ)\examples\anim_util.obj \ $(DIROBJ)\examples\jpegdec.obj \
$(DIROBJ)\examples\metadata.obj \
IMAGEIO_DEC_OBJS = \ $(DIROBJ)\examples\pngdec.obj \
$(DIROBJ)\imageio\image_dec.obj \ $(DIROBJ)\examples\tiffdec.obj \
$(DIROBJ)\imageio\jpegdec.obj \ $(DIROBJ)\examples\wicdec.obj \
$(DIROBJ)\imageio\metadata.obj \
$(DIROBJ)\imageio\pngdec.obj \
$(DIROBJ)\imageio\tiffdec.obj \
$(DIROBJ)\imageio\webpdec.obj \
$(DIROBJ)\imageio\wicdec.obj \
IMAGEIO_ENC_OBJS = \
$(DIROBJ)\imageio\image_enc.obj \
EX_GIF_DEC_OBJS = \
$(DIROBJ)\examples\gifdec.obj \
EX_UTIL_OBJS = \ EX_UTIL_OBJS = \
$(DIROBJ)\examples\example_util.obj \ $(DIROBJ)\examples\example_util.obj \
ENC_OBJS = \ ENC_OBJS = \
$(DIROBJ)\enc\alpha_enc.obj \ $(DIROBJ)\enc\alpha.obj \
$(DIROBJ)\enc\analysis_enc.obj \ $(DIROBJ)\enc\analysis.obj \
$(DIROBJ)\enc\backward_references_enc.obj \ $(DIROBJ)\enc\backward_references.obj \
$(DIROBJ)\enc\config_enc.obj \ $(DIROBJ)\enc\config.obj \
$(DIROBJ)\enc\cost_enc.obj \ $(DIROBJ)\enc\cost.obj \
$(DIROBJ)\enc\delta_palettization_enc.obj \ $(DIROBJ)\enc\filter.obj \
$(DIROBJ)\enc\filter_enc.obj \ $(DIROBJ)\enc\frame.obj \
$(DIROBJ)\enc\frame_enc.obj \ $(DIROBJ)\enc\histogram.obj \
$(DIROBJ)\enc\histogram_enc.obj \ $(DIROBJ)\enc\iterator.obj \
$(DIROBJ)\enc\iterator_enc.obj \ $(DIROBJ)\enc\layer.obj \
$(DIROBJ)\enc\near_lossless_enc.obj \ $(DIROBJ)\enc\picture.obj \
$(DIROBJ)\enc\picture_enc.obj \ $(DIROBJ)\enc\quant.obj \
$(DIROBJ)\enc\picture_csp_enc.obj \ $(DIROBJ)\enc\syntax.obj \
$(DIROBJ)\enc\picture_psnr_enc.obj \ $(DIROBJ)\enc\token.obj \
$(DIROBJ)\enc\picture_rescale_enc.obj \ $(DIROBJ)\enc\tree.obj \
$(DIROBJ)\enc\picture_tools_enc.obj \ $(DIROBJ)\enc\vp8l.obj \
$(DIROBJ)\enc\predictor_enc.obj \ $(DIROBJ)\enc\webpenc.obj \
$(DIROBJ)\enc\quant_enc.obj \
$(DIROBJ)\enc\syntax_enc.obj \
$(DIROBJ)\enc\token_enc.obj \
$(DIROBJ)\enc\tree_enc.obj \
$(DIROBJ)\enc\vp8l_enc.obj \
$(DIROBJ)\enc\webp_enc.obj \
EXTRAS_OBJS = \
$(DIROBJ)\extras\extras.obj \
$(DIROBJ)\extras\quality_estimate.obj \
IMAGEIO_UTIL_OBJS = \
$(DIROBJ)\imageio\imageio_util.obj \
MUX_OBJS = \ MUX_OBJS = \
$(DIROBJ)\mux\anim_encode.obj \
$(DIROBJ)\mux\muxedit.obj \ $(DIROBJ)\mux\muxedit.obj \
$(DIROBJ)\mux\muxinternal.obj \ $(DIROBJ)\mux\muxinternal.obj \
$(DIROBJ)\mux\muxread.obj \ $(DIROBJ)\mux\muxread.obj \
UTILS_DEC_OBJS = \ UTILS_DEC_OBJS = \
$(DIROBJ)\utils\bit_reader_utils.obj \ $(DIROBJ)\utils\bit_reader.obj \
$(DIROBJ)\utils\color_cache_utils.obj \ $(DIROBJ)\utils\color_cache.obj \
$(DIROBJ)\utils\filters_utils.obj \ $(DIROBJ)\utils\filters.obj \
$(DIROBJ)\utils\huffman_utils.obj \ $(DIROBJ)\utils\huffman.obj \
$(DIROBJ)\utils\quant_levels_dec_utils.obj \ $(DIROBJ)\utils\quant_levels_dec.obj \
$(DIROBJ)\utils\rescaler_utils.obj \ $(DIROBJ)\utils\rescaler.obj \
$(DIROBJ)\utils\random_utils.obj \ $(DIROBJ)\utils\thread.obj \
$(DIROBJ)\utils\thread_utils.obj \
$(DIROBJ)\utils\utils.obj \ $(DIROBJ)\utils\utils.obj \
UTILS_ENC_OBJS = \ UTILS_ENC_OBJS = \
$(DIROBJ)\utils\bit_writer_utils.obj \ $(DIROBJ)\utils\bit_writer.obj \
$(DIROBJ)\utils\huffman_encode_utils.obj \ $(DIROBJ)\utils\huffman_encode.obj \
$(DIROBJ)\utils\quant_levels_utils.obj \ $(DIROBJ)\utils\quant_levels.obj \
LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS) LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \ LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
@ -337,51 +238,19 @@ LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS) LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS)
OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP) OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP)
!IF "$(ARCH)" == "ARM"
ex: $(OUT_LIBS)
all: ex
!ELSE
OUT_EXAMPLES = $(DIRBIN)\cwebp.exe $(DIRBIN)\dwebp.exe OUT_EXAMPLES = $(DIRBIN)\cwebp.exe $(DIRBIN)\dwebp.exe
EXTRA_EXAMPLES = $(DIRBIN)\vwebp.exe $(DIRBIN)\webpmux.exe \ EXTRA_EXAMPLES = $(DIRBIN)\vwebp.exe $(DIRBIN)\webpmux.exe
$(DIRBIN)\img2webp.exe $(DIRBIN)\get_disto.exe \
$(DIRBIN)\webp_quality.exe
ex: $(OUT_LIBS) $(OUT_EXAMPLES) ex: $(OUT_LIBS) $(OUT_EXAMPLES)
all: ex $(EXTRA_EXAMPLES) all: ex $(EXTRA_EXAMPLES)
# NB: gif2webp.exe and anim_diff.exe are excluded from 'all' as libgif requires $(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(EX_FORMAT_DEC_OBJS)
# C99 support which is only available from VS2013 onward. $(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj
gif2webp: $(DIRBIN)\gif2webp.exe $(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj
anim_diff: $(DIRBIN)\anim_diff.exe $(DIRBIN)\vwebp.exe: $(EX_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
$(DIRBIN)\anim_diff.exe: $(DIROBJ)\examples\anim_diff.obj $(EX_ANIM_UTIL_OBJS)
$(DIRBIN)\anim_diff.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
$(DIRBIN)\anim_diff.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
$(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(IMAGEIO_DEC_OBJS)
$(DIRBIN)\cwebp.exe: $(IMAGEIO_UTIL_OBJS)
$(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(IMAGEIO_DEC_OBJS)
$(DIRBIN)\dwebp.exe: $(IMAGEIO_ENC_OBJS)
$(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS)
$(DIRBIN)\gif2webp.exe: $(DIROBJ)\examples\gif2webp.obj $(EX_GIF_DEC_OBJS)
$(DIRBIN)\gif2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBPMUX)
$(DIRBIN)\gif2webp.exe: $(LIBWEBP)
$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj $(EX_UTIL_OBJS)
$(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
$(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX) $(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX)
$(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP) $(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(LIBWEBP)
$(DIRBIN)\img2webp.exe: $(DIROBJ)\examples\img2webp.obj $(LIBWEBPMUX)
$(DIRBIN)\img2webp.exe: $(IMAGEIO_DEC_OBJS)
$(DIRBIN)\img2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
$(DIRBIN)\get_disto.exe: $(DIROBJ)\extras\get_disto.obj
$(DIRBIN)\get_disto.exe: $(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
$(DIRBIN)\webp_quality.exe: $(DIROBJ)\extras\webp_quality.obj
$(DIRBIN)\webp_quality.exe: $(IMAGEIO_UTIL_OBJS)
$(DIRBIN)\webp_quality.exe: $(EXTRAS_OBJS) $(LIBWEBP)
$(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP) $(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP)
$(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS): $(OUTPUT_DIRS) $(EX_UTIL_OBJS) $(EX_FORMAT_DEC_OBJS): $(OUTPUT_DIRS)
$(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS) $(EXTRAS_OBJS): $(OUTPUT_DIRS)
!ENDIF # ARCH == ARM
experimental: experimental:
$(MAKE) /f Makefile.vc \ $(MAKE) /f Makefile.vc \
@ -397,29 +266,20 @@ $(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): $(OUTPUT_DIRS)
!IF "$(DLLBUILD)" == "TRUE" !IF "$(DLLBUILD)" == "TRUE"
$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \ $(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \
$(DIROBJ)\$(DLLINC) $(DIROBJ)\$(DLLINC) $(DIROBJ)\$(DLLC)
{$(DIROBJ)}.c{$(DIROBJ)}.obj: {$(DIROBJ)}.c{$(DIROBJ)}.obj:
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@ $< $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@ $<
{src}.rc{$(DIROBJ)}.res: $(LIBWEBPMUX): $(LIBWEBP)
$(RC) /fo$@ $< $(LIBWEBPDEMUX): $(LIBWEBP)
{src\demux}.rc{$(DIROBJ)\demux}.res:
$(RC) /fo$@ $<
{src\mux}.rc{$(DIROBJ)\mux}.res:
$(RC) /fo$@ $<
$(LIBWEBP): $(DIROBJ)\$(LIBWEBP_BASENAME:_debug=).res
$(LIBWEBPDECODER): $(DIROBJ)\$(LIBWEBPDECODER_BASENAME:_debug=).res
$(LIBWEBPMUX): $(LIBWEBP) $(DIROBJ)\mux\$(LIBWEBPMUX_BASENAME:_debug=).res
$(LIBWEBPDEMUX): $(LIBWEBP) $(DIROBJ)\demux\$(LIBWEBPDEMUX_BASENAME:_debug=).res
$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX): $(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
$(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $** $(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $**
-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y -xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
clean:: clean::
@-erase /s $(DIROBJ)\$(DLLINC) 2> NUL @-erase /s $(DIROBJ)\$(DLLC) $(DIROBJ)\$(DLLINC) 2> NUL
!ELSE !ELSE
$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX): $(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
$(LNKLIB) /out:$@ $** $(LNKLIB) /out:$@ $**
@ -436,31 +296,19 @@ $(DIROBJ)\$(DLLINC):
@echo #define WEBP_EXTERN(type) __declspec(dllexport) type >> $@ @echo #define WEBP_EXTERN(type) __declspec(dllexport) type >> $@
@echo #endif /* WEBP_DLL_H_ */ >> $@ @echo #endif /* WEBP_DLL_H_ */ >> $@
# expose a WebPFree() function for use in managed code
$(DIROBJ)\$(DLLC): $(DIROBJ)\$(DLLINC)
@echo #include ^<stdlib.h^> > $@
@echo #include "webp_dll.h" >> $@
@echo // This function should be used in place of free() for memory >> $@
@echo // returned by the WebP API. >> $@
@echo WEBP_EXTERN(void) WebPFree(void* ptr) { >> $@
@echo free(ptr); >> $@
@echo } >> $@
.SUFFIXES: .c .obj .res .exe .SUFFIXES: .c .obj .res .exe
# File-specific flag builds. Note batch rules take precedence over wildcards,
# so for now name each file individually.
$(DIROBJ)\dsp\enc_avx2.obj: src\dsp\enc_avx2.c
$(CC) $(CFLAGS) $(AVX2_FLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ \
src\dsp\$(@B).c
$(DIROBJ)\examples\anim_diff.obj: examples\anim_diff.c
$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
/Fo$(DIROBJ)\examples\ examples\$(@B).c
$(DIROBJ)\examples\anim_util.obj: examples\anim_util.c
$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
/Fo$(DIROBJ)\examples\ examples\$(@B).c
$(DIROBJ)\examples\gif2webp.obj: examples\gif2webp.c
$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
/Fo$(DIROBJ)\examples\ examples\$(@B).c
$(DIROBJ)\examples\gifdec.obj: examples\gifdec.c
$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
/Fo$(DIROBJ)\examples\ examples\$(@B).c
# Batch rules
{examples}.c{$(DIROBJ)\examples}.obj:: {examples}.c{$(DIROBJ)\examples}.obj::
$(CC) $(CFLAGS) /Fd$(DIROBJ)\examples\ /Fo$(DIROBJ)\examples\ $< $(CC) $(CFLAGS) /Fd$(DIROBJ)\examples\ /Fo$(DIROBJ)\examples\ $<
{extras}.c{$(DIROBJ)\extras}.obj::
$(CC) $(CFLAGS) /Fd$(DIROBJ)\extras\ /Fo$(DIROBJ)\extras\ $<
{imageio}.c{$(DIROBJ)\imageio}.obj::
$(CC) $(CFLAGS) /Fd$(DIROBJ)\imageio\ /Fo$(DIROBJ)\imageio\ $<
{src\dec}.c{$(DIROBJ)\dec}.obj:: {src\dec}.c{$(DIROBJ)\dec}.obj::
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $< $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $<
{src\demux}.c{$(DIROBJ)\demux}.obj:: {src\demux}.c{$(DIROBJ)\demux}.obj::
@ -480,12 +328,6 @@ $(DIROBJ)\examples\gifdec.obj: examples\gifdec.c
$(MT) -manifest $@.manifest -outputresource:$@;1 $(MT) -manifest $@.manifest -outputresource:$@;1
del $@.manifest del $@.manifest
{$(DIROBJ)\extras}.obj{$(DIRBIN)}.exe:
$(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
ole32.lib windowscodecs.lib shlwapi.lib
$(MT) -manifest $@.manifest -outputresource:$@;1
del $@.manifest
clean:: clean::
@-erase /s $(DIROBJ)\*.dll 2> NUL @-erase /s $(DIROBJ)\*.dll 2> NUL
@-erase /s $(DIROBJ)\*.exp 2> NUL @-erase /s $(DIROBJ)\*.exp 2> NUL

116
NEWS
View File

@ -1,116 +1,3 @@
- 1/26/2017: version 0.6.0
* lossless performance and compression improvements
* miscellaneous performance improvements (SSE2, NEON, MSA)
* webpmux gained a -duration option allowing for frame timing modification
* new img2webp utility allowing a sequence of images to be converted to
animated webp
* API changes:
- libwebp:
WebPPictureSharpARGBToYUVA
WebPPlaneDistortion
- libwebpmux / gif2webp:
WebPAnimEncoderOptions: kmax <= 0 now disables keyframes, kmax == 1
forces all keyframes. See mux.h and the gif2webp
manpage for details.
- 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)
* reverted alpha plane encoding with color cache for compatibility with
libwebp 0.4.0->0.4.3 (issues #291, #298)
* lossless encoding performance improvements
* memory reduction in both lossless encoding and decoding
* force mux output to be in the extended format (VP8X) when undefined chunks
are present (issue #294)
* gradle, cmake build support
* workaround for compiler bug causing 64-bit decode failures on android
devices using clang-3.8 in the r11c NDK
* various WebPAnimEncoder improvements
- 12/17/2015: version 0.5.0
* miscellaneous bug & build fixes (issues #234, #258, #274, #275, #278)
* encoder & decoder speed-ups on x86/ARM/MIPS for lossy & lossless
- note! YUV->RGB conversion was sped-up, but the results will be slightly
different from previous releases
* various lossless encoder improvements
* gif2webp improvements, -min_size option added
* tools fully support input from stdin and output to stdout (issue #168)
* New WebPAnimEncoder API for creating animations
* New WebPAnimDecoder API for decoding animations
* other API changes:
- libwebp:
WebPPictureSmartARGBToYUVA() (-pre 4 in cwebp)
WebPConfig::exact (-exact in cwebp; -alpha_cleanup is now the default)
WebPConfig::near_lossless (-near_lossless in cwebp)
WebPFree() (free'ing webp allocated memory in other languages)
WebPConfigLosslessPreset()
WebPMemoryWriterClear()
- libwebpdemux: removed experimental fragment related fields and functions
- libwebpmux: WebPMuxSetCanvasSize()
* new libwebpextras library with some uncommon import functions:
WebPImportGray/WebPImportRGB565/WebPImportRGB4444
- 10/15/15: version 0.4.4
This is a binary compatible release.
* rescaling out-of-bounds read fix (issue #254)
* various build fixes and improvements (issues #253, #259, #262, #267, #268)
* container documentation update
* gif2webp transparency fix (issue #245)
- 3/3/15: version 0.4.3
This is a binary compatible release.
* Android / gcc / iOS / MSVS build fixes and improvements
* lossless decode fix (issue #239 -- since 0.4.0)
* documentation / vwebp updates for animation
* multi-threading fix (issue #234)
- 10/13/14: version 0.4.2
This is a binary compatible release.
* Android / gcc build fixes
* (Windows) fix reading from stdin and writing to stdout
* gif2webp: miscellaneous fixes
* fix 'alpha-leak' with lossy compression (issue #220)
* the lossless bitstream spec has been amended to reflect the current code
- 7/24/14: version 0.4.1
This is a binary compatible release.
* AArch64 (arm64) & MIPS support/optimizations
* NEON assembly additions:
- ~25% faster lossy decode / encode (-m 4)
- ~10% faster lossless decode
- ~5-10% faster lossless encode (-m 3/4)
* dwebp/vwebp can read from stdin
* cwebp/gif2webp can write to stdout
* cwebp can read webp files; useful if storing sources as webp lossless
- 12/19/13: version 0.4.0
* improved gif2webp tool
* numerous fixes, compression improvement and speed-up
* dither option added to decoder (dwebp -dither 50 ...)
* improved multi-threaded modes (-mt option)
* improved filtering strength determination
* New function: WebPMuxGetCanvasSize
* BMP and TIFF format output added to 'dwebp'
* Significant memory reduction for decoding lossy images with alpha.
* Intertwined decoding of RGB and alpha for a shorter
time-to-first-decoded-pixel.
* WebPIterator has a new member 'has_alpha' denoting whether the frame
contains transparency.
* Container spec amended with new 'blending method' for animation.
- 6/13/13: version 0.3.1 - 6/13/13: version 0.3.1
This is a binary compatible release. This is a binary compatible release.
* Add incremental decoding support for images containing ALPH and ICCP chunks. * Add incremental decoding support for images containing ALPH and ICCP chunks.
@ -161,8 +48,7 @@
- 9/19/11: version 0.1.3 - 9/19/11: version 0.1.3
* Advanced decoding APIs. * Advanced decoding APIs.
* On-the-fly cropping and rescaling of images. * On-the-fly cropping and rescaling of images.
* SSE2 instructions for decoding performance optimizations on x86 based * SSE2 instructions for decoding performance optimizations on x86 based platforms.
platforms.
* Support Multi-threaded decoding. * Support Multi-threaded decoding.
* 40% improvement in Decoding performance. * 40% improvement in Decoding performance.
* Add support for RGB565, RGBA4444 & ARGB image colorspace. * Add support for RGB565, RGBA4444 & ARGB image colorspace.

39
PATENTS
View File

@ -1,23 +1,22 @@
Additional IP Rights Grant (Patents) Additional IP Rights Grant (Patents)
------------------------------------
"These implementations" means the copyrightable works that implement the WebM "This implementation" means the copyrightable works distributed by
codecs distributed by Google as part of the WebM Project. Google as part of the WebM Project.
Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, Google hereby grants to you a perpetual, worldwide, non-exclusive,
royalty-free, irrevocable (except as stated in this section) patent license to no-charge, royalty-free, irrevocable (except as stated in this section)
make, have made, use, offer to sell, sell, import, transfer, and otherwise patent license to make, have made, use, offer to sell, sell, import,
run, modify and propagate the contents of these implementations of WebM, where transfer, and otherwise run, modify and propagate the contents of this
such license applies only to those patent claims, both currently owned by implementation of VP8, where such license applies only to those patent
Google and acquired in the future, licensable by Google that are necessarily claims, both currently owned by Google and acquired in the future,
infringed by these implementations of WebM. This grant does not include claims licensable by Google that are necessarily infringed by this
that would be infringed only as a consequence of further modification of these implementation of VP8. This grant does not include claims that would be
implementations. If you or your agent or exclusive licensee institute or order infringed only as a consequence of further modification of this
or agree to the institution of patent litigation or any other patent implementation. If you or your agent or exclusive licensee institute or
enforcement activity against any entity (including a cross-claim or order or agree to the institution of patent litigation against any
counterclaim in a lawsuit) alleging that any of these implementations of WebM entity (including a cross-claim or counterclaim in a lawsuit) alleging
or any code incorporated within any of these implementations of WebM that this implementation of VP8 or any code incorporated within this
constitute direct or contributory patent infringement, or inducement of implementation of VP8 constitutes direct or contributory patent
patent infringement, then any patent rights granted to you under this License infringement, or inducement of patent infringement, then any patent
for these implementations of WebM shall terminate as of the date such rights granted to you under this License for this implementation of VP8
litigation is filed. shall terminate as of the date such litigation is filed.

328
README
View File

@ -4,7 +4,7 @@
\__\__/\____/\_____/__/ ____ ___ \__\__/\____/\_____/__/ ____ ___
/ _/ / \ \ / _ \/ _/ / _/ / \ \ / _ \/ _/
/ \_/ / / \ \ __/ \__ / \_/ / / \ \ __/ \__
\____/____/\_____/_____/____/v0.6.0 \____/____/\_____/_____/____/v0.3.1
Description: Description:
============ ============
@ -15,12 +15,11 @@ as well as the command line tools 'cwebp' and 'dwebp'.
See http://developers.google.com/speed/webp See http://developers.google.com/speed/webp
The latest source tree is available at Latest sources are available from http://www.webmproject.org/code/
https://chromium.googlesource.com/webm/libwebp
It is released under the same license as the WebM project. It is released under the same license as the WebM project.
See http://www.webmproject.org/license/software/ or the See http://www.webmproject.org/license/software/ or the
"COPYING" file for details. An additional intellectual file "COPYING" file for details. An additional intellectual
property rights grant can be found in the file PATENTS. property rights grant can be found in the file PATENTS.
Building: Building:
@ -54,12 +53,6 @@ Please refer to makefile.unix for additional details and customizations.
Using autoconf tools: Using autoconf tools:
--------------------- ---------------------
Prerequisites:
A compiler (e.g., gcc), make, autoconf, automake, libtool.
On a Debian-like system the following should install everything you need for a
minimal build:
$ sudo apt-get install gcc make autoconf automake libtool
When building from git sources, you will need to run autogen.sh to generate the When building from git sources, you will need to run autogen.sh to generate the
configure script. configure script.
@ -84,77 +77,10 @@ be installed independently using a minor modification in the corresponding
Makefile.am configure files (see comments there). See './configure --help' for Makefile.am configure files (see comments there). See './configure --help' for
more options. more options.
Building for MIPS Linux:
------------------------
MIPS Linux toolchain stable available releases can be found at:
https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/
# Add toolchain to PATH
export PATH=$PATH:/path/to/toolchain/bin
# 32-bit build for mips32r5 (p5600)
HOST=mips-mti-linux-gnu
MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \
-msched-weight -mload-store-pairs -fPIE"
MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie"
# 64-bit build for mips64r6 (i6400)
HOST=mips-img-linux-gnu
MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \
-msched-weight -mload-store-pairs -fPIE"
MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie"
./configure --host=${HOST} --build=`config.guess` \
CC="${HOST}-gcc -EL" \
CFLAGS="$MIPS_CFLAGS" \
LDFLAGS="$MIPS_LDFLAGS"
make
make install
CMake:
------
The support for CMake is minimal: it only helps you compile libwebp, cwebp and
dwebp.
Prerequisites:
A compiler (e.g., gcc with autotools) and CMake.
On a Debian-like system the following should install everything you need for a
minimal build:
$ sudo apt-get install build-essential cmake
When building from git sources, you will need to run cmake to generate the
configure script.
mkdir build && cd build && cmake ../
make
make install
If you also want cwebp or dwebp, you will need to enable them through CMake:
cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
or through your favorite interface (like ccmake or cmake-qt-gui).
Gradle:
-------
The support for Gradle is minimal: it only helps you compile libwebp, cwebp and
dwebp and webpmux_example.
Prerequisites:
A compiler (e.g., gcc with autotools) and gradle.
On a Debian-like system the following should install everything you need for a
minimal build:
$ sudo apt-get install build-essential gradle
When building from git sources, you will need to run the Gradle wrapper with the
appropriate target, e.g. :
./gradlew buildAllExecutables
SWIG bindings: SWIG bindings:
-------------- --------------
To generate language bindings from swig/libwebp.swig at least swig-1.3 To generate language bindings from swig/libwebp.i at least swig-1.3
(http://www.swig.org) is required. (http://www.swig.org) is required.
Currently the following functions are mapped: Currently the following functions are mapped:
@ -189,7 +115,7 @@ DLL that can be loaded via System.loadLibrary("webp_jni").
Python bindings: Python bindings:
To build the swig-generated Python extension code at least Python 2.6 is To build the swig-generated Python extension code at least Python 2.6 is
required. Python < 2.6 may build with some minor changes to libwebp.swig or the required. Python < 2.6 may build with some minor changes to libwebp.i or the
generated code, but is untested. generated code, but is untested.
Encoding tool: Encoding tool:
@ -214,34 +140,28 @@ A longer list of options is available using the -longhelp command line flag:
Usage: Usage:
cwebp [-preset <...>] [options] in_file [-o out_file] cwebp [-preset <...>] [options] in_file [-o out_file]
If input size (-s) for an image is not specified, it is If input size (-s) for an image is not specified, it is assumed to be a PNG,
assumed to be a PNG, JPEG, TIFF or WebP file. JPEG or TIFF file.
options:
Options: -h / -help ............ short help
-h / -help ............. short help -H / -longhelp ........ long help
-H / -longhelp ......... long help -q <float> ............. quality factor (0:small..100:big)
-q <float> ............. quality factor (0:small..100:big), default=75 -alpha_q <int> ......... Transparency-compression quality (0..100).
-alpha_q <int> ......... transparency-compression quality (0..100), -preset <string> ....... Preset setting, one of:
default=100
-preset <string> ....... preset setting, one of:
default, photo, picture, default, photo, picture,
drawing, icon, text drawing, icon, text
-preset must come first, as it overwrites other parameters -preset must come first, as it overwrites other parameters.
-z <int> ............... activates lossless preset with given -m <int> ............... compression method (0=fast, 6=slowest)
level in [0:fast, ..., 9:slowest] -segments <int> ........ number of segments to use (1..4)
-size <int> ............ Target size (in bytes)
-psnr <float> .......... Target PSNR (in dB. typically: 42)
-m <int> ............... compression method (0=fast, 6=slowest), default=4 -s <int> <int> ......... Input size (width x height) for YUV
-segments <int> ........ number of segments to use (1..4), default=4 -sns <int> ............. Spatial Noise Shaping (0:off, 100:max)
-size <int> ............ target size (in bytes) -f <int> ............... filter strength (0=off..100)
-psnr <float> .......... target PSNR (in dB. typically: 42) -sharpness <int> ....... filter sharpness (0:most .. 7:least sharp)
-strong ................ use strong filter instead of simple (default).
-s <int> <int> ......... input size (width x height) for YUV -nostrong .............. use simple filter instead of strong.
-sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
-f <int> ............... filter strength (0=off..100), default=60
-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
-strong ................ use strong filter instead of simple (default)
-nostrong .............. use simple filter instead of strong
-sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
-partition_limit <int> . limit quality to fit the 512k limit on -partition_limit <int> . limit quality to fit the 512k limit on
the first partition (0=no degradation ... 100=full) the first partition (0=no degradation ... 100=full)
-pass <int> ............ analysis pass number (1..10) -pass <int> ............ analysis pass number (1..10)
@ -249,42 +169,37 @@ Options:
-resize <w> <h> ........ resize picture (after any cropping) -resize <w> <h> ........ resize picture (after any cropping)
-mt .................... use multi-threading if available -mt .................... use multi-threading if available
-low_memory ............ reduce memory usage (slower encoding) -low_memory ............ reduce memory usage (slower encoding)
-map <int> ............. print map of extra info -map <int> ............. print map of extra info.
-print_psnr ............ prints averaged PSNR distortion -print_psnr ............ prints averaged PSNR distortion.
-print_ssim ............ prints averaged SSIM distortion -print_ssim ............ prints averaged SSIM distortion.
-print_lsim ............ prints local-similarity distortion -print_lsim ............ prints local-similarity distortion.
-d <file.pgm> .......... dump the compressed output (PGM file) -d <file.pgm> .......... dump the compressed output (PGM file).
-alpha_method <int> .... transparency-compression method (0..1), default=1 -alpha_method <int> .... Transparency-compression method (0..1)
-alpha_filter <string> . predictive filtering for alpha plane, -alpha_filter <string> . predictive filtering for alpha plane.
one of: none, fast (default) or best One of: none, fast (default) or best.
-exact ................. preserve RGB values in transparent area, default=off -alpha_cleanup ......... Clean RGB values in transparent area.
-blend_alpha <hex> ..... blend colors against background color -noalpha ............... discard any transparency information.
expressed as RGB values written in -lossless .............. Encode image losslessly.
hexadecimal, e.g. 0xc0e0d0 for red=0xc0 -hint <string> ......... Specify image characteristics hint.
green=0xe0 and blue=0xd0 One of: photo, picture or graph
-noalpha ............... discard any transparency information
-lossless .............. encode image losslessly, default=off
-near_lossless <int> ... use near-lossless image
preprocessing (0..100=off), default=100
-hint <string> ......... specify image characteristics hint,
one of: photo, picture or graph
-metadata <string> ..... comma separated list of metadata to -metadata <string> ..... comma separated list of metadata to
copy from the input to the output if present. copy from the input to the output if present.
Valid values: all, none (default), exif, icc, xmp Valid values: all, none (default), exif, icc, xmp
-short ................. condense printed message -short ................. condense printed message
-quiet ................. don't print anything -quiet ................. don't print anything.
-version ............... print version number and exit -version ............... print version number and exit.
-noasm ................. disable all assembly optimizations -noasm ................. disable all assembly optimizations.
-v ..................... verbose, e.g. print encoding/decoding times -v ..................... verbose, e.g. print encoding/decoding times
-progress .............. report encoding progress -progress .............. report encoding progress
Experimental Options: Experimental Options:
-jpeg_like ............. roughly match expected JPEG size -jpeg_like ............. Roughly match expected JPEG size.
-af .................... auto-adjust filter strength -af .................... auto-adjust filter strength.
-pre <int> ............. pre-processing filter -pre <int> ............. pre-processing filter
The main options you might want to try in order to further tune the The main options you might want to try in order to further tune the
visual quality are: visual quality are:
-preset -preset
@ -336,29 +251,21 @@ Decodes the WebP image file to PNG format [Default]
Use following options to convert into alternate image formats: Use following options to convert into alternate image formats:
-pam ......... save the raw RGBA samples as a color PAM -pam ......... save the raw RGBA samples as a color PAM
-ppm ......... save the raw RGB samples as a color PPM -ppm ......... save the raw RGB samples as a color PPM
-bmp ......... save as uncompressed BMP format
-tiff ........ save as uncompressed TIFF format
-pgm ......... save the raw YUV samples as a grayscale PGM -pgm ......... save the raw YUV samples as a grayscale PGM
file with IMC4 layout file with IMC4 layout.
-yuv ......... save the raw YUV samples in flat layout -yuv ......... save the raw YUV samples in flat layout.
Other options are: Other options are:
-version ..... print version number and exit -version .... print version number and exit.
-nofancy ..... don't use the fancy YUV420 upscaler -nofancy ..... don't use the fancy YUV420 upscaler.
-nofilter .... disable in-loop filtering -nofilter .... disable in-loop filtering.
-nodither .... disable dithering
-dither <d> .. dithering strength (in 0..100)
-alpha_dither use alpha-plane dithering if needed
-mt .......... use multi-threading -mt .......... use multi-threading
-crop <x> <y> <w> <h> ... crop output with the given rectangle -crop <x> <y> <w> <h> ... crop output with the given rectangle
-resize <w> <h> ......... scale the output (*after* any cropping) -scale <w> <h> .......... scale the output (*after* any cropping)
-flip ........ flip the output vertically -alpha ....... only save the alpha plane.
-alpha ....... only save the alpha plane -h ....... this help message.
-incremental . use incremental decoding (useful for tests) -v ....... verbose (e.g. print encoding/decoding times)
-h ........... this help message -noasm ....... disable all assembly optimizations.
-v ........... verbose (e.g. print encoding/decoding times)
-quiet ....... quiet mode, don't print anything
-noasm ....... disable all assembly optimizations
Visualization tool: Visualization tool:
=================== ===================
@ -372,21 +279,18 @@ Usage: vwebp in_file [options]
Decodes the WebP image file and visualize it using OpenGL Decodes the WebP image file and visualize it using OpenGL
Options are: Options are:
-version ..... print version number and exit -version .... print version number and exit.
-noicc ....... don't use the icc profile if present -noicc ....... don't use the icc profile if present.
-nofancy ..... don't use the fancy YUV420 upscaler -nofancy ..... don't use the fancy YUV420 upscaler.
-nofilter .... disable in-loop filtering -nofilter .... disable in-loop filtering.
-dither <int> dithering strength (0..100), default=50 -mt .......... use multi-threading.
-noalphadither disable alpha plane dithering -info ........ print info.
-mt .......... use multi-threading -h ....... this help message.
-info ........ print info
-h ........... this help message
Keyboard shortcuts: Keyboard shortcuts:
'c' ................ toggle use of color profile 'c' ................ toggle use of color profile.
'i' ................ overlay file information 'i' ................ overlay file information.
'd' ................ disable blending & disposal (debug) 'q' / 'Q' / ESC .... quit.
'q' / 'Q' / ESC .... quit
Building: Building:
--------- ---------
@ -414,101 +318,6 @@ $ make -f makefile.unix examples/vwebp
> nmake /f Makefile.vc CFG=release-static \ > nmake /f Makefile.vc CFG=release-static \
../obj/x64/release-static/bin/vwebp.exe ../obj/x64/release-static/bin/vwebp.exe
Animation creation tool:
========================
The utility 'img2webp' can turn a sequence of input images (PNG, JPEG, ...)
into an animated WebP file. It offers fine control over duration, encoding
modes, etc.
Usage:
img2webp [file-level options] [image files...] [per-frame options...]
File-level options (only used at the start of compression):
-min_size ............ minimize size
-loop <int> .......... loop count (default: 0, = infinite loop)
-kmax <int> .......... maximum number of frame between key-frames
(0=only keyframes)
-kmin <int> .......... minimum number of frame between key-frames
(0=disable key-frames altogether)
-mixed ............... use mixed lossy/lossless automatic mode
-v ................... verbose mode
-h ................... this help
Per-frame options (only used for subsequent images input):
-d <int> ............. frame duration in ms (default: 100)
-lossless ........... use lossless mode (default)
-lossy ... ........... use lossy mode
-q <float> ........... quality
-m <int> ............. method to use
example: img2webp -loop 2 in0.png -lossy in1.jpg
-d 80 in2.tiff -o out.webp
Animated GIF conversion:
========================
Animated GIF files can be converted to WebP files with animation using the
gif2webp utility available under examples/. The files can then be viewed using
vwebp.
Usage:
gif2webp [options] gif_file -o webp_file
Options:
-h / -help ............. this help
-lossy ................. encode image using lossy compression
-mixed ................. for each frame in the image, pick lossy
or lossless compression heuristically
-q <float> ............. quality factor (0:small..100:big)
-m <int> ............... compression method (0=fast, 6=slowest)
-min_size .............. minimize output size (default:off)
lossless compression by default; can be
combined with -q, -m, -lossy or -mixed
options
-kmin <int> ............ min distance between key frames
-kmax <int> ............ max distance between key frames
-f <int> ............... filter strength (0=off..100)
-metadata <string> ..... comma separated list of metadata to
copy from the input to the output if present
Valid values: all, none, icc, xmp (default)
-mt .................... use multi-threading if available
-version ............... print version number and exit
-v ..................... verbose
-quiet ................. don't print anything
Building:
---------
With the libgif development files installed, gif2webp can be built using
makefile.unix:
$ make -f makefile.unix examples/gif2webp
or using autoconf:
$ ./configure --enable-everything
$ make
Comparison of animated images:
==============================
Test utility anim_diff under examples/ can be used to compare two animated
images (each can be GIF or WebP).
Usage: anim_diff <image1> <image2> [options]
Options:
-dump_frames <folder> dump decoded frames in PAM format
-min_psnr <float> ... minimum per-frame PSNR
-raw_comparison ..... if this flag is not used, RGB is
premultiplied before comparison
Building:
---------
With the libgif development files and a C++ compiler installed, anim_diff can
be built using makefile.unix:
$ make -f makefile.unix examples/anim_diff
or using autoconf:
$ ./configure --enable-everything
$ make
Encoding API: Encoding API:
============= =============
@ -586,20 +395,15 @@ The encoding flow looks like:
// Set up a byte-output write method. WebPMemoryWriter, for instance. // Set up a byte-output write method. WebPMemoryWriter, for instance.
WebPMemoryWriter wrt; WebPMemoryWriter wrt;
WebPMemoryWriterInit(&wrt); // initialize 'wrt'
pic.writer = MyFileWriter; pic.writer = MyFileWriter;
pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work; pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work;
// initialize 'wrt' here...
// Compress! // Compress!
int ok = WebPEncode(&config, &pic); // ok = 0 => error occurred! int ok = WebPEncode(&config, &pic); // ok = 0 => error occurred!
WebPPictureFree(&pic); // must be called independently of the 'ok' result. WebPPictureFree(&pic); // must be called independently of the 'ok' result.
// output data should have been handled by the writer at that point. // output data should have been handled by the writer at that point.
// -> compressed data is the memory buffer described by wrt.mem / wrt.size
// deallocate the memory used by compressed data
WebPMemoryWriterClear(&wrt);
-------------------------------------- END PSEUDO EXAMPLE -------------------------------------- END PSEUDO EXAMPLE
@ -738,8 +542,8 @@ an otherwise too-large picture. Some CPU can be saved too, incidentally.
Bugs: Bugs:
===== =====
Please report all bugs to the issue tracker: Please report all bugs to our issue tracker:
https://bugs.chromium.org/p/webp http://code.google.com/p/webp/issues
Patches welcome! See this page to get started: Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/ http://www.webmproject.org/code/contribute/submitting-patches/

View File

@ -1,7 +1,7 @@
 __ __ ____ ____ ____ __ __ _ __ __  __ __ ____ ____ ____ __ __ _ __ __
/ \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\ / \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
\ / __/ _ \ __/ / / (_/ /__ \ / __/ _ \ __/ / / (_/ /__
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v0.4.0 \__\__/\_____/_____/__/ \__//_/\_____/__/___/v0.1.1
Description: Description:
@ -25,8 +25,6 @@ A list of options is available using the -help command line flag:
> webpmux -help > webpmux -help
Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
webpmux -set SET_OPTIONS INPUT -o OUTPUT webpmux -set SET_OPTIONS INPUT -o OUTPUT
webpmux -duration DURATION_OPTIONS [-duration ...]
INPUT -o OUTPUT
webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT] webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
[-bgcolor BACKGROUND_COLOR] -o OUTPUT [-bgcolor BACKGROUND_COLOR] -o OUTPUT
@ -35,46 +33,34 @@ Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
webpmux -version webpmux -version
GET_OPTIONS: GET_OPTIONS:
Extract relevant data: Extract relevant data.
icc get ICC profile icc Get ICC profile.
exif get EXIF metadata exif Get EXIF metadata.
xmp get XMP metadata xmp Get XMP metadata.
frame n get nth frame frame n Get nth frame.
SET_OPTIONS: SET_OPTIONS:
Set color profile/metadata: Set color profile/metadata.
icc file.icc set ICC profile icc file.icc Set ICC profile.
exif file.exif set EXIF metadata exif file.exif Set EXIF metadata.
xmp file.xmp set XMP metadata xmp file.xmp Set XMP metadata.
where: 'file.icc' contains the ICC profile to be set, where: 'file.icc' contains the ICC profile to be set,
'file.exif' contains the EXIF metadata to be set 'file.exif' contains the EXIF metadata to be set
'file.xmp' contains the XMP metadata to be set 'file.xmp' contains the XMP metadata to be set
DURATION_OPTIONS:
Set duration of selected frames:
duration set duration for each frames
duration,frame set duration of a particular frame
duration,start,end set duration of frames in the
interval [start,end])
where: 'duration' is the duration in milliseconds
'start' is the start frame index
'end' is the inclusive end frame index
The special 'end' value '0' means: last frame.
STRIP_OPTIONS: STRIP_OPTIONS:
Strip color profile/metadata: Strip color profile/metadata.
icc strip ICC profile icc Strip ICC profile.
exif strip EXIF metadata exif Strip EXIF metadata.
xmp strip XMP metadata xmp Strip XMP metadata.
FRAME_OPTIONS(i): FRAME_OPTIONS(i):
Create animation: Create animation.
file_i +di+[xi+yi[+mi[bi]]] file_i +di+xi+yi+mi
where: 'file_i' is the i'th animation frame (WebP format), where: 'file_i' is the i'th animation frame (WebP format),
'di' is the pause duration before next frame, 'di' is the pause duration before next frame.
'xi','yi' specify the image offset for this frame, 'xi','yi' specify the image offset for this frame.
'mi' is the dispose method for this frame (0 or 1), 'mi' is the dispose method for this frame (0 or 1).
'bi' is the blending method for this frame (+b or -b)
LOOP_COUNT: LOOP_COUNT:
Number of times to repeat the animation. Number of times to repeat the animation.
@ -85,7 +71,7 @@ BACKGROUND_COLOR:
A,R,G,B A,R,G,B
where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying
the Alpha, Red, Green and Blue component values respectively the Alpha, Red, Green and Blue component values respectively
[Default: 255,255,255,255] [Default: 255,255,255,255].
INPUT & OUTPUT are in WebP format. INPUT & OUTPUT are in WebP format.
@ -146,7 +132,7 @@ WebP files. This API currently supports reading of XMP/EXIF metadata, ICC
profile and animated images. Other features may be added in subsequent profile and animated images. Other features may be added in subsequent
releases. releases.
Code example: Demuxing WebP data to extract all the frames, ICC profile Code Example: Demuxing WebP data to extract all the frames, ICC profile
and EXIF/XMP metadata. and EXIF/XMP metadata.
WebPDemuxer* demux = WebPDemux(&webp_data); WebPDemuxer* demux = WebPDemux(&webp_data);
@ -183,36 +169,12 @@ and EXIF/XMP metadata.
For a detailed Demux API reference, please refer to the header file For a detailed Demux API reference, please refer to the header file
(src/webp/demux.h). (src/webp/demux.h).
AnimEncoder API:
================
The AnimEncoder API can be used to create animated WebP images.
Code example:
WebPAnimEncoderOptions enc_options;
WebPAnimEncoderOptionsInit(&enc_options);
// ... (Tune 'enc_options' as needed).
WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
while(<there are more frames>) {
WebPConfig config;
WebPConfigInit(&config);
// ... (Tune 'config' as needed).
WebPAnimEncoderAdd(enc, frame, duration, &config);
}
WebPAnimEncoderAssemble(enc, webp_data);
WebPAnimEncoderDelete(enc);
// ... (Write the 'webp_data' to a file, or re-mux it further).
For a detailed AnimEncoder API reference, please refer to the header file
(src/webp/mux.h).
Bugs: Bugs:
===== =====
Please report all bugs to the issue tracker: Please report all bugs to our issue tracker:
https://bugs.chromium.org/p/webp http://code.google.com/p/webp/issues
Patches welcome! See this page to get started: Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/ http://www.webmproject.org/code/contribute/submitting-patches/

View File

@ -1,404 +0,0 @@
// Define dependencies.
buildscript {
repositories {
maven {
url "https://jcenter.bintray.com"
}
}
dependencies {
classpath "com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}"
}
}
// Define versions in the project.
project.ext {
buildToolsVersion = "${BUILD_TOOLS_VERSION}"
compileSdkVersion = COMPILE_SDK_VERSION.toInteger()
}
// Core libraries and executables.
apply plugin: "c"
def NEON
model {
buildTypes {
debug
release
}
platforms {
arm {
architecture "arm"
}
arm64 {
architecture "arm64"
}
x86 {
architecture "x86"
}
x64 {
architecture "x86_64"
}
mips32r2
mips32r5
mips64r6
}
toolChains {
gcc(Gcc) {
target("mips32r2") {
cCompiler.args "-mips32r2"
}
target("mips32r5") {
cCompiler.args "-mips32r5"
}
target("mips64r6") {
cCompiler.args "-mips64r6"
}
}
}
binaries {
all {
if (toolChain in Gcc) {
cCompiler.args "-fPIC"
cCompiler.args "-Wall"
cCompiler.define "ANDROID"
cCompiler.define "HAVE_MALLOC_H"
}
// Optimizations.
if (buildType == buildTypes.release) {
if (toolChain in Gcc) {
cCompiler.args "-finline-functions"
cCompiler.args "-ffast-math"
cCompiler.args "-ffunction-sections"
cCompiler.args "-fdata-sections"
}
if (toolChain in Clang) {
cCompiler.args "-frename-registers -s"
}
}
// Check for NEON usage.
if (getTargetPlatform() == "arm" || getTargetPlatform() == "arm64") {
NEON = "c.neon"
} else {
NEON = "c"
}
}
// Link to pthread for shared libraries.
withType(SharedLibraryBinarySpec) {
if (toolChain in Gcc) {
cCompiler.define "HAVE_PTHREAD"
cCompiler.define "WEBP_USE_THREAD"
linker.args "-pthread"
}
}
}
components {
webp(NativeLibrarySpec) {
sources {
c {
source {
srcDir "src/dec"
include "alpha_dec.c"
include "buffer_dec.c"
include "frame_dec.c"
include "idec_dec.c"
include "io_dec.c"
include "quant_dec.c"
include "tree_dec.c"
include "vp8_dec.c"
include "vp8l_dec.c"
include "webp_dec.c"
srcDir "src/dsp"
include "alpha_processing.c"
include "alpha_processing_mips_dsp_r2.c"
include "alpha_processing_neon.$NEON"
include "alpha_processing_sse2.c"
include "alpha_processing_sse41.c"
include "argb.c"
include "argb_mips_dsp_r2.c"
include "argb_sse2.c"
include "cpu.c"
include "dec.c"
include "dec_clip_tables.c"
include "dec_mips32.c"
include "dec_mips_dsp_r2.c"
include "dec_msa.c"
include "dec_neon.$NEON"
include "dec_sse2.c"
include "dec_sse41.c"
include "filters.c"
include "filters_mips_dsp_r2.c"
include "filters_msa.c"
include "filters_neon.$NEON"
include "filters_sse2.c"
include "lossless.c"
include "lossless_mips_dsp_r2.c"
include "lossless_msa.c"
include "lossless_neon.$NEON"
include "lossless_sse2.c"
include "rescaler.c"
include "rescaler_mips32.c"
include "rescaler_mips_dsp_r2.c"
include "rescaler_msa.c"
include "rescaler_neon.$NEON"
include "rescaler_sse2.c"
include "upsampling.c"
include "upsampling_mips_dsp_r2.c"
include "upsampling_msa.c"
include "upsampling_neon.$NEON"
include "upsampling_sse2.c"
include "yuv.c"
include "yuv_mips32.c"
include "yuv_mips_dsp_r2.c"
include "yuv_sse2.c"
srcDir "src/utils"
include "bit_reader_utils.c"
include "color_cache_utils.c"
include "filters_utils.c"
include "huffman_utils.c"
include "quant_levels_dec_utils.c"
include "random_utils.c"
include "rescaler_utils.c"
include "thread_utils.c"
include "utils.c"
srcDir "src/dsp"
include "cost.c"
include "cost_mips32.c"
include "cost_mips_dsp_r2.c"
include "cost_sse2.c"
include "enc.c"
include "enc_avx2.c"
include "enc_mips32.c"
include "enc_mips_dsp_r2.c"
include "enc_msa.c"
include "enc_neon.$NEON"
include "enc_sse2.c"
include "enc_sse41.c"
include "lossless_enc.c"
include "lossless_enc_mips32.c"
include "lossless_enc_mips_dsp_r2.c"
include "lossless_enc_msa.c"
include "lossless_enc_neon.$NEON"
include "lossless_enc_sse2.c"
include "lossless_enc_sse41.c"
srcDir "src/enc"
include "alpha_enc.c"
include "analysis_enc.c"
include "backward_references_enc.c"
include "config_enc.c"
include "cost_enc.c"
include "delta_palettization_enc.c"
include "filter_enc.c"
include "frame_enc.c"
include "histogram_enc.c"
include "iterator_enc.c"
include "near_lossless_enc.c"
include "picture_enc.c"
include "picture_csp_enc.c"
include "picture_psnr_enc.c"
include "picture_rescale_enc.c"
include "picture_tools_enc.c"
include "predictor_enc.c"
include "quant_enc.c"
include "syntax_enc.c"
include "token_enc.c"
include "tree_enc.c"
include "vp8l_enc.c"
include "webp_enc.c"
srcDir "src/utils"
include "bit_writer_utils.c"
include "huffman_encode_utils.c"
include "quant_levels_utils.c"
}
exportedHeaders {
srcDir "src"
}
}
}
}
webpdemux(NativeLibrarySpec) {
sources {
c {
source {
srcDir "src/demux"
include "anim_decode.c"
include "demux.c"
}
}
}
}
webpmux(NativeLibrarySpec) {
sources {
c {
source {
srcDir "src/mux/"
include "anim_encode.c"
include "muxedit.c"
include "muxinternal.c"
include "muxread.c"
}
}
}
}
// Executables from examples.
example_util(NativeLibrarySpec) {
binaries {
all {
lib library: "webp", linkage: "static"
}
}
sources {
c {
source {
srcDir "./examples"
include "example_util.c"
}
}
}
}
imageio_util(NativeLibrarySpec) {
binaries {
all {
lib library: "webp", linkage: "static"
}
}
sources {
c {
source {
srcDir "./imageio"
include "imageio_util.c"
}
}
}
}
imagedec(NativeLibrarySpec) {
binaries {
all {
lib library: "webp", linkage: "static"
}
}
sources {
c {
source {
srcDir "./imageio"
include "image_dec.c"
include "jpegdec.c"
include "metadata.c"
include "pngdec.c"
include "tiffdec.c"
include "webpdec.c"
}
}
}
}
imageenc(NativeLibrarySpec) {
binaries {
all {
lib library: "webp", linkage: "static"
lib library: "imageio_util", linkage: "static"
}
}
sources {
c {
source {
srcDir "./imageio"
include "image_enc.c"
}
}
}
}
cwebp(NativeExecutableSpec) {
binaries {
all {
lib library: "example_util", linkage: "static"
lib library: "imagedec", linkage: "static"
lib library: "imageio_util", linkage: "static"
lib library: "webp", linkage: "static"
}
}
sources {
c {
source {
srcDir "./examples"
include "cwebp.c"
}
}
}
}
dwebp(NativeExecutableSpec) {
binaries {
all {
lib library: "example_util", linkage: "static"
lib library: "imagedec", linkage: "static"
lib library: "imageenc", linkage: "static"
lib library: "imageio_util", linkage: "static"
lib library: "webp"
}
}
sources {
c {
source {
srcDir "./examples"
include "dwebp.c"
}
}
}
}
webpmux_example(NativeExecutableSpec) {
binaries {
all {
lib library: "example_util", linkage: "static"
lib library: "imageio_util", linkage: "static"
lib library: "webpmux", linkage: "static"
lib library: "webp"
}
}
sources {
c {
source {
srcDir "./examples"
include "webpmux.c"
}
}
}
}
img2webp_example(NativeExecutableSpec) {
binaries {
all {
lib library: "example_util", linkage: "static"
lib library: "imagedec", linkage: "static"
lib library: "imageio_util", linkage: "static"
lib library: "webpmux", linkage: "static"
lib library: "webp"
}
}
sources {
c {
source {
srcDir "./examples"
include "img2webp.c"
}
}
}
}
}
tasks {
// Task to test all possible configurations.
buildAllExecutables(Task) {
dependsOn $.binaries.findAll { it.buildable }
}
}
}
// Task to generate the wrapper.
task wrapper(type: Wrapper) {
gradleVersion = '2.13'
}

View File

@ -1,141 +0,0 @@
# 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
)

View File

@ -1,143 +0,0 @@
/* Adapted from the autotools src/webp/config.h.in. */
/* Define if building universal (internal helper macro) */
/* TODO: handle properly in CMake */
#cmakedefine AC_APPLE_UNIVERSAL_BUILD 1
/* Set to 1 if __builtin_bswap16 is available */
#cmakedefine HAVE_BUILTIN_BSWAP16 1
/* Set to 1 if __builtin_bswap32 is available */
#cmakedefine HAVE_BUILTIN_BSWAP32 1
/* Set to 1 if __builtin_bswap64 is available */
#cmakedefine HAVE_BUILTIN_BSWAP64 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#cmakedefine HAVE_DLFCN_H 1
/* Define to 1 if you have the <GLUT/glut.h> header file. */
#cmakedefine HAVE_GLUT_GLUT_H 1
/* Define to 1 if you have the <GL/glut.h> header file. */
#cmakedefine HAVE_GL_GLUT_H 1
/* Define to 1 if you have the <inttypes.h> header file. */
#cmakedefine HAVE_INTTYPES_H 1
/* Define to 1 if you have the <memory.h> header file. */
#cmakedefine HAVE_MEMORY_H 1
/* Define to 1 if you have the <OpenGL/glut.h> header file. */
#cmakedefine HAVE_OPENGL_GLUT_H 1
/* Have PTHREAD_PRIO_INHERIT. */
#cmakedefine HAVE_PTHREAD_PRIO_INHERIT @HAVE_PTHREAD_PRIO_INHERIT@
/* Define to 1 if you have the <shlwapi.h> header file. */
#cmakedefine HAVE_SHLWAPI_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#cmakedefine HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#cmakedefine HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#cmakedefine HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#cmakedefine HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#cmakedefine HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#cmakedefine HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#cmakedefine HAVE_UNISTD_H 1
/* Define to 1 if you have the <wincodec.h> header file. */
#cmakedefine HAVE_WINCODEC_H 1
/* Define to 1 if you have the <windows.h> header file. */
#cmakedefine HAVE_WINDOWS_H 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
/* TODO: handle properly in CMake */
#cmakedefine LT_OBJDIR "@LT_OBJDIR@"
/* Name of package */
#cmakedefine PACKAGE "@PROJECT_NAME@"
/* Define to the address where bug reports for this package should be sent. */
#cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
/* Define to the full name of this package. */
#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@"
/* Define to the full name and version of this package. */
#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@"
/* Define to the one symbol short name of this package. */
#cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@"
/* Define to the home page for this package. */
#cmakedefine PACKAGE_URL "@PACKAGE_URL@"
/* Define to the version of this package. */
#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#cmakedefine PTHREAD_CREATE_JOINABLE 1
/* Define to 1 if you have the ANSI C header files. */
#cmakedefine STDC_HEADERS 1
/* Version number of package */
#cmakedefine VERSION "@VERSION@"
/* Enable experimental code */
#cmakedefine WEBP_EXPERIMENTAL_FEATURES 1
/* Set to 1 if AVX2 is supported */
#cmakedefine WEBP_HAVE_AVX2 1
/* Set to 1 if GIF library is installed */
#cmakedefine WEBP_HAVE_GIF 1
/* Set to 1 if OpenGL is supported */
#cmakedefine WEBP_HAVE_GL 1
/* Set to 1 if JPEG library is installed */
#cmakedefine WEBP_HAVE_JPEG 1
/* Set to 1 if PNG library is installed */
#cmakedefine WEBP_HAVE_PNG 1
/* Set to 1 if SSE2 is supported */
#cmakedefine WEBP_HAVE_SSE2 1
/* Set to 1 if SSE4.1 is supported */
#cmakedefine WEBP_HAVE_SSE41 1
/* Set to 1 if TIFF library is installed */
#cmakedefine WEBP_HAVE_TIFF 1
/* Undefine this to disable thread support. */
#cmakedefine WEBP_USE_THREAD 1
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
# undef WORDS_BIGENDIAN
# endif
#endif

View File

@ -1,113 +0,0 @@
## 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)
# Do one more check for Clang to circumvent CMake issue 13194.
if(COMMAND check_compiler_flag_common_patterns)
# Only in CMake 3.0 and above.
check_compiler_flag_common_patterns(COMMON_PATTERNS)
else()
set(COMMON_PATTERNS)
endif()
set(CMAKE_REQUIRED_DEFINITIONS ${SIMD_COMPILE_FLAG})
check_c_source_compiles("int main(void) {return 0;}" FLAG2
FAIL_REGEX "warning: argument unused during compilation:"
${COMMON_PATTERNS}
)
if(NOT FLAG2)
unset(HAS_COMPILE_FLAG CACHE)
endif()
endif()
if(HAS_COMPILE_FLAG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILE_FLAG}")
endif()
endif()
endif()
endif()
endforeach()

View File

@ -1,8 +1,7 @@
AC_INIT([libwebp], [0.6.0], AC_INIT([libwebp], [0.3.1],
[https://bugs.chromium.org/p/webp],, [http://code.google.com/p/webp/issues],,
[http://developers.google.com/speed/webp]) [http://developers.google.com/speed/webp])
AC_CANONICAL_HOST AC_CANONICAL_TARGET
AC_PREREQ([2.60])
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
dnl === automake >= 1.12 requires this for 'unusual archivers' support. dnl === automake >= 1.12 requires this for 'unusual archivers' support.
@ -10,15 +9,11 @@ dnl === it must occur before LT_INIT (AC_PROG_LIBTOOL).
m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
AC_PROG_LIBTOOL AC_PROG_LIBTOOL
AC_PROG_SED
AM_PROG_CC_C_O AM_PROG_CC_C_O
dnl === Enable less verbose output when building. dnl === Enable less verbose output when building.
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl == test endianness
AC_C_BIGENDIAN
dnl === SET_IF_UNSET(shell_var, value) dnl === SET_IF_UNSET(shell_var, value)
dnl === Set the shell variable 'shell_var' to 'value' if it is unset. dnl === Set the shell variable 'shell_var' to 'value' if it is unset.
AC_DEFUN([SET_IF_UNSET], [test "${$1+set}" = "set" || $1=$2]) AC_DEFUN([SET_IF_UNSET], [test "${$1+set}" = "set" || $1=$2])
@ -29,222 +24,40 @@ AC_ARG_ENABLE([everything],
disabled with --disable-target]), disabled with --disable-target]),
[SET_IF_UNSET([enable_libwebpdecoder], [$enableval]) [SET_IF_UNSET([enable_libwebpdecoder], [$enableval])
SET_IF_UNSET([enable_libwebpdemux], [$enableval]) SET_IF_UNSET([enable_libwebpdemux], [$enableval])
SET_IF_UNSET([enable_libwebpextras], [$enableval])
SET_IF_UNSET([enable_libwebpmux], [$enableval])]) SET_IF_UNSET([enable_libwebpmux], [$enableval])])
dnl === If --enable-asserts is not defined, define NDEBUG
AC_MSG_CHECKING(whether asserts are enabled)
AC_ARG_ENABLE([asserts],
AS_HELP_STRING([--enable-asserts],
[Enable assert checks]))
if test "x${enable_asserts-no}" = "xno"; then
AM_CPPFLAGS="${AM_CPPFLAGS} -DNDEBUG"
fi
AC_MSG_RESULT(${enable_asserts-no})
AC_SUBST([AM_CPPFLAGS])
AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR], AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR],
[Path to the pkgconfig directory @<:@LIBDIR/pkgconfig@:>@]), [Path to the pkgconfig directory @<:@LIBDIR/pkgconfig@:>@]),
[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
AC_SUBST([pkgconfigdir]) AC_SUBST([pkgconfigdir])
dnl === TEST_AND_ADD_CFLAGS(var, flag) dnl === TEST_AND_ADD_CFLAGS(flag)
dnl === Checks whether $CC supports 'flag' and adds it to 'var' dnl === Checks whether $CC supports 'flag' and adds it to AM_CFLAGS on success.
dnl === on success.
AC_DEFUN([TEST_AND_ADD_CFLAGS], AC_DEFUN([TEST_AND_ADD_CFLAGS],
[SAVED_CFLAGS="$CFLAGS" [SAVED_CFLAGS="$CFLAGS"
CFLAGS="-Werror $2" CFLAGS="-Werror $1"
AC_MSG_CHECKING([whether $CC supports $2]) AC_MSG_CHECKING([whether $CC supports $1])
dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition. dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition.
AC_COMPILE_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])], AC_COMPILE_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])],
[AC_MSG_RESULT([yes])] [AC_MSG_RESULT([yes])]
dnl Simply append the variable avoiding a dnl Simply append the variable avoiding a
dnl compatibility ifdef for AS_VAR_APPEND as this dnl compatibility ifdef for AS_VAR_APPEND as this
dnl variable shouldn't grow all that large. dnl variable shouldn't grow all that large.
[$1="${$1} $2"], [AM_CFLAGS="$AM_CFLAGS $1"],
[AC_MSG_RESULT([no])]) [AC_MSG_RESULT([no])])
CFLAGS="$SAVED_CFLAGS"]) CFLAGS="$SAVED_CFLAGS"])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-fvisibility=hidden]) TEST_AND_ADD_CFLAGS([-Wall])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wall]) TEST_AND_ADD_CFLAGS([-Wdeclaration-after-statement])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wdeclaration-after-statement]) TEST_AND_ADD_CFLAGS([-Wextra])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra]) TEST_AND_ADD_CFLAGS([-Wmissing-declarations])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wfloat-conversion]) TEST_AND_ADD_CFLAGS([-Wmissing-prototypes])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-nonliteral]) TEST_AND_ADD_CFLAGS([-Wold-style-definition])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-security]) TEST_AND_ADD_CFLAGS([-Wshadow])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations]) TEST_AND_ADD_CFLAGS([-Wunused-but-set-variable])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes]) TEST_AND_ADD_CFLAGS([-Wunused])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition]) TEST_AND_ADD_CFLAGS([-Wvla])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshorten-64-to-32])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused-but-set-variable])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused])
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wvla])
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62040
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61622
AS_IF([test "$GCC" = "yes" ], [
gcc_version=`$CC -dumpversion`
gcc_wht_bug=""
case "$host_cpu" in
aarch64|arm64)
case "$gcc_version" in
4.9|4.9.0|4.9.1) gcc_wht_bug=yes ;;
esac
esac
AS_IF([test "$gcc_wht_bug" = "yes"], [
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-frename-registers])])])
# Use -flax-vector-conversions, if available, when building intrinsics with
# older versions of gcc. The flag appeared in 4.3.x, but if backported, and
# -fno-lax-vector-conversions is set, errors may occur with the intrinsics
# files along with the older system includes, e.g., emmintrin.h.
# Originally observed with cc (GCC) 4.2.1 20070831 patched [FreeBSD] (9.3).
# https://bugs.chromium.org/p/webp/issues/detail?id=274
AS_IF([test "$GCC" = "yes" ], [
case "$host_cpu" in
amd64|i?86|x86_64)
AC_COMPILE_IFELSE(
dnl only check for -flax-vector-conversions with older gcc, skip
dnl clang as it reports itself as 4.2.1, but the flag isn't needed.
[AC_LANG_SOURCE([#if !defined(__clang__) && defined(__GNUC__) && \
((__GNUC__ << 8) | __GNUC_MINOR__) < 0x403
#error old gcc
#endif
int main(void) { return 0; }
])],,
[TEST_AND_ADD_CFLAGS([INTRINSICS_CFLAGS],
[-flax-vector-conversions])])
;;
esac])
AC_SUBST([AM_CFLAGS]) AC_SUBST([AM_CFLAGS])
dnl === Check for machine specific flags
AC_ARG_ENABLE([avx2],
AS_HELP_STRING([--disable-avx2],
[Disable detection of AVX2 support
@<:@default=auto@:>@]))
AS_IF([test "x$enable_avx2" != "xno" -a "x$enable_sse4_1" != "xno" \
-a "x$enable_sse2" != "xno"], [
AVX2_CFLAGS="$INTRINSICS_CFLAGS $AVX2_FLAGS"
TEST_AND_ADD_CFLAGS([AVX2_FLAGS], [-mavx2])
AS_IF([test -n "$AVX2_FLAGS"], [
SAVED_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS $AVX2_FLAGS"
AC_CHECK_HEADER([immintrin.h],
[AC_DEFINE(WEBP_HAVE_AVX2, [1],
[Set to 1 if AVX2 is supported])],
[AVX2_FLAGS=""],
dnl it's illegal to directly include avx2intrin.h, but it's
dnl included conditionally in immintrin.h, tricky!
[#ifndef __AVX2__
#error avx2 is not enabled
#endif
])
CFLAGS=$SAVED_CFLAGS])
AC_SUBST([AVX2_FLAGS])])
AC_ARG_ENABLE([sse4.1],
AS_HELP_STRING([--disable-sse4.1],
[Disable detection of SSE4.1 support
@<:@default=auto@:>@]))
AS_IF([test "x$enable_sse4_1" != "xno" -a "x$enable_sse2" != "xno"], [
SSE41_FLAGS="$INTRINSICS_CFLAGS $SSE41_FLAGS"
TEST_AND_ADD_CFLAGS([SSE41_FLAGS], [-msse4.1])
AS_IF([test -n "$SSE41_FLAGS"], [
SAVED_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS $SSE41_FLAGS"
AC_CHECK_HEADER([smmintrin.h],
[AC_DEFINE(WEBP_HAVE_SSE41, [1],
[Set to 1 if SSE4.1 is supported])],
[SSE41_FLAGS=""])
CFLAGS=$SAVED_CFLAGS])
AC_SUBST([SSE41_FLAGS])])
AC_ARG_ENABLE([sse2],
AS_HELP_STRING([--disable-sse2],
[Disable detection of SSE2 support
@<:@default=auto@:>@]))
AS_IF([test "x$enable_sse2" != "xno"], [
SSE2_FLAGS="$INTRINSICS_CFLAGS $SSE2_FLAGS"
TEST_AND_ADD_CFLAGS([SSE2_FLAGS], [-msse2])
AS_IF([test -n "$SSE2_FLAGS"], [
SAVED_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS $SSE2_FLAGS"
AC_CHECK_HEADER([emmintrin.h],
[AC_DEFINE(WEBP_HAVE_SSE2, [1],
[Set to 1 if SSE2 is supported])],
[SSE2_FLAGS=""])
CFLAGS=$SAVED_CFLAGS])
AC_SUBST([SSE2_FLAGS])])
AC_ARG_ENABLE([neon],
AS_HELP_STRING([--disable-neon],
[Disable detection of NEON support
@<:@default=auto@:>@]))
AC_ARG_ENABLE([neon_rtcd],
AS_HELP_STRING([--disable-neon-rtcd],
[Disable runtime detection of NEON support via
/proc/cpuinfo on Linux hosts
@<:@default=auto@:>@]))
# For ARM(7) hosts:
# Both NEON flags unset and NEON support detected = build all modules with NEON
# NEON detected with the use of -mfpu=neon = build only NEON modules with NEON
AS_IF([test "x$enable_neon" != "xno"], [
case "$host_cpu" in
arm|armv7*)
# 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])])
dnl === CLEAR_LIBVARS([var_pfx]) dnl === CLEAR_LIBVARS([var_pfx])
dnl === Clears <var_pfx>_{INCLUDES,LIBS}. dnl === Clears <var_pfx>_{INCLUDES,LIBS}.
AC_DEFUN([CLEAR_LIBVARS], [$1_INCLUDES=""; $1_LIBS=""]) AC_DEFUN([CLEAR_LIBVARS], [$1_INCLUDES=""; $1_LIBS=""])
@ -280,26 +93,6 @@ AC_DEFUN([LIBCHECK_EPILOGUE],
CPPFLAGS=$SAVED_CPPFLAGS CPPFLAGS=$SAVED_CPPFLAGS
LIBS=$SAVED_LIBS]) LIBS=$SAVED_LIBS])
dnl === Check for gcc builtins
dnl === CHECK_FOR_BUILTIN([builtin], [param], [define])
dnl === links a C AC_LANG_PROGRAM, with <builtin>(<param>)
dnl === AC_DEFINE'ing <define> if successful.
AC_DEFUN([CHECK_FOR_BUILTIN],
[AC_LANG_PUSH([C])
AC_MSG_CHECKING([for $1])
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [(void)$1($2)])],
[AC_MSG_RESULT([yes])
AC_DEFINE([$3], [1],
[Set to 1 if $1 is available])],
[AC_MSG_RESULT([no])]),
AC_LANG_POP])
dnl AC_CHECK_FUNC doesn't work with builtin's.
CHECK_FOR_BUILTIN([__builtin_bswap16], [1u << 15], [HAVE_BUILTIN_BSWAP16])
CHECK_FOR_BUILTIN([__builtin_bswap32], [1u << 31], [HAVE_BUILTIN_BSWAP32])
CHECK_FOR_BUILTIN([__builtin_bswap64], [1ull << 63], [HAVE_BUILTIN_BSWAP64])
dnl === Check for pthread support dnl === Check for pthread support
AC_ARG_ENABLE([threading], AC_ARG_ENABLE([threading],
AS_HELP_STRING([--disable-threading], AS_HELP_STRING([--disable-threading],
@ -313,265 +106,213 @@ if test "$enable_threading" = "yes"; then
CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC" CC="$PTHREAD_CC"
], ],
[AC_CHECK_FUNC([_beginthreadex], [enable_threading=no])
[AC_DEFINE([WEBP_USE_THREAD], [1],
[Undefine this to disable thread
support.])],
[enable_threading=no])])
fi fi
AC_MSG_NOTICE([checking if threading is enabled... ${enable_threading-no}]) AC_MSG_NOTICE([checking if threading is enabled... ${enable_threading-no}])
dnl === check for OpenGL/GLUT support === dnl === check for OpenGL/GLUT support ===
CLEAR_LIBVARS([GL])
WITHLIB_OPTION([gl], [GL])
AC_ARG_ENABLE([gl], AS_HELP_STRING([--disable-gl], LIBCHECK_PROLOGUE([GL])
[Disable detection of OpenGL support
@<:@default=auto@:>@]))
AS_IF([test "x$enable_gl" != "xno"], [
CLEAR_LIBVARS([GL])
WITHLIB_OPTION([gl], [GL])
LIBCHECK_PROLOGUE([GL]) glut_cflags="none"
glut_ldflags="none"
case $host_os in
darwin*)
# Special case for OSX builds. Append these to give the user a chance to
# override with --with-gl*
glut_cflags="$glut_cflags|-framework GLUT -framework OpenGL"
glut_ldflags="$glut_ldflags|-framework GLUT -framework OpenGL"
;;
esac
glut_cflags="none" GLUT_SAVED_CPPFLAGS="$CPPFLAGS"
glut_ldflags="none" SAVED_IFS="$IFS"
case $host_os in IFS="|"
darwin*) for flag in $glut_cflags; do
# Special case for OSX builds. Append these to give the user a chance to # restore IFS immediately as the autoconf macros may need the default.
# override with --with-gl* IFS="$SAVED_IFS"
glut_cflags="$glut_cflags|-framework GLUT -framework OpenGL" unset ac_cv_header_GL_glut_h
glut_ldflags="$glut_ldflags|-framework GLUT -framework OpenGL" unset ac_cv_header_OpenGL_glut_h
;;
case $flag in
none) ;;
*) CPPFLAGS="$flag $CPPFLAGS";;
esac esac
AC_CHECK_HEADERS([GL/glut.h GLUT/glut.h OpenGL/glut.h],
[glut_headers=yes;
test "$flag" = "none" || GL_INCLUDES="$CPPFLAGS";
break])
CPPFLAGS="$GLUT_SAVED_CPPFLAGS"
test "$glut_headers" = "yes" && break
done
IFS="$SAVED_IFS"
GLUT_SAVED_CPPFLAGS="$CPPFLAGS" if test "$glut_headers" = "yes"; then
AC_LANG_PUSH([C])
GLUT_SAVED_LDFLAGS="$LDFLAGS"
SAVED_IFS="$IFS" SAVED_IFS="$IFS"
IFS="|" IFS="|"
for flag in $glut_cflags; do for flag in $glut_ldflags; do
# restore IFS immediately as the autoconf macros may need the default. # restore IFS immediately as the autoconf macros may need the default.
IFS="$SAVED_IFS" IFS="$SAVED_IFS"
unset ac_cv_header_GL_glut_h unset ac_cv_search_glBegin
unset ac_cv_header_OpenGL_glut_h
case $flag in case $flag in
none) ;; none) ;;
*) CPPFLAGS="$flag $CPPFLAGS";; *) LDFLAGS="$flag $LDFLAGS";;
esac esac
AC_CHECK_HEADERS([GL/glut.h GLUT/glut.h OpenGL/glut.h],
[glut_headers=yes; # find libGL
test "$flag" = "none" || GL_INCLUDES="$CPPFLAGS"; GL_SAVED_LIBS="$LIBS"
break]) AC_SEARCH_LIBS([glBegin], [GL OpenGL])
CPPFLAGS="$GLUT_SAVED_CPPFLAGS" LIBS="$GL_SAVED_LIBS"
test "$glut_headers" = "yes" && break
# A direct link to libGL may not be necessary on e.g., linux.
GLUT_SAVED_LIBS="$LIBS"
for lib in "" "-lglut" "-lglut $ac_cv_search_glBegin"; do
LIBS="$lib"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([
#ifdef __cplusplus
# define EXTERN_C extern "C"
#else
# define EXTERN_C
#endif
EXTERN_C char glOrtho();
EXTERN_C char glutMainLoop();
],[
glOrtho();
glutMainLoop();
])
],
[glut_support=yes], []
)
if test "$glut_support" = "yes"; then
GL_LIBS="$LDFLAGS $lib"
break
fi
done
LIBS="$GLUT_SAVED_LIBS"
LDFLAGS="$GLUT_SAVED_LDFLAGS"
test "$glut_support" = "yes" && break
done done
IFS="$SAVED_IFS" IFS="$SAVED_IFS"
AC_LANG_POP
fi
if test "$glut_headers" = "yes"; then LIBCHECK_EPILOGUE([GL])
AC_LANG_PUSH([C])
GLUT_SAVED_LDFLAGS="$LDFLAGS"
SAVED_IFS="$IFS"
IFS="|"
for flag in $glut_ldflags; do
# restore IFS immediately as the autoconf macros may need the default.
IFS="$SAVED_IFS"
unset ac_cv_search_glBegin
case $flag in if test "$glut_support" = "yes" -a "$enable_libwebpdemux" = "yes"; then
none) ;; build_vwebp=yes
*) LDFLAGS="$flag $LDFLAGS";; fi
esac
# find libGL
GL_SAVED_LIBS="$LIBS"
AC_SEARCH_LIBS([glBegin], [GL OpenGL opengl32])
LIBS="$GL_SAVED_LIBS"
# A direct link to libGL may not be necessary on e.g., linux.
GLUT_SAVED_LIBS="$LIBS"
for lib in "" "-lglut" "-lglut $ac_cv_search_glBegin"; do
LIBS="$lib"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([
#ifdef __cplusplus
# define EXTERN_C extern "C"
#else
# define EXTERN_C
#endif
EXTERN_C char glOrtho();
EXTERN_C char glutMainLoop();
],[
glOrtho();
glutMainLoop();
])
],
AC_DEFINE(WEBP_HAVE_GL, [1],
[Set to 1 if OpenGL is supported])
[glut_support=yes], []
)
if test "$glut_support" = "yes"; then
GL_LIBS="$LDFLAGS $lib"
break
fi
done
LIBS="$GLUT_SAVED_LIBS"
LDFLAGS="$GLUT_SAVED_LDFLAGS"
test "$glut_support" = "yes" && break
done
IFS="$SAVED_IFS"
AC_LANG_POP
fi
LIBCHECK_EPILOGUE([GL])
if test "$glut_support" = "yes" -a "$enable_libwebpdemux" = "yes"; then
build_vwebp=yes
fi
])
AM_CONDITIONAL([BUILD_VWEBP], [test "$build_vwebp" = "yes"]) AM_CONDITIONAL([BUILD_VWEBP], [test "$build_vwebp" = "yes"])
dnl === check for PNG support === dnl === check for PNG support ===
AC_ARG_ENABLE([png], AS_HELP_STRING([--disable-png], CLEAR_LIBVARS([PNG])
[Disable detection of PNG format support AC_PATH_PROGS(LIBPNG_CONFIG,
@<:@default=auto@:>@])) [libpng-config libpng15-config libpng14-config libpng12-config])
AS_IF([test "x$enable_png" != "xno"], [ if test -n "$LIBPNG_CONFIG"; then
CLEAR_LIBVARS([PNG]) PNG_INCLUDES=`$LIBPNG_CONFIG --cflags`
AC_PATH_PROGS([LIBPNG_CONFIG], PNG_PREFIX=`$LIBPNG_CONFIG --prefix`
[libpng-config libpng16-config libpng15-config libpng14-config \ if test "${PNG_PREFIX}/lib" != "/usr/lib" ; then
libpng12-config]) PNG_LIBS="-L${PNG_PREFIX}/lib"
if test -n "$LIBPNG_CONFIG"; then
PNG_INCLUDES=`$LIBPNG_CONFIG --cflags`
PNG_LIBS="`$LIBPNG_CONFIG --ldflags`"
fi fi
PNG_LIBS="$PNG_LIBS `$LIBPNG_CONFIG --libs`"
fi
WITHLIB_OPTION([png], [PNG]) WITHLIB_OPTION([png], [PNG])
LIBCHECK_PROLOGUE([PNG]) LIBCHECK_PROLOGUE([PNG])
AC_CHECK_HEADER(png.h, AC_CHECK_HEADER(png.h,
AC_SEARCH_LIBS(png_get_libpng_ver, [png], AC_SEARCH_LIBS(png_get_libpng_ver, [png],
[test "$ac_cv_search_png_get_libpng_ver" = "none required" \ [test "$ac_cv_search_png_get_libpng_ver" = "none required" \
|| PNG_LIBS="$PNG_LIBS $ac_cv_search_png_get_libpng_ver" || PNG_LIBS="$PNG_LIBS $ac_cv_search_png_get_libpng_ver"
PNG_INCLUDES="$PNG_INCLUDES -DWEBP_HAVE_PNG" PNG_INCLUDES="$PNG_INCLUDES -DWEBP_HAVE_PNG"
AC_DEFINE(WEBP_HAVE_PNG, [1], AC_DEFINE(WEBP_HAVE_PNG, [1],
[Set to 1 if PNG library is installed]) [Set to 1 if PNG library is installed])
png_support=yes png_support=yes
], ],
[AC_MSG_WARN(Optional png library not found) [AC_MSG_WARN(Optional png library not found)
PNG_LIBS="" PNG_LIBS=""
PNG_INCLUDES="" PNG_INCLUDES=""
], ],
[$MATH_LIBS]), [$MATH_LIBS]),
[AC_MSG_WARN(png library not available - no png.h) [AC_MSG_WARN(png library not available - no png.h)
PNG_LIBS="" PNG_LIBS=""
PNG_INCLUDES="" PNG_INCLUDES=""
], ],
) )
LIBCHECK_EPILOGUE([PNG]) LIBCHECK_EPILOGUE([PNG])
])
dnl === check for JPEG support === dnl === check for JPEG support ===
AC_ARG_ENABLE([jpeg], CLEAR_LIBVARS([JPEG])
AS_HELP_STRING([--disable-jpeg], WITHLIB_OPTION([jpeg], [JPEG])
[Disable detection of JPEG format support
@<:@default=auto@:>@]))
AS_IF([test "x$enable_jpeg" != "xno"], [
CLEAR_LIBVARS([JPEG])
WITHLIB_OPTION([jpeg], [JPEG])
LIBCHECK_PROLOGUE([JPEG]) LIBCHECK_PROLOGUE([JPEG])
AC_CHECK_HEADER(jpeglib.h, AC_CHECK_HEADER(jpeglib.h,
AC_CHECK_LIB(jpeg, jpeg_set_defaults, AC_CHECK_LIB(jpeg, jpeg_set_defaults,
[JPEG_LIBS="$JPEG_LIBS -ljpeg" [JPEG_LIBS="$JPEG_LIBS -ljpeg"
JPEG_INCLUDES="$JPEG_INCLUDES -DWEBP_HAVE_JPEG" JPEG_INCLUDES="$JPEG_INCLUDES -DWEBP_HAVE_JPEG"
AC_DEFINE(WEBP_HAVE_JPEG, [1], AC_DEFINE(WEBP_HAVE_JPEG, [1],
[Set to 1 if JPEG library is installed]) [Set to 1 if JPEG library is installed])
jpeg_support=yes jpeg_support=yes
], ],
AC_MSG_WARN(Optional jpeg library not found), AC_MSG_WARN(Optional jpeg library not found),
[$MATH_LIBS]), [$MATH_LIBS]),
AC_MSG_WARN(jpeg library not available - no jpeglib.h) AC_MSG_WARN(jpeg library not available - no jpeglib.h)
) )
LIBCHECK_EPILOGUE([JPEG]) LIBCHECK_EPILOGUE([JPEG])
])
dnl === check for TIFF support === dnl === check for TIFF support ===
AC_ARG_ENABLE([tiff], CLEAR_LIBVARS([TIFF])
AS_HELP_STRING([--disable-tiff], WITHLIB_OPTION([tiff], [TIFF])
[Disable detection of TIFF format support
@<:@default=auto@:>@]))
AS_IF([test "x$enable_tiff" != "xno"], [
CLEAR_LIBVARS([TIFF])
WITHLIB_OPTION([tiff], [TIFF])
LIBCHECK_PROLOGUE([TIFF]) LIBCHECK_PROLOGUE([TIFF])
AC_CHECK_HEADER(tiffio.h, AC_CHECK_HEADER(tiffio.h,
AC_CHECK_LIB(tiff, TIFFGetVersion, AC_CHECK_LIB(tiff, TIFFGetVersion,
[TIFF_LIBS="$TIFF_LIBS -ltiff" [TIFF_LIBS="$TIFF_LIBS -ltiff"
TIFF_INCLUDES="$TIFF_INCLUDES -DWEBP_HAVE_TIFF" TIFF_INCLUDES="$TIFF_INCLUDES -DWEBP_HAVE_TIFF"
AC_DEFINE(WEBP_HAVE_TIFF, [1], AC_DEFINE(WEBP_HAVE_TIFF, [1],
[Set to 1 if TIFF library is installed]) [Set to 1 if TIFF library is installed])
tiff_support=yes tiff_support=yes
], ],
AC_MSG_WARN(Optional tiff library not found), AC_MSG_WARN(Optional tiff library not found),
[$MATH_LIBS]), [$MATH_LIBS]),
AC_MSG_WARN(tiff library not available - no tiffio.h) AC_MSG_WARN(tiff library not available - no tiffio.h)
) )
LIBCHECK_EPILOGUE([TIFF]) LIBCHECK_EPILOGUE([TIFF])
])
dnl === check for GIF support === dnl === check for GIF support ===
AC_ARG_ENABLE([gif], AS_HELP_STRING([--disable-gif], CLEAR_LIBVARS([GIF])
[Disable detection of GIF format support WITHLIB_OPTION([gif], [GIF])
@<:@default=auto@:>@]))
AS_IF([test "x$enable_gif" != "xno"], [
CLEAR_LIBVARS([GIF])
WITHLIB_OPTION([gif], [GIF])
LIBCHECK_PROLOGUE([GIF]) LIBCHECK_PROLOGUE([GIF])
AC_CHECK_HEADER(gif_lib.h, AC_CHECK_HEADER(gif_lib.h,
AC_CHECK_LIB([gif], [DGifOpenFileHandle], AC_CHECK_LIB([gif], [DGifOpenFileHandle],
[GIF_LIBS="$GIF_LIBS -lgif" [GIF_LIBS="$GIF_LIBS -lgif"
AC_DEFINE(WEBP_HAVE_GIF, [1], gif_support=yes
[Set to 1 if GIF library is installed]) ],
gif_support=yes AC_MSG_WARN(Optional gif library not found),
], [$MATH_LIBS]),
AC_MSG_WARN(Optional gif library not found), AC_MSG_WARN(gif library not available - no gif_lib.h)
[$MATH_LIBS]), )
AC_MSG_WARN(gif library not available - no gif_lib.h) LIBCHECK_EPILOGUE([GIF])
)
LIBCHECK_EPILOGUE([GIF])
if test "$gif_support" = "yes" -a \ if test "$gif_support" = "yes" -a \
"$enable_libwebpdemux" = "yes"; then "$enable_libwebpmux" = "yes"; then
build_animdiff=yes build_gif2webp=yes
fi
if test "$gif_support" = "yes" -a \
"$enable_libwebpmux" = "yes"; then
build_gif2webp=yes
fi
])
AM_CONDITIONAL([BUILD_ANIMDIFF], [test "${build_animdiff}" = "yes"])
AM_CONDITIONAL([BUILD_GIF2WEBP], [test "${build_gif2webp}" = "yes"])
if test "$enable_libwebpmux" = "yes"; then
build_img2webp=yes
fi fi
AM_CONDITIONAL([BUILD_IMG2WEBP], [test "${build_img2webp}" = "yes"]) AM_CONDITIONAL([BUILD_GIF2WEBP], [test "${build_gif2webp}" = "yes"])
dnl === check for WIC support === dnl === check for WIC support ===
AC_ARG_ENABLE([wic], if test "$target_os" = "mingw32"; then
AS_HELP_STRING([--disable-wic],
[Disable Windows Imaging Component (WIC) detection.
@<:@default=auto@:>@]),,
[enable_wic=yes])
case $host_os in
mingw*)
if test "$enable_wic" = "yes"; then
AC_CHECK_HEADERS([wincodec.h shlwapi.h windows.h]) AC_CHECK_HEADERS([wincodec.h shlwapi.h windows.h])
if test "$ac_cv_header_wincodec_h" = "yes"; then if test "$ac_cv_header_wincodec_h" = "yes"; then
AC_MSG_CHECKING(for Windows Imaging Component support) AC_MSG_CHECKING(for Windows Imaging Component support)
@ -611,7 +352,6 @@ if test "$enable_wic" = "yes"; then
AC_MSG_RESULT(${wic_support-no}) AC_MSG_RESULT(${wic_support-no})
fi fi
fi fi
esac
dnl === If --enable-swap-16bit-csp is defined, add -DWEBP_SWAP_16BIT_CSP dnl === If --enable-swap-16bit-csp is defined, add -DWEBP_SWAP_16BIT_CSP
@ -663,21 +403,13 @@ AC_ARG_ENABLE([libwebpdecoder],
AC_MSG_RESULT(${enable_libwebpdecoder-no}) AC_MSG_RESULT(${enable_libwebpdecoder-no})
AM_CONDITIONAL([BUILD_LIBWEBPDECODER], [test "$enable_libwebpdecoder" = "yes"]) AM_CONDITIONAL([BUILD_LIBWEBPDECODER], [test "$enable_libwebpdecoder" = "yes"])
dnl === Check whether libwebpextras should be built
AC_MSG_CHECKING(whether libwebpextras is to be built)
AC_ARG_ENABLE([libwebpextras],
AS_HELP_STRING([--enable-libwebpextras],
[Build libwebpextras @<:@default=no@:>@]))
AC_MSG_RESULT(${enable_libwebpextras-no})
AM_CONDITIONAL([WANT_EXTRAS], [test "$enable_libwebpextras" = "yes"])
dnl ========================= dnl =========================
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([src/webp/config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \ AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \
examples/Makefile extras/Makefile imageio/Makefile \ examples/Makefile src/dec/Makefile \
src/dec/Makefile src/enc/Makefile src/dsp/Makefile \ src/enc/Makefile src/dsp/Makefile \
src/demux/Makefile src/mux/Makefile \ src/demux/Makefile src/mux/Makefile \
src/utils/Makefile \ src/utils/Makefile \
src/libwebp.pc src/libwebpdecoder.pc \ src/libwebp.pc src/libwebpdecoder.pc \
@ -692,12 +424,11 @@ WebP Configuration Summary
Shared libraries: ${enable_shared} Shared libraries: ${enable_shared}
Static libraries: ${enable_static} Static libraries: ${enable_static}
Threading support: ${enable_threading-no} Threaded decode: ${enable_threading-no}
libwebp: yes libwebp: yes
libwebpdecoder: ${enable_libwebpdecoder-no} libwebpdecoder: ${enable_libwebpdecoder-no}
libwebpdemux: ${enable_libwebpdemux-no} libwebpdemux: ${enable_libwebpdemux-no}
libwebpmux: ${enable_libwebpmux-no} libwebpmux: ${enable_libwebpmux-no}
libwebpextras: ${enable_libwebpextras-no}
Tools: Tools:
cwebp : yes cwebp : yes
@ -712,10 +443,7 @@ dwebp : yes
===================== =====================
PNG : ${png_support-no} PNG : ${png_support-no}
WIC : ${wic_support-no} WIC : ${wic_support-no}
GIF support : ${gif_support-no} gif2webp : ${build_gif2webp-no}
anim_diff : ${build_animdiff-no} webpmux : ${enable_libwebpmux-no}
gif2webp : ${build_gif2webp-no} vwebp : ${build_vwebp-no}
img2webp : ${build_img2webp-no}
webpmux : ${enable_libwebpmux-no}
vwebp : ${build_vwebp-no}
]) ])

View File

@ -46,16 +46,25 @@ for:
* **Animation.** An image may have multiple frames with pauses between them, * **Animation.** An image may have multiple frames with pauses between them,
making it an animation. making it an animation.
* **Image Fragmentation.** A single bitstream in WebP has an inherent
limitation for width or height of 2^14 pixels, and, when using VP8, a 512
KiB limit on the size of the first compressed partition. To support larger
images, the format supports images that are composed of multiple fragments,
each encoded as a separate bitstream. All fragments logically form a single
image: they have common metadata, color profile, etc. Image fragmentation
may also improve efficiency for larger images, e.g., grass can be encoded
differently than sky.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [RFC 2119][]. document are to be interpreted as described in [RFC 2119][].
Bit numbering in chunk diagrams starts at `0` for the most significant bit
('MSB 0') as described in [RFC 1166][].
**Note:** Out of the features mentioned above, lossy compression, lossless **Note:** Out of the features mentioned above, lossy compression, lossless
compression, transparency, metadata, color profile and animation are finalized compression, transparency, metadata, color profile and animation are finalized
and are to be considered stable. and are to be considered stable. On the other hand, image fragmentation is
experimental as of now, and is open to discussion, feedback and comments.
The same is indicated using annotation "_status: experimental_" in the relevant
sections of this document.
Terminology &amp; Basics Terminology &amp; Basics
------------------------ ------------------------
@ -70,7 +79,7 @@ Below are additional terms used throughout this document:
_Reader/Writer_ _Reader/Writer_
: Code that reads WebP files is referred to as a _reader_, while code that : Code that reads WebP files is referred to as a _reader_, while code that
writes them is referred to as a _writer_. writes them is referred to as a _writer_.
_uint16_ _uint16_
@ -92,12 +101,10 @@ _FourCC_
_1-based_ _1-based_
: An unsigned integer field storing values offset by `-1`. e.g., Such a field : An unsigned integer field storing values offset by `-1`. e.g., Such a field
would store value _25_ as _24_. would store value _25_ as _24_.
RIFF file format
RIFF File Format
---------------- ----------------
The WebP file format is based on the RIFF (resource interchange file format) The WebP file format is based on the RIFF (resource interchange file format)
document format. document format.
@ -137,8 +144,7 @@ _ChunkHeader('ABCD')_
chunks that apply to any RIFF file format, while FourCCs specific to a file chunks that apply to any RIFF file format, while FourCCs specific to a file
format are all lowercase. WebP does not follow this convention. format are all lowercase. WebP does not follow this convention.
WebP file header
WebP File Header
---------------- ----------------
0 1 2 3 0 1 2 3
@ -158,8 +164,8 @@ WebP File Header
File Size: 32 bits (_uint32_) File Size: 32 bits (_uint32_)
: The size of the file in bytes starting at offset 8. The maximum value of : The size of the file in bytes starting at offset 8. The maximum value of
this field is 2^32 minus 10 bytes and thus the size of the whole file is at this field is 2^32 minus 10 bytes and thus the size of the whole file is at
most 4GiB minus 2 bytes. most 4GiB minus 2 bytes.
'WEBP': 32 bits 'WEBP': 32 bits
@ -171,8 +177,7 @@ the 'WEBP' FourCC. The file SHOULD NOT contain anything after it. As the size
of any chunk is even, the size given by the RIFF header is also even. The of any chunk is even, the size given by the RIFF header is also even. The
contents of individual chunks will be described in the following sections. contents of individual chunks will be described in the following sections.
Simple file format (lossy)
Simple File Format (Lossy)
-------------------------- --------------------------
This layout SHOULD be used if the image requires _lossy_ encoding and does not This layout SHOULD be used if the image requires _lossy_ encoding and does not
@ -210,8 +215,7 @@ width and height. That is assumed to be the width and height of the canvas.
The VP8 specification describes how to decode the image into Y'CbCr The VP8 specification describes how to decode the image into Y'CbCr
format. To convert to RGB, Rec. 601 SHOULD be used. format. To convert to RGB, Rec. 601 SHOULD be used.
Simple file format (lossless)
Simple File Format (Lossless)
----------------------------- -----------------------------
**Note:** Older readers may not support files using the lossless format. **Note:** Older readers may not support files using the lossless format.
@ -249,8 +253,7 @@ The current specification of the VP8L bitstream can be found at
contains the VP8L image width and height. That is assumed to be the width contains the VP8L image width and height. That is assumed to be the width
and height of the canvas. and height of the canvas.
Extended file format
Extended File Format
-------------------- --------------------
**Note:** Older readers may not support files using the extended format. **Note:** Older readers may not support files using the extended format.
@ -271,15 +274,13 @@ An extended format file consists of:
* An optional list of [unknown chunks](#unknown-chunks). _\[status: experimental\]_ * An optional list of [unknown chunks](#unknown-chunks). _\[status: experimental\]_
For a _still image_, the _image data_ consists of a single frame, which is made For a _still image_, the _image data_ consists of a single frame, whereas for
up of: an _animated image_, it consists of multiple frames. More details about frames
can be found in the [Animation](#animation) section.
* An optional [alpha subchunk](#alpha). Moreover, each frame can be fragmented or non-fragmented, as will be described
in the [Extended WebP file header](#extended_header) section. More details about
* A [bitstream subchunk](#bitstream-vp8vp8l). fragments can be found in the [Fragments](#fragments) section.
For an _animated image_, the _image data_ consists of multiple frames. More
details about frames can be found in the [Animation](#animation) section.
All chunks SHOULD be placed in the same order as listed above. If a chunk All chunks SHOULD be placed in the same order as listed above. If a chunk
appears in the wrong place, the file is invalid, but readers MAY parse the appears in the wrong place, the file is invalid, but readers MAY parse the
@ -301,7 +302,7 @@ Extended WebP file header:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8X') | | ChunkHeader('VP8X') |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Rsv|I|L|E|X|A|R| Reserved | |Rsv|I|L|E|X|A|F| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Canvas Width Minus One | ... | Canvas Width Minus One | ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -319,7 +320,7 @@ ICC profile (I): 1 bit
Alpha (L): 1 bit Alpha (L): 1 bit
: Set if any of the frames of the image contain transparency information : Set if any of the frames of the image contain transparency information
("alpha"). ("alpha").
EXIF metadata (E): 1 bit EXIF metadata (E): 1 bit
@ -332,11 +333,11 @@ XMP metadata (X): 1 bit
Animation (A): 1 bit Animation (A): 1 bit
: Set if this is an animated image. Data in 'ANIM' and 'ANMF' chunks should be : Set if this is an animated image. Data in 'ANIM' and 'ANMF' chunks should be
used to control the animation. used to control the animation.
Reserved (R): 1 bit Image Fragmentation (F): 1 bit _\[status: experimental\]_
: SHOULD be `0`. : Set if any of the frames in the image are represented by fragments.
Reserved: 24 bits Reserved: 24 bits
@ -381,20 +382,13 @@ animation.
Background Color: 32 bits (_uint32_) Background Color: 32 bits (_uint32_)
: The default background color of the canvas in \[Blue, Green, Red, Alpha\] : The default background color of the canvas in \[Blue, Green, Red, Alpha\]
byte order. This color MAY be used to fill the unused space on the canvas byte order. This color is used to fill the unused space on the canvas around the
around the frames, as well as the transparent pixels of the first frame. frames, as well as the transparent pixels of the first frame. Background color
Background color is also used when disposal method is `1`. is also used when disposal method is `1`.
**Note**: **Note**: Viewers that have a preferred background against which to present the
images (web browsers, for example) should ignore this value and use their
* Background color MAY contain a transparency value (alpha), even if the preferred background color instead.
_Alpha_ flag in [VP8X chunk](#extended_header) is unset.
* Viewer applications SHOULD treat the background color value as a hint, and
are not required to use it.
* The canvas is cleared at the start of each loop. The background color MAY be
used to achieve this.
Loop Count: 16 bits (_uint16_) Loop Count: 16 bits (_uint16_)
@ -404,6 +398,7 @@ This chunk MUST appear if the _Animation_ flag in the VP8X chunk is set.
If the _Animation_ flag is not set and this chunk is present, it If the _Animation_ flag is not set and this chunk is present, it
SHOULD be ignored. SHOULD be ignored.
ANMF chunk: ANMF chunk:
For animated images, this chunk contains information about a _single_ frame. For animated images, this chunk contains information about a _single_ frame.
@ -420,7 +415,7 @@ If the _Animation flag_ is not set, then this chunk SHOULD NOT be present.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
... | Frame Height Minus One | ... | Frame Height Minus One |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame Duration | Reserved |B|D| | Frame Duration | Reserved |D|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Frame Data | | Frame Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -446,37 +441,27 @@ Frame Height Minus One: 24 bits (_uint24_)
Frame Duration: 24 bits (_uint24_) Frame Duration: 24 bits (_uint24_)
: The time to wait before displaying the next frame, in 1 millisecond units. : The time to wait before displaying the next frame, in 1 millisecond units.
In particular, frame duration of 0 is useful when one wants to update In particular, frame duration of 0 is useful when one wants to update multiple
multiple areas of the canvas at once during the animation. areas of the canvas at once during the animation.
Reserved: 6 bits Reserved: 7 bits
: SHOULD be 0. : SHOULD be 0.
Blending method (B): 1 bit
: Indicates how transparent pixels of _the current frame_ are to be blended
with corresponding pixels of the previous canvas:
* `0`: Use alpha blending. After disposing of the previous frame, render the
current frame on the canvas using [alpha-blending](#alpha-blending). If
the current frame does not have an alpha channel, assume alpha value of
255, effectively replacing the rectangle.
* `1`: Do not blend. After disposing of the previous frame, render the
current frame on the canvas by overwriting the rectangle covered by the
current frame.
Disposal method (D): 1 bit Disposal method (D): 1 bit
: Indicates how _the current frame_ is to be treated after it has been : Indicates how _the current frame_ is to be treated after it has been displayed
displayed (before rendering the next frame) on the canvas: (before rendering the next frame) on the canvas:
* `0`: Do not dispose. Leave the canvas as is. * `0`: Do not dispose. Leave the canvas as is.
* `1`: Dispose to background color. Fill the _rectangle_ on the canvas * `1`: Dispose to background color. Fill the _rectangle_ on the canvas covered
covered by the _current frame_ with background color specified in the by the _current frame_ with background color specified in the
[ANIM chunk](#anim_chunk). [ANIM chunk](#anim_chunk).
After disposing the current frame, render the next frame on the canvas using
[alpha-blending](#alpha-blending). If the next frame does not have an alpha
channel, assume alpha value of 255, effectively replacing the rectangle.
**Notes**: **Notes**:
@ -507,7 +492,9 @@ Disposal method (D): 1 bit
Frame Data: _Chunk Size_ - `16` bytes Frame Data: _Chunk Size_ - `16` bytes
: Consists of: : For a fragmented frame, it consists of multiple [fragment chunks](#fragments).
: For a non-fragmented frame, it consists of:
* An optional [alpha subchunk](#alpha) for the frame. * An optional [alpha subchunk](#alpha) for the frame.
@ -518,6 +505,49 @@ Frame Data: _Chunk Size_ - `16` bytes
**Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual **Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual
_padded_ chunks as described by the [RIFF file format](#riff-file-format). _padded_ chunks as described by the [RIFF file format](#riff-file-format).
#### Fragments _\[status: experimental\]_
For images that are represented by fragments, this chunk contains data for
a single fragment. If the _Image Fragmentation Flag_ is not set, then this chunk
SHOULD NOT be present.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('FRGM') |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Fragment X | ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
... Fragment Y | Fragment Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Fragment X: 24 bits (_uint24_)
: The X coordinate of the upper left corner of the fragment is `Fragment X * 2`
Fragment Y: 24 bits (_uint24_)
: The Y coordinate of the upper left corner of the fragment is `Fragment Y * 2`
Fragment Data: _Chunk Size_ - `6` bytes
: It contains:
* An optional [alpha subchunk](#alpha) for the fragment.
* The [bitstream subchunk](#bitstream-vp8vp8l) for the fragment.
* An optional list of [unknown chunks](#unknown-chunks).
Note: The width and height of the fragment is obtained from the bitstream
subchunk.
The fragments of a frame SHOULD have the following properties:
* They collectively cover the whole frame.
* No pair of fragments have any overlapping region on the frame.
* No portion of any fragment should be located outside of the canvas.
#### Alpha #### Alpha
0 1 2 3 0 1 2 3
@ -535,20 +565,20 @@ Reserved (Rsv): 2 bits
Pre-processing (P): 2 bits Pre-processing (P): 2 bits
: These INFORMATIVE bits are used to signal the pre-processing that has : These INFORMATIVE bits are used to signal the pre-processing that has
been performed during compression. The decoder can use this information to been performed during compression. The decoder can use this information to
e.g. dither the values or smooth the gradients prior to display. e.g. dither the values or smooth the gradients prior to display.
* `0`: no pre-processing * `0`: no pre-processing
* `1`: level reduction * `1`: level reduction
Filtering method (F): 2 bits Filtering method (F): 2 bits
: The filtering method used: : The filtering method used:
* `0`: None. * `0`: None.
* `1`: Horizontal filter. * `1`: Horizontal filter.
* `2`: Vertical filter. * `2`: Vertical filter.
* `3`: Gradient filter. * `3`: Gradient filter.
For each pixel, filtering is performed using the following calculations. For each pixel, filtering is performed using the following calculations.
Assume the alpha values surrounding the current `X` position are labeled as: Assume the alpha values surrounding the current `X` position are labeled as:
@ -592,15 +622,15 @@ Compression method (C): 2 bits
: The compression method used: : The compression method used:
* `0`: No compression. * `0`: No compression.
* `1`: Compressed using the WebP lossless format. * `1`: Compressed using the WebP lossless format.
Alpha bitstream: _Chunk Size_ - `1` bytes Alpha bitstream: _Chunk Size_ - `1` bytes
: Encoded alpha bitstream. : Encoded alpha bitstream.
This optional chunk contains encoded alpha data for this frame. A frame This optional chunk contains encoded alpha data for this frame/fragment. A
containing a 'VP8L' chunk SHOULD NOT contain this chunk. frame/fragment containing a 'VP8L' chunk SHOULD NOT contain this chunk.
**Rationale**: The transparency information is already part of the 'VP8L' **Rationale**: The transparency information is already part of the 'VP8L'
chunk. chunk.
@ -631,15 +661,15 @@ compression method is '0') or compressed using the lossless format
#### Bitstream (VP8/VP8L) #### Bitstream (VP8/VP8L)
This chunk contains compressed bitstream data for a single frame. This chunk contains compressed bitstream data for a single frame/fragment.
A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the
significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using
"VP8L" as its tag. "VP8L" as its tag.
The formats of VP8 and VP8L chunks are as described in sections The formats of VP8 and VP8L chunks are as described in sections
[Simple File Format (Lossy)](#simple-file-format-lossy) [Simple file format (lossy)](#simple-file-format-lossy)
and [Simple File Format (Lossless)](#simple-file-format-lossless) respectively. and [Simple file format (lossless)](#simple-file-format-lossless) respectively.
#### Color profile #### Color profile
@ -687,6 +717,7 @@ EXIF Metadata: _Chunk Size_ bytes
: image metadata in EXIF format. : image metadata in EXIF format.
XMP chunk: XMP chunk:
0 1 2 3 0 1 2 3
@ -717,17 +748,47 @@ A file MAY contain unknown chunks:
* At the end of the file as described in [Extended WebP file * At the end of the file as described in [Extended WebP file
header](#extended_header) section. header](#extended_header) section.
* At the end of ANMF chunks as described in the * At the end of FRGM and ANMF chunks as described in [Fragments](#fragments)
[Animation](#animation) section. and [Animation](#animation) sections.
Readers SHOULD ignore these chunks. Writers SHOULD preserve them in their Readers SHOULD ignore these chunks. Writers SHOULD preserve them in their
original order (unless they specifically intend to modify these chunks). original order (unless they specifically intend to modify these chunks).
### Assembling the Canvas from frames ### Assembling the Canvas from fragments/frames
Here we provide an overview of how a reader should assemble a canvas in the Here we provide an overview of how a reader should assemble a canvas in case
case of an animated image. The notation _VP8X.field_ means the field in the of a fragmented-image and in case of an animated image. The notation
'VP8X' chunk with the same description. _VP8X.field_ means the field in the 'VP8X' chunk with the same description.
Displaying a _fragmented image_ canvas MUST be equivalent to the following
pseudocode: _\[status: experimental\]_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
assert VP8X.flags.hasFragments
canvas ← new black image of size VP8X.canvasWidth x VP8X.canvasHeight.
frgm_params ← nil
for chunk in image_data:
assert chunk.tag is "FRGM"
frgm_params.fragmentX = Fragment X
frgm_params.fragmentY = Fragment Y
for subchunk in 'Fragment Data':
if subchunk.tag == "ALPH":
assert alpha subchunks not found in 'Fragment Data' earlier
frgm_params.alpha = alpha_data
else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
assert bitstream subchunks not found in 'Fragment Data' earlier
frgm_params.bitstream = bitstream_data
frgm_params.fragmentWidth = Width extracted from bitstream subchunk
frgm_params.fragmentHeight = Height extracted from bitstream subchunk
assert VP8X.canvasWidth >=
frgm_params.fragmentX + frgm_params.fragmentWidth
assert VP8X.canvasHeight >=
frgm_params.fragmentY + frgm_params.fragmentHeight
assert fragment has the properties mentioned in "Image Fragments" section.
render fragment with frame_params.alpha and frame_params.bitstream on canvas
with top-left corner in (frgm_params.fragmentX, frgm_params.fragmentY).
canvas contains the decoded canvas.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Displaying an _animated image_ canvas MUST be equivalent to the following Displaying an _animated image_ canvas MUST be equivalent to the following
pseudocode: pseudocode:
@ -735,25 +796,28 @@ pseudocode:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
assert VP8X.flags.hasAnimation assert VP8X.flags.hasAnimation
canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
background color ANIM.background_color. background color ANIM.background_color.
loop_count ← ANIM.loopCount loop_count ← ANIM.loopCount
dispose_method ← ANIM.disposeMethod dispose_method ← ANIM.disposeMethod
if loop_count == 0: if loop_count == 0:
loop_count = ∞ loop_count = ∞
frame_params ← nil frame_params ← nil
assert next chunk in image_data is ANMF for loop = 0, ..., loop_count - 1
for loop = 0..loop_count - 1 assert next chunk in image_data is ANMF
clear canvas to ANIM.background_color or application defined color frame_params.frameX = Frame X
until eof or non-ANMF chunk frame_params.frameY = Frame Y
frame_params.frameX = Frame X frame_params.frameWidth = Frame Width Minus One + 1
frame_params.frameY = Frame Y frame_params.frameHeight = Frame Height Minus One + 1
frame_params.frameWidth = Frame Width Minus One + 1 frame_params.frameDuration = Frame Duration
frame_params.frameHeight = Frame Height Minus One + 1 assert VP8X.canvasWidth >= frame_params.frameX + frame_params.frameWidth
frame_params.frameDuration = Frame Duration assert VP8X.canvasHeight >= frame_params.frameY + frame_params.frameHeight
frame_right = frame_params.frameX + frame_params.frameWidth if VP8X.flags.hasFragments and first subchunk in 'Frame Data' is FRGM
frame_bottom = frame_params.frameY + frame_params.frameHeight // Fragmented frame.
assert VP8X.canvasWidth >= frame_right frame_params.{bitstream,alpha} = canvas decoded from subchunks in
assert VP8X.canvasHeight >= frame_bottom 'Frame Data' as per the pseudocode for
_fragmented image_ above.
else
// Non-fragmented frame.
for subchunk in 'Frame Data': for subchunk in 'Frame Data':
if subchunk.tag == "ALPH": if subchunk.tag == "ALPH":
assert alpha subchunks not found in 'Frame Data' earlier assert alpha subchunks not found in 'Frame Data' earlier
@ -761,15 +825,14 @@ for loop = 0..loop_count - 1
else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L": else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
assert bitstream subchunks not found in 'Frame Data' earlier assert bitstream subchunks not found in 'Frame Data' earlier
frame_params.bitstream = bitstream_data frame_params.bitstream = bitstream_data
render frame with frame_params.alpha and frame_params.bitstream on render frame with frame_params.alpha and frame_params.bitstream on canvas
canvas with top-left corner at (frame_params.frameX, with top-left corner in (frame_params.frameX, frame_params.frameY), using
frame_params.frameY), using dispose method dispose_method. dispose method dispose_method.
canvas contains the decoded image. Show the contents of the image for frame_params.frameDuration * 1ms.
Show the contents of the canvas for frame_params.frameDuration * 1ms. canvas contains the decoded canvas.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Example file layouts
Example File Layouts
-------------------- --------------------
A lossy encoded image with alpha may look as follows: A lossy encoded image with alpha may look as follows:
@ -801,6 +864,17 @@ RIFF/WEBP
+- XMP (metadata) +- XMP (metadata)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A fragmented image may look as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RIFF/WEBP
+- VP8X (descriptions of features used)
+- FRGM (fragment1 parameters + data)
+- FRGM (fragment2 parameters + data)
+- FRGM (fragment3 parameters + data)
+- FRGM (fragment4 parameters + data)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An animated image with EXIF metadata may look as follows: An animated image with EXIF metadata may look as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -815,8 +889,7 @@ RIFF/WEBP
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[vp8spec]: http://tools.ietf.org/html/rfc6386 [vp8spec]: http://tools.ietf.org/html/rfc6386
[webpllspec]: https://chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt [webpllspec]: https://gerrit.chromium.org/gerrit/gitweb?p=webm/libwebp.git;a=blob;f=doc/webp-lossless-bitstream-spec.txt;hb=master
[iccspec]: http://www.color.org/icc_specs2.xalter [iccspec]: http://www.color.org/icc_specs2.xalter
[metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf [metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
[rfc 1166]: http://tools.ietf.org/html/rfc1166
[rfc 2119]: http://tools.ietf.org/html/rfc2119 [rfc 2119]: http://tools.ietf.org/html/rfc2119

View File

@ -14,7 +14,6 @@ Specification for WebP Lossless Bitstream
_Jyrki Alakuijala, Ph.D., Google, Inc., 2012-06-19_ _Jyrki Alakuijala, Ph.D., Google, Inc., 2012-06-19_
Paragraphs marked as \[AMENDED\] were amended on 2014-09-16.
Abstract Abstract
-------- --------
@ -173,8 +172,8 @@ It should be set to 0 when all alpha values are 255 in the picture, and
int alpha_is_used = ReadBits(1); int alpha_is_used = ReadBits(1);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The version_number is a 3 bit code that must be set to 0. Any other value The version_number is a 3 bit code that must be discarded by the decoder
should be treated as an error. \[AMENDED\] at this time. Complying encoders write a 3-bit value 0.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int version_number = ReadBits(3); int version_number = ReadBits(3);
@ -331,7 +330,7 @@ uint32 Select(uint32 L, uint32 T, uint32 TL) {
abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T)); abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
// Return either left or top, the one closer to the prediction. // Return either left or top, the one closer to the prediction.
if (pL < pT) { // \[AMENDED\] if (pL <= pT) {
return L; return L;
} else { } else {
return T; return T;
@ -429,11 +428,6 @@ 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 The multiplication is to be done using more precision (with at least
16-bit dynamics). The sign extension property of the shift operation 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, does not matter here: only the lowest 8 bits are used from the result,
@ -548,9 +542,6 @@ color.
argb = color_table[GREEN(argb)]; argb = color_table[GREEN(argb)];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the index is equal or larger than color_table_size, the argb color value
should be set to 0x00000000 (transparent black). \[AMENDED\]
When the color table is small (equal to or less than 16 colors), several When the color table is small (equal to or less than 16 colors), several
pixels are bundled into a single pixel. The pixel bundling packs several pixels are bundled into a single pixel. The pixel bundling packs several
(2, 4, or 8) pixels into a single pixel, reducing the image width (2, 4, or 8) pixels into a single pixel, reducing the image width

View File

@ -1,82 +0,0 @@
LOCAL_PATH := $(call my-dir)
################################################################################
# libexample_util
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
example_util.c \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
LOCAL_MODULE := example_util
include $(BUILD_STATIC_LIBRARY)
################################################################################
# cwebp
include $(CLEAR_VARS)
# Note: to enable jpeg/png encoding the sources from AOSP can be used with
# minor modification to their Android.mk files.
LOCAL_SRC_FILES := \
cwebp.c \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webp
LOCAL_MODULE := cwebp
include $(BUILD_EXECUTABLE)
################################################################################
# dwebp
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
dwebp.c \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
LOCAL_STATIC_LIBRARIES := example_util imagedec imageenc webp
LOCAL_MODULE := dwebp
include $(BUILD_EXECUTABLE)
################################################################################
# webpmux
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
webpmux.c \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
LOCAL_STATIC_LIBRARIES := example_util imageio_util webpmux webp
LOCAL_MODULE := webpmux_example
include $(BUILD_EXECUTABLE)
################################################################################
# img2webp
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
img2webp.c \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpmux webp
LOCAL_MODULE := img2webp_example
include $(BUILD_EXECUTABLE)

View File

@ -1,75 +1,52 @@
AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src AM_CPPFLAGS = -I$(top_srcdir)/src
bin_PROGRAMS = dwebp cwebp bin_PROGRAMS = dwebp cwebp
if BUILD_ANIMDIFF if BUILD_VWEBP
noinst_PROGRAMS = anim_diff bin_PROGRAMS += vwebp
endif
if BUILD_GIF2WEBP
bin_PROGRAMS += gif2webp
endif
if BUILD_IMG2WEBP
bin_PROGRAMS += img2webp
endif endif
if WANT_MUX if WANT_MUX
bin_PROGRAMS += webpmux bin_PROGRAMS += webpmux
endif endif
if BUILD_VWEBP
bin_PROGRAMS += vwebp if BUILD_GIF2WEBP
bin_PROGRAMS += gif2webp
endif endif
noinst_LTLIBRARIES = libexample_util.la noinst_LTLIBRARIES = libexampleutil.la
libexample_util_la_SOURCES = example_util.c example_util.h libexampleutil_la_SOURCES = example_util.c example_util.h
libexample_util_la_LIBADD = ../src/libwebp.la
anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h
anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GIF_INCLUDES)
anim_diff_LDADD = ../src/demux/libwebpdemux.la
anim_diff_LDADD += libexample_util.la ../imageio/libimageio_util.la
anim_diff_LDADD += $(GIF_LIBS) -lm
cwebp_SOURCES = cwebp.c stopwatch.h
cwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
cwebp_LDADD = libexample_util.la ../imageio/libimageio_util.la
cwebp_LDADD += ../imageio/libimagedec.la ../src/libwebp.la
cwebp_LDADD += $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS)
dwebp_SOURCES = dwebp.c stopwatch.h dwebp_SOURCES = dwebp.c stopwatch.h
dwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) dwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES) dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES)
dwebp_LDADD = libexample_util.la dwebp_LDADD = libexampleutil.la $(PNG_LIBS) $(JPEG_LIBS)
dwebp_LDADD += ../imageio/libimagedec.la
dwebp_LDADD += ../imageio/libimageenc.la
dwebp_LDADD += ../imageio/libimageio_util.la
dwebp_LDADD += ../src/libwebp.la
dwebp_LDADD +=$(PNG_LIBS) $(JPEG_LIBS)
gif2webp_SOURCES = gif2webp.c gifdec.c gifdec.h cwebp_SOURCES = cwebp.c metadata.c metadata.h stopwatch.h
cwebp_SOURCES += jpegdec.c jpegdec.h
cwebp_SOURCES += pngdec.c pngdec.h
cwebp_SOURCES += tiffdec.c tiffdec.h
cwebp_SOURCES += wicdec.c wicdec.h
cwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
cwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
cwebp_LDADD = ../src/libwebp.la $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS)
gif2webp_SOURCES = gif2webp.c
gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GIF_INCLUDES) gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GIF_INCLUDES)
gif2webp_LDADD = libexample_util.la ../imageio/libimageio_util.la gif2webp_LDADD = libexampleutil.la ../src/mux/libwebpmux.la ../src/libwebp.la
gif2webp_LDADD += ../src/mux/libwebpmux.la ../src/libwebp.la $(GIF_LIBS) gif2webp_LDADD += $(GIF_LIBS)
vwebp_SOURCES = vwebp.c
vwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GL_INCLUDES)
vwebp_LDADD = libexample_util.la ../imageio/libimageio_util.la
vwebp_LDADD += ../src/demux/libwebpdemux.la $(GL_LIBS)
webpmux_SOURCES = webpmux.c webpmux_SOURCES = webpmux.c
webpmux_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) webpmux_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
webpmux_LDADD = libexample_util.la ../imageio/libimageio_util.la webpmux_LDADD = libexampleutil.la ../src/mux/libwebpmux.la ../src/libwebp.la
webpmux_LDADD += ../src/mux/libwebpmux.la ../src/libwebp.la
img2webp_SOURCES = img2webp.c vwebp_SOURCES = vwebp.c
img2webp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) vwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GL_INCLUDES)
img2webp_LDADD = libexample_util.la ../imageio/libimageio_util.la vwebp_LDADD = libexampleutil.la ../src/demux/libwebpdemux.la $(GL_LIBS)
img2webp_LDADD += ../imageio/libimagedec.la
img2webp_LDADD += ../src/mux/libwebpmux.la ../src/libwebp.la
img2webp_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS)
if BUILD_LIBWEBPDECODER if BUILD_LIBWEBPDECODER
anim_diff_LDADD += ../src/libwebpdecoder.la dwebp_LDADD += ../src/libwebpdecoder.la
vwebp_LDADD += ../src/libwebpdecoder.la vwebp_LDADD += ../src/libwebpdecoder.la
else else
anim_diff_LDADD += ../src/libwebp.la dwebp_LDADD += ../src/libwebp.la
vwebp_LDADD += ../src/libwebp.la vwebp_LDADD += ../src/libwebp.la
endif endif

View File

@ -1,303 +0,0 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Checks if given pair of animated GIF/WebP images are identical:
// That is: their reconstructed canvases match pixel-by-pixel and their other
// animation properties (loop count etc) also match.
//
// example: anim_diff foo.gif bar.webp
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h> // for 'strtod'.
#include <string.h> // for 'strcmp'.
#include "./anim_util.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
// Returns true if 'a + b' will overflow.
static int AdditionWillOverflow(int a, int b) {
return (b > 0) && (a > INT_MAX - b);
}
static int FramesAreEqual(const uint8_t* const rgba1,
const uint8_t* const rgba2, int width, int height) {
const int stride = width * 4; // Always true for 'DecodedFrame.rgba'.
return !memcmp(rgba1, rgba2, stride * height);
}
static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
int max_allowed_diff) {
const int src_a = (src >> 24) & 0xff;
const int src_r = (src >> 16) & 0xff;
const int src_g = (src >> 8) & 0xff;
const int src_b = (src >> 0) & 0xff;
const int dst_a = (dst >> 24) & 0xff;
const int dst_r = (dst >> 16) & 0xff;
const int dst_g = (dst >> 8) & 0xff;
const int dst_b = (dst >> 0) & 0xff;
return (abs(src_r * src_a - dst_r * dst_a) <= (max_allowed_diff * 255)) &&
(abs(src_g * src_a - dst_g * dst_a) <= (max_allowed_diff * 255)) &&
(abs(src_b * src_a - dst_b * dst_a) <= (max_allowed_diff * 255)) &&
(abs(src_a - dst_a) <= max_allowed_diff);
}
static int FramesAreSimilar(const uint8_t* const rgba1,
const uint8_t* const rgba2,
int width, int height, int max_allowed_diff) {
int i, j;
assert(max_allowed_diff > 0);
for (j = 0; j < height; ++j) {
for (i = 0; i < width; ++i) {
const int stride = width * 4;
const size_t offset = j * stride + i;
if (!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) {
return 0;
}
}
}
return 1;
}
// Minimize number of frames by combining successive frames that have at max
// 'max_diff' difference per channel between corresponding pixels.
static void MinimizeAnimationFrames(AnimatedImage* const img, int max_diff) {
uint32_t i;
for (i = 1; i < img->num_frames; ++i) {
DecodedFrame* const frame1 = &img->frames[i - 1];
DecodedFrame* const frame2 = &img->frames[i];
const uint8_t* const rgba1 = frame1->rgba;
const uint8_t* const rgba2 = frame2->rgba;
int should_merge_frames = 0;
// If merging frames will result in integer overflow for 'duration',
// skip merging.
if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue;
if (max_diff > 0) {
should_merge_frames = FramesAreSimilar(rgba1, rgba2, img->canvas_width,
img->canvas_height, max_diff);
} else {
should_merge_frames =
FramesAreEqual(rgba1, rgba2, img->canvas_width, img->canvas_height);
}
if (should_merge_frames) { // Merge 'i+1'th frame into 'i'th frame.
frame1->duration += frame2->duration;
if (i + 1 < img->num_frames) {
memmove(&img->frames[i], &img->frames[i + 1],
(img->num_frames - i - 1) * sizeof(*img->frames));
}
--img->num_frames;
--i;
}
}
}
static int CompareValues(uint32_t a, uint32_t b, const char* output_str) {
if (a != b) {
fprintf(stderr, "%s: %d vs %d\n", output_str, a, b);
return 0;
}
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,
const AnimatedImage* const img2,
int premultiply,
double min_psnr) {
int ok = 1;
const int is_multi_frame_image = (img1->num_frames > 1);
uint32_t i;
ok = CompareValues(img1->canvas_width, img2->canvas_width,
"Canvas width mismatch") && ok;
ok = CompareValues(img1->canvas_height, img2->canvas_height,
"Canvas height mismatch") && ok;
ok = CompareValues(img1->num_frames, img2->num_frames,
"Frame count mismatch") && ok;
if (!ok) return 0; // These are fatal failures, can't proceed.
if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
ok = CompareValues(img1->loop_count, img2->loop_count,
"Loop count mismatch") && ok;
ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
premultiply) && ok;
}
for (i = 0; i < img1->num_frames; ++i) {
// Pixel-by-pixel comparison.
const uint8_t* const rgba1 = img1->frames[i].rgba;
const uint8_t* const rgba2 = img2->frames[i].rgba;
int max_diff;
double psnr;
if (is_multi_frame_image) { // Check relevant for multi-frame images only.
const char format[] = "Frame #%d, duration mismatch";
char tmp[sizeof(format) + 8];
ok = ok && (snprintf(tmp, sizeof(tmp), format, i) >= 0);
ok = ok && CompareValues(img1->frames[i].duration,
img2->frames[i].duration, tmp);
}
GetDiffAndPSNR(rgba1, rgba2, img1->canvas_width, img1->canvas_height,
premultiply, &max_diff, &psnr);
if (min_psnr > 0.) {
if (psnr < min_psnr) {
fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i,
psnr, min_psnr);
ok = 0;
}
} else {
if (max_diff != 0) {
fprintf(stderr, "Frame #%d, max pixel diff: %d\n", i, max_diff);
ok = 0;
}
}
}
return ok;
}
static void Help(void) {
printf("Usage: anim_diff <image1> <image2> [options]\n");
printf("\nOptions:\n");
printf(" -dump_frames <folder> dump decoded frames in PAM format\n");
printf(" -min_psnr <float> ... minimum per-frame PSNR\n");
printf(" -raw_comparison ..... if this flag is not used, RGB is\n");
printf(" premultiplied before comparison\n");
#ifdef WEBP_EXPERIMENTAL_FEATURES
printf(" -max_diff <int> ..... maximum allowed difference per channel "
" between corresponding pixels in subsequent"
" frames\n");
#endif
}
int main(int argc, const char* argv[]) {
int return_code = -1;
int dump_frames = 0;
const char* dump_folder = NULL;
double min_psnr = 0.;
int got_input1 = 0;
int got_input2 = 0;
int premultiply = 1;
int max_diff = 0;
int i, c;
const char* files[2] = { NULL, NULL };
AnimatedImage images[2];
if (argc < 3) {
Help();
return -1;
}
for (c = 1; c < argc; ++c) {
int parse_error = 0;
if (!strcmp(argv[c], "-dump_frames")) {
if (c < argc - 1) {
dump_frames = 1;
dump_folder = argv[++c];
} else {
parse_error = 1;
}
} else if (!strcmp(argv[c], "-min_psnr")) {
if (c < argc - 1) {
const char* const v = argv[++c];
char* end = NULL;
const double d = strtod(v, &end);
if (end == v) {
parse_error = 1;
fprintf(stderr, "Error! '%s' is not a floating point number.\n", v);
}
min_psnr = d;
} else {
parse_error = 1;
}
} else if (!strcmp(argv[c], "-raw_comparison")) {
premultiply = 0;
#ifdef WEBP_EXPERIMENTAL_FEATURES
} else if (!strcmp(argv[c], "-max_diff")) {
if (c < argc - 1) {
const char* const v = argv[++c];
char* end = NULL;
const int n = (int)strtol(v, &end, 10);
if (end == v) {
parse_error = 1;
fprintf(stderr, "Error! '%s' is not an integer.\n", v);
}
max_diff = n;
} else {
parse_error = 1;
}
#endif
} else {
if (!got_input1) {
files[0] = argv[c];
got_input1 = 1;
} else if (!got_input2) {
files[1] = argv[c];
got_input2 = 1;
} else {
parse_error = 1;
}
}
if (parse_error) {
Help();
return -1;
}
}
if (!got_input2) {
Help();
return -1;
}
if (dump_frames) {
printf("Dumping decoded frames in: %s\n", dump_folder);
}
memset(images, 0, sizeof(images));
for (i = 0; i < 2; ++i) {
printf("Decoding file: %s\n", files[i]);
if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) {
fprintf(stderr, "Error decoding file: %s\n Aborting.\n", files[i]);
return_code = -2;
goto End;
} else {
MinimizeAnimationFrames(&images[i], max_diff);
}
}
if (!CompareAnimatedImagePair(&images[0], &images[1],
premultiply, min_psnr)) {
fprintf(stderr, "\nFiles %s and %s differ.\n", files[0], files[1]);
return_code = -3;
} else {
printf("\nFiles %s and %s are identical.\n", files[0], files[1]);
return_code = 0;
}
End:
ClearAnimatedImage(&images[0]);
ClearAnimatedImage(&images[1]);
return return_code;
}

View File

@ -1,773 +0,0 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Utilities for animated images
#include "./anim_util.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#ifdef WEBP_HAVE_GIF
#include <gif_lib.h>
#endif
#include "webp/format_constants.h"
#include "webp/decode.h"
#include "webp/demux.h"
#include "../imageio/imageio_util.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
static const int kNumChannels = 4;
// -----------------------------------------------------------------------------
// Common utilities.
// Returns true if the frame covers the full canvas.
static int IsFullFrame(int width, int height,
int canvas_width, int canvas_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;
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);
free(frames);
return 0;
}
free(image->raw_mem);
image->num_frames = num_frames;
image->frames = frames;
for (i = 0; i < num_frames; ++i) {
frames[i].rgba = mem + i * rgba_size;
frames[i].duration = 0;
frames[i].is_key_frame = 0;
}
image->raw_mem = mem;
return 1;
}
void ClearAnimatedImage(AnimatedImage* const image) {
if (image != NULL) {
free(image->raw_mem);
free(image->frames);
image->num_frames = 0;
image->frames = NULL;
image->raw_mem = NULL;
}
}
// Clear the canvas to transparent.
static void ZeroFillCanvas(uint8_t* rgba,
uint32_t canvas_width, uint32_t canvas_height) {
memset(rgba, 0, canvas_width * kNumChannels * canvas_height);
}
// Clear given frame rectangle to transparent.
static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset,
int y_offset, int width, int height) {
int j;
assert(width * kNumChannels <= rgba_stride);
rgba += y_offset * rgba_stride + x_offset * kNumChannels;
for (j = 0; j < height; ++j) {
memset(rgba, 0, width * kNumChannels);
rgba += rgba_stride;
}
}
// Copy width * height pixels from 'src' to 'dst'.
static void CopyCanvas(const uint8_t* src, uint8_t* dst,
uint32_t width, uint32_t height) {
assert(src != NULL && dst != NULL);
memcpy(dst, src, width * kNumChannels * height);
}
// Copy pixels in the given rectangle from 'src' to 'dst' honoring the 'stride'.
static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
int x_offset, int y_offset,
int width, int height) {
int j;
const int width_in_bytes = width * kNumChannels;
const size_t offset = y_offset * stride + x_offset * kNumChannels;
assert(width_in_bytes <= stride);
src += offset;
dst += offset;
for (j = 0; j < height; ++j) {
memcpy(dst, src, width_in_bytes);
src += stride;
dst += stride;
}
}
// Canonicalize all transparent pixels to transparent black to aid comparison.
static void CleanupTransparentPixels(uint32_t* rgba,
uint32_t width, uint32_t height) {
const uint32_t* const rgba_end = rgba + width * height;
while (rgba < rgba_end) {
const uint8_t alpha = (*rgba >> 24) & 0xff;
if (alpha == 0) {
*rgba = 0;
}
++rgba;
}
}
// Dump frame to a PAM file. Returns true on success.
static int DumpFrame(const char filename[], const char dump_folder[],
uint32_t frame_num, const uint8_t rgba[],
int canvas_width, int canvas_height) {
int ok = 0;
size_t max_len;
int y;
const char* base_name = NULL;
char* file_name = NULL;
FILE* f = NULL;
const char* row;
base_name = strrchr(filename, '/');
base_name = (base_name == NULL) ? filename : base_name + 1;
max_len = strlen(dump_folder) + 1 + strlen(base_name)
+ strlen("_frame_") + strlen(".pam") + 8;
file_name = (char*)malloc(max_len * sizeof(*file_name));
if (file_name == NULL) goto End;
if (snprintf(file_name, max_len, "%s/%s_frame_%d.pam",
dump_folder, base_name, frame_num) < 0) {
fprintf(stderr, "Error while generating file name\n");
goto End;
}
f = fopen(file_name, "wb");
if (f == NULL) {
fprintf(stderr, "Error opening file for writing: %s\n", file_name);
ok = 0;
goto End;
}
if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n"
"DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
canvas_width, canvas_height) < 0) {
fprintf(stderr, "Write error for file %s\n", file_name);
goto End;
}
row = (const char*)rgba;
for (y = 0; y < canvas_height; ++y) {
if (fwrite(row, canvas_width * kNumChannels, 1, f) != 1) {
fprintf(stderr, "Error writing to file: %s\n", file_name);
goto End;
}
row += canvas_width * kNumChannels;
}
ok = 1;
End:
if (f != NULL) fclose(f);
free(file_name);
return ok;
}
// -----------------------------------------------------------------------------
// WebP Decoding.
// Returns true if this is a valid WebP bitstream.
static int IsWebP(const WebPData* const webp_data) {
return (WebPGetInfo(webp_data->bytes, webp_data->size, NULL, NULL) != 0);
}
// Read animated WebP bitstream 'file_str' into 'AnimatedImage' struct.
static int ReadAnimatedWebP(const char filename[],
const WebPData* const webp_data,
AnimatedImage* const image, int dump_frames,
const char dump_folder[]) {
int ok = 0;
int dump_ok = 1;
uint32_t frame_index = 0;
int prev_frame_timestamp = 0;
WebPAnimDecoder* dec;
WebPAnimInfo anim_info;
memset(image, 0, sizeof(*image));
dec = WebPAnimDecoderNew(webp_data, NULL);
if (dec == NULL) {
fprintf(stderr, "Error parsing image: %s\n", filename);
goto End;
}
if (!WebPAnimDecoderGetInfo(dec, &anim_info)) {
fprintf(stderr, "Error getting global info about the animation\n");
goto End;
}
// Animation properties.
image->canvas_width = anim_info.canvas_width;
image->canvas_height = anim_info.canvas_height;
image->loop_count = anim_info.loop_count;
image->bgcolor = anim_info.bgcolor;
// Allocate frames.
if (!AllocateFrames(image, anim_info.frame_count)) return 0;
// Decode frames.
while (WebPAnimDecoderHasMoreFrames(dec)) {
DecodedFrame* curr_frame;
uint8_t* curr_rgba;
uint8_t* frame_rgba;
int timestamp;
if (!WebPAnimDecoderGetNext(dec, &frame_rgba, &timestamp)) {
fprintf(stderr, "Error decoding frame #%u\n", frame_index);
goto End;
}
assert(frame_index < anim_info.frame_count);
curr_frame = &image->frames[frame_index];
curr_rgba = curr_frame->rgba;
curr_frame->duration = timestamp - prev_frame_timestamp;
curr_frame->is_key_frame = 0; // Unused.
memcpy(curr_rgba, frame_rgba,
image->canvas_width * kNumChannels * image->canvas_height);
// Needed only because we may want to compare with GIF later.
CleanupTransparentPixels((uint32_t*)curr_rgba,
image->canvas_width, image->canvas_height);
if (dump_frames && dump_ok) {
dump_ok = DumpFrame(filename, dump_folder, frame_index, curr_rgba,
image->canvas_width, image->canvas_height);
if (!dump_ok) { // Print error once, but continue decode loop.
fprintf(stderr, "Error dumping frames to %s\n", dump_folder);
}
}
++frame_index;
prev_frame_timestamp = timestamp;
}
ok = dump_ok;
End:
WebPAnimDecoderDelete(dec);
return ok;
}
// -----------------------------------------------------------------------------
// 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 &&
(!memcmp(GIF_STAMP, data->bytes, GIF_STAMP_LEN) ||
!memcmp(GIF87_STAMP, data->bytes, GIF_STAMP_LEN) ||
!memcmp(GIF89_STAMP, data->bytes, GIF_STAMP_LEN));
}
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
# define LOCAL_GIF_PREREQ(maj, min) \
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
#else
# define LOCAL_GIF_VERSION 0
# define LOCAL_GIF_PREREQ(maj, min) 0
#endif
#if !LOCAL_GIF_PREREQ(5, 0)
// Added in v5.0
typedef struct {
int DisposalMode;
#define DISPOSAL_UNSPECIFIED 0 // No disposal specified
#define DISPOSE_DO_NOT 1 // Leave image in place
#define DISPOSE_BACKGROUND 2 // Set area to background color
#define DISPOSE_PREVIOUS 3 // Restore to previous content
int UserInputFlag; // User confirmation required before disposal
int DelayTime; // Pre-display delay in 0.01sec units
int TransparentColor; // Palette index for transparency, -1 if none
#define NO_TRANSPARENT_COLOR -1
} GraphicsControlBlock;
static int DGifExtensionToGCB(const size_t GifExtensionLength,
const GifByteType* GifExtension,
GraphicsControlBlock* gcb) {
if (GifExtensionLength != 4) {
return GIF_ERROR;
}
gcb->DisposalMode = (GifExtension[0] >> 2) & 0x07;
gcb->UserInputFlag = (GifExtension[0] & 0x02) != 0;
gcb->DelayTime = GifExtension[1] | (GifExtension[2] << 8);
if (GifExtension[0] & 0x01) {
gcb->TransparentColor = (int)GifExtension[3];
} else {
gcb->TransparentColor = NO_TRANSPARENT_COLOR;
}
return GIF_OK;
}
static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex,
GraphicsControlBlock* gcb) {
int i;
if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
return GIF_ERROR;
}
gcb->DisposalMode = DISPOSAL_UNSPECIFIED;
gcb->UserInputFlag = 0;
gcb->DelayTime = 0;
gcb->TransparentColor = NO_TRANSPARENT_COLOR;
for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
return DGifExtensionToGCB(
ep->ByteCount, (const GifByteType*)ep->Bytes, gcb);
}
}
return GIF_ERROR;
}
#define CONTINUE_EXT_FUNC_CODE 0x00
// Signature was changed in v5.0
#define DGifOpenFileName(a, b) DGifOpenFileName(a)
#endif // !LOCAL_GIF_PREREQ(5, 0)
// Signature changed in v5.1
#if !LOCAL_GIF_PREREQ(5, 1)
#define DGifCloseFile(a, b) DGifCloseFile(a)
#endif
static void GIFDisplayError(const GifFileType* const gif, int gif_error) {
// libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
#if LOCAL_GIF_PREREQ(4, 2)
#if LOCAL_GIF_PREREQ(5, 0)
const char* error_str =
GifErrorString((gif == NULL) ? gif_error : gif->Error);
#else
const char* error_str = GifErrorString();
(void)gif;
#endif
if (error_str == NULL) error_str = "Unknown error";
fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
#else
(void)gif;
fprintf(stderr, "GIFLib Error %d: ", gif_error);
PrintGifError();
fprintf(stderr, "\n");
#endif
}
static int IsKeyFrameGIF(const GifImageDesc* prev_desc, int prev_dispose,
const DecodedFrame* const prev_frame,
int canvas_width, int canvas_height) {
if (prev_frame == NULL) return 1;
if (prev_dispose == DISPOSE_BACKGROUND) {
if (IsFullFrame(prev_desc->Width, prev_desc->Height,
canvas_width, canvas_height)) {
return 1;
}
if (prev_frame->is_key_frame) return 1;
}
return 0;
}
static int GetTransparentIndexGIF(GifFileType* gif) {
GraphicsControlBlock first_gcb;
memset(&first_gcb, 0, sizeof(first_gcb));
DGifSavedExtensionToGCB(gif, 0, &first_gcb);
return first_gcb.TransparentColor;
}
static uint32_t GetBackgroundColorGIF(GifFileType* gif) {
const int transparent_index = GetTransparentIndexGIF(gif);
const ColorMapObject* const color_map = gif->SColorMap;
if (transparent_index != NO_TRANSPARENT_COLOR &&
gif->SBackGroundColor == transparent_index) {
return 0x00000000; // Special case: transparent black.
} else if (color_map == NULL || color_map->Colors == NULL
|| gif->SBackGroundColor >= color_map->ColorCount) {
return 0xffffffff; // Invalid: assume white.
} else {
const GifColorType color = color_map->Colors[gif->SBackGroundColor];
return (0xff << 24) |
(color.Red << 16) |
(color.Green << 8) |
(color.Blue << 0);
}
}
// Find appropriate app extension and get loop count from the next extension.
static uint32_t GetLoopCountGIF(const GifFileType* const gif) {
int i;
for (i = 0; i < gif->ImageCount; ++i) {
const SavedImage* const image = &gif->SavedImages[i];
int j;
for (j = 0; (j + 1) < image->ExtensionBlockCount; ++j) {
const ExtensionBlock* const eb1 = image->ExtensionBlocks + j;
const ExtensionBlock* const eb2 = image->ExtensionBlocks + j + 1;
const char* const signature = (const char*)eb1->Bytes;
const int signature_is_ok =
(eb1->Function == APPLICATION_EXT_FUNC_CODE) &&
(eb1->ByteCount == 11) &&
(!memcmp(signature, "NETSCAPE2.0", 11) ||
!memcmp(signature, "ANIMEXTS1.0", 11));
if (signature_is_ok &&
eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 &&
eb2->Bytes[0] == 1) {
return ((uint32_t)(eb2->Bytes[2]) << 8) +
((uint32_t)(eb2->Bytes[1]) << 0);
}
}
}
return 0; // Default.
}
// Get duration of 'n'th frame in milliseconds.
static int GetFrameDurationGIF(GifFileType* gif, int n) {
GraphicsControlBlock gcb;
memset(&gcb, 0, sizeof(gcb));
DGifSavedExtensionToGCB(gif, n, &gcb);
return gcb.DelayTime * 10;
}
// Returns true if frame 'target' completely covers 'covered'.
static int CoversFrameGIF(const GifImageDesc* const target,
const GifImageDesc* const covered) {
return target->Left <= covered->Left &&
covered->Left + covered->Width <= target->Left + target->Width &&
target->Top <= covered->Top &&
covered->Top + covered->Height <= target->Top + target->Height;
}
static void RemapPixelsGIF(const uint8_t* const src,
const ColorMapObject* const cmap,
int transparent_color, int len, uint8_t* dst) {
int i;
for (i = 0; i < len; ++i) {
if (src[i] != transparent_color) {
// If a pixel in the current frame is transparent, we don't modify it, so
// that we can see-through the corresponding pixel from an earlier frame.
const GifColorType c = cmap->Colors[src[i]];
dst[4 * i + 0] = c.Red;
dst[4 * i + 1] = c.Green;
dst[4 * i + 2] = c.Blue;
dst[4 * i + 3] = 0xff;
}
}
}
static int ReadFrameGIF(const SavedImage* const gif_image,
const ColorMapObject* cmap, int transparent_color,
int out_stride, uint8_t* const dst) {
const GifImageDesc* image_desc = &gif_image->ImageDesc;
const uint8_t* in;
uint8_t* out;
int j;
if (image_desc->ColorMap) cmap = image_desc->ColorMap;
if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
fprintf(stderr, "Potentially corrupt color map.\n");
return 0;
}
in = (const uint8_t*)gif_image->RasterBits;
out = dst + image_desc->Top * out_stride + image_desc->Left * kNumChannels;
for (j = 0; j < image_desc->Height; ++j) {
RemapPixelsGIF(in, cmap, transparent_color, image_desc->Width, out);
in += image_desc->Width;
out += out_stride;
}
return 1;
}
// Read animated GIF bitstream from 'filename' into 'AnimatedImage' struct.
static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
int dump_frames, const char dump_folder[]) {
uint32_t frame_count;
uint32_t canvas_width, canvas_height;
uint32_t i;
int gif_error;
GifFileType* gif;
gif = DGifOpenFileName(filename, NULL);
if (gif == NULL) {
fprintf(stderr, "Could not read file: %s.\n", filename);
return 0;
}
gif_error = DGifSlurp(gif);
if (gif_error != GIF_OK) {
fprintf(stderr, "Could not parse image: %s.\n", filename);
GIFDisplayError(gif, gif_error);
DGifCloseFile(gif, NULL);
return 0;
}
// Animation properties.
image->canvas_width = (uint32_t)gif->SWidth;
image->canvas_height = (uint32_t)gif->SHeight;
if (image->canvas_width > MAX_CANVAS_SIZE ||
image->canvas_height > MAX_CANVAS_SIZE) {
fprintf(stderr, "Invalid canvas dimension: %d x %d\n",
image->canvas_width, image->canvas_height);
DGifCloseFile(gif, NULL);
return 0;
}
image->loop_count = GetLoopCountGIF(gif);
image->bgcolor = GetBackgroundColorGIF(gif);
frame_count = (uint32_t)gif->ImageCount;
if (frame_count == 0) {
DGifCloseFile(gif, NULL);
return 0;
}
if (image->canvas_width == 0 || image->canvas_height == 0) {
image->canvas_width = gif->SavedImages[0].ImageDesc.Width;
image->canvas_height = gif->SavedImages[0].ImageDesc.Height;
gif->SavedImages[0].ImageDesc.Left = 0;
gif->SavedImages[0].ImageDesc.Top = 0;
if (image->canvas_width == 0 || image->canvas_height == 0) {
fprintf(stderr, "Invalid canvas size in GIF.\n");
DGifCloseFile(gif, NULL);
return 0;
}
}
// Allocate frames.
AllocateFrames(image, frame_count);
canvas_width = image->canvas_width;
canvas_height = image->canvas_height;
// Decode and reconstruct frames.
for (i = 0; i < frame_count; ++i) {
const int canvas_width_in_bytes = canvas_width * kNumChannels;
const SavedImage* const curr_gif_image = &gif->SavedImages[i];
GraphicsControlBlock curr_gcb;
DecodedFrame* curr_frame;
uint8_t* curr_rgba;
memset(&curr_gcb, 0, sizeof(curr_gcb));
DGifSavedExtensionToGCB(gif, i, &curr_gcb);
curr_frame = &image->frames[i];
curr_rgba = curr_frame->rgba;
curr_frame->duration = GetFrameDurationGIF(gif, i);
if (i == 0) { // Initialize as transparent.
curr_frame->is_key_frame = 1;
ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
} else {
DecodedFrame* const prev_frame = &image->frames[i - 1];
const GifImageDesc* const prev_desc = &gif->SavedImages[i - 1].ImageDesc;
GraphicsControlBlock prev_gcb;
memset(&prev_gcb, 0, sizeof(prev_gcb));
DGifSavedExtensionToGCB(gif, i - 1, &prev_gcb);
curr_frame->is_key_frame =
IsKeyFrameGIF(prev_desc, prev_gcb.DisposalMode, prev_frame,
canvas_width, canvas_height);
if (curr_frame->is_key_frame) { // Initialize as transparent.
ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
} else {
int prev_frame_disposed, curr_frame_opaque;
int prev_frame_completely_covered;
// Initialize with previous canvas.
uint8_t* const prev_rgba = image->frames[i - 1].rgba;
CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height);
// Dispose previous frame rectangle.
prev_frame_disposed =
(prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
curr_frame_opaque =
(curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
prev_frame_completely_covered =
curr_frame_opaque &&
CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc);
if (prev_frame_disposed && !prev_frame_completely_covered) {
switch (prev_gcb.DisposalMode) {
case DISPOSE_BACKGROUND: {
ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
prev_desc->Left, prev_desc->Top,
prev_desc->Width, prev_desc->Height);
break;
}
case DISPOSE_PREVIOUS: {
int src_frame_num = i - 2;
while (src_frame_num >= 0) {
GraphicsControlBlock src_frame_gcb;
memset(&src_frame_gcb, 0, sizeof(src_frame_gcb));
DGifSavedExtensionToGCB(gif, src_frame_num, &src_frame_gcb);
if (src_frame_gcb.DisposalMode != DISPOSE_PREVIOUS) break;
--src_frame_num;
}
if (src_frame_num >= 0) {
// Restore pixels inside previous frame rectangle to
// corresponding pixels in source canvas.
uint8_t* const src_frame_rgba =
image->frames[src_frame_num].rgba;
CopyFrameRectangle(src_frame_rgba, curr_rgba,
canvas_width_in_bytes,
prev_desc->Left, prev_desc->Top,
prev_desc->Width, prev_desc->Height);
} else {
// Source canvas doesn't exist. So clear previous frame
// rectangle to background.
ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
prev_desc->Left, prev_desc->Top,
prev_desc->Width, prev_desc->Height);
}
break;
}
default:
break; // Nothing to do.
}
}
}
}
// Decode current frame.
if (!ReadFrameGIF(curr_gif_image, gif->SColorMap, curr_gcb.TransparentColor,
canvas_width_in_bytes, curr_rgba)) {
DGifCloseFile(gif, NULL);
return 0;
}
if (dump_frames) {
if (!DumpFrame(filename, dump_folder, i, curr_rgba,
canvas_width, canvas_height)) {
DGifCloseFile(gif, NULL);
return 0;
}
}
}
DGifCloseFile(gif, NULL);
return 1;
}
#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;
(void)image;
(void)dump_frames;
(void)dump_folder;
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
"package before building.\n");
return 0;
}
#endif // WEBP_HAVE_GIF
// -----------------------------------------------------------------------------
int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
int dump_frames, const char dump_folder[]) {
int ok = 0;
WebPData webp_data;
WebPDataInit(&webp_data);
memset(image, 0, sizeof(*image));
if (!ImgIoUtilReadFile(filename, &webp_data.bytes, &webp_data.size)) {
fprintf(stderr, "Error reading file: %s\n", filename);
return 0;
}
if (IsWebP(&webp_data)) {
ok = ReadAnimatedWebP(filename, &webp_data, image, dump_frames,
dump_folder);
} else if (IsGIF(&webp_data)) {
ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder);
} else {
fprintf(stderr,
"Unknown file type: %s. Supported file types are WebP and GIF\n",
filename);
ok = 0;
}
if (!ok) ClearAnimatedImage(image);
WebPDataClear(&webp_data);
return ok;
}
static void Accumulate(double v1, double v2, double* const max_diff,
double* const sse) {
const double diff = fabs(v1 - v2);
if (diff > *max_diff) *max_diff = diff;
*sse += diff * diff;
}
void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
uint32_t width, uint32_t height, int premultiply,
int* const max_diff, double* const psnr) {
const uint32_t stride = width * kNumChannels;
const int kAlphaChannel = kNumChannels - 1;
double f_max_diff = 0.;
double sse = 0.;
uint32_t x, y;
for (y = 0; y < height; ++y) {
for (x = 0; x < stride; x += kNumChannels) {
int k;
const size_t offset = (size_t)y * stride + x;
const int alpha1 = rgba1[offset + kAlphaChannel];
const int alpha2 = rgba2[offset + kAlphaChannel];
Accumulate(alpha1, alpha2, &f_max_diff, &sse);
if (!premultiply) {
for (k = 0; k < kAlphaChannel; ++k) {
Accumulate(rgba1[offset + k], rgba2[offset + k], &f_max_diff, &sse);
}
} else {
// premultiply R/G/B channels with alpha value
for (k = 0; k < kAlphaChannel; ++k) {
Accumulate(rgba1[offset + k] * alpha1 / 255.,
rgba2[offset + k] * alpha2 / 255.,
&f_max_diff, &sse);
}
}
}
}
*max_diff = (int)f_max_diff;
if (*max_diff == 0) {
*psnr = 99.; // PSNR when images are identical.
} else {
sse /= stride * height;
*psnr = 4.3429448 * log(255. * 255. / sse);
}
}

View File

@ -1,63 +0,0 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Utilities for animated images
#ifndef WEBP_EXAMPLES_ANIM_UTIL_H_
#define WEBP_EXAMPLES_ANIM_UTIL_H_
#ifdef HAVE_CONFIG_H
#include "webp/config.h"
#endif
#include "webp/types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint8_t* rgba; // Decoded and reconstructed full frame.
int duration; // Frame duration in milliseconds.
int is_key_frame; // True if this frame is a key-frame.
} DecodedFrame;
typedef struct {
uint32_t canvas_width;
uint32_t canvas_height;
uint32_t bgcolor;
uint32_t loop_count;
DecodedFrame* frames;
uint32_t num_frames;
void* raw_mem;
} AnimatedImage;
// Deallocate everything in 'image' (but not the object itself).
void ClearAnimatedImage(AnimatedImage* const image);
// Read animated image file into 'AnimatedImage' struct.
// If 'dump_frames' is true, dump frames to 'dump_folder'.
// Previous content of 'image' is obliterated.
// Upon successful return, content of 'image' must be deleted by
// calling 'ClearAnimatedImage'.
int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
int dump_frames, const char dump_folder[]);
// Given two RGBA buffers, calculate max pixel difference and PSNR.
// If 'premultiply' is true, R/G/B values will be pre-multiplied by the
// transparency before comparison.
void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
uint32_t width, uint32_t height, int premultiply,
int* const max_diff, double* const psnr);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WEBP_EXAMPLES_ANIM_UTIL_H_

View File

@ -17,23 +17,27 @@
#include <string.h> #include <string.h>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif #endif
#include "../examples/example_util.h"
#include "../imageio/image_dec.h"
#include "../imageio/imageio_util.h"
#include "./stopwatch.h"
#include "webp/encode.h" #include "webp/encode.h"
#include "./metadata.h"
#include "./stopwatch.h"
#include "./jpegdec.h"
#include "./pngdec.h"
#include "./tiffdec.h"
#include "./wicdec.h"
#ifndef WEBP_DLL #ifndef WEBP_DLL
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
extern void* VP8GetCPUInfo; // opaque forward declaration. extern void* VP8GetCPUInfo; // opaque forward declaration.
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_DLL #endif // WEBP_DLL
@ -42,81 +46,118 @@ extern void* VP8GetCPUInfo; // opaque forward declaration.
static int verbose = 0; static int verbose = 0;
static int ReadYUV(const uint8_t* const data, size_t data_size, static int ReadYUV(FILE* in_file, WebPPicture* const pic) {
WebPPicture* const pic) {
const int use_argb = pic->use_argb; const int use_argb = pic->use_argb;
const int uv_width = (pic->width + 1) / 2; const int uv_width = (pic->width + 1) / 2;
const int uv_height = (pic->height + 1) / 2; const int uv_height = (pic->height + 1) / 2;
const int y_plane_size = pic->width * pic->height; int y;
const int uv_plane_size = uv_width * uv_height; int ok = 0;
const size_t expected_data_size = y_plane_size + 2 * uv_plane_size;
if (data_size != expected_data_size) {
fprintf(stderr,
"input data doesn't have the expected size (%d instead of %d)\n",
(int)data_size, (int)expected_data_size);
return 0;
}
pic->use_argb = 0; pic->use_argb = 0;
if (!WebPPictureAlloc(pic)) return 0; if (!WebPPictureAlloc(pic)) return ok;
ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride,
pic->width, pic->height); for (y = 0; y < pic->height; ++y) {
ImgIoUtilCopyPlane(data + y_plane_size, uv_width, if (fread(pic->y + y * pic->y_stride, pic->width, 1, in_file) != 1) {
pic->u, pic->uv_stride, uv_width, uv_height); goto End;
ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width, }
pic->v, pic->uv_stride, uv_width, uv_height); }
return use_argb ? WebPPictureYUVAToARGB(pic) : 1; for (y = 0; y < uv_height; ++y) {
if (fread(pic->u + y * pic->uv_stride, uv_width, 1, in_file) != 1)
goto End;
}
for (y = 0; y < uv_height; ++y) {
if (fread(pic->v + y * pic->uv_stride, uv_width, 1, in_file) != 1)
goto End;
}
ok = 1;
if (use_argb) ok = WebPPictureYUVAToARGB(pic);
End:
return ok;
} }
#ifdef HAVE_WINCODEC_H #ifdef HAVE_WINCODEC_H
static int ReadPicture(const char* const filename, WebPPicture* const pic, static int ReadPicture(const char* const filename, WebPPicture* const pic,
int keep_alpha, Metadata* const metadata) { int keep_alpha, Metadata* const metadata) {
int ok = 0; int ok;
const uint8_t* data = NULL;
size_t data_size = 0;
if (pic->width != 0 && pic->height != 0) { if (pic->width != 0 && pic->height != 0) {
ok = ImgIoUtilReadFile(filename, &data, &data_size); // If image size is specified, infer it as YUV format.
ok = ok && ReadYUV(data, data_size, pic); FILE* in_file = fopen(filename, "rb");
if (in_file == NULL) {
fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
return 0;
}
ok = ReadYUV(in_file, pic);
fclose(in_file);
} else { } else {
// If no size specified, try to decode it using WIC. // If no size specified, try to decode it using WIC.
ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata); ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata);
if (!ok) {
ok = ImgIoUtilReadFile(filename, &data, &data_size);
ok = ok && ReadWebP(data, data_size, pic, keep_alpha, metadata);
}
} }
if (!ok) { if (!ok) {
fprintf(stderr, "Error! Could not process file %s\n", filename); fprintf(stderr, "Error! Could not process file %s\n", filename);
} }
free((void*)data);
return ok; return ok;
} }
#else // !HAVE_WINCODEC_H #else // !HAVE_WINCODEC_H
typedef enum {
PNG_ = 0,
JPEG_,
TIFF_, // 'TIFF' clashes with libtiff
UNSUPPORTED
} InputFileFormat;
static InputFileFormat GetImageType(FILE* in_file) {
InputFileFormat format = UNSUPPORTED;
uint32_t magic;
uint8_t buf[4];
if ((fread(&buf[0], 4, 1, in_file) != 1) ||
(fseek(in_file, 0, SEEK_SET) != 0)) {
return format;
}
magic = ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
if (magic == 0x89504E47U) {
format = PNG_;
} else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) {
format = JPEG_;
} else if (magic == 0x49492A00 || magic == 0x4D4D002A) {
format = TIFF_;
}
return format;
}
static int ReadPicture(const char* const filename, WebPPicture* const pic, static int ReadPicture(const char* const filename, WebPPicture* const pic,
int keep_alpha, Metadata* const metadata) { int keep_alpha, Metadata* const metadata) {
const uint8_t* data = NULL;
size_t data_size = 0;
int ok = 0; int ok = 0;
FILE* in_file = fopen(filename, "rb");
ok = ImgIoUtilReadFile(filename, &data, &data_size); if (in_file == NULL) {
if (!ok) goto End; fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
return ok;
}
if (pic->width == 0 || pic->height == 0) { if (pic->width == 0 || pic->height == 0) {
WebPImageReader reader = WebPGuessImageReader(data, data_size); // If no size specified, try to decode it as PNG/JPEG (as appropriate).
ok = reader(data, data_size, pic, keep_alpha, metadata); const InputFileFormat format = GetImageType(in_file);
if (format == PNG_) {
ok = ReadPNG(in_file, pic, keep_alpha, metadata);
} else if (format == JPEG_) {
ok = ReadJPEG(in_file, pic, metadata);
} else if (format == TIFF_) {
ok = ReadTIFF(filename, pic, keep_alpha, metadata);
}
} else { } else {
// If image size is specified, infer it as YUV format. // If image size is specified, infer it as YUV format.
ok = ReadYUV(data, data_size, pic); ok = ReadYUV(in_file, pic);
} }
End:
if (!ok) { if (!ok) {
fprintf(stderr, "Error! Could not process file %s\n", filename); fprintf(stderr, "Error! Could not process file %s\n", filename);
} }
free((void*)data);
fclose(in_file);
return ok; return ok;
} }
@ -160,8 +201,6 @@ static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
const char* const description) { const char* const description) {
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n", fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
description, stats->lossless_size); description, stats->lossless_size);
fprintf(stderr, " * Header size: %d bytes, image data size: %d\n",
stats->lossless_hdr_size, stats->lossless_data_size);
if (stats->lossless_features) { if (stats->lossless_features) {
fprintf(stderr, " * Lossless features used:"); fprintf(stderr, " * Lossless features used:");
if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION"); if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
@ -227,6 +266,10 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
fprintf(stderr, " transparency: %6d (%.1f dB)\n", fprintf(stderr, " transparency: %6d (%.1f dB)\n",
stats->alpha_data_size, stats->PSNR[4]); stats->alpha_data_size, stats->PSNR[4]);
} }
if (stats->layer_data_size) {
fprintf(stderr, " enhancement: %6d\n",
stats->layer_data_size);
}
fprintf(stderr, " Residuals bytes " fprintf(stderr, " Residuals bytes "
"|segment 1|segment 2|segment 3" "|segment 1|segment 2|segment 3"
"|segment 4| total\n"); "|segment 4| total\n");
@ -255,9 +298,6 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
PrintFullLosslessInfo(stats, "alpha"); PrintFullLosslessInfo(stats, "alpha");
} }
} }
}
static void PrintMapInfo(const WebPPicture* const pic) {
if (pic->extra_info != NULL) { if (pic->extra_info != NULL) {
const int mb_w = (pic->width + 15) / 16; const int mb_w = (pic->width + 15) / 16;
const int mb_h = (pic->height + 15) / 16; const int mb_h = (pic->height + 15) / 16;
@ -267,18 +307,18 @@ static void PrintMapInfo(const WebPPicture* const pic) {
for (x = 0; x < mb_w; ++x) { for (x = 0; x < mb_w; ++x) {
const int c = pic->extra_info[x + y * mb_w]; const int c = pic->extra_info[x + y * mb_w];
if (type == 1) { // intra4/intra16 if (type == 1) { // intra4/intra16
fprintf(stderr, "%c", "+."[c]); printf("%c", "+."[c]);
} else if (type == 2) { // segments } else if (type == 2) { // segments
fprintf(stderr, "%c", ".-*X"[c]); printf("%c", ".-*X"[c]);
} else if (type == 3) { // quantizers } else if (type == 3) { // quantizers
fprintf(stderr, "%.2d ", c); printf("%.2d ", c);
} else if (type == 6 || type == 7) { } else if (type == 6 || type == 7) {
fprintf(stderr, "%3d ", c); printf("%3d ", c);
} else { } else {
fprintf(stderr, "0x%.2x ", c); printf("0x%.2x ", c);
} }
} }
fprintf(stderr, "\n"); printf("\n");
} }
} }
} }
@ -297,10 +337,6 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
const int uv_width = (picture->width + 1) / 2; const int uv_width = (picture->width + 1) / 2;
const int uv_height = (picture->height + 1) / 2; const int uv_height = (picture->height + 1) / 2;
const int stride = (picture->width + 1) & ~1; const int stride = (picture->width + 1) & ~1;
const uint8_t* src_y = picture->y;
const uint8_t* src_u = picture->u;
const uint8_t* src_v = picture->v;
const uint8_t* src_a = picture->a;
const int alpha_height = const int alpha_height =
WebPPictureHasTransparency(picture) ? picture->height : 0; WebPPictureHasTransparency(picture) ? picture->height : 0;
const int height = picture->height + uv_height + alpha_height; const int height = picture->height + uv_height + alpha_height;
@ -308,20 +344,20 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
if (f == NULL) return 0; if (f == NULL) return 0;
fprintf(f, "P5\n%d %d\n255\n", stride, height); fprintf(f, "P5\n%d %d\n255\n", stride, height);
for (y = 0; y < picture->height; ++y) { for (y = 0; y < picture->height; ++y) {
if (fwrite(src_y, picture->width, 1, f) != 1) return 0; if (fwrite(picture->y + y * picture->y_stride, picture->width, 1, f) != 1)
return 0;
if (picture->width & 1) fputc(0, f); // pad if (picture->width & 1) fputc(0, f); // pad
src_y += picture->y_stride;
} }
for (y = 0; y < uv_height; ++y) { for (y = 0; y < uv_height; ++y) {
if (fwrite(src_u, uv_width, 1, f) != 1) return 0; if (fwrite(picture->u + y * picture->uv_stride, uv_width, 1, f) != 1)
if (fwrite(src_v, uv_width, 1, f) != 1) return 0; return 0;
src_u += picture->uv_stride; if (fwrite(picture->v + y * picture->uv_stride, uv_width, 1, f) != 1)
src_v += picture->uv_stride; return 0;
} }
for (y = 0; y < alpha_height; ++y) { for (y = 0; y < alpha_height; ++y) {
if (fwrite(src_a, picture->width, 1, f) != 1) return 0; if (fwrite(picture->a + y * picture->a_stride, picture->width, 1, f) != 1)
return 0;
if (picture->width & 1) fputc(0, f); // pad if (picture->width & 1) fputc(0, f); // pad
src_a += picture->a_stride;
} }
fclose(f); fclose(f);
return 1; return 1;
@ -458,14 +494,11 @@ static int WriteWebPWithMetadata(FILE* const out,
if (has_vp8x) { // update the existing VP8X flags if (has_vp8x) { // update the existing VP8X flags
webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff); webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff);
ok = ok && (fwrite(webp, kVP8XChunkSize, 1, out) == 1); ok = ok && (fwrite(webp, kVP8XChunkSize, 1, out) == 1);
webp += kVP8XChunkSize;
webp_size -= kVP8XChunkSize; webp_size -= kVP8XChunkSize;
} else { } else {
const int is_lossless = !memcmp(webp, "VP8L", kTagSize); const int is_lossless = !memcmp(webp, "VP8L", kTagSize);
if (is_lossless) { // The alpha flag is forced with lossless images.
// Presence of alpha is stored in the 29th bit of VP8L data. if (is_lossless) flags |= kAlphaFlag;
if (webp[kChunkHeaderSize + 3] & (1 << 5)) flags |= kAlphaFlag;
}
ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1); ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1);
ok = ok && WriteLE32(out, flags); ok = ok && WriteLE32(out, flags);
ok = ok && WriteLE24(out, picture->width - 1); ok = ok && WriteLE24(out, picture->width - 1);
@ -495,8 +528,9 @@ static int WriteWebPWithMetadata(FILE* const out,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static int ProgressReport(int percent, const WebPPicture* const picture) { static int ProgressReport(int percent, const WebPPicture* const picture) {
fprintf(stderr, "[%s]: %3d %% \r", printf("[%s]: %3d %% \r",
(char*)picture->user_data, percent); (char*)picture->user_data, percent);
fflush(stdout);
return 1; // all ok return 1; // all ok
} }
@ -513,44 +547,35 @@ static void HelpShort(void) {
static void HelpLong(void) { static void HelpLong(void) {
printf("Usage:\n"); printf("Usage:\n");
printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n"); printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
printf("If input size (-s) for an image is not specified, it is\n" printf("If input size (-s) for an image is not specified, "
"assumed to be a PNG, JPEG, TIFF or WebP file.\n"); "it is assumed to be a PNG, JPEG or TIFF file.\n");
#ifdef HAVE_WINCODEC_H #ifdef HAVE_WINCODEC_H
printf("Windows builds can take as input any of the files handled by WIC.\n"); printf("Windows builds can take as input any of the files handled by WIC\n");
#endif #endif
printf("\nOptions:\n"); printf("options:\n");
printf(" -h / -help ............. short help\n"); printf(" -h / -help ............ short help\n");
printf(" -H / -longhelp ......... long help\n"); printf(" -H / -longhelp ........ long help\n");
printf(" -q <float> ............. quality factor (0:small..100:big), " printf(" -q <float> ............. quality factor (0:small..100:big)\n");
"default=75\n"); printf(" -alpha_q <int> ......... Transparency-compression quality "
printf(" -alpha_q <int> ......... transparency-compression quality (0..100)," "(0..100).\n");
"\n default=100\n"); printf(" -preset <string> ....... Preset setting, one of:\n");
printf(" -preset <string> ....... preset setting, one of:\n");
printf(" default, photo, picture,\n"); printf(" default, photo, picture,\n");
printf(" drawing, icon, text\n"); printf(" drawing, icon, text\n");
printf(" -preset must come first, as it overwrites other parameters\n"); printf(" -preset must come first, as it overwrites other parameters.");
printf(" -z <int> ............... activates lossless preset with given\n"
" level in [0:fast, ..., 9:slowest]\n");
printf("\n"); printf("\n");
printf(" -m <int> ............... compression method (0=fast, 6=slowest), " printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
"default=4\n"); printf(" -segments <int> ........ number of segments to use (1..4)\n");
printf(" -segments <int> ........ number of segments to use (1..4), " printf(" -size <int> ............ Target size (in bytes)\n");
"default=4\n"); printf(" -psnr <float> .......... Target PSNR (in dB. typically: 42)\n");
printf(" -size <int> ............ target size (in bytes)\n");
printf(" -psnr <float> .......... target PSNR (in dB. typically: 42)\n");
printf("\n"); printf("\n");
printf(" -s <int> <int> ......... input size (width x height) for YUV\n"); printf(" -s <int> <int> ......... Input size (width x height) for YUV\n");
printf(" -sns <int> ............. spatial noise shaping (0:off, 100:max), " printf(" -sns <int> ............. Spatial Noise Shaping (0:off, 100:max)\n");
"default=50\n"); printf(" -f <int> ............... filter strength (0=off..100)\n");
printf(" -f <int> ............... filter strength (0=off..100), "
"default=60\n");
printf(" -sharpness <int> ....... " printf(" -sharpness <int> ....... "
"filter sharpness (0:most .. 7:least sharp), default=0\n"); "filter sharpness (0:most .. 7:least sharp)\n");
printf(" -strong ................ use strong filter instead " printf(" -strong ................ use strong filter instead "
"of simple (default)\n"); "of simple (default).\n");
printf(" -nostrong .............. use simple filter instead of strong\n"); printf(" -nostrong .............. use simple filter instead of strong.\n");
printf(" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
"conversion\n");
printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n"); printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n");
printf(" " printf(" "
"the first partition (0=no degradation ... 100=full)\n"); "the first partition (0=no degradation ... 100=full)\n");
@ -559,31 +584,22 @@ static void HelpLong(void) {
printf(" -resize <w> <h> ........ resize picture (after any cropping)\n"); printf(" -resize <w> <h> ........ resize picture (after any cropping)\n");
printf(" -mt .................... use multi-threading if available\n"); printf(" -mt .................... use multi-threading if available\n");
printf(" -low_memory ............ reduce memory usage (slower encoding)\n"); printf(" -low_memory ............ reduce memory usage (slower encoding)\n");
printf(" -map <int> ............. print map of extra info\n"); #ifdef WEBP_EXPERIMENTAL_FEATURES
printf(" -print_psnr ............ prints averaged PSNR distortion\n"); printf(" -444 / -422 / -gray ..... Change colorspace\n");
printf(" -print_ssim ............ prints averaged SSIM distortion\n"); #endif
printf(" -print_lsim ............ prints local-similarity distortion\n"); printf(" -map <int> ............. print map of extra info.\n");
printf(" -d <file.pgm> .......... dump the compressed output (PGM file)\n"); printf(" -print_psnr ............ prints averaged PSNR distortion.\n");
printf(" -alpha_method <int> .... transparency-compression method (0..1), " printf(" -print_ssim ............ prints averaged SSIM distortion.\n");
"default=1\n"); printf(" -print_lsim ............ prints local-similarity distortion.\n");
printf(" -alpha_filter <string> . predictive filtering for alpha plane,\n"); printf(" -d <file.pgm> .......... dump the compressed output (PGM file).\n");
printf(" one of: none, fast (default) or best\n"); printf(" -alpha_method <int> .... Transparency-compression method (0..1)\n");
printf(" -exact ................. preserve RGB values in transparent area, " printf(" -alpha_filter <string> . predictive filtering for alpha plane.\n");
"default=off\n"); printf(" One of: none, fast (default) or best.\n");
printf(" -blend_alpha <hex> ..... blend colors against background color\n" printf(" -alpha_cleanup ......... Clean RGB values in transparent area.\n");
" expressed as RGB values written in\n" printf(" -noalpha ............... discard any transparency information.\n");
" hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n" printf(" -lossless .............. Encode image losslessly.\n");
" green=0xe0 and blue=0xd0\n"); printf(" -hint <string> ......... Specify image characteristics hint.\n");
printf(" -noalpha ............... discard any transparency information\n"); printf(" One of: photo, picture or graph\n");
printf(" -lossless .............. encode image losslessly, default=off\n");
printf(" -near_lossless <int> ... use near-lossless image\n"
" preprocessing (0..100=off), "
"default=100\n");
#ifdef WEBP_EXPERIMENTAL_FEATURES /* not documented yet */
printf(" -delta_palette ......... use delta palettization\n");
#endif // WEBP_EXPERIMENTAL_FEATURES
printf(" -hint <string> ......... specify image characteristics hint,\n");
printf(" one of: photo, picture or graph\n");
printf("\n"); printf("\n");
printf(" -metadata <string> ..... comma separated list of metadata to\n"); printf(" -metadata <string> ..... comma separated list of metadata to\n");
@ -594,18 +610,18 @@ static void HelpLong(void) {
printf("\n"); printf("\n");
printf(" -short ................. condense printed message\n"); printf(" -short ................. condense printed message\n");
printf(" -quiet ................. don't print anything\n"); printf(" -quiet ................. don't print anything.\n");
printf(" -version ............... print version number and exit\n"); printf(" -version ............... print version number and exit.\n");
#ifndef WEBP_DLL #ifndef WEBP_DLL
printf(" -noasm ................. disable all assembly optimizations\n"); printf(" -noasm ................. disable all assembly optimizations.\n");
#endif #endif
printf(" -v ..................... verbose, e.g. print encoding/decoding " printf(" -v ..................... verbose, e.g. print encoding/decoding "
"times\n"); "times\n");
printf(" -progress .............. report encoding progress\n"); printf(" -progress .............. report encoding progress\n");
printf("\n"); printf("\n");
printf("Experimental Options:\n"); printf("Experimental Options:\n");
printf(" -jpeg_like ............. roughly match expected JPEG size\n"); printf(" -jpeg_like ............. Roughly match expected JPEG size.\n");
printf(" -af .................... auto-adjust filter strength\n"); printf(" -af .................... auto-adjust filter strength.\n");
printf(" -pre <int> ............. pre-processing filter\n"); printf(" -pre <int> ............. pre-processing filter\n");
printf("\n"); printf("\n");
} }
@ -613,7 +629,7 @@ static void HelpLong(void) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Error messages // Error messages
static const char* const kErrorMessages[VP8_ENC_ERROR_LAST] = { static const char* const kErrorMessages[] = {
"OK", "OK",
"OUT_OF_MEMORY: Out of memory allocating objects", "OUT_OF_MEMORY: Out of memory allocating objects",
"BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer", "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
@ -642,12 +658,8 @@ int main(int argc, const char *argv[]) {
int short_output = 0; int short_output = 0;
int quiet = 0; int quiet = 0;
int keep_alpha = 1; int keep_alpha = 1;
int blend_alpha = 0;
uint32_t background_color = 0xffffffu;
int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0; int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
int resize_w = 0, resize_h = 0; int resize_w = 0, resize_h = 0;
int lossless_preset = 6;
int use_lossless_preset = -1; // -1=unset, 0=don't use, 1=use it
int show_progress = 0; int show_progress = 0;
int keep_metadata = 0; int keep_metadata = 0;
int metadata_written = 0; int metadata_written = 0;
@ -675,7 +687,6 @@ int main(int argc, const char *argv[]) {
} }
for (c = 1; c < argc; ++c) { for (c = 1; c < argc; ++c) {
int parse_error = 0;
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
HelpShort(); HelpShort();
return 0; return 0;
@ -697,40 +708,20 @@ int main(int argc, const char *argv[]) {
config.show_compressed = 1; config.show_compressed = 1;
print_distortion = 2; print_distortion = 2;
} else if (!strcmp(argv[c], "-short")) { } else if (!strcmp(argv[c], "-short")) {
++short_output; short_output++;
} else if (!strcmp(argv[c], "-s") && c < argc - 2) { } else if (!strcmp(argv[c], "-s") && c < argc - 2) {
picture.width = ExUtilGetInt(argv[++c], 0, &parse_error); picture.width = strtol(argv[++c], NULL, 0);
picture.height = ExUtilGetInt(argv[++c], 0, &parse_error); picture.height = strtol(argv[++c], NULL, 0);
if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 ||
picture.height > WEBP_MAX_DIMENSION || picture.height < 0) {
fprintf(stderr,
"Specified dimension (%d x %d) is out of range.\n",
picture.width, picture.height);
goto Error;
}
} else if (!strcmp(argv[c], "-m") && c < argc - 1) { } else if (!strcmp(argv[c], "-m") && c < argc - 1) {
config.method = ExUtilGetInt(argv[++c], 0, &parse_error); config.method = strtol(argv[++c], NULL, 0);
use_lossless_preset = 0; // disable -z option
} else if (!strcmp(argv[c], "-q") && c < argc - 1) { } else if (!strcmp(argv[c], "-q") && c < argc - 1) {
config.quality = ExUtilGetFloat(argv[++c], &parse_error); config.quality = (float)strtod(argv[++c], NULL);
use_lossless_preset = 0; // disable -z option
} else if (!strcmp(argv[c], "-z") && c < argc - 1) {
lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error);
if (use_lossless_preset != 0) use_lossless_preset = 1;
} else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) { } else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) {
config.alpha_quality = ExUtilGetInt(argv[++c], 0, &parse_error); config.alpha_quality = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) { } else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
config.alpha_compression = ExUtilGetInt(argv[++c], 0, &parse_error); config.alpha_compression = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-alpha_cleanup")) { } else if (!strcmp(argv[c], "-alpha_cleanup")) {
// This flag is obsolete, does opposite of -exact. keep_alpha = keep_alpha ? 2 : 0;
config.exact = 0;
} else if (!strcmp(argv[c], "-exact")) {
config.exact = 1;
} else if (!strcmp(argv[c], "-blend_alpha") && c < argc - 1) {
blend_alpha = 1;
// background color is given in hex with an optional '0x' prefix
background_color = ExUtilGetInt(argv[++c], 16, &parse_error);
background_color = background_color & 0x00ffffffu;
} else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) { } else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
++c; ++c;
if (!strcmp(argv[c], "none")) { if (!strcmp(argv[c], "none")) {
@ -747,14 +738,7 @@ int main(int argc, const char *argv[]) {
keep_alpha = 0; keep_alpha = 0;
} else if (!strcmp(argv[c], "-lossless")) { } else if (!strcmp(argv[c], "-lossless")) {
config.lossless = 1; config.lossless = 1;
} else if (!strcmp(argv[c], "-near_lossless") && c < argc - 1) { picture.use_argb = 1;
config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
config.lossless = 1; // use near-lossless only with lossless
#ifdef WEBP_EXPERIMENTAL_FEATURES
} else if (!strcmp(argv[c], "-delta_palette")) {
config.use_delta_palette = 1;
config.lossless = 1; // delta-palette is for lossless only
#endif // WEBP_EXPERIMENTAL_FEATURES
} else if (!strcmp(argv[c], "-hint") && c < argc - 1) { } else if (!strcmp(argv[c], "-hint") && c < argc - 1) {
++c; ++c;
if (!strcmp(argv[c], "photo")) { if (!strcmp(argv[c], "photo")) {
@ -768,13 +752,13 @@ int main(int argc, const char *argv[]) {
goto Error; goto Error;
} }
} else if (!strcmp(argv[c], "-size") && c < argc - 1) { } else if (!strcmp(argv[c], "-size") && c < argc - 1) {
config.target_size = ExUtilGetInt(argv[++c], 0, &parse_error); config.target_size = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-psnr") && c < argc - 1) { } else if (!strcmp(argv[c], "-psnr") && c < argc - 1) {
config.target_PSNR = ExUtilGetFloat(argv[++c], &parse_error); config.target_PSNR = (float)strtod(argv[++c], NULL);
} else if (!strcmp(argv[c], "-sns") && c < argc - 1) { } else if (!strcmp(argv[c], "-sns") && c < argc - 1) {
config.sns_strength = ExUtilGetInt(argv[++c], 0, &parse_error); config.sns_strength = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-f") && c < argc - 1) { } else if (!strcmp(argv[c], "-f") && c < argc - 1) {
config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error); config.filter_strength = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-af")) { } else if (!strcmp(argv[c], "-af")) {
config.autofilter = 1; config.autofilter = 1;
} else if (!strcmp(argv[c], "-jpeg_like")) { } else if (!strcmp(argv[c], "-jpeg_like")) {
@ -788,28 +772,34 @@ int main(int argc, const char *argv[]) {
} else if (!strcmp(argv[c], "-nostrong")) { } else if (!strcmp(argv[c], "-nostrong")) {
config.filter_type = 0; config.filter_type = 0;
} else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) { } else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
config.filter_sharpness = ExUtilGetInt(argv[++c], 0, &parse_error); config.filter_sharpness = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-sharp_yuv")) {
config.use_sharp_yuv = 1;
} else if (!strcmp(argv[c], "-pass") && c < argc - 1) { } else if (!strcmp(argv[c], "-pass") && c < argc - 1) {
config.pass = ExUtilGetInt(argv[++c], 0, &parse_error); config.pass = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-pre") && c < argc - 1) { } else if (!strcmp(argv[c], "-pre") && c < argc - 1) {
config.preprocessing = ExUtilGetInt(argv[++c], 0, &parse_error); config.preprocessing = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-segments") && c < argc - 1) { } else if (!strcmp(argv[c], "-segments") && c < argc - 1) {
config.segments = ExUtilGetInt(argv[++c], 0, &parse_error); config.segments = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) { } else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) {
config.partition_limit = ExUtilGetInt(argv[++c], 0, &parse_error); config.partition_limit = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-map") && c < argc - 1) { } else if (!strcmp(argv[c], "-map") && c < argc - 1) {
picture.extra_info_type = ExUtilGetInt(argv[++c], 0, &parse_error); picture.extra_info_type = strtol(argv[++c], NULL, 0);
#ifdef WEBP_EXPERIMENTAL_FEATURES
} else if (!strcmp(argv[c], "-444")) {
picture.colorspace = WEBP_YUV444;
} else if (!strcmp(argv[c], "-422")) {
picture.colorspace = WEBP_YUV422;
} else if (!strcmp(argv[c], "-gray")) {
picture.colorspace = WEBP_YUV400;
#endif
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) { } else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
crop = 1; crop = 1;
crop_x = ExUtilGetInt(argv[++c], 0, &parse_error); crop_x = strtol(argv[++c], NULL, 0);
crop_y = ExUtilGetInt(argv[++c], 0, &parse_error); crop_y = strtol(argv[++c], NULL, 0);
crop_w = ExUtilGetInt(argv[++c], 0, &parse_error); crop_w = strtol(argv[++c], NULL, 0);
crop_h = ExUtilGetInt(argv[++c], 0, &parse_error); crop_h = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-resize") && c < argc - 2) { } else if (!strcmp(argv[c], "-resize") && c < argc - 2) {
resize_w = ExUtilGetInt(argv[++c], 0, &parse_error); resize_w = strtol(argv[++c], NULL, 0);
resize_h = ExUtilGetInt(argv[++c], 0, &parse_error); resize_h = strtol(argv[++c], NULL, 0);
#ifndef WEBP_DLL #ifndef WEBP_DLL
} else if (!strcmp(argv[c], "-noasm")) { } else if (!strcmp(argv[c], "-noasm")) {
VP8GetCPUInfo = NULL; VP8GetCPUInfo = NULL;
@ -817,7 +807,7 @@ int main(int argc, const char *argv[]) {
} else if (!strcmp(argv[c], "-version")) { } else if (!strcmp(argv[c], "-version")) {
const int version = WebPGetEncoderVersion(); const int version = WebPGetEncoderVersion();
printf("%d.%d.%d\n", printf("%d.%d.%d\n",
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
return 0; return 0;
} else if (!strcmp(argv[c], "-progress")) { } else if (!strcmp(argv[c], "-progress")) {
show_progress = 1; show_progress = 1;
@ -894,9 +884,6 @@ int main(int argc, const char *argv[]) {
#endif #endif
} else if (!strcmp(argv[c], "-v")) { } else if (!strcmp(argv[c], "-v")) {
verbose = 1; verbose = 1;
} else if (!strcmp(argv[c], "--")) {
if (c < argc - 1) in_file = argv[++c];
break;
} else if (argv[c][0] == '-') { } else if (argv[c][0] == '-') {
fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]); fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
HelpLong(); HelpLong();
@ -904,11 +891,6 @@ int main(int argc, const char *argv[]) {
} else { } else {
in_file = argv[c]; in_file = argv[c];
} }
if (parse_error) {
HelpLong();
return -1;
}
} }
if (in_file == NULL) { if (in_file == NULL) {
fprintf(stderr, "No input file specified!\n"); fprintf(stderr, "No input file specified!\n");
@ -916,13 +898,6 @@ int main(int argc, const char *argv[]) {
goto Error; goto Error;
} }
if (use_lossless_preset == 1) {
if (!WebPConfigLosslessPreset(&config, lossless_preset)) {
fprintf(stderr, "Invalid lossless preset (-z %d)\n", lossless_preset);
goto Error;
}
}
// Check for unsupported command line options for lossless mode and log // Check for unsupported command line options for lossless mode and log
// warning for such options. // warning for such options.
if (!quiet && config.lossless == 1) { if (!quiet && config.lossless == 1) {
@ -935,25 +910,15 @@ int main(int argc, const char *argv[]) {
" encoding. Ignoring this option!\n"); " encoding. Ignoring this option!\n");
} }
} }
// If a target size or PSNR was given, but somehow the -pass option was
// omitted, force a reasonable value.
if (config.target_size > 0 || config.target_PSNR > 0) {
if (config.pass == 1) config.pass = 6;
}
if (!WebPValidateConfig(&config)) { if (!WebPValidateConfig(&config)) {
fprintf(stderr, "Error! Invalid configuration.\n"); fprintf(stderr, "Error! Invalid configuration.\n");
goto Error; goto Error;
} }
// Read the input. We need to decide if we prefer ARGB or YUVA // Read the input
// samples, depending on the expected compression mode (this saves
// some conversion steps).
picture.use_argb = (config.lossless || config.use_sharp_yuv ||
config.preprocessing > 0 ||
crop || (resize_w | resize_h) > 0);
if (verbose) { if (verbose) {
StopwatchReset(&stop_watch); StopwatchReadAndReset(&stop_watch);
} }
if (!ReadPicture(in_file, &picture, keep_alpha, if (!ReadPicture(in_file, &picture, keep_alpha,
(keep_metadata == 0) ? NULL : &metadata)) { (keep_metadata == 0) ? NULL : &metadata)) {
@ -961,9 +926,8 @@ int main(int argc, const char *argv[]) {
goto Error; goto Error;
} }
picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL; picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL;
if (keep_alpha == 2) {
if (blend_alpha) { WebPCleanupTransparentArea(&picture);
WebPBlendAlpha(&picture, background_color);
} }
if (verbose) { if (verbose) {
@ -972,9 +936,8 @@ int main(int argc, const char *argv[]) {
} }
// Open the output // Open the output
if (out_file != NULL) { if (out_file) {
const int use_stdout = !strcmp(out_file, "-"); out = fopen(out_file, "wb");
out = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(out_file, "wb");
if (out == NULL) { if (out == NULL) {
fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file); fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
goto Error; goto Error;
@ -1002,9 +965,9 @@ int main(int argc, const char *argv[]) {
picture.user_data = (void*)in_file; picture.user_data = (void*)in_file;
} }
// Crop & resize. // Compress
if (verbose) { if (verbose) {
StopwatchReset(&stop_watch); StopwatchReadAndReset(&stop_watch);
} }
if (crop != 0) { if (crop != 0) {
// We use self-cropping using a view. // We use self-cropping using a view.
@ -1019,22 +982,12 @@ int main(int argc, const char *argv[]) {
goto Error; goto Error;
} }
} }
if (verbose && (crop != 0 || (resize_w | resize_h) > 0)) {
const double preproc_time = StopwatchReadAndReset(&stop_watch);
fprintf(stderr, "Time to crop/resize picture: %.3fs\n", preproc_time);
}
if (picture.extra_info_type > 0) { if (picture.extra_info_type > 0) {
AllocExtraInfo(&picture); AllocExtraInfo(&picture);
} }
if (print_distortion >= 0) { // Save original picture for later comparison if (print_distortion >= 0) { // Save original picture for later comparison
WebPPictureCopy(&picture, &original_picture); WebPPictureCopy(&picture, &original_picture);
} }
// Compress.
if (verbose) {
StopwatchReset(&stop_watch);
}
if (!WebPEncode(&config, &picture)) { if (!WebPEncode(&config, &picture)) {
fprintf(stderr, "Error! Cannot encode picture as WebP\n"); fprintf(stderr, "Error! Cannot encode picture as WebP\n");
fprintf(stderr, "Error code: %d (%s)\n", fprintf(stderr, "Error code: %d (%s)\n",
@ -1055,75 +1008,42 @@ int main(int argc, const char *argv[]) {
} }
} }
if (keep_metadata != 0) { if (keep_metadata != 0 && out != NULL) {
if (out != NULL) { if (!WriteWebPWithMetadata(out, &picture, &memory_writer,
if (!WriteWebPWithMetadata(out, &picture, &memory_writer, &metadata, keep_metadata, &metadata_written)) {
&metadata, keep_metadata, &metadata_written)) { fprintf(stderr, "Error writing WebP file with metadata!\n");
fprintf(stderr, "Error writing WebP file with metadata!\n"); goto Error;
goto Error;
}
} else { // output is disabled, just display the metadata stats.
const struct {
const MetadataPayload* const payload;
int flag;
} *iter, info[] = {
{ &metadata.exif, METADATA_EXIF },
{ &metadata.iccp, METADATA_ICC },
{ &metadata.xmp, METADATA_XMP },
{ NULL, 0 }
};
uint32_t unused1 = 0;
uint64_t unused2 = 0;
for (iter = info; iter->payload != NULL; ++iter) {
if (UpdateFlagsAndSize(iter->payload, !!(keep_metadata & iter->flag),
0, &unused1, &unused2)) {
metadata_written |= iter->flag;
}
}
} }
} }
if (!quiet) { if (!quiet) {
if (!short_output || print_distortion < 0) { if (config.lossless) {
if (config.lossless) { PrintExtraInfoLossless(&picture, short_output, in_file);
PrintExtraInfoLossless(&picture, short_output, in_file); } else {
} else { PrintExtraInfoLossy(&picture, short_output, config.low_memory, in_file);
PrintExtraInfoLossy(&picture, short_output, config.low_memory, in_file);
}
}
if (!short_output && picture.extra_info_type > 0) {
PrintMapInfo(&picture);
}
if (print_distortion >= 0) { // print distortion
static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
float values[5];
if (!WebPPictureDistortion(&picture, &original_picture,
print_distortion, values)) {
fprintf(stderr, "Error while computing the distortion.\n");
goto Error;
}
if (!short_output) {
fprintf(stderr, "%s: ", distortion_names[print_distortion]);
fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f Total:%.2f\n",
values[0], values[1], values[2], values[3], values[4]);
} else {
fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]);
}
} }
if (!short_output) { if (!short_output) {
PrintMetadataInfo(&metadata, metadata_written); PrintMetadataInfo(&metadata, metadata_written);
} }
} }
if (!quiet && !short_output && print_distortion >= 0) { // print distortion
static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
float values[5];
WebPPictureDistortion(&picture, &original_picture,
print_distortion, values);
fprintf(stderr, "%s: Y:%.2f U:%.2f V:%.2f A:%.2f Total:%.2f\n",
distortion_names[print_distortion],
values[0], values[1], values[2], values[3], values[4]);
}
return_value = 0; return_value = 0;
Error: Error:
WebPMemoryWriterClear(&memory_writer); free(memory_writer.mem);
free(picture.extra_info); free(picture.extra_info);
MetadataFree(&metadata); MetadataFree(&metadata);
WebPPictureFree(&picture); WebPPictureFree(&picture);
WebPPictureFree(&original_picture); WebPPictureFree(&original_picture);
if (out != NULL && out != stdout) { if (out != NULL) {
fclose(out); fclose(out);
} }

View File

@ -17,178 +17,367 @@
#include <string.h> #include <string.h>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif #endif
#include "../examples/example_util.h" #ifdef WEBP_HAVE_PNG
#include "../imageio/image_enc.h" #include <png.h>
#include "../imageio/webpdec.h" #endif
#ifdef HAVE_WINCODEC_H
#ifdef __MINGW32__
#define INITGUID // Without this GUIDs are declared extern and fail to link
#endif
#define CINTERFACE
#define COBJMACROS
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
// code with COBJMACROS.
#include <shlwapi.h>
#include <windows.h>
#include <wincodec.h>
#endif
#include "webp/decode.h"
#include "./example_util.h"
#include "./stopwatch.h" #include "./stopwatch.h"
static int verbose = 0; static int verbose = 0;
static int quiet = 0;
#ifndef WEBP_DLL #ifndef WEBP_DLL
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
extern void* VP8GetCPUInfo; // opaque forward declaration. extern void* VP8GetCPUInfo; // opaque forward declaration.
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_DLL #endif // WEBP_DLL
//------------------------------------------------------------------------------
static int SaveOutput(const WebPDecBuffer* const buffer, // Output types
WebPOutputFileFormat format, const char* const out_file) { typedef enum {
const int use_stdout = (out_file != NULL) && !strcmp(out_file, "-"); PNG = 0,
int ok = 1; PAM,
Stopwatch stop_watch; PPM,
PGM,
YUV,
ALPHA_PLANE_ONLY // this is for experimenting only
} OutputFileFormat;
if (verbose) { #ifdef HAVE_WINCODEC_H
StopwatchReset(&stop_watch);
#define IFS(fn) \
do { \
if (SUCCEEDED(hr)) { \
hr = (fn); \
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
} \
} while (0)
#ifdef __cplusplus
#define MAKE_REFGUID(x) (x)
#else
#define MAKE_REFGUID(x) &(x)
#endif
static HRESULT CreateOutputStream(const char* out_file_name, IStream** stream) {
HRESULT hr = S_OK;
IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream));
if (FAILED(hr)) {
fprintf(stderr, "Error opening output file %s (%08lx)\n",
out_file_name, hr);
} }
ok = WebPSaveImage(buffer, format, out_file); return hr;
}
if (ok) { static HRESULT WriteUsingWIC(const char* out_file_name, REFGUID container_guid,
if (!quiet) { unsigned char* rgb, int stride,
if (use_stdout) { uint32_t width, uint32_t height, int has_alpha) {
fprintf(stderr, "Saved to stdout\n"); HRESULT hr = S_OK;
} else { IWICImagingFactory* factory = NULL;
fprintf(stderr, "Saved file %s\n", out_file); IWICBitmapFrameEncode* frame = NULL;
} IWICBitmapEncoder* encoder = NULL;
IStream* stream = NULL;
WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
: GUID_WICPixelFormat24bppBGR;
IFS(CoInitialize(NULL));
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
CLSCTX_INPROC_SERVER,
MAKE_REFGUID(IID_IWICImagingFactory),
(LPVOID*)&factory));
if (hr == REGDB_E_CLASSNOTREG) {
fprintf(stderr,
"Couldn't access Windows Imaging Component (are you running "
"Windows XP SP3 or newer?). PNG support not available. "
"Use -ppm or -pgm for available PPM and PGM formats.\n");
}
IFS(CreateOutputStream(out_file_name, &stream));
IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
&encoder));
IFS(IWICBitmapEncoder_Initialize(encoder, stream,
WICBitmapEncoderNoCache));
IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
height * stride, rgb));
IFS(IWICBitmapFrameEncode_Commit(frame));
IFS(IWICBitmapEncoder_Commit(encoder));
if (frame != NULL) IUnknown_Release(frame);
if (encoder != NULL) IUnknown_Release(encoder);
if (factory != NULL) IUnknown_Release(factory);
if (stream != NULL) IUnknown_Release(stream);
return hr;
}
static int WritePNG(const char* out_file_name,
const WebPDecBuffer* const buffer) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
unsigned char* const rgb = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const int has_alpha = (buffer->colorspace == MODE_BGRA);
return SUCCEEDED(WriteUsingWIC(out_file_name,
MAKE_REFGUID(GUID_ContainerFormatPng),
rgb, stride, width, height, has_alpha));
}
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
static void PNGAPI error_function(png_structp png, png_const_charp dummy) {
(void)dummy; // remove variable-unused warning
longjmp(png_jmpbuf(png), 1);
}
static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
unsigned char* const rgb = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const int has_alpha = (buffer->colorspace == MODE_RGBA);
png_structp png;
png_infop info;
png_uint_32 y;
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, error_function, NULL);
if (png == NULL) {
return 0;
}
info = png_create_info_struct(png);
if (info == NULL) {
png_destroy_write_struct(&png, NULL);
return 0;
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_write_struct(&png, &info);
return 0;
}
png_init_io(png, out_file);
png_set_IHDR(png, info, width, height, 8,
has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(png, info);
for (y = 0; y < height; ++y) {
png_bytep row = rgb + y * stride;
png_write_rows(png, &row, 1);
}
png_write_end(png, info);
png_destroy_write_struct(&png, &info);
return 1;
}
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
(void)out_file;
(void)buffer;
fprintf(stderr, "PNG support not compiled. Please install the libpng "
"development package before building.\n");
fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
return 0;
}
#endif
static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const unsigned char* const rgb = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const size_t bytes_per_px = alpha ? 4 : 3;
uint32_t y;
if (alpha) {
fprintf(fout, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n"
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
} else {
fprintf(fout, "P6\n%d %d\n255\n", width, height);
}
for (y = 0; y < height; ++y) {
if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) {
return 0;
} }
if (verbose) { }
const double write_time = StopwatchReadAndReset(&stop_watch); return 1;
fprintf(stderr, "Time to write output: %.3fs\n", write_time); }
static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const unsigned char* const a = buffer->u.YUVA.a;
const int a_stride = buffer->u.YUVA.a_stride;
uint32_t y;
assert(a != NULL);
fprintf(fout, "P5\n%d %d\n255\n", width, height);
for (y = 0; y < height; ++y) {
if (fwrite(a + y * a_stride, width, 1, fout) != 1) {
return 0;
}
}
return 1;
}
// format=PGM: save a grayscale PGM file using the IMC4 layout
// (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for
// viewing the samples, esp. for odd dimensions.
// format=YUV: just save the Y/U/V/A planes sequentially without header.
static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer,
OutputFileFormat format) {
const int width = buffer->width;
const int height = buffer->height;
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
int ok = 1;
int y;
const int pad = (format == YUV) ? 0 : 1;
const int uv_width = (width + 1) / 2;
const int uv_height = (height + 1) / 2;
const int out_stride = (width + pad) & ~pad;
const int a_height = yuv->a ? height : 0;
if (format == PGM) {
fprintf(fout, "P5\n%d %d\n255\n",
out_stride, height + uv_height + a_height);
}
for (y = 0; ok && y < height; ++y) {
ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1);
if (format == PGM) {
if (width & 1) fputc(0, fout); // padding byte
}
}
if (format == PGM) { // IMC4 layout
for (y = 0; ok && y < uv_height; ++y) {
ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1);
ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1);
} }
} else { } else {
if (use_stdout) { for (y = 0; ok && y < uv_height; ++y) {
fprintf(stderr, "Error writing to stdout !!\n"); ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1);
} else { }
fprintf(stderr, "Error writing file %s !!\n", out_file); for (y = 0; ok && y < uv_height; ++y) {
ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1);
}
}
for (y = 0; ok && y < a_height; ++y) {
ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1);
if (format == PGM) {
if (width & 1) fputc(0, fout); // padding byte
} }
} }
return ok; return ok;
} }
static void SaveOutput(const WebPDecBuffer* const buffer,
OutputFileFormat format, const char* const out_file) {
FILE* fout = NULL;
int needs_open_file = 1;
int ok = 1;
Stopwatch stop_watch;
if (verbose)
StopwatchReadAndReset(&stop_watch);
#ifdef HAVE_WINCODEC_H
needs_open_file = (format != PNG);
#endif
if (needs_open_file) {
fout = fopen(out_file, "wb");
if (!fout) {
fprintf(stderr, "Error opening output file %s\n", out_file);
return;
}
}
if (format == PNG) {
#ifdef HAVE_WINCODEC_H
ok &= WritePNG(out_file, buffer);
#else
ok &= WritePNG(fout, buffer);
#endif
} else if (format == PAM) {
ok &= WritePPM(fout, buffer, 1);
} else if (format == PPM) {
ok &= WritePPM(fout, buffer, 0);
} else if (format == PGM || format == YUV) {
ok &= WritePGMOrYUV(fout, buffer, format);
} else if (format == ALPHA_PLANE_ONLY) {
ok &= WriteAlphaPlane(fout, buffer);
}
if (fout) {
fclose(fout);
}
if (ok) {
printf("Saved file %s\n", out_file);
if (verbose) {
const double write_time = StopwatchReadAndReset(&stop_watch);
printf("Time to write output: %.3fs\n", write_time);
}
} else {
fprintf(stderr, "Error writing file %s !!\n", out_file);
}
}
static void Help(void) { static void Help(void) {
printf("Usage: dwebp in_file [options] [-o out_file]\n\n" printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
"Decodes the WebP image file to PNG format [Default]\n" "Decodes the WebP image file to PNG format [Default]\n"
"Use following options to convert into alternate image formats:\n" "Use following options to convert into alternate image formats:\n"
" -pam ......... save the raw RGBA samples as a color PAM\n" " -pam ......... save the raw RGBA samples as a color PAM\n"
" -ppm ......... save the raw RGB samples as a color PPM\n" " -ppm ......... save the raw RGB samples as a color PPM\n"
" -bmp ......... save as uncompressed BMP format\n"
" -tiff ........ save as uncompressed TIFF format\n"
" -pgm ......... save the raw YUV samples as a grayscale PGM\n" " -pgm ......... save the raw YUV samples as a grayscale PGM\n"
" file with IMC4 layout\n" " file with IMC4 layout.\n"
" -yuv ......... save the raw YUV samples in flat layout\n" " -yuv ......... save the raw YUV samples in flat layout.\n"
"\n" "\n"
" Other options are:\n" " Other options are:\n"
" -version ..... print version number and exit\n" " -version .... print version number and exit.\n"
" -nofancy ..... don't use the fancy YUV420 upscaler\n" " -nofancy ..... don't use the fancy YUV420 upscaler.\n"
" -nofilter .... disable in-loop filtering\n" " -nofilter .... disable in-loop filtering.\n"
" -nodither .... disable dithering\n"
" -dither <d> .. dithering strength (in 0..100)\n"
" -alpha_dither use alpha-plane dithering if needed\n"
" -mt .......... use multi-threading\n" " -mt .......... use multi-threading\n"
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n" " -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
" -resize <w> <h> ......... scale the output (*after* any cropping)\n" " -scale <w> <h> .......... scale the output (*after* any cropping)\n"
" -flip ........ flip the output vertically\n" " -alpha ....... only save the alpha plane.\n"
" -alpha ....... only save the alpha plane\n" " -h ....... this help message.\n"
" -incremental . use incremental decoding (useful for tests)\n" " -v ....... verbose (e.g. print encoding/decoding times)\n"
" -h ........... this help message\n"
" -v ........... verbose (e.g. print encoding/decoding times)\n"
" -quiet ....... quiet mode, don't print anything\n"
#ifndef WEBP_DLL #ifndef WEBP_DLL
" -noasm ....... disable all assembly optimizations\n" " -noasm ....... disable all assembly optimizations.\n"
#endif #endif
); );
} }
static const char* const kFormatType[] = { static const char* const kStatusMessages[] = {
"unspecified", "lossy", "lossless" "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
"UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
}; };
static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
WebPOutputFileFormat format,
int use_external_memory) {
uint8_t* external_buffer = NULL;
WebPDecBuffer* const output_buffer = &config->output;
int w = config->input.width;
int h = config->input.height;
if (config->options.use_scaling) {
w = config->options.scaled_width;
h = config->options.scaled_height;
} else if (config->options.use_cropping) {
w = config->options.crop_width;
h = config->options.crop_height;
}
if (format >= RGB && format <= rgbA_4444) {
const int bpp = (format == RGB || format == BGR) ? 3
: (format == RGBA_4444 || format == rgbA_4444 ||
format == RGB_565) ? 2
: 4;
uint32_t stride = bpp * w + 7; // <- just for exercising
external_buffer = (uint8_t*)malloc(stride * h);
if (external_buffer == NULL) return NULL;
output_buffer->u.RGBA.stride = stride;
output_buffer->u.RGBA.size = stride * h;
output_buffer->u.RGBA.rgba = external_buffer;
} else { // YUV and YUVA
const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace);
uint8_t* tmp;
uint32_t stride = w + 3;
uint32_t uv_stride = (w + 1) / 2 + 13;
uint32_t total_size = stride * h * (has_alpha ? 2 : 1)
+ 2 * uv_stride * (h + 1) / 2;
assert(format >= YUV && format <= YUVA);
external_buffer = (uint8_t*)malloc(total_size);
if (external_buffer == NULL) return NULL;
tmp = external_buffer;
output_buffer->u.YUVA.y = tmp;
output_buffer->u.YUVA.y_stride = stride;
output_buffer->u.YUVA.y_size = stride * h;
tmp += output_buffer->u.YUVA.y_size;
if (has_alpha) {
output_buffer->u.YUVA.a = tmp;
output_buffer->u.YUVA.a_stride = stride;
output_buffer->u.YUVA.a_size = stride * h;
tmp += output_buffer->u.YUVA.a_size;
} else {
output_buffer->u.YUVA.a = NULL;
output_buffer->u.YUVA.a_stride = 0;
}
output_buffer->u.YUVA.u = tmp;
output_buffer->u.YUVA.u_stride = uv_stride;
output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2;
tmp += output_buffer->u.YUVA.u_size;
output_buffer->u.YUVA.v = tmp;
output_buffer->u.YUVA.v_stride = uv_stride;
output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2;
tmp += output_buffer->u.YUVA.v_size;
assert(tmp <= external_buffer + total_size);
}
output_buffer->is_external_memory = use_external_memory;
return external_buffer;
}
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {
int ok = 0;
const char *in_file = NULL; const char *in_file = NULL;
const char *out_file = NULL; const char *out_file = NULL;
WebPDecoderConfig config; WebPDecoderConfig config;
WebPDecBuffer* const output_buffer = &config.output; WebPDecBuffer* const output_buffer = &config.output;
WebPBitstreamFeatures* const bitstream = &config.input; WebPBitstreamFeatures* const bitstream = &config.input;
WebPOutputFileFormat format = PNG; OutputFileFormat format = PNG;
uint8_t* external_buffer = NULL;
int use_external_memory = 0;
const uint8_t* data = NULL;
int incremental = 0;
int c; int c;
if (!WebPInitDecoderConfig(&config)) { if (!WebPInitDecoderConfig(&config)) {
@ -197,7 +386,6 @@ int main(int argc, const char *argv[]) {
} }
for (c = 1; c < argc; ++c) { for (c = 1; c < argc; ++c) {
int parse_error = 0;
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help(); Help();
return 0; return 0;
@ -213,12 +401,6 @@ int main(int argc, const char *argv[]) {
format = PAM; format = PAM;
} else if (!strcmp(argv[c], "-ppm")) { } else if (!strcmp(argv[c], "-ppm")) {
format = PPM; format = PPM;
} else if (!strcmp(argv[c], "-bmp")) {
format = BMP;
} else if (!strcmp(argv[c], "-tiff")) {
format = TIFF;
} else if (!strcmp(argv[c], "-quiet")) {
quiet = 1;
} else if (!strcmp(argv[c], "-version")) { } else if (!strcmp(argv[c], "-version")) {
const int version = WebPGetDecoderVersion(); const int version = WebPGetDecoderVersion();
printf("%d.%d.%d\n", printf("%d.%d.%d\n",
@ -227,65 +409,25 @@ int main(int argc, const char *argv[]) {
} else if (!strcmp(argv[c], "-pgm")) { } else if (!strcmp(argv[c], "-pgm")) {
format = PGM; format = PGM;
} else if (!strcmp(argv[c], "-yuv")) { } else if (!strcmp(argv[c], "-yuv")) {
format = RAW_YUV; format = YUV;
} else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) {
const char* const fmt = argv[++c];
if (!strcmp(fmt, "RGB")) format = RGB;
else if (!strcmp(fmt, "RGBA")) format = RGBA;
else if (!strcmp(fmt, "BGR")) format = BGR;
else if (!strcmp(fmt, "BGRA")) format = BGRA;
else if (!strcmp(fmt, "ARGB")) format = ARGB;
else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444;
else if (!strcmp(fmt, "RGB_565")) format = RGB_565;
else if (!strcmp(fmt, "rgbA")) format = rgbA;
else if (!strcmp(fmt, "bgrA")) format = bgrA;
else if (!strcmp(fmt, "Argb")) format = Argb;
else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444;
else if (!strcmp(fmt, "YUV")) format = YUV;
else if (!strcmp(fmt, "YUVA")) format = YUVA;
else {
fprintf(stderr, "Can't parse pixel_format %s\n", fmt);
parse_error = 1;
}
} else if (!strcmp(argv[c], "-external_memory") && c < argc - 1) {
use_external_memory = ExUtilGetInt(argv[++c], 0, &parse_error);
parse_error |= (use_external_memory > 2 || use_external_memory < 0);
if (parse_error) {
fprintf(stderr, "Can't parse 'external_memory' value %s\n", argv[c]);
}
} else if (!strcmp(argv[c], "-mt")) { } else if (!strcmp(argv[c], "-mt")) {
config.options.use_threads = 1; config.options.use_threads = 1;
} else if (!strcmp(argv[c], "-alpha_dither")) {
config.options.alpha_dithering_strength = 100;
} else if (!strcmp(argv[c], "-nodither")) {
config.options.dithering_strength = 0;
} else if (!strcmp(argv[c], "-dither") && c < argc - 1) {
config.options.dithering_strength =
ExUtilGetInt(argv[++c], 0, &parse_error);
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) { } else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
config.options.use_cropping = 1; config.options.use_cropping = 1;
config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error); config.options.crop_left = strtol(argv[++c], NULL, 0);
config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error); config.options.crop_top = strtol(argv[++c], NULL, 0);
config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error); config.options.crop_width = strtol(argv[++c], NULL, 0);
config.options.crop_height = ExUtilGetInt(argv[++c], 0, &parse_error); config.options.crop_height = strtol(argv[++c], NULL, 0);
} else if ((!strcmp(argv[c], "-scale") || !strcmp(argv[c], "-resize")) && } else if (!strcmp(argv[c], "-scale") && c < argc - 2) {
c < argc - 2) { // '-scale' is left for compatibility
config.options.use_scaling = 1; config.options.use_scaling = 1;
config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error); config.options.scaled_width = strtol(argv[++c], NULL, 0);
config.options.scaled_height = ExUtilGetInt(argv[++c], 0, &parse_error); config.options.scaled_height = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-flip")) {
config.options.flip = 1;
} else if (!strcmp(argv[c], "-v")) { } else if (!strcmp(argv[c], "-v")) {
verbose = 1; verbose = 1;
#ifndef WEBP_DLL #ifndef WEBP_DLL
} else if (!strcmp(argv[c], "-noasm")) { } else if (!strcmp(argv[c], "-noasm")) {
VP8GetCPUInfo = NULL; VP8GetCPUInfo = NULL;
#endif #endif
} else if (!strcmp(argv[c], "-incremental")) {
incremental = 1;
} else if (!strcmp(argv[c], "--")) {
if (c < argc - 1) in_file = argv[++c];
break;
} else if (argv[c][0] == '-') { } else if (argv[c][0] == '-') {
fprintf(stderr, "Unknown option '%s'\n", argv[c]); fprintf(stderr, "Unknown option '%s'\n", argv[c]);
Help(); Help();
@ -293,11 +435,6 @@ int main(int argc, const char *argv[]) {
} else { } else {
in_file = argv[c]; in_file = argv[c];
} }
if (parse_error) {
Help();
return -1;
}
} }
if (in_file == NULL) { if (in_file == NULL) {
@ -306,13 +443,28 @@ int main(int argc, const char *argv[]) {
return -1; return -1;
} }
if (quiet) verbose = 0;
{ {
Stopwatch stop_watch;
VP8StatusCode status = VP8_STATUS_OK; VP8StatusCode status = VP8_STATUS_OK;
int ok;
size_t data_size = 0; size_t data_size = 0;
if (!LoadWebP(in_file, &data, &data_size, bitstream)) { const uint8_t* data = NULL;
return -1;
if (!ExUtilReadFile(in_file, &data, &data_size)) return -1;
if (verbose)
StopwatchReadAndReset(&stop_watch);
status = WebPGetFeatures(data, data_size, bitstream);
if (status != VP8_STATUS_OK) {
goto end;
}
if (bitstream->has_animation) {
fprintf(stderr,
"Error! Decoding of an animated WebP file is not supported.\n"
" Use webpmux to extract the individual frames or\n"
" vwebp to view this image.\n");
} }
switch (format) { switch (format) {
@ -329,90 +481,47 @@ int main(int argc, const char *argv[]) {
case PPM: case PPM:
output_buffer->colorspace = MODE_RGB; // drops alpha for PPM output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
break; break;
case BMP:
output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
break;
case TIFF: // note: force pre-multiplied alpha
output_buffer->colorspace =
bitstream->has_alpha ? MODE_rgbA : MODE_RGB;
break;
case PGM: case PGM:
case RAW_YUV: case YUV:
output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
break; break;
case ALPHA_PLANE_ONLY: case ALPHA_PLANE_ONLY:
output_buffer->colorspace = MODE_YUVA; output_buffer->colorspace = MODE_YUVA;
break; break;
// forced modes: default:
case RGB: output_buffer->colorspace = MODE_RGB; break; free((void*)data);
case RGBA: output_buffer->colorspace = MODE_RGBA; break; return -1;
case BGR: output_buffer->colorspace = MODE_BGR; break;
case BGRA: output_buffer->colorspace = MODE_BGRA; break;
case ARGB: output_buffer->colorspace = MODE_ARGB; break;
case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break;
case RGB_565: output_buffer->colorspace = MODE_RGB_565; break;
case rgbA: output_buffer->colorspace = MODE_rgbA; break;
case bgrA: output_buffer->colorspace = MODE_bgrA; break;
case Argb: output_buffer->colorspace = MODE_Argb; break;
case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break;
case YUV: output_buffer->colorspace = MODE_YUV; break;
case YUVA: output_buffer->colorspace = MODE_YUVA; break;
default: goto Exit;
} }
status = WebPDecode(data, data_size, &config);
if (use_external_memory > 0 && format >= RGB) { if (verbose) {
external_buffer = AllocateExternalBuffer(&config, format, const double decode_time = StopwatchReadAndReset(&stop_watch);
use_external_memory); printf("Time to decode picture: %.3fs\n", decode_time);
if (external_buffer == NULL) goto Exit;
} }
end:
{ free((void*)data);
Stopwatch stop_watch;
if (verbose) StopwatchReset(&stop_watch);
if (incremental) {
status = DecodeWebPIncremental(data, data_size, &config);
} else {
status = DecodeWebP(data, data_size, &config);
}
if (verbose) {
const double decode_time = StopwatchReadAndReset(&stop_watch);
fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time);
}
}
ok = (status == VP8_STATUS_OK); ok = (status == VP8_STATUS_OK);
if (!ok) { if (!ok) {
PrintWebPError(in_file, status); fprintf(stderr, "Decoding of %s failed.\n", in_file);
goto Exit; fprintf(stderr, "Status: %d (%s)\n", status, kStatusMessages[status]);
return -1;
} }
} }
if (out_file != NULL) { if (out_file) {
if (!quiet) { printf("Decoded %s. Dimensions: %d x %d%s. Now saving...\n", in_file,
fprintf(stderr, "Decoded %s. Dimensions: %d x %d %s. Format: %s. " output_buffer->width, output_buffer->height,
"Now saving...\n", bitstream->has_alpha ? " (with alpha)" : "");
in_file, output_buffer->width, output_buffer->height, SaveOutput(output_buffer, format, out_file);
bitstream->has_alpha ? " (with alpha)" : "",
kFormatType[bitstream->format]);
}
ok = SaveOutput(output_buffer, format, out_file);
} else { } else {
if (!quiet) { printf("File %s can be decoded (dimensions: %d x %d)%s.\n",
fprintf(stderr, "File %s can be decoded " in_file, output_buffer->width, output_buffer->height,
"(dimensions: %d x %d %s. Format: %s).\n", bitstream->has_alpha ? " (with alpha)" : "");
in_file, output_buffer->width, output_buffer->height, printf("Nothing written; use -o flag to save the result as e.g. PNG.\n");
bitstream->has_alpha ? " (with alpha)" : "",
kFormatType[bitstream->format]);
fprintf(stderr, "Nothing written; "
"use -o flag to save the result as e.g. PNG.\n");
}
} }
Exit:
WebPFreeDecBuffer(output_buffer); WebPFreeDecBuffer(output_buffer);
free((void*)external_buffer);
free((void*)data); return 0;
return ok ? 0 : -1;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -11,48 +11,69 @@
// //
#include "./example_util.h" #include "./example_util.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
//------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus)
// String parsing extern "C" {
#endif
uint32_t ExUtilGetUInt(const char* const v, int base, int* const error) { // -----------------------------------------------------------------------------
char* end = NULL; // File I/O
const uint32_t n = (v != NULL) ? (uint32_t)strtoul(v, &end, base) : 0u;
if (end == v && error != NULL && !*error) { int ExUtilReadFile(const char* const file_name,
*error = 1; const uint8_t** data, size_t* data_size) {
fprintf(stderr, "Error! '%s' is not an integer.\n", int ok;
(v != NULL) ? v : "(null)"); void* file_data;
size_t file_size;
FILE* in;
if (file_name == NULL || data == NULL || data_size == NULL) return 0;
*data = NULL;
*data_size = 0;
in = fopen(file_name, "rb");
if (in == NULL) {
fprintf(stderr, "cannot open input file '%s'\n", file_name);
return 0;
} }
return n; fseek(in, 0, SEEK_END);
} file_size = ftell(in);
fseek(in, 0, SEEK_SET);
file_data = malloc(file_size);
if (file_data == NULL) return 0;
ok = (fread(file_data, file_size, 1, in) == 1);
fclose(in);
int ExUtilGetInt(const char* const v, int base, int* const error) { if (!ok) {
return (int)ExUtilGetUInt(v, base, error); fprintf(stderr, "Could not read %d bytes of data from file %s\n",
} (int)file_size, file_name);
free(file_data);
int ExUtilGetInts(const char* v, int base, int max_output, int output[]) { return 0;
int n, error = 0;
for (n = 0; v != NULL && n < max_output; ++n) {
const int value = ExUtilGetInt(v, base, &error);
if (error) return -1;
output[n] = value;
v = strchr(v, ',');
if (v != NULL) ++v; // skip over the trailing ','
} }
return n; *data = (uint8_t*)file_data;
*data_size = file_size;
return 1;
} }
float ExUtilGetFloat(const char* const v, int* const error) { int ExUtilWriteFile(const char* const file_name,
char* end = NULL; const uint8_t* data, size_t data_size) {
const float f = (v != NULL) ? (float)strtod(v, &end) : 0.f; int ok;
if (end == v && error != NULL && !*error) { FILE* out;
*error = 1;
fprintf(stderr, "Error! '%s' is not a floating point number.\n", if (file_name == NULL || data == NULL) {
(v != NULL) ? v : "(null)"); return 0;
} }
return f; out = fopen(file_name, "wb");
if (out == NULL) {
fprintf(stderr, "Error! Cannot open output file '%s'\n", file_name);
return 0;
}
ok = (fwrite(data, data_size, 1, out) == 1);
fclose(out);
return ok;
} }
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -15,27 +15,21 @@
#include "webp/types.h" #include "webp/types.h"
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
//------------------------------------------------------------------------------ // Allocates storage for entire file 'file_name' and returns contents and size
// String parsing // in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
// be deleted using free().
int ExUtilReadFile(const char* const file_name,
const uint8_t** data, size_t* data_size);
// Parses 'v' using strto(ul|l|d)(). If error is non-NULL, '*error' is set to // Write a data segment into a file named 'file_name'. Returns true if ok.
// true on failure while on success it is left unmodified to allow chaining of int ExUtilWriteFile(const char* const file_name,
// calls. An error is only printed on the first occurrence. const uint8_t* data, size_t data_size);
uint32_t ExUtilGetUInt(const char* const v, int base, int* const error);
int ExUtilGetInt(const char* const v, int base, int* const error);
float ExUtilGetFloat(const char* const v, int* const error);
// This variant of ExUtilGetInt() will parse multiple integers from a #if defined(__cplusplus) || defined(c_plusplus)
// comma-separated list. Up to 'max_output' integers are parsed.
// The result is placed in the output[] array, and the number of integers
// actually parsed is returned, or -1 if an error occurred.
int ExUtilGetInts(const char* v, int base, int max_output, int output[]);
#ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View File

@ -18,23 +18,140 @@
#include <string.h> #include <string.h>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif #endif
#ifdef WEBP_HAVE_GIF
#include <gif_lib.h> #include <gif_lib.h>
#include "webp/encode.h" #include "webp/encode.h"
#include "webp/mux.h" #include "webp/mux.h"
#include "../examples/example_util.h" #include "./example_util.h"
#include "../imageio/imageio_util.h"
#include "./gifdec.h" #define GIF_TRANSPARENT_MASK 0x01
#define GIF_DISPOSE_MASK 0x07
#define GIF_DISPOSE_SHIFT 2
#define TRANSPARENT_COLOR 0x00ffffff
#define WHITE_COLOR 0xffffffff
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static int transparent_index = GIF_INDEX_INVALID; // Opaque by default. static int transparent_index = -1; // No transparency by default.
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = { static void ClearPicture(WebPPicture* const picture, uint32_t color) {
int x, y;
for (y = 0; y < picture->height; ++y) {
uint32_t* const dst = picture->argb + y * picture->argb_stride;
for (x = 0; x < picture->width; ++x) dst[x] = color;
}
}
static void Remap(const uint8_t* const src, const GifFileType* const gif,
uint32_t* dst, int len) {
int i;
const GifColorType* colors;
const ColorMapObject* const cmap =
gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
if (cmap == NULL) return;
colors = cmap->Colors;
for (i = 0; i < len; ++i) {
const GifColorType c = colors[src[i]];
dst[i] = (src[i] == transparent_index) ? TRANSPARENT_COLOR
: c.Blue | (c.Green << 8) | (c.Red << 16) | (0xff << 24);
}
}
static int ReadSubImage(GifFileType* gif, WebPPicture* pic, WebPPicture* view) {
const GifImageDesc image_desc = gif->Image;
const int offset_x = image_desc.Left;
const int offset_y = image_desc.Top;
const int sub_w = image_desc.Width;
const int sub_h = image_desc.Height;
uint32_t* dst = NULL;
uint8_t* tmp = NULL;
int ok = 0;
// Use a view for the sub-picture:
if (!WebPPictureView(pic, offset_x, offset_y, sub_w, sub_h, view)) {
fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
sub_w, sub_h, offset_x, offset_y);
goto End;
}
dst = view->argb;
tmp = (uint8_t*)malloc(sub_w * sizeof(*tmp));
if (tmp == NULL) goto End;
if (image_desc.Interlace) { // Interlaced image.
// We need 4 passes, with the following offsets and jumps.
const int interlace_offsets[] = { 0, 4, 2, 1 };
const int interlace_jumps[] = { 8, 8, 4, 2 };
int pass;
for (pass = 0; pass < 4; ++pass) {
int y;
for (y = interlace_offsets[pass]; y < sub_h; y += interlace_jumps[pass]) {
if (DGifGetLine(gif, tmp, sub_w) == GIF_ERROR) goto End;
Remap(tmp, gif, dst + y * view->argb_stride, sub_w);
}
}
} else { // Non-interlaced image.
int y;
for (y = 0; y < sub_h; ++y) {
if (DGifGetLine(gif, tmp, sub_w) == GIF_ERROR) goto End;
Remap(tmp, gif, dst + y * view->argb_stride, sub_w);
}
}
// re-align the view with even offset (and adjust dimensions if needed).
WebPPictureView(pic, offset_x & ~1, offset_y & ~1,
sub_w + (offset_x & 1), sub_h + (offset_y & 1), view);
ok = 1;
End:
free(tmp);
return ok;
}
static int GetBackgroundColor(const ColorMapObject* const color_map,
GifWord bgcolor_idx, uint32_t* const bgcolor) {
if (transparent_index != -1 && bgcolor_idx == transparent_index) {
*bgcolor = TRANSPARENT_COLOR; // Special case.
return 1;
} else if (color_map == NULL || color_map->Colors == NULL
|| bgcolor_idx >= color_map->ColorCount) {
return 0; // Invalid color map or index.
} else {
const GifColorType color = color_map->Colors[bgcolor_idx];
*bgcolor = (0xff << 24)
| (color.Red << 16)
| (color.Green << 8)
| (color.Blue << 0);
return 1;
}
}
static void DisplayGifError(const GifFileType* const gif, int gif_error) {
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
// libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) && \
((GIFLIB_MAJOR == 4 && GIFLIB_MINOR >= 2) || GIFLIB_MAJOR > 4)
#if GIFLIB_MAJOR >= 5
// Static string actually, hence the const char* cast.
const char* error_str = (const char*)GifErrorString(
(gif == NULL) ? gif_error : gif->Error);
#else
const char* error_str = (const char*)GifErrorString();
(void)gif;
#endif
if (error_str == NULL) error_str = "Unknown error";
fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
#else
(void)gif;
fprintf(stderr, "GIFLib Error %d: ", gif_error);
PrintGifError();
fprintf(stderr, "\n");
#endif
}
static const char* const kErrorMessages[] = {
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA", "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA" "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
}; };
@ -44,41 +161,21 @@ static const char* ErrorString(WebPMuxError err) {
return kErrorMessages[-err]; return kErrorMessages[-err];
} }
enum {
METADATA_ICC = (1 << 0),
METADATA_XMP = (1 << 1),
METADATA_ALL = METADATA_ICC | METADATA_XMP
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static void Help(void) { static void Help(void) {
printf("Usage:\n"); printf("Usage:\n");
printf(" gif2webp [options] gif_file -o webp_file\n"); printf(" gif2webp [options] gif_file -o webp_file\n");
printf("Options:\n"); printf("options:\n");
printf(" -h / -help ............. this help\n"); printf(" -h / -help ............ this help\n");
printf(" -lossy ................. encode image using lossy compression\n"); printf(" -lossy ................. Encode image using lossy compression.\n");
printf(" -mixed ................. for each frame in the image, pick lossy\n"
" or lossless compression heuristically\n");
printf(" -q <float> ............. quality factor (0:small..100:big)\n"); printf(" -q <float> ............. quality factor (0:small..100:big)\n");
printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n"); printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
printf(" -min_size .............. minimize output size (default:off)\n"
" lossless compression by default; can be\n"
" combined with -q, -m, -lossy or -mixed\n"
" options\n");
printf(" -kmin <int> ............ min distance between key frames\n");
printf(" -kmax <int> ............ max distance between key frames\n");
printf(" -f <int> ............... filter strength (0=off..100)\n"); printf(" -f <int> ............... filter strength (0=off..100)\n");
printf(" -metadata <string> ..... comma separated list of metadata to\n");
printf(" ");
printf("copy from the input to the output if present\n");
printf(" "
"Valid values: all, none, icc, xmp (default)\n");
printf(" -mt .................... use multi-threading if available\n");
printf("\n"); printf("\n");
printf(" -version ............... print version number and exit\n"); printf(" -version ............... print version number and exit.\n");
printf(" -v ..................... verbose\n"); printf(" -v ..................... verbose.\n");
printf(" -quiet ................. don't print anything\n"); printf(" -quiet ................. don't print anything.\n");
printf("\n"); printf("\n");
} }
@ -92,55 +189,36 @@ int main(int argc, const char *argv[]) {
const char *in_file = NULL, *out_file = NULL; const char *in_file = NULL, *out_file = NULL;
FILE* out = NULL; FILE* out = NULL;
GifFileType* gif = NULL; GifFileType* gif = NULL;
int frame_duration = 0; WebPPicture picture;
int frame_timestamp = 0; WebPMuxFrameInfo frame;
GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE; WebPMuxAnimParams anim = { WHITE_COLOR, 0 };
WebPPicture frame; // Frame rectangle only (not disposed). int is_first_frame = 1;
WebPPicture curr_canvas; // Not disposed.
WebPPicture prev_canvas; // Disposed.
WebPAnimEncoder* enc = NULL;
WebPAnimEncoderOptions enc_options;
WebPConfig config;
int is_first_frame = 1; // Whether we are processing the first frame.
int done; int done;
int c; int c;
int quiet = 0; int quiet = 0;
WebPData webp_data; WebPConfig config;
int keep_metadata = METADATA_XMP; // ICC not output by default.
WebPData icc_data;
int stored_icc = 0; // Whether we have already stored an ICC profile.
WebPData xmp_data;
int stored_xmp = 0; // Whether we have already stored an XMP profile.
int loop_count = 0;
int stored_loop_count = 0; // Whether we have found an explicit loop count.
WebPMux* mux = NULL; WebPMux* mux = NULL;
WebPData webp_data = { NULL, 0 };
int stored_icc = 0; // Whether we have already stored an ICC profile.
int stored_xmp = 0;
int default_kmin = 1; // Whether to use default kmin value. memset(&frame, 0, sizeof(frame));
int default_kmax = 1; frame.id = WEBP_CHUNK_ANMF;
frame.dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
if (!WebPConfigInit(&config) || !WebPAnimEncoderOptionsInit(&enc_options) || if (!WebPConfigInit(&config) || !WebPPictureInit(&picture)) {
!WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) ||
!WebPPictureInit(&prev_canvas)) {
fprintf(stderr, "Error! Version mismatch!\n"); fprintf(stderr, "Error! Version mismatch!\n");
return -1; return -1;
} }
config.lossless = 1; // Use lossless compression by default. config.lossless = 1; // Use lossless compression by default.
WebPDataInit(&webp_data);
WebPDataInit(&icc_data);
WebPDataInit(&xmp_data);
if (argc == 1) { if (argc == 1) {
Help(); Help();
return 0; return 0;
} }
for (c = 1; c < argc; ++c) { for (c = 1; c < argc; ++c) {
int parse_error = 0;
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help(); Help();
return 0; return 0;
@ -148,64 +226,12 @@ int main(int argc, const char *argv[]) {
out_file = argv[++c]; out_file = argv[++c];
} else if (!strcmp(argv[c], "-lossy")) { } else if (!strcmp(argv[c], "-lossy")) {
config.lossless = 0; config.lossless = 0;
} else if (!strcmp(argv[c], "-mixed")) {
enc_options.allow_mixed = 1;
config.lossless = 0;
} else if (!strcmp(argv[c], "-q") && c < argc - 1) { } else if (!strcmp(argv[c], "-q") && c < argc - 1) {
config.quality = ExUtilGetFloat(argv[++c], &parse_error); config.quality = (float)strtod(argv[++c], NULL);
} else if (!strcmp(argv[c], "-m") && c < argc - 1) { } else if (!strcmp(argv[c], "-m") && c < argc - 1) {
config.method = ExUtilGetInt(argv[++c], 0, &parse_error); config.method = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-min_size")) {
enc_options.minimize_size = 1;
} else if (!strcmp(argv[c], "-kmax") && c < argc - 1) {
enc_options.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
default_kmax = 0;
} else if (!strcmp(argv[c], "-kmin") && c < argc - 1) {
enc_options.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
default_kmin = 0;
} else if (!strcmp(argv[c], "-f") && c < argc - 1) { } else if (!strcmp(argv[c], "-f") && c < argc - 1) {
config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error); config.filter_strength = strtol(argv[++c], NULL, 0);
} else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
static const struct {
const char* option;
int flag;
} kTokens[] = {
{ "all", METADATA_ALL },
{ "none", 0 },
{ "icc", METADATA_ICC },
{ "xmp", METADATA_XMP },
};
const size_t kNumTokens = sizeof(kTokens) / sizeof(*kTokens);
const char* start = argv[++c];
const char* const end = start + strlen(start);
keep_metadata = 0;
while (start < end) {
size_t i;
const char* token = strchr(start, ',');
if (token == NULL) token = end;
for (i = 0; i < kNumTokens; ++i) {
if ((size_t)(token - start) == strlen(kTokens[i].option) &&
!strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) {
if (kTokens[i].flag != 0) {
keep_metadata |= kTokens[i].flag;
} else {
keep_metadata = 0;
}
break;
}
}
if (i == kNumTokens) {
fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
(int)(token - start), start);
Help();
return -1;
}
start = token + 1;
}
} else if (!strcmp(argv[c], "-mt")) {
++config.thread_level;
} else if (!strcmp(argv[c], "-version")) { } else if (!strcmp(argv[c], "-version")) {
const int enc_version = WebPGetEncoderVersion(); const int enc_version = WebPGetEncoderVersion();
const int mux_version = WebPGetMuxVersion(); const int mux_version = WebPGetMuxVersion();
@ -216,13 +242,8 @@ int main(int argc, const char *argv[]) {
return 0; return 0;
} else if (!strcmp(argv[c], "-quiet")) { } else if (!strcmp(argv[c], "-quiet")) {
quiet = 1; quiet = 1;
enc_options.verbose = 0;
} else if (!strcmp(argv[c], "-v")) { } else if (!strcmp(argv[c], "-v")) {
verbose = 1; verbose = 1;
enc_options.verbose = 1;
} else if (!strcmp(argv[c], "--")) {
if (c < argc - 1) in_file = argv[++c];
break;
} else if (argv[c][0] == '-') { } else if (argv[c][0] == '-') {
fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]); fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
Help(); Help();
@ -230,21 +251,7 @@ int main(int argc, const char *argv[]) {
} else { } else {
in_file = argv[c]; in_file = argv[c];
} }
if (parse_error) {
Help();
return -1;
}
} }
// Appropriate default kmin, kmax values for lossy and lossless.
if (default_kmin) {
enc_options.kmin = config.lossless ? 9 : 3;
}
if (default_kmax) {
enc_options.kmax = config.lossless ? 17 : 5;
}
if (!WebPValidateConfig(&config)) { if (!WebPValidateConfig(&config)) {
fprintf(stderr, "Error! Invalid configuration.\n"); fprintf(stderr, "Error! Invalid configuration.\n");
goto End; goto End;
@ -257,13 +264,26 @@ int main(int argc, const char *argv[]) {
} }
// Start the decoder object // Start the decoder object
#if LOCAL_GIF_PREREQ(5,0) #if defined(GIFLIB_MAJOR) && (GIFLIB_MAJOR >= 5)
// There was an API change in version 5.0.0.
gif = DGifOpenFileName(in_file, &gif_error); gif = DGifOpenFileName(in_file, &gif_error);
#else #else
gif = DGifOpenFileName(in_file); gif = DGifOpenFileName(in_file);
#endif #endif
if (gif == NULL) goto End; if (gif == NULL) goto End;
// Allocate picture buffer
picture.width = gif->SWidth;
picture.height = gif->SHeight;
picture.use_argb = 1;
if (!WebPPictureAlloc(&picture)) goto End;
mux = WebPMuxNew();
if (mux == NULL) {
fprintf(stderr, "ERROR: could not create a mux object.\n");
goto End;
}
// Loop over GIF images // Loop over GIF images
done = 0; done = 0;
do { do {
@ -272,86 +292,59 @@ int main(int argc, const char *argv[]) {
switch (type) { switch (type) {
case IMAGE_DESC_RECORD_TYPE: { case IMAGE_DESC_RECORD_TYPE: {
GIFFrameRect gif_rect; WebPPicture sub_image;
GifImageDesc* const image_desc = &gif->Image; WebPMemoryWriter memory;
if (frame.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
ClearPicture(&picture, anim.bgcolor);
}
if (!DGifGetImageDesc(gif)) goto End; if (!DGifGetImageDesc(gif)) goto End;
if (!ReadSubImage(gif, &picture, &sub_image)) goto End;
if (is_first_frame) { if (!config.lossless) {
if (verbose) { // We need to call BGRA variant because of the way we do Remap(). Note
printf("Canvas screen: %d x %d\n", gif->SWidth, gif->SHeight); // that 'sub_image' will no longer be a view and own some memory.
} WebPPictureImportBGRA(
// Fix some broken GIF global headers that report &sub_image, (uint8_t*)sub_image.argb,
// 0 x 0 screen dimension. sub_image.argb_stride * sizeof(*sub_image.argb));
if (gif->SWidth == 0 || gif->SHeight == 0) { sub_image.use_argb = 0;
image_desc->Left = 0; } else {
image_desc->Top = 0; sub_image.use_argb = 1;
gif->SWidth = image_desc->Width;
gif->SHeight = image_desc->Height;
if (gif->SWidth <= 0 || gif->SHeight <= 0) {
goto End;
}
if (verbose) {
printf("Fixed canvas screen dimension to: %d x %d\n",
gif->SWidth, gif->SHeight);
}
}
// Allocate current buffer.
frame.width = gif->SWidth;
frame.height = gif->SHeight;
frame.use_argb = 1;
if (!WebPPictureAlloc(&frame)) goto End;
GIFClearPic(&frame, NULL);
WebPPictureCopy(&frame, &curr_canvas);
WebPPictureCopy(&frame, &prev_canvas);
// Background color.
GIFGetBackgroundColor(gif->SColorMap, gif->SBackGroundColor,
transparent_index,
&enc_options.anim_params.bgcolor);
// Initialize encoder.
enc = WebPAnimEncoderNew(curr_canvas.width, curr_canvas.height,
&enc_options);
if (enc == NULL) {
fprintf(stderr,
"Error! Could not create encoder object. Possibly due to "
"a memory error.\n");
goto End;
}
is_first_frame = 0;
} }
// Some even more broken GIF can have sub-rect with zero width/height. sub_image.writer = WebPMemoryWrite;
if (image_desc->Width == 0 || image_desc->Height == 0) { sub_image.custom_ptr = &memory;
image_desc->Width = gif->SWidth; WebPMemoryWriterInit(&memory);
image_desc->Height = gif->SHeight; if (!WebPEncode(&config, &sub_image)) {
} fprintf(stderr, "Error! Cannot encode picture as WebP\n");
fprintf(stderr, "Error code: %d\n", sub_image.error_code);
if (!GIFReadFrame(gif, transparent_index, &gif_rect, &frame)) {
goto End; goto End;
} }
// Blend frame rectangle with previous canvas to compose full canvas.
// Note that 'curr_canvas' is same as 'prev_canvas' at this point.
GIFBlendFrames(&frame, &gif_rect, &curr_canvas);
if (!WebPAnimEncoderAdd(enc, &curr_canvas, frame_timestamp, &config)) { // Now we have all the info about the frame, as a Graphic Control
fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc)); // Extension Block always appears before the Image Descriptor Block.
// So add the frame to mux.
frame.x_offset = gif->Image.Left & ~1;
frame.y_offset = gif->Image.Top & ~1;
frame.bitstream.bytes = memory.mem;
frame.bitstream.size = memory.size;
err = WebPMuxPushFrame(mux, &frame, 1);
if (err != WEBP_MUX_OK) {
fprintf(stderr, "ERROR (%s): Could not add animation frame.\n",
ErrorString(err));
goto End;
} }
if (verbose) {
// Update canvases. printf("Added frame %dx%d (offset:%d,%d duration:%d) ",
GIFDisposeFrame(orig_dispose, &gif_rect, &prev_canvas, &curr_canvas); sub_image.width, sub_image.height,
GIFCopyPixels(&curr_canvas, &prev_canvas); frame.x_offset, frame.y_offset,
frame.duration);
// Update timestamp (for next frame). printf("dispose:%d transparent index:%d\n",
frame_timestamp += frame_duration; frame.dispose_method, transparent_index);
}
// In GIF, graphic control extensions are optional for a frame, so we WebPDataClear(&frame.bitstream);
// may not get one before reading the next frame. To handle this case, WebPPictureFree(&sub_image);
// we reset frame properties to reasonable defaults for the next frame.
orig_dispose = GIF_DISPOSE_NONE;
frame_duration = 0;
transparent_index = GIF_INDEX_INVALID;
break; break;
} }
case EXTENSION_RECORD_TYPE: { case EXTENSION_RECORD_TYPE: {
@ -360,16 +353,35 @@ int main(int argc, const char *argv[]) {
if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) { if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) {
goto End; goto End;
} }
if (data == NULL) continue;
switch (extension) { switch (extension) {
case COMMENT_EXT_FUNC_CODE: { case COMMENT_EXT_FUNC_CODE: {
break; // Do nothing for now. break; // Do nothing for now.
} }
case GRAPHICS_EXT_FUNC_CODE: { case GRAPHICS_EXT_FUNC_CODE: {
if (!GIFReadGraphicsExtension(data, &frame_duration, &orig_dispose, const int flags = data[1];
&transparent_index)) { const int dispose = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
goto End; const int delay = data[2] | (data[3] << 8); // In 10 ms units.
if (data[0] != 4) goto End;
frame.duration = delay * 10; // Duration is in 1 ms units for WebP.
if (dispose == 3) {
fprintf(stderr, "WARNING: GIF_DISPOSE_RESTORE not supported.");
// failsafe. TODO(urvang): emulate the correct behaviour by
// recoding the whole frame.
frame.dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
} else {
frame.dispose_method =
(dispose == 2) ? WEBP_MUX_DISPOSE_BACKGROUND
: WEBP_MUX_DISPOSE_NONE;
}
transparent_index = (flags & GIF_TRANSPARENT_MASK) ? data[4] : -1;
if (is_first_frame) {
if (!GetBackgroundColor(gif->SColorMap, gif->SBackGroundColor,
&anim.bgcolor)) {
fprintf(stderr, "GIF decode warning: invalid background color "
"index. Assuming white background.\n");
}
ClearPicture(&picture, anim.bgcolor);
is_first_frame = 0;
} }
break; break;
} }
@ -378,27 +390,69 @@ int main(int argc, const char *argv[]) {
} }
case APPLICATION_EXT_FUNC_CODE: { case APPLICATION_EXT_FUNC_CODE: {
if (data[0] != 11) break; // Chunk is too short if (data[0] != 11) break; // Chunk is too short
if (!memcmp(data + 1, "NETSCAPE2.0", 11) || if (!memcmp(data + 1, "NETSCAPE2.0", 11)) {
!memcmp(data + 1, "ANIMEXTS1.0", 11)) { // Recognize and parse Netscape2.0 NAB extension for loop count.
if (!GIFReadLoopCount(gif, &data, &loop_count)) { if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End;
goto End; if (data == NULL) goto End; // Loop count sub-block missing.
} if (data[0] != 3 && data[1] != 1) break; // wrong size/marker
if (verbose) { anim.loop_count = data[2] | (data[3] << 8);
fprintf(stderr, "Loop count: %d\n", loop_count); if (verbose) printf("Loop count: %d\n", anim.loop_count);
}
stored_loop_count = (loop_count != 0);
} else { // An extension containing metadata. } else { // An extension containing metadata.
// We only store the first encountered chunk of each type, and // We only store the first encountered chunk of each type.
// only if requested by the user. const int is_xmp =
const int is_xmp = (keep_metadata & METADATA_XMP) && !stored_xmp && !memcmp(data + 1, "XMP DataXMP", 11);
!stored_xmp && const int is_icc =
!memcmp(data + 1, "XMP DataXMP", 11); !stored_icc && !memcmp(data + 1, "ICCRGBG1012", 11);
const int is_icc = (keep_metadata & METADATA_ICC) &&
!stored_icc &&
!memcmp(data + 1, "ICCRGBG1012", 11);
if (is_xmp || is_icc) { if (is_xmp || is_icc) {
if (!GIFReadMetadata(gif, &data, const char* const fourccs[2] = { "XMP " , "ICCP" };
is_xmp ? &xmp_data : &icc_data)) { const char* const features[2] = { "XMP" , "ICC" };
WebPData metadata = { NULL, 0 };
// Construct metadata from sub-blocks.
// Usual case (including ICC profile): In each sub-block, the
// first byte specifies its size in bytes (0 to 255) and the
// rest of the bytes contain the data.
// Special case for XMP data: In each sub-block, the first byte
// is also part of the XMP payload. XMP in GIF also has a 257
// byte padding data. See the XMP specification for details.
while (1) {
WebPData prev_metadata = metadata;
WebPData subblock;
if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) {
WebPDataClear(&metadata);
goto End;
}
if (data == NULL) break; // Finished.
subblock.size = is_xmp ? data[0] + 1 : data[0];
assert(subblock.size > 0);
subblock.bytes = is_xmp ? data : data + 1;
metadata.bytes =
(uint8_t*)realloc((void*)metadata.bytes,
prev_metadata.size + subblock.size);
if (metadata.bytes == NULL) {
WebPDataClear(&prev_metadata);
goto End;
}
metadata.size += subblock.size;
memcpy((void*)(metadata.bytes + prev_metadata.size),
subblock.bytes, subblock.size);
}
if (is_xmp) {
// XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
const size_t xmp_pading_size = 257;
if (metadata.size > xmp_pading_size) {
metadata.size -= xmp_pading_size;
}
}
// Add metadata chunk.
err = WebPMuxSetChunk(mux, fourccs[is_icc], &metadata, 1);
if (verbose) {
printf("%s size: %d\n", features[is_icc], (int)metadata.size);
}
WebPDataClear(&metadata);
if (err != WEBP_MUX_OK) {
fprintf(stderr, "ERROR (%s): Could not set %s chunk.\n",
ErrorString(err), features[is_icc]);
goto End; goto End;
} }
if (is_icc) { if (is_icc) {
@ -432,88 +486,30 @@ int main(int argc, const char *argv[]) {
} }
} while (!done); } while (!done);
// Last NULL frame. // Finish muxing
if (!WebPAnimEncoderAdd(enc, NULL, frame_timestamp, NULL)) { err = WebPMuxSetAnimationParams(mux, &anim);
fprintf(stderr, "Error flushing WebP muxer.\n"); if (err != WEBP_MUX_OK) {
fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc)); fprintf(stderr, "ERROR (%s): Could not set animation parameters.\n",
} ErrorString(err));
if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
goto End; goto End;
} }
if (stored_loop_count || stored_icc || stored_xmp) { err = WebPMuxAssemble(mux, &webp_data);
// Re-mux to add loop count and/or metadata as needed. if (err != WEBP_MUX_OK) {
mux = WebPMuxCreate(&webp_data, 1); fprintf(stderr, "ERROR (%s) assembling the WebP file.\n", ErrorString(err));
if (mux == NULL) { goto End;
fprintf(stderr, "ERROR: Could not re-mux to add loop count/metadata.\n");
goto End;
}
WebPDataClear(&webp_data);
if (stored_loop_count) { // Update loop count.
WebPMuxAnimParams new_params;
err = WebPMuxGetAnimationParams(mux, &new_params);
if (err != WEBP_MUX_OK) {
fprintf(stderr, "ERROR (%s): Could not fetch loop count.\n",
ErrorString(err));
goto End;
}
new_params.loop_count = loop_count;
err = WebPMuxSetAnimationParams(mux, &new_params);
if (err != WEBP_MUX_OK) {
fprintf(stderr, "ERROR (%s): Could not update loop count.\n",
ErrorString(err));
goto End;
}
}
if (stored_icc) { // Add ICCP chunk.
err = WebPMuxSetChunk(mux, "ICCP", &icc_data, 1);
if (verbose) {
fprintf(stderr, "ICC size: %d\n", (int)icc_data.size);
}
if (err != WEBP_MUX_OK) {
fprintf(stderr, "ERROR (%s): Could not set ICC chunk.\n",
ErrorString(err));
goto End;
}
}
if (stored_xmp) { // Add XMP chunk.
err = WebPMuxSetChunk(mux, "XMP ", &xmp_data, 1);
if (verbose) {
fprintf(stderr, "XMP size: %d\n", (int)xmp_data.size);
}
if (err != WEBP_MUX_OK) {
fprintf(stderr, "ERROR (%s): Could not set XMP chunk.\n",
ErrorString(err));
goto End;
}
}
err = WebPMuxAssemble(mux, &webp_data);
if (err != WEBP_MUX_OK) {
fprintf(stderr, "ERROR (%s): Could not assemble when re-muxing to add "
"loop count/metadata.\n", ErrorString(err));
goto End;
}
} }
if (out_file != NULL) { if (out_file != NULL) {
if (!ImgIoUtilWriteFile(out_file, webp_data.bytes, webp_data.size)) { if (!ExUtilWriteFile(out_file, webp_data.bytes, webp_data.size)) {
fprintf(stderr, "Error writing output file: %s\n", out_file); fprintf(stderr, "Error writing output file: %s\n", out_file);
goto End; goto End;
} }
if (!quiet) { if (!quiet) {
fprintf(stderr, "Saved output file (%d bytes): %s\n", printf("Saved output file: %s\n", out_file);
(int)webp_data.size, out_file);
} }
} else { } else {
if (!quiet) { if (!quiet) {
fprintf(stderr, "Nothing written; use -o flag to save the result " printf("Nothing written; use -o flag to save the result.\n");
"(%d bytes).\n", (int)webp_data.size);
} }
} }
@ -522,38 +518,19 @@ int main(int argc, const char *argv[]) {
gif_error = GIF_OK; gif_error = GIF_OK;
End: End:
WebPDataClear(&icc_data);
WebPDataClear(&xmp_data);
WebPMuxDelete(mux);
WebPDataClear(&webp_data); WebPDataClear(&webp_data);
WebPPictureFree(&frame); WebPMuxDelete(mux);
WebPPictureFree(&curr_canvas); WebPPictureFree(&picture);
WebPPictureFree(&prev_canvas);
WebPAnimEncoderDelete(enc);
if (out != NULL && out_file != NULL) fclose(out); if (out != NULL && out_file != NULL) fclose(out);
if (gif_error != GIF_OK) { if (gif_error != GIF_OK) {
GIFDisplayError(gif, gif_error); DisplayGifError(gif, gif_error);
} }
if (gif != NULL) { if (gif != NULL) {
#if LOCAL_GIF_PREREQ(5,1)
DGifCloseFile(gif, &gif_error);
#else
DGifCloseFile(gif); DGifCloseFile(gif);
#endif
} }
return !ok; return !ok;
} }
#else // !WEBP_HAVE_GIF
int main(int argc, const char *argv[]) {
fprintf(stderr, "GIF support not enabled in %s.\n", argv[0]);
(void)argc;
return 0;
}
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -1,410 +0,0 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// GIF decode.
#include "./gifdec.h"
#include <stdio.h>
#ifdef WEBP_HAVE_GIF
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "webp/encode.h"
#include "webp/mux_types.h"
#define GIF_TRANSPARENT_COLOR 0x00000000u
#define GIF_WHITE_COLOR 0xffffffffu
#define GIF_TRANSPARENT_MASK 0x01
#define GIF_DISPOSE_MASK 0x07
#define GIF_DISPOSE_SHIFT 2
// from utils/utils.h
extern void WebPCopyPlane(const uint8_t* src, int src_stride,
uint8_t* dst, int dst_stride,
int width, int height);
extern void WebPCopyPixels(const WebPPicture* const src,
WebPPicture* const dst);
void GIFGetBackgroundColor(const ColorMapObject* const color_map,
int bgcolor_index, int transparent_index,
uint32_t* const bgcolor) {
if (transparent_index != GIF_INDEX_INVALID &&
bgcolor_index == transparent_index) {
*bgcolor = GIF_TRANSPARENT_COLOR; // Special case.
} else if (color_map == NULL || color_map->Colors == NULL
|| bgcolor_index >= color_map->ColorCount) {
*bgcolor = GIF_WHITE_COLOR;
fprintf(stderr,
"GIF decode warning: invalid background color index. Assuming "
"white background.\n");
} else {
const GifColorType color = color_map->Colors[bgcolor_index];
*bgcolor = (0xffu << 24)
| (color.Red << 16)
| (color.Green << 8)
| (color.Blue << 0);
}
}
int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
GIFDisposeMethod* const dispose,
int* const transparent_index) {
const int flags = buf[1];
const int dispose_raw = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
const int duration_raw = buf[2] | (buf[3] << 8); // In 10 ms units.
if (buf[0] != 4) return 0;
*duration = duration_raw * 10; // Duration is in 1 ms units.
switch (dispose_raw) {
case 3:
*dispose = GIF_DISPOSE_RESTORE_PREVIOUS;
break;
case 2:
*dispose = GIF_DISPOSE_BACKGROUND;
break;
case 1:
case 0:
default:
*dispose = GIF_DISPOSE_NONE;
break;
}
*transparent_index =
(flags & GIF_TRANSPARENT_MASK) ? buf[4] : GIF_INDEX_INVALID;
return 1;
}
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 1;
if (cmap->Colors == NULL || cmap->ColorCount <= 0) return 0;
colors = cmap->Colors;
for (i = 0; i < len; ++i) {
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,
GIFFrameRect* const gif_rect, WebPPicture* const picture) {
WebPPicture sub_image;
const GifImageDesc* const image_desc = &gif->Image;
uint32_t* dst = NULL;
uint8_t* tmp = NULL;
const GIFFrameRect rect = {
image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
};
const 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)) {
fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
rect.width, rect.height, rect.x_offset, rect.y_offset);
return 0;
}
dst = sub_image.argb;
tmp = (uint8_t*)malloc(rect.width * sizeof(*tmp));
if (tmp == NULL) goto End;
if (image_desc->Interlace) { // Interlaced image.
// We need 4 passes, with the following offsets and jumps.
const int interlace_offsets[] = { 0, 4, 2, 1 };
const int interlace_jumps[] = { 8, 8, 4, 2 };
int pass;
for (pass = 0; pass < 4; ++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;
if (!Remap(gif, tmp, rect.width, transparent_index, row)) goto End;
}
}
} else { // Non-interlaced image.
int 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;
if (!Remap(gif, tmp, rect.width, transparent_index, ptr)) goto End;
}
}
ok = 1;
End:
if (!ok) picture->error_code = sub_image.error_code;
WebPPictureFree(&sub_image);
free(tmp);
return ok;
}
int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf,
int* const loop_count) {
assert(!memcmp(*buf + 1, "NETSCAPE2.0", 11) ||
!memcmp(*buf + 1, "ANIMEXTS1.0", 11));
if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
return 0;
}
if (*buf == NULL) {
return 0; // Loop count sub-block missing.
}
if ((*buf)[0] < 3 || (*buf)[1] != 1) {
return 0; // wrong size/marker
}
*loop_count = (*buf)[2] | ((*buf)[3] << 8);
return 1;
}
int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
WebPData* const metadata) {
const int is_xmp = !memcmp(*buf + 1, "XMP DataXMP", 11);
const int is_icc = !memcmp(*buf + 1, "ICCRGBG1012", 11);
assert(is_xmp || is_icc);
(void)is_icc; // silence unused warning.
// Construct metadata from sub-blocks.
// Usual case (including ICC profile): In each sub-block, the
// first byte specifies its size in bytes (0 to 255) and the
// rest of the bytes contain the data.
// Special case for XMP data: In each sub-block, the first byte
// is also part of the XMP payload. XMP in GIF also has a 257
// byte padding data. See the XMP specification for details.
while (1) {
WebPData subblock;
const uint8_t* tmp;
if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
return 0;
}
if (*buf == NULL) break; // Finished.
subblock.size = is_xmp ? (*buf)[0] + 1 : (*buf)[0];
assert(subblock.size > 0);
subblock.bytes = is_xmp ? *buf : *buf + 1;
// Note: We store returned value in 'tmp' first, to avoid
// leaking old memory in metadata->bytes on error.
tmp = (uint8_t*)realloc((void*)metadata->bytes,
metadata->size + subblock.size);
if (tmp == NULL) {
return 0;
}
memcpy((void*)(tmp + metadata->size),
subblock.bytes, subblock.size);
metadata->bytes = tmp;
metadata->size += subblock.size;
}
if (is_xmp) {
// XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
const size_t xmp_pading_size = 257;
if (metadata->size > xmp_pading_size) {
metadata->size -= xmp_pading_size;
}
}
return 1;
}
static void ClearRectangle(WebPPicture* const picture,
int left, int top, int width, int height) {
int i, j;
const size_t stride = picture->argb_stride;
uint32_t* dst = picture->argb + top * stride + left;
for (j = 0; j < height; ++j, dst += stride) {
for (i = 0; i < width; ++i) dst[i] = GIF_TRANSPARENT_COLOR;
}
}
void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) {
if (rect != NULL) {
ClearRectangle(pic, rect->x_offset, rect->y_offset,
rect->width, rect->height);
} else {
ClearRectangle(pic, 0, 0, pic->width, pic->height);
}
}
void GIFCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
WebPCopyPixels(src, dst);
}
void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
const WebPPicture* const prev_canvas,
WebPPicture* const curr_canvas) {
assert(rect != NULL);
if (dispose == GIF_DISPOSE_BACKGROUND) {
GIFClearPic(curr_canvas, rect);
} else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
const size_t src_stride = prev_canvas->argb_stride;
const uint32_t* const src = prev_canvas->argb + rect->x_offset
+ rect->y_offset * src_stride;
const 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, (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 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) {
for (i = rect->x_offset; i < rect->x_offset + rect->width; ++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_stride + i] = src_pixel;
}
}
}
}
void GIFDisplayError(const GifFileType* const gif, int gif_error) {
// libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
#if LOCAL_GIF_PREREQ(4,2)
#if LOCAL_GIF_PREREQ(5,0)
// Static string actually, hence the const char* cast.
const char* error_str = (const char*)GifErrorString(
(gif == NULL) ? gif_error : gif->Error);
#else
const char* error_str = (const char*)GifErrorString();
(void)gif;
#endif
if (error_str == NULL) error_str = "Unknown error";
fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
#else
(void)gif;
fprintf(stderr, "GIFLib Error %d: ", gif_error);
PrintGifError();
fprintf(stderr, "\n");
#endif
}
#else // !WEBP_HAVE_GIF
static void ErrorGIFNotAvailable() {
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
"package before building.\n");
}
void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
int bgcolor_index, int transparent_index,
uint32_t* const bgcolor) {
(void)color_map;
(void)bgcolor_index;
(void)transparent_index;
(void)bgcolor;
ErrorGIFNotAvailable();
}
int GIFReadGraphicsExtension(const GifByteType* const data, int* const duration,
GIFDisposeMethod* const dispose,
int* const transparent_index) {
(void)data;
(void)duration;
(void)dispose;
(void)transparent_index;
ErrorGIFNotAvailable();
return 0;
}
int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
GIFFrameRect* const gif_rect,
struct WebPPicture* const picture) {
(void)gif;
(void)transparent_index;
(void)gif_rect;
(void)picture;
ErrorGIFNotAvailable();
return 0;
}
int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
int* const loop_count) {
(void)gif;
(void)buf;
(void)loop_count;
ErrorGIFNotAvailable();
return 0;
}
int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
struct WebPData* const metadata) {
(void)gif;
(void)buf;
(void)metadata;
ErrorGIFNotAvailable();
return 0;
}
void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
const struct WebPPicture* const prev_canvas,
struct WebPPicture* const curr_canvas) {
(void)dispose;
(void)rect;
(void)prev_canvas;
(void)curr_canvas;
ErrorGIFNotAvailable();
}
void GIFBlendFrames(const struct WebPPicture* const src,
const GIFFrameRect* const rect,
struct WebPPicture* const dst) {
(void)src;
(void)rect;
(void)dst;
ErrorGIFNotAvailable();
}
void GIFDisplayError(const struct GifFileType* const gif, int gif_error) {
(void)gif;
(void)gif_error;
ErrorGIFNotAvailable();
}
void GIFClearPic(struct WebPPicture* const pic,
const GIFFrameRect* const rect) {
(void)pic;
(void)rect;
ErrorGIFNotAvailable();
}
void GIFCopyPixels(const struct WebPPicture* const src,
struct WebPPicture* const dst) {
(void)src;
(void)dst;
ErrorGIFNotAvailable();
}
#endif // WEBP_HAVE_GIF
// -----------------------------------------------------------------------------

View File

@ -1,116 +0,0 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// GIF decode.
#ifndef WEBP_EXAMPLES_GIFDEC_H_
#define WEBP_EXAMPLES_GIFDEC_H_
#include <stdio.h>
#include "webp/types.h"
#ifdef HAVE_CONFIG_H
#include "webp/config.h"
#endif
#ifdef WEBP_HAVE_GIF
#include <gif_lib.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
# define LOCAL_GIF_PREREQ(maj, min) \
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
#else
# define LOCAL_GIF_VERSION 0
# define LOCAL_GIF_PREREQ(maj, min) 0
#endif
#define GIF_INDEX_INVALID (-1)
typedef enum GIFDisposeMethod {
GIF_DISPOSE_NONE,
GIF_DISPOSE_BACKGROUND,
GIF_DISPOSE_RESTORE_PREVIOUS
} GIFDisposeMethod;
typedef struct {
int x_offset, y_offset, width, height;
} GIFFrameRect;
struct WebPData;
struct WebPPicture;
#ifndef WEBP_HAVE_GIF
struct ColorMapObject;
struct GifFileType;
typedef unsigned char GifByteType;
#endif
// Given the index of background color and transparent color, returns the
// corresponding background color (in BGRA format) in 'bgcolor'.
void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
int bgcolor_index, int transparent_index,
uint32_t* const bgcolor);
// Parses the given graphics extension data to get frame duration (in 1ms
// units), dispose method and transparent color index.
// Returns true on success.
int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
GIFDisposeMethod* const dispose,
int* const transparent_index);
// Reads the next GIF frame from 'gif' into 'picture'. Also, returns the GIF
// frame dimensions and offsets in 'rect'.
// Returns true on success.
int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
GIFFrameRect* const gif_rect,
struct WebPPicture* const picture);
// Parses loop count from the given Netscape extension data.
int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
int* const loop_count);
// Parses the given ICC or XMP extension data and stores it into 'metadata'.
// Returns true on success.
int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
struct WebPData* const metadata);
// Dispose the pixels within 'rect' of 'curr_canvas' based on 'dispose' method
// and 'prev_canvas'.
void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
const struct WebPPicture* const prev_canvas,
struct WebPPicture* const curr_canvas);
// Given 'src' picture and its frame rectangle 'rect', blend it into 'dst'.
void GIFBlendFrames(const struct WebPPicture* const src,
const GIFFrameRect* const rect,
struct WebPPicture* const dst);
// Prints an error string based on 'gif_error'.
void GIFDisplayError(const struct GifFileType* const gif, int gif_error);
// In the given 'pic', clear the pixels in 'rect' to transparent color.
void GIFClearPic(struct WebPPicture* const pic, const GIFFrameRect* const rect);
// Copy pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are assumed
// to be already allocated.
void GIFCopyPixels(const struct WebPPicture* const src,
struct WebPPicture* const dst);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WEBP_EXAMPLES_GIFDEC_H_

View File

@ -1,300 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// generate an animated WebP out of a sequence of images
// (PNG, JPEG, ...)
//
// Example usage:
// img2webp -o out.webp -q 40 -mixed -duration 40 input??.png
//
// Author: skal@google.com (Pascal Massimino)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_CONFIG_H
#include "webp/config.h"
#endif
#include "../examples/example_util.h"
#include "../imageio/image_dec.h"
#include "../imageio/imageio_util.h"
#include "./stopwatch.h"
#include "webp/encode.h"
#include "webp/mux.h"
//------------------------------------------------------------------------------
static void Help(void) {
printf("Usage:\n\n");
printf(" img2webp [file-level options] [image files...] "
"[per-frame options...]\n");
printf("\n");
printf("File-level options (only used at the start of compression):\n");
printf(" -min_size ............ minimize size\n");
printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
printf(" -kmax <int> .......... maximum number of frame between key-frames\n"
" (0=only keyframes)\n");
printf(" -kmin <int> .......... minimum number of frame between key-frames\n"
" (0=disable key-frames altogether)\n");
printf(" -mixed ............... use mixed lossy/lossless automatic mode\n");
printf(" -v ................... verbose mode\n");
printf(" -h ................... this help\n");
printf("\n");
printf("Per-frame options (only used for subsequent images input):\n");
printf(" -d <int> ............. frame duration in ms (default: 100)\n");
printf(" -lossless ........... use lossless mode (default)\n");
printf(" -lossy ... ........... use lossy mode\n");
printf(" -q <float> ........... quality\n");
printf(" -m <int> ............. method to use\n");
printf("\n");
printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
" -d 80 in2.tiff -o out.webp\n");
}
//------------------------------------------------------------------------------
static int ReadImage(const char filename[], WebPPicture* const pic) {
const uint8_t* data = NULL;
size_t data_size = 0;
WebPImageReader reader;
int ok;
#ifdef HAVE_WINCODEC_H
// Try to decode the file using WIC falling back to the other readers for
// e.g., WebP.
ok = ReadPictureWithWIC(filename, pic, 1, NULL);
if (ok) return 1;
#endif
if (!ImgIoUtilReadFile(filename, &data, &data_size)) return 0;
reader = WebPGuessImageReader(data, data_size);
ok = reader(data, data_size, pic, 1, NULL);
free((void*)data);
return ok;
}
static int SetLoopCount(int loop_count, WebPData* const webp_data) {
int ok = 1;
WebPMuxError err;
uint32_t features;
WebPMuxAnimParams new_params;
WebPMux* const mux = WebPMuxCreate(webp_data, 1);
if (mux == NULL) return 0;
err = WebPMuxGetFeatures(mux, &features);
ok = (err == WEBP_MUX_OK);
if (!ok || !(features & ANIMATION_FLAG)) goto End;
err = WebPMuxGetAnimationParams(mux, &new_params);
ok = (err == WEBP_MUX_OK);
if (ok) {
new_params.loop_count = loop_count;
err = WebPMuxSetAnimationParams(mux, &new_params);
ok = (err == WEBP_MUX_OK);
}
if (ok) {
WebPDataClear(webp_data);
err = WebPMuxAssemble(mux, webp_data);
ok = (err == WEBP_MUX_OK);
}
End:
WebPMuxDelete(mux);
if (!ok) {
fprintf(stderr, "Error during loop-count setting\n");
}
return ok;
}
//------------------------------------------------------------------------------
int main(int argc, char* argv[]) {
const char* output = NULL;
WebPAnimEncoder* enc = NULL;
int verbose = 0;
int pic_num = 0;
int duration = 100;
int timestamp_ms = 0;
int ok = 1;
int loop_count = 0;
int width = 0, height = 0;
WebPAnimEncoderOptions anim_config;
WebPConfig config;
WebPPicture pic;
WebPData webp_data;
int c;
int have_input = 0;
WebPDataInit(&webp_data);
if (!WebPAnimEncoderOptionsInit(&anim_config) ||
!WebPConfigInit(&config) ||
!WebPPictureInit(&pic)) {
fprintf(stderr, "Library version mismatch!\n");
return 1;
}
// 1st pass of option parsing
for (c = 1; ok && c < argc; ++c) {
if (argv[c][0] == '-') {
int parse_error = 0;
if (!strcmp(argv[c], "-o") && c + 1 < argc) {
argv[c] = NULL;
output = argv[++c];
} else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) {
argv[c] = NULL;
anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
} else if (!strcmp(argv[c], "-kmax") && c + 1 < argc) {
argv[c] = NULL;
anim_config.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
} else if (!strcmp(argv[c], "-loop") && c + 1 < argc) {
argv[c] = NULL;
loop_count = ExUtilGetInt(argv[++c], 0, &parse_error);
if (loop_count < 0) {
fprintf(stderr, "Invalid non-positive loop-count (%d)\n", loop_count);
parse_error = 1;
}
} else if (!strcmp(argv[c], "-min_size")) {
anim_config.minimize_size = 1;
} else if (!strcmp(argv[c], "-mixed")) {
anim_config.allow_mixed = 1;
config.lossless = 0;
} else if (!strcmp(argv[c], "-v")) {
verbose = 1;
} else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help();
return 0;
} else {
continue;
}
ok = !parse_error;
if (!ok) goto End;
argv[c] = NULL; // mark option as 'parsed' during 1st pass
} else {
have_input |= 1;
}
}
if (!have_input) {
fprintf(stderr, "No input file(s) for generating animation!\n");
return 0;
}
// image-reading pass
pic_num = 0;
config.lossless = 1;
for (c = 1; ok && c < argc; ++c) {
if (argv[c] == NULL) continue;
if (argv[c][0] == '-') { // parse local options
int parse_error = 0;
if (!strcmp(argv[c], "-lossy")) {
if (!anim_config.allow_mixed) config.lossless = 0;
} else if (!strcmp(argv[c], "-lossless")) {
if (!anim_config.allow_mixed) config.lossless = 1;
} else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
config.quality = ExUtilGetFloat(argv[++c], &parse_error);
} else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
} else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
duration = ExUtilGetInt(argv[++c], 0, &parse_error);
if (duration <= 0) {
fprintf(stderr, "Invalid negative duration (%d)\n", duration);
parse_error = 1;
}
} else {
parse_error = 1; // shouldn't be here.
fprintf(stderr, "Unknown option [%s]\n", argv[c]);
}
ok = !parse_error;
if (!ok) goto End;
continue;
}
if (ok) {
ok = WebPValidateConfig(&config);
if (!ok) {
fprintf(stderr, "Invalid configuration.\n");
goto End;
}
}
// read next input image
pic.use_argb = 1;
ok = ReadImage(argv[c], &pic);
if (!ok) goto End;
if (enc == NULL) {
width = pic.width;
height = pic.height;
enc = WebPAnimEncoderNew(width, height, &anim_config);
ok = (enc != NULL);
if (!ok) {
fprintf(stderr, "Could not create WebPAnimEncoder object.\n");
}
}
if (ok) {
ok = (width == pic.width && height == pic.height);
if (!ok) {
fprintf(stderr, "Frame #%d dimension mismatched! "
"Got %d x %d. Was expecting %d x %d.\n",
pic_num, pic.width, pic.height, width, height);
}
}
if (ok) {
ok = WebPAnimEncoderAdd(enc, &pic, timestamp_ms, &config);
if (!ok) {
fprintf(stderr, "Error while adding frame #%d\n", pic_num);
}
}
WebPPictureFree(&pic);
if (!ok) goto End;
if (verbose) {
fprintf(stderr, "Added frame #%3d at time %4d (file: %s)\n",
pic_num, timestamp_ms, argv[c]);
}
timestamp_ms += duration;
++pic_num;
}
// add a last fake frame to signal the last duration
ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
ok = ok && WebPAnimEncoderAssemble(enc, &webp_data);
if (!ok) {
fprintf(stderr, "Error during final animation assembly.\n");
}
End:
// free resources
WebPAnimEncoderDelete(enc);
if (ok && loop_count > 0) { // Re-mux to add loop count.
ok = SetLoopCount(loop_count, &webp_data);
}
if (ok) {
if (output != NULL) {
ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size);
if (ok) fprintf(stderr, "output file: %s ", output);
} else {
fprintf(stderr, "[no output file specified] ");
}
}
if (ok) {
fprintf(stderr, "[%d frames, %u bytes].\n",
pic_num, (unsigned int)webp_data.size);
}
WebPDataClear(&webp_data);
return ok ? 0 : 1;
}

View File

@ -12,20 +12,18 @@
#include "./jpegdec.h" #include "./jpegdec.h"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif #endif
#include <stdio.h> #include <stdio.h>
#ifdef WEBP_HAVE_JPEG #ifdef WEBP_HAVE_JPEG
#include <jpeglib.h> #include <jpeglib.h>
#include <jerror.h>
#include <setjmp.h> #include <setjmp.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "webp/encode.h" #include "webp/encode.h"
#include "./imageio_util.h"
#include "./metadata.h" #include "./metadata.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -210,89 +208,34 @@ static void my_error_exit(j_common_ptr dinfo) {
longjmp(myerr->setjmp_buffer, 1); longjmp(myerr->setjmp_buffer, 1);
} }
typedef struct { int ReadJPEG(FILE* in_file, WebPPicture* const pic, Metadata* const metadata) {
struct jpeg_source_mgr pub; int ok = 0;
const uint8_t* data; int stride, width, height;
size_t data_size; struct jpeg_decompress_struct dinfo;
} JPEGReadContext;
static void ContextInit(j_decompress_ptr cinfo) {
JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
ctx->pub.next_input_byte = ctx->data;
ctx->pub.bytes_in_buffer = ctx->data_size;
}
static boolean ContextFill(j_decompress_ptr cinfo) {
// we shouldn't get here.
ERREXIT(cinfo, JERR_FILE_READ);
return FALSE;
}
static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
size_t jump = (size_t)jump_size;
if (jump > ctx->pub.bytes_in_buffer) { // Don't overflow the buffer.
jump = ctx->pub.bytes_in_buffer;
}
ctx->pub.bytes_in_buffer -= jump;
ctx->pub.next_input_byte += jump;
}
static void ContextTerm(j_decompress_ptr cinfo) {
(void)cinfo;
}
static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
JPEGReadContext* const ctx) {
cinfo->src = (struct jpeg_source_mgr*)ctx;
ctx->pub.init_source = ContextInit;
ctx->pub.fill_input_buffer = ContextFill;
ctx->pub.skip_input_data = ContextSkip;
ctx->pub.resync_to_restart = jpeg_resync_to_restart;
ctx->pub.term_source = ContextTerm;
ctx->pub.bytes_in_buffer = 0;
ctx->pub.next_input_byte = NULL;
}
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 width, height;
int64_t stride;
volatile struct jpeg_decompress_struct dinfo;
struct my_error_mgr jerr; struct my_error_mgr jerr;
uint8_t* volatile rgb = NULL; uint8_t* rgb = NULL;
JSAMPROW buffer[1]; JSAMPROW buffer[1];
JPEGReadContext ctx;
if (data == NULL || data_size == 0 || pic == NULL) return 0;
(void)keep_alpha;
memset(&ctx, 0, sizeof(ctx));
ctx.data = data;
ctx.data_size = data_size;
memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp sanity
dinfo.err = jpeg_std_error(&jerr.pub); dinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit; jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer)) { if (setjmp(jerr.setjmp_buffer)) {
Error: Error:
MetadataFree(metadata); MetadataFree(metadata);
jpeg_destroy_decompress((j_decompress_ptr)&dinfo); jpeg_destroy_decompress(&dinfo);
goto End; goto End;
} }
jpeg_create_decompress((j_decompress_ptr)&dinfo); jpeg_create_decompress(&dinfo);
ContextSetup(&dinfo, &ctx); jpeg_stdio_src(&dinfo, in_file);
if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo); if (metadata != NULL) SaveMetadataMarkers(&dinfo);
jpeg_read_header((j_decompress_ptr)&dinfo, TRUE); jpeg_read_header(&dinfo, TRUE);
dinfo.out_color_space = JCS_RGB; dinfo.out_color_space = JCS_RGB;
dinfo.dct_method = JDCT_IFAST;
dinfo.do_fancy_upsampling = TRUE; dinfo.do_fancy_upsampling = TRUE;
jpeg_start_decompress((j_decompress_ptr)&dinfo); jpeg_start_decompress(&dinfo);
if (dinfo.output_components != 3) { if (dinfo.output_components != 3) {
goto Error; goto Error;
@ -300,41 +243,36 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
width = dinfo.output_width; width = dinfo.output_width;
height = dinfo.output_height; height = dinfo.output_height;
stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb); stride = dinfo.output_width * dinfo.output_components * sizeof(*rgb);
if (stride != (int)stride || rgb = (uint8_t*)malloc(stride * height);
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
goto End;
}
rgb = (uint8_t*)malloc((size_t)stride * height);
if (rgb == NULL) { if (rgb == NULL) {
goto End; goto End;
} }
buffer[0] = (JSAMPLE*)rgb; buffer[0] = (JSAMPLE*)rgb;
while (dinfo.output_scanline < dinfo.output_height) { while (dinfo.output_scanline < dinfo.output_height) {
if (jpeg_read_scanlines((j_decompress_ptr)&dinfo, buffer, 1) != 1) { if (jpeg_read_scanlines(&dinfo, buffer, 1) != 1) {
goto End; goto End;
} }
buffer[0] += stride; buffer[0] += stride;
} }
if (metadata != NULL) { if (metadata != NULL) {
ok = ExtractMetadataFromJPEG((j_decompress_ptr)&dinfo, metadata); ok = ExtractMetadataFromJPEG(&dinfo, metadata);
if (!ok) { if (!ok) {
fprintf(stderr, "Error extracting JPEG metadata!\n"); fprintf(stderr, "Error extracting JPEG metadata!\n");
goto Error; goto Error;
} }
} }
jpeg_finish_decompress((j_decompress_ptr)&dinfo); jpeg_finish_decompress(&dinfo);
jpeg_destroy_decompress((j_decompress_ptr)&dinfo); jpeg_destroy_decompress(&dinfo);
// WebP conversion. // WebP conversion.
pic->width = width; pic->width = width;
pic->height = height; pic->height = height;
ok = WebPPictureImportRGB(pic, rgb, (int)stride); ok = WebPPictureImportRGB(pic, rgb, stride);
if (!ok) goto Error; if (!ok) goto Error;
End: End:
@ -342,13 +280,10 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
return ok; return ok;
} }
#else // !WEBP_HAVE_JPEG #else // !WEBP_HAVE_JPEG
int ReadJPEG(const uint8_t* const data, size_t data_size, int ReadJPEG(FILE* in_file, struct WebPPicture* const pic,
struct WebPPicture* const pic, int keep_alpha,
struct Metadata* const metadata) { struct Metadata* const metadata) {
(void)data; (void)in_file;
(void)data_size;
(void)pic; (void)pic;
(void)keep_alpha;
(void)metadata; (void)metadata;
fprintf(stderr, "JPEG support not compiled. Please install the libjpeg " fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
"development package before building.\n"); "development package before building.\n");

View File

@ -9,29 +9,27 @@
// //
// JPEG decode. // JPEG decode.
#ifndef WEBP_IMAGEIO_JPEGDEC_H_ #ifndef WEBP_EXAMPLES_JPEGDEC_H_
#define WEBP_IMAGEIO_JPEGDEC_H_ #define WEBP_EXAMPLES_JPEGDEC_H_
#include <stdio.h>
#include "webp/types.h" #include "webp/types.h"
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
struct Metadata; struct Metadata;
struct WebPPicture; struct WebPPicture;
// Reads a JPEG from 'data', returning the decoded output in 'pic'. // Reads a JPEG from 'in_file', returning the decoded output in 'pic'.
// The output is RGB or YUV depending on pic->use_argb value. // The output is RGB.
// Returns true on success. // Returns true on success.
// 'keep_alpha' has no effect, but is kept for coherence with other signatures int ReadJPEG(FILE* in_file, struct WebPPicture* const pic,
// for image readers.
int ReadJPEG(const uint8_t* const data, size_t data_size,
struct WebPPicture* const pic, int keep_alpha,
struct Metadata* const metadata); struct Metadata* const metadata);
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_IMAGEIO_JPEGDEC_H_ #endif // WEBP_EXAMPLES_JPEGDEC_H_

View File

@ -10,12 +10,12 @@
// Metadata types and functions. // Metadata types and functions.
// //
#ifndef WEBP_IMAGEIO_METADATA_H_ #ifndef WEBP_EXAMPLES_METADATA_H_
#define WEBP_IMAGEIO_METADATA_H_ #define WEBP_EXAMPLES_METADATA_H_
#include "webp/types.h" #include "webp/types.h"
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
@ -40,8 +40,8 @@ void MetadataFree(Metadata* const metadata);
int MetadataCopy(const char* metadata, size_t metadata_len, int MetadataCopy(const char* metadata, size_t metadata_len,
MetadataPayload* const payload); MetadataPayload* const payload);
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_IMAGEIO_METADATA_H_ #endif // WEBP_EXAMPLES_METADATA_H_

View File

@ -12,7 +12,7 @@
#include "./pngdec.h" #include "./pngdec.h"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif #endif
#include <stdio.h> #include <stdio.h>
@ -21,14 +21,12 @@
#include <png.h> #include <png.h>
#include <setjmp.h> // note: this must be included *after* png.h #include <setjmp.h> // note: this must be included *after* png.h
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "webp/encode.h" #include "webp/encode.h"
#include "./imageio_util.h"
#include "./metadata.h" #include "./metadata.h"
static void PNGAPI error_function(png_structp png, png_const_charp error) { static void PNGAPI error_function(png_structp png, png_const_charp dummy) {
if (error != NULL) fprintf(stderr, "libpng error: %s\n", error); (void)dummy; // remove variable-unused warning
longjmp(png_jmpbuf(png), 1); longjmp(png_jmpbuf(png), 1);
} }
@ -132,8 +130,8 @@ static int ExtractMetadataFromPNG(png_structp png,
for (p = 0; p < 2; ++p) { for (p = 0; p < 2; ++p) {
png_infop const info = (p == 0) ? head_info : end_info; png_infop const info = (p == 0) ? head_info : end_info;
png_textp text = NULL; png_textp text = NULL;
const png_uint_32 num = png_get_text(png, info, &text, NULL); const int num = png_get_text(png, info, &text, NULL);
png_uint_32 i; int i;
// Look for EXIF / XMP metadata. // Look for EXIF / XMP metadata.
for (i = 0; i < num; ++i, ++text) { for (i = 0; i < num; ++i, ++text) {
int j; int j;
@ -189,49 +187,30 @@ static int ExtractMetadataFromPNG(png_structp png,
return 1; return 1;
} }
typedef struct { int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha,
const uint8_t* data; Metadata* const metadata) {
size_t data_size; png_structp png;
png_size_t offset; png_infop info = NULL;
} PNGReadContext; png_infop end_info = NULL;
static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) {
PNGReadContext* const ctx = (PNGReadContext*)png_get_io_ptr(png_ptr);
if (ctx->data_size - ctx->offset < length) {
png_error(png_ptr, "ReadFunc: invalid read length (overflow)!");
}
memcpy(data, ctx->data + ctx->offset, length);
ctx->offset += length;
}
int ReadPNG(const uint8_t* const data, size_t data_size,
struct WebPPicture* const pic,
int keep_alpha, struct Metadata* const metadata) {
volatile png_structp png = NULL;
volatile png_infop info = NULL;
volatile png_infop end_info = NULL;
PNGReadContext context = { NULL, 0, 0 };
int color_type, bit_depth, interlaced; int color_type, bit_depth, interlaced;
int has_alpha; int has_alpha;
int num_passes; int num_passes;
int p; int p;
volatile int ok = 0; int ok = 0;
png_uint_32 width, height, y; png_uint_32 width, height, y;
int64_t stride; int stride;
uint8_t* volatile rgb = NULL; uint8_t* rgb = NULL;
if (data == NULL || data_size == 0 || pic == NULL) return 0;
context.data = data;
context.data_size = data_size;
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (png == NULL) goto End; if (png == NULL) {
goto End;
}
png_set_error_fn(png, 0, error_function, NULL); png_set_error_fn(png, 0, error_function, NULL);
if (setjmp(png_jmpbuf(png))) { if (setjmp(png_jmpbuf(png))) {
Error: Error:
MetadataFree(metadata); MetadataFree(metadata);
png_destroy_read_struct(&png, &info, &end_info);
goto End; goto End;
} }
@ -240,7 +219,7 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
end_info = png_create_info_struct(png); end_info = png_create_info_struct(png);
if (end_info == NULL) goto Error; if (end_info == NULL) goto Error;
png_set_read_fn(png, &context, ReadFunc); png_init_io(png, in_file);
png_read_info(png, info); png_read_info(png, info);
if (!png_get_IHDR(png, info, if (!png_get_IHDR(png, info,
&width, &height, &bit_depth, &color_type, &interlaced, &width, &height, &bit_depth, &color_type, &interlaced,
@ -248,9 +227,7 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
png_set_strip_16(png); png_set_strip_16(png);
png_set_packing(png); png_set_packing(png);
if (color_type == PNG_COLOR_TYPE_PALETTE) { if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);
png_set_palette_to_rgb(png);
}
if (color_type == PNG_COLOR_TYPE_GRAY || if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
if (bit_depth < 8) { if (bit_depth < 8) {
@ -272,20 +249,13 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
num_passes = png_set_interlace_handling(png); num_passes = png_set_interlace_handling(png);
png_read_update_info(png, info); png_read_update_info(png, info);
stride = (has_alpha ? 4 : 3) * width * sizeof(*rgb);
stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb); rgb = (uint8_t*)malloc(stride * height);
if (stride != (int)stride ||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
goto Error;
}
rgb = (uint8_t*)malloc((size_t)stride * height);
if (rgb == NULL) goto Error; if (rgb == NULL) goto Error;
for (p = 0; p < num_passes; ++p) { for (p = 0; p < num_passes; ++p) {
png_bytep row = rgb;
for (y = 0; y < height; ++y) { for (y = 0; y < height; ++y) {
png_bytep row = rgb + y * stride;
png_read_rows(png, &row, NULL, 1); png_read_rows(png, &row, NULL, 1);
row += stride;
} }
} }
png_read_end(png, end_info); png_read_end(png, end_info);
@ -296,29 +266,25 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
goto Error; goto Error;
} }
pic->width = (int)width; png_destroy_read_struct(&png, &info, &end_info);
pic->height = (int)height;
ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, (int)stride) pic->width = width;
: WebPPictureImportRGB(pic, rgb, (int)stride); pic->height = height;
ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride)
: WebPPictureImportRGB(pic, rgb, stride);
if (!ok) { if (!ok) {
goto Error; goto Error;
} }
End: End:
if (png != NULL) {
png_destroy_read_struct((png_structpp)&png,
(png_infopp)&info, (png_infopp)&end_info);
}
free(rgb); free(rgb);
return ok; return ok;
} }
#else // !WEBP_HAVE_PNG #else // !WEBP_HAVE_PNG
int ReadPNG(const uint8_t* const data, size_t data_size, int ReadPNG(FILE* in_file, struct WebPPicture* const pic, int keep_alpha,
struct WebPPicture* const pic, struct Metadata* const metadata) {
int keep_alpha, struct Metadata* const metadata) { (void)in_file;
(void)data;
(void)data_size;
(void)pic; (void)pic;
(void)keep_alpha; (void)keep_alpha;
(void)metadata; (void)metadata;

View File

@ -9,29 +9,27 @@
// //
// PNG decode. // PNG decode.
#ifndef WEBP_IMAGEIO_PNGDEC_H_ #ifndef WEBP_EXAMPLES_PNGDEC_H_
#define WEBP_IMAGEIO_PNGDEC_H_ #define WEBP_EXAMPLES_PNGDEC_H_
#include "webp/types.h" #include <stdio.h>
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
struct Metadata; struct Metadata;
struct WebPPicture; struct WebPPicture;
// Reads a PNG from 'data', returning the decoded output in 'pic'. // Reads a PNG from 'in_file', returning the decoded output in 'pic'.
// Output is RGBA or YUVA, depending on pic->use_argb value.
// If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA // If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV. // otherwise it will be RGB.
// Returns true on success. // Returns true on success.
int ReadPNG(const uint8_t* const data, size_t data_size, int ReadPNG(FILE* in_file, struct WebPPicture* const pic, int keep_alpha,
struct WebPPicture* const pic, struct Metadata* const metadata);
int keep_alpha, struct Metadata* const metadata);
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_IMAGEIO_PNGDEC_H_ #endif // WEBP_EXAMPLES_PNGDEC_H_

View File

@ -14,17 +14,11 @@
#ifndef WEBP_EXAMPLES_STOPWATCH_H_ #ifndef WEBP_EXAMPLES_STOPWATCH_H_
#define WEBP_EXAMPLES_STOPWATCH_H_ #define WEBP_EXAMPLES_STOPWATCH_H_
#include "webp/types.h"
#if defined _WIN32 && !defined __GNUC__ #if defined _WIN32 && !defined __GNUC__
#include <windows.h> #include <windows.h>
typedef LARGE_INTEGER Stopwatch; typedef LARGE_INTEGER Stopwatch;
static WEBP_INLINE void StopwatchReset(Stopwatch* watch) {
QueryPerformanceCounter(watch);
}
static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) { static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
const LARGE_INTEGER old_value = *watch; const LARGE_INTEGER old_value = *watch;
LARGE_INTEGER freq; LARGE_INTEGER freq;
@ -39,23 +33,15 @@ static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
#else /* !_WIN32 */ #else /* !_WIN32 */
#include <string.h> // memcpy
#include <sys/time.h> #include <sys/time.h>
typedef struct timeval Stopwatch; typedef struct timeval Stopwatch;
static WEBP_INLINE void StopwatchReset(Stopwatch* watch) {
gettimeofday(watch, NULL);
}
static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) { static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
struct timeval old_value; const struct timeval old_value = *watch;
double delta_sec, delta_usec;
memcpy(&old_value, watch, sizeof(old_value));
gettimeofday(watch, NULL); gettimeofday(watch, NULL);
delta_sec = (double)watch->tv_sec - old_value.tv_sec; return watch->tv_sec - old_value.tv_sec +
delta_usec = (double)watch->tv_usec - old_value.tv_usec; (watch->tv_usec - old_value.tv_usec) / 1000000.0;
return delta_sec + delta_usec / 1000000.0;
} }
#endif /* _WIN32 */ #endif /* _WIN32 */

View File

@ -12,17 +12,15 @@
#include "./tiffdec.h" #include "./tiffdec.h"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif #endif
#include <stdio.h> #include <stdio.h>
#include <string.h>
#ifdef WEBP_HAVE_TIFF #ifdef WEBP_HAVE_TIFF
#include <tiffio.h> #include <tiffio.h>
#include "webp/encode.h" #include "webp/encode.h"
#include "./imageio_util.h"
#include "./metadata.h" #include "./metadata.h"
static const struct { static const struct {
@ -65,75 +63,17 @@ static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) {
return 1; return 1;
} }
// Ad-hoc structure to supply read-from-memory functionalities. int ReadTIFF(const char* const filename,
typedef struct {
const uint8_t* data;
toff_t size;
toff_t pos;
} MyData;
static int MyClose(thandle_t opaque) {
(void)opaque;
return 0;
}
static toff_t MySize(thandle_t opaque) {
const MyData* const my_data = (MyData*)opaque;
return my_data->size;
}
static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) {
MyData* const my_data = (MyData*)opaque;
offset += (whence == SEEK_CUR) ? my_data->pos
: (whence == SEEK_SET) ? 0
: my_data->size;
if (offset > my_data->size) return (toff_t)-1;
my_data->pos = offset;
return offset;
}
static int MyMapFile(thandle_t opaque, void** base, toff_t* size) {
(void)opaque;
(void)base;
(void)size;
return 0;
}
static void MyUnmapFile(thandle_t opaque, void* base, toff_t size) {
(void)opaque;
(void)base;
(void)size;
}
static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
MyData* const my_data = (MyData*)opaque;
if (my_data->pos + size > my_data->size) {
size = my_data->size - my_data->pos;
}
if (size > 0) {
memcpy(dst, my_data->data + my_data->pos, size);
my_data->pos += size;
}
return size;
}
int ReadTIFF(const uint8_t* const data, size_t data_size,
WebPPicture* const pic, int keep_alpha, WebPPicture* const pic, int keep_alpha,
Metadata* const metadata) { Metadata* const metadata) {
MyData my_data = { data, (toff_t)data_size, 0 }; TIFF* const tif = TIFFOpen(filename, "r");
TIFF* tif;
uint32 width, height; uint32 width, height;
uint32* raster; uint32* raster;
int64_t alloc_size;
int ok = 0; int ok = 0;
tdir_t dircount; tdir_t dircount;
if (data == NULL || data_size == 0 || pic == NULL) return 0;
tif = TIFFClientOpen("Memory", "r", &my_data,
MyRead, MyRead, MySeek, MyClose,
MySize, MyMapFile, MyUnmapFile);
if (tif == NULL) { if (tif == NULL) {
fprintf(stderr, "Error! Cannot parse TIFF file\n"); fprintf(stderr, "Error! Cannot open TIFF file '%s'\n", filename);
return 0; return 0;
} }
@ -147,18 +87,9 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) && if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) &&
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) { TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) {
fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n"); fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
goto End; return 0;
} }
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 (raster != NULL) {
if (TIFFReadRGBAImageOriented(tif, width, height, raster, if (TIFFReadRGBAImageOriented(tif, width, height, raster,
ORIENTATION_TOPLEFT, 1)) { ORIENTATION_TOPLEFT, 1)) {
@ -166,7 +97,7 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
pic->width = width; pic->width = width;
pic->height = height; pic->height = height;
// TIFF data is ABGR // TIFF data is ABGR
#ifdef WORDS_BIGENDIAN #ifdef __BIG_ENDIAN__
TIFFSwabArrayOfLong(raster, width * height); TIFFSwabArrayOfLong(raster, width * height);
#endif #endif
ok = keep_alpha ok = keep_alpha
@ -188,16 +119,15 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
} }
} }
} }
End:
TIFFClose(tif); TIFFClose(tif);
return ok; return ok;
} }
#else // !WEBP_HAVE_TIFF #else // !WEBP_HAVE_TIFF
int ReadTIFF(const uint8_t* const data, size_t data_size, int ReadTIFF(const char* const filename,
struct WebPPicture* const pic, int keep_alpha, struct WebPPicture* const pic, int keep_alpha,
struct Metadata* const metadata) { struct Metadata* const metadata) {
(void)data; (void)filename;
(void)data_size;
(void)pic; (void)pic;
(void)keep_alpha; (void)keep_alpha;
(void)metadata; (void)metadata;

View File

@ -9,29 +9,26 @@
// //
// TIFF decode. // TIFF decode.
#ifndef WEBP_IMAGEIO_TIFFDEC_H_ #ifndef WEBP_EXAMPLES_TIFFDEC_H_
#define WEBP_IMAGEIO_TIFFDEC_H_ #define WEBP_EXAMPLES_TIFFDEC_H_
#include "webp/types.h" #if defined(__cplusplus) || defined(c_plusplus)
#ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
struct Metadata; struct Metadata;
struct WebPPicture; struct WebPPicture;
// Reads a TIFF from 'data', returning the decoded output in 'pic'. // Reads a TIFF from 'filename', returning the decoded output in 'pic'.
// Output is RGBA or YUVA, depending on pic->use_argb value.
// If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA // If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV. // otherwise it will be RGB.
// Returns true on success. // Returns true on success.
int ReadTIFF(const uint8_t* const data, size_t data_size, int ReadTIFF(const char* const filename,
struct WebPPicture* const pic, int keep_alpha, struct WebPPicture* const pic, int keep_alpha,
struct Metadata* const metadata); struct Metadata* const metadata);
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_IMAGEIO_TIFFDEC_H_ #endif // WEBP_EXAMPLES_TIFFDEC_H_

View File

@ -11,19 +11,13 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif
#if defined(__unix__) || defined(__CYGWIN__)
#define _POSIX_C_SOURCE 200112L // for setenv
#endif #endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#if defined(WEBP_HAVE_GL)
#if defined(HAVE_GLUT_GLUT_H) #if defined(HAVE_GLUT_GLUT_H)
#include <GLUT/glut.h> #include <GLUT/glut.h>
#else #else
@ -40,13 +34,14 @@
#include "webp/decode.h" #include "webp/decode.h"
#include "webp/demux.h" #include "webp/demux.h"
#include "../examples/example_util.h" #include "./example_util.h"
#include "../imageio/imageio_util.h"
#if defined(_MSC_VER) && _MSC_VER < 1900 #ifdef _MSC_VER
#define snprintf _snprintf #define snprintf _snprintf
#endif #endif
static void Help(void);
// Unfortunate global variables. Gathered into a struct for comfort. // Unfortunate global variables. Gathered into a struct for comfort.
static struct { static struct {
int has_animation; int has_animation;
@ -54,7 +49,6 @@ static struct {
int done; int done;
int decoding_error; int decoding_error;
int print_info; int print_info;
int only_deltas;
int use_color_profile; int use_color_profile;
int canvas_width, canvas_height; int canvas_width, canvas_height;
@ -63,13 +57,16 @@ static struct {
const char* file_name; const char* file_name;
WebPData data; WebPData data;
WebPDecoderConfig config; WebPDecoderConfig* config;
const WebPDecBuffer* pic; const WebPDecBuffer* pic;
WebPDemuxer* dmux; WebPDemuxer* dmux;
WebPIterator curr_frame; WebPIterator frameiter;
WebPIterator prev_frame; struct {
int width, height;
int x_offset, y_offset;
enum WebPMuxAnimDispose dispose_method;
} prev_frame;
WebPChunkIterator iccp; WebPChunkIterator iccp;
int viewport_width, viewport_height;
} kParams; } kParams;
static void ClearPreviousPic(void) { static void ClearPreviousPic(void) {
@ -80,23 +77,12 @@ static void ClearPreviousPic(void) {
static void ClearParams(void) { static void ClearParams(void) {
ClearPreviousPic(); ClearPreviousPic();
WebPDataClear(&kParams.data); WebPDataClear(&kParams.data);
WebPDemuxReleaseIterator(&kParams.curr_frame); WebPDemuxReleaseIterator(&kParams.frameiter);
WebPDemuxReleaseIterator(&kParams.prev_frame);
WebPDemuxReleaseChunkIterator(&kParams.iccp); WebPDemuxReleaseChunkIterator(&kParams.iccp);
WebPDemuxDelete(kParams.dmux); WebPDemuxDelete(kParams.dmux);
kParams.dmux = NULL; kParams.dmux = NULL;
} }
// Sets the previous frame to the dimensions of the canvas and has it dispose
// to background to cause the canvas to be cleared.
static void ClearPreviousFrame(void) {
WebPIterator* const prev = &kParams.prev_frame;
prev->width = kParams.canvas_width;
prev->height = kParams.canvas_height;
prev->x_offset = prev->y_offset = 0;
prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Color profile handling // Color profile handling
static int ApplyColorProfile(const WebPData* const profile, static int ApplyColorProfile(const WebPData* const profile,
@ -161,25 +147,25 @@ static int ApplyColorProfile(const WebPData* const profile,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// File decoding // File decoding
static int Decode(void) { // Fills kParams.curr_frame static int Decode(void) { // Fills kParams.frameiter
const WebPIterator* const curr = &kParams.curr_frame; const WebPIterator* const iter = &kParams.frameiter;
WebPDecoderConfig* const config = &kParams.config; WebPDecoderConfig* const config = kParams.config;
WebPDecBuffer* const output_buffer = &config->output; WebPDecBuffer* const output_buffer = &config->output;
int ok = 0; int ok = 0;
ClearPreviousPic(); ClearPreviousPic();
output_buffer->colorspace = MODE_RGBA; output_buffer->colorspace = MODE_RGBA;
ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size, ok = (WebPDecode(iter->fragment.bytes, iter->fragment.size,
config) == VP8_STATUS_OK); config) == VP8_STATUS_OK);
if (!ok) { if (!ok) {
fprintf(stderr, "Decoding of frame #%d failed!\n", curr->frame_num); fprintf(stderr, "Decoding of frame #%d failed!\n", iter->frame_num);
} else { } else {
kParams.pic = output_buffer; kParams.pic = output_buffer;
if (kParams.use_color_profile) { if (kParams.use_color_profile) {
ok = ApplyColorProfile(&kParams.iccp.chunk, output_buffer); ok = ApplyColorProfile(&kParams.iccp.chunk, output_buffer);
if (!ok) { if (!ok) {
fprintf(stderr, "Applying color profile to frame #%d failed!\n", fprintf(stderr, "Applying color profile to frame #%d failed!\n",
curr->frame_num); iter->frame_num);
} }
} }
} }
@ -190,21 +176,19 @@ static void decode_callback(int what) {
if (what == 0 && !kParams.done) { if (what == 0 && !kParams.done) {
int duration = 0; int duration = 0;
if (kParams.dmux != NULL) { if (kParams.dmux != NULL) {
WebPIterator* const curr = &kParams.curr_frame; WebPIterator* const iter = &kParams.frameiter;
if (!WebPDemuxNextFrame(curr)) { if (!WebPDemuxNextFrame(iter)) {
WebPDemuxReleaseIterator(curr); WebPDemuxReleaseIterator(iter);
if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) { if (WebPDemuxGetFrame(kParams.dmux, 1, iter)) {
--kParams.loop_count; --kParams.loop_count;
kParams.done = (kParams.loop_count == 0); kParams.done = (kParams.loop_count == 0);
if (kParams.done) return;
ClearPreviousFrame();
} else { } else {
kParams.decoding_error = 1; kParams.decoding_error = 1;
kParams.done = 1; kParams.done = 1;
return; return;
} }
} }
duration = curr->duration; duration = iter->duration;
} }
if (!Decode()) { if (!Decode()) {
kParams.decoding_error = 1; kParams.decoding_error = 1;
@ -249,27 +233,18 @@ static void HandleKey(unsigned char key, int pos_x, int pos_y) {
} }
} else if (key == 'i') { } else if (key == 'i') {
kParams.print_info = 1 - kParams.print_info; 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();
} else if (key == 'd') {
kParams.only_deltas = 1 - kParams.only_deltas;
glutPostRedisplay(); glutPostRedisplay();
} }
} }
static void HandleReshape(int width, int height) { static void HandleReshape(int width, int height) {
// TODO(skal): should we preserve aspect ratio? // TODO(skal): proper handling of resize, esp. for large pictures.
// Also: handle larger-than-screen pictures correctly. // + key control of the zoom.
glViewport(0, 0, width, height); glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
kParams.viewport_width = width;
kParams.viewport_height = height;
if (!kParams.has_animation) ClearPreviousFrame();
} }
static void PrintString(const char* const text) { static void PrintString(const char* const text) {
@ -306,55 +281,40 @@ static void DrawCheckerBoard(void) {
static void HandleDisplay(void) { static void HandleDisplay(void) {
const WebPDecBuffer* const pic = kParams.pic; const WebPDecBuffer* const pic = kParams.pic;
const WebPIterator* const curr = &kParams.curr_frame; const WebPIterator* const iter = &kParams.frameiter;
WebPIterator* const prev = &kParams.prev_frame;
GLfloat xoff, yoff; GLfloat xoff, yoff;
if (pic == NULL) return; if (pic == NULL) return;
glPushMatrix(); glPushMatrix();
glPixelZoom((GLfloat)(+1. / kParams.canvas_width * kParams.viewport_width), glPixelZoom(1, -1);
(GLfloat)(-1. / kParams.canvas_height * kParams.viewport_height)); xoff = (GLfloat)(2. * iter->x_offset / kParams.canvas_width);
xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width); yoff = (GLfloat)(2. * iter->y_offset / kParams.canvas_height);
yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
glRasterPos2f(-1.f + xoff, 1.f - yoff); glRasterPos2f(-1.f + xoff, 1.f - yoff);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4); glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
if (kParams.only_deltas) { if (kParams.prev_frame.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
DrawCheckerBoard(); // TODO(later): these offsets and those above should factor in window size.
} else if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND || // they will be incorrect if the window is resized.
curr->blend_method == WEBP_MUX_NO_BLEND) {
// glScissor() takes window coordinates (0,0 at bottom left). // glScissor() takes window coordinates (0,0 at bottom left).
int window_x, window_y; const int window_x = kParams.prev_frame.x_offset;
int frame_w, frame_h; const int window_y = kParams.canvas_height -
if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { kParams.prev_frame.y_offset -
// Clear the previous frame rectangle. kParams.prev_frame.height;
window_x = prev->x_offset;
window_y = kParams.canvas_height - prev->y_offset - prev->height;
frame_w = prev->width;
frame_h = prev->height;
} else { // curr->blend_method == WEBP_MUX_NO_BLEND.
// We simulate no-blending behavior by first clearing the current frame
// rectangle (to a checker-board) and then alpha-blending against it.
window_x = curr->x_offset;
window_y = kParams.canvas_height - curr->y_offset - curr->height;
frame_w = curr->width;
frame_h = curr->height;
}
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
// Only update the requested area, not the whole canvas. // Only updated the requested area, not the whole canvas.
window_x = window_x * kParams.viewport_width / kParams.canvas_width; glScissor(window_x, window_y,
window_y = window_y * kParams.viewport_height / kParams.canvas_height; kParams.prev_frame.width, kParams.prev_frame.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 glClear(GL_COLOR_BUFFER_BIT); // use clear color
DrawCheckerBoard(); DrawCheckerBoard();
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
} }
kParams.prev_frame.width = iter->width;
*prev = *curr; kParams.prev_frame.height = iter->height;
kParams.prev_frame.x_offset = iter->x_offset;
kParams.prev_frame.y_offset = iter->y_offset;
kParams.prev_frame.dispose_method = iter->dispose_method;
glDrawPixels(pic->width, pic->height, glDrawPixels(pic->width, pic->height,
GL_RGBA, GL_UNSIGNED_BYTE, GL_RGBA, GL_UNSIGNED_BYTE,
@ -370,9 +330,9 @@ static void HandleDisplay(void) {
glColor4f(0.90f, 0.0f, 0.90f, 1.0f); glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
glRasterPos2f(-0.95f, 0.80f); glRasterPos2f(-0.95f, 0.80f);
PrintString(tmp); PrintString(tmp);
if (curr->x_offset != 0 || curr->y_offset != 0) { if (iter->x_offset != 0 || iter->y_offset != 0) {
snprintf(tmp, sizeof(tmp), " (offset:%d,%d)", snprintf(tmp, sizeof(tmp), " (offset:%d,%d)",
curr->x_offset, curr->y_offset); iter->x_offset, iter->y_offset);
glRasterPos2f(-0.95f, 0.70f); glRasterPos2f(-0.95f, 0.70f);
PrintString(tmp); PrintString(tmp);
} }
@ -388,7 +348,6 @@ static void StartDisplay(void) {
glutInitWindowSize(width, height); glutInitWindowSize(width, height);
glutCreateWindow("WebP viewer"); glutCreateWindow("WebP viewer");
glutDisplayFunc(HandleDisplay); glutDisplayFunc(HandleDisplay);
glutReshapeFunc(HandleReshape);
glutIdleFunc(NULL); glutIdleFunc(NULL);
glutKeyboardFunc(HandleKey); glutKeyboardFunc(HandleKey);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -397,6 +356,7 @@ static void StartDisplay(void) {
GetColorf(kParams.bg_color, 8), GetColorf(kParams.bg_color, 8),
GetColorf(kParams.bg_color, 16), GetColorf(kParams.bg_color, 16),
GetColorf(kParams.bg_color, 24)); GetColorf(kParams.bg_color, 24));
HandleReshape(width, height);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
DrawCheckerBoard(); DrawCheckerBoard();
} }
@ -408,53 +368,42 @@ static void Help(void) {
printf("Usage: vwebp in_file [options]\n\n" printf("Usage: vwebp in_file [options]\n\n"
"Decodes the WebP image file and visualize it using OpenGL\n" "Decodes the WebP image file and visualize it using OpenGL\n"
"Options are:\n" "Options are:\n"
" -version ..... print version number and exit\n" " -version .... print version number and exit.\n"
" -noicc ....... don't use the icc profile if present\n" " -noicc ....... don't use the icc profile if present.\n"
" -nofancy ..... don't use the fancy YUV420 upscaler\n" " -nofancy ..... don't use the fancy YUV420 upscaler.\n"
" -nofilter .... disable in-loop filtering\n" " -nofilter .... disable in-loop filtering.\n"
" -dither <int> dithering strength (0..100), default=50\n" " -mt .......... use multi-threading.\n"
" -noalphadither disable alpha plane dithering\n" " -info ........ print info.\n"
" -mt .......... use multi-threading\n" " -h ....... this help message.\n"
" -info ........ print info\n"
" -h ........... this help message\n"
"\n" "\n"
"Keyboard shortcuts:\n" "Keyboard shortcuts:\n"
" 'c' ................ toggle use of color profile\n" " 'c' ................ toggle use of color profile.\n"
" 'i' ................ overlay file information\n" " 'i' ................ overlay file information.\n"
" 'd' ................ disable blending & disposal (debug)\n" " 'q' / 'Q' / ESC .... quit.\n"
" 'q' / 'Q' / ESC .... quit\n"
); );
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
WebPDecoderConfig config;
int c; int c;
WebPDecoderConfig* const config = &kParams.config;
WebPIterator* const curr = &kParams.curr_frame;
if (!WebPInitDecoderConfig(config)) { if (!WebPInitDecoderConfig(&config)) {
fprintf(stderr, "Library version mismatch!\n"); fprintf(stderr, "Library version mismatch!\n");
return -1; return -1;
} }
config->options.dithering_strength = 50; kParams.config = &config;
config->options.alpha_dithering_strength = 100;
kParams.use_color_profile = 1; kParams.use_color_profile = 1;
for (c = 1; c < argc; ++c) { for (c = 1; c < argc; ++c) {
int parse_error = 0;
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help(); Help();
return 0; return 0;
} else if (!strcmp(argv[c], "-noicc")) { } else if (!strcmp(argv[c], "-noicc")) {
kParams.use_color_profile = 0; kParams.use_color_profile = 0;
} else if (!strcmp(argv[c], "-nofancy")) { } else if (!strcmp(argv[c], "-nofancy")) {
config->options.no_fancy_upsampling = 1; config.options.no_fancy_upsampling = 1;
} else if (!strcmp(argv[c], "-nofilter")) { } else if (!strcmp(argv[c], "-nofilter")) {
config->options.bypass_filtering = 1; config.options.bypass_filtering = 1;
} else if (!strcmp(argv[c], "-noalphadither")) {
config->options.alpha_dithering_strength = 0;
} else if (!strcmp(argv[c], "-dither") && c + 1 < argc) {
config->options.dithering_strength =
ExUtilGetInt(argv[++c], 0, &parse_error);
} else if (!strcmp(argv[c], "-info")) { } else if (!strcmp(argv[c], "-info")) {
kParams.print_info = 1; kParams.print_info = 1;
} else if (!strcmp(argv[c], "-version")) { } else if (!strcmp(argv[c], "-version")) {
@ -466,10 +415,7 @@ int main(int argc, char *argv[]) {
(dmux_version >> 8) & 0xff, dmux_version & 0xff); (dmux_version >> 8) & 0xff, dmux_version & 0xff);
return 0; return 0;
} else if (!strcmp(argv[c], "-mt")) { } else if (!strcmp(argv[c], "-mt")) {
config->options.use_threads = 1; config.options.use_threads = 1;
} else if (!strcmp(argv[c], "--")) {
if (c < argc - 1) kParams.file_name = argv[++c];
break;
} else if (argv[c][0] == '-') { } else if (argv[c][0] == '-') {
printf("Unknown option '%s'\n", argv[c]); printf("Unknown option '%s'\n", argv[c]);
Help(); Help();
@ -477,11 +423,6 @@ int main(int argc, char *argv[]) {
} else { } else {
kParams.file_name = argv[c]; kParams.file_name = argv[c];
} }
if (parse_error) {
Help();
return -1;
}
} }
if (kParams.file_name == NULL) { if (kParams.file_name == NULL) {
@ -490,8 +431,8 @@ int main(int argc, char *argv[]) {
return 0; return 0;
} }
if (!ImgIoUtilReadFile(kParams.file_name, if (!ExUtilReadFile(kParams.file_name,
&kParams.data.bytes, &kParams.data.size)) { &kParams.data.bytes, &kParams.data.size)) {
goto Error; goto Error;
} }
@ -506,13 +447,20 @@ int main(int argc, char *argv[]) {
goto Error; goto Error;
} }
if (WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & FRAGMENTS_FLAG) {
fprintf(stderr, "Image fragments are not supported for now!\n");
goto Error;
}
kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH); kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH);
kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT); kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT);
if (kParams.print_info) { if (kParams.print_info) {
printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height); printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
} }
ClearPreviousFrame(); kParams.prev_frame.width = kParams.canvas_width;
kParams.prev_frame.height = kParams.canvas_height;
kParams.prev_frame.x_offset = kParams.prev_frame.y_offset = 0;
kParams.prev_frame.dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
memset(&kParams.iccp, 0, sizeof(kParams.iccp)); memset(&kParams.iccp, 0, sizeof(kParams.iccp));
kParams.has_color_profile = kParams.has_color_profile =
@ -528,28 +476,22 @@ int main(int argc, char *argv[]) {
#endif #endif
} }
if (!WebPDemuxGetFrame(kParams.dmux, 1, curr)) goto Error; if (!WebPDemuxGetFrame(kParams.dmux, 1, &kParams.frameiter)) goto Error;
kParams.has_animation = (curr->num_frames > 1); kParams.has_animation = (kParams.frameiter.num_frames > 1);
kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT); kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR); kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR);
printf("VP8X: Found %d images in file (loop count = %d)\n", printf("VP8X: Found %d images in file (loop count = %d)\n",
curr->num_frames, kParams.loop_count); kParams.frameiter.num_frames, kParams.loop_count);
// Decode first frame // Decode first frame
if (!Decode()) goto Error; if (!Decode()) goto Error;
// Position iterator to last frame. Next call to HandleDisplay will wrap over. // Position iterator to last frame. Next call to HandleDisplay will wrap over.
// We take this into account by bumping up loop_count. // We take this into account by bumping up loop_count.
WebPDemuxGetFrame(kParams.dmux, 0, curr); WebPDemuxGetFrame(kParams.dmux, 0, &kParams.frameiter);
if (kParams.loop_count) ++kParams.loop_count; if (kParams.loop_count) ++kParams.loop_count;
#if defined(__unix__) || defined(__CYGWIN__)
// Work around GLUT compositor bug.
// https://bugs.launchpad.net/ubuntu/+source/freeglut/+bug/369891
setenv("XLIB_SKIP_ARGB_VISUALS", "1", 1);
#endif
// Start display (and timer) // Start display (and timer)
glutInit(&argc, argv); glutInit(&argc, argv);
#ifdef FREEGLUT #ifdef FREEGLUT
@ -569,14 +511,4 @@ int main(int argc, char *argv[]) {
return -1; return -1;
} }
#else // !WEBP_HAVE_GL
int main(int argc, const char *argv[]) {
fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
(void)argc;
return 0;
}
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -28,6 +28,7 @@
webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Extract relevant data from WebP container file: Extract relevant data from WebP container file:
webpmux -get frgm n in.webp -o out_fragment.webp
webpmux -get frame n in.webp -o out_frame.webp webpmux -get frame n in.webp -o out_frame.webp
webpmux -get icc in.webp -o image_profile.icc webpmux -get icc in.webp -o image_profile.icc
webpmux -get exif in.webp -o image_metadata.exif webpmux -get exif in.webp -o image_metadata.exif
@ -38,11 +39,6 @@
webpmux -strip exif in.webp -o out.webp webpmux -strip exif in.webp -o out.webp
webpmux -strip xmp in.webp -o out.webp webpmux -strip xmp in.webp -o out.webp
Change duration of frame intervals:
webpmux -duration 150 in.webp -o out.webp
webpmux -duration 33,2 in.webp -o out.webp
webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp
Misc: Misc:
webpmux -info in.webp webpmux -info in.webp
webpmux [ -h | -help ] webpmux [ -h | -help ]
@ -50,17 +46,15 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif #endif
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "webp/decode.h"
#include "webp/mux.h" #include "webp/mux.h"
#include "../examples/example_util.h" #include "./example_util.h"
#include "../imageio/imageio_util.h"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Config object to parse command-line arguments. // Config object to parse command-line arguments.
@ -71,8 +65,7 @@ typedef enum {
ACTION_SET, ACTION_SET,
ACTION_STRIP, ACTION_STRIP,
ACTION_INFO, ACTION_INFO,
ACTION_HELP, ACTION_HELP
ACTION_DURATION
} ActionType; } ActionType;
typedef enum { typedef enum {
@ -94,17 +87,17 @@ typedef enum {
FEATURE_XMP, FEATURE_XMP,
FEATURE_ICCP, FEATURE_ICCP,
FEATURE_ANMF, FEATURE_ANMF,
FEATURE_DURATION, FEATURE_FRGM,
LAST_FEATURE LAST_FEATURE
} FeatureType; } FeatureType;
static const char* const kFourccList[LAST_FEATURE] = { static const char* const kFourccList[LAST_FEATURE] = {
NULL, "EXIF", "XMP ", "ICCP", "ANMF" NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM"
}; };
static const char* const kDescriptions[LAST_FEATURE] = { static const char* const kDescriptions[LAST_FEATURE] = {
NULL, "EXIF metadata", "XMP metadata", "ICC profile", NULL, "EXIF metadata", "XMP metadata", "ICC profile",
"Animation frame" "Animation frame", "Image fragment"
}; };
typedef struct { typedef struct {
@ -136,7 +129,7 @@ static int CountOccurrences(const char* arglist[], int list_length,
return num_occurences; return num_occurences;
} }
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = { static const char* const kErrorMessages[] = {
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA", "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA" "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
}; };
@ -152,6 +145,12 @@ static const char* ErrorString(WebPMuxError err) {
return err; \ return err; \
} }
#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
if (err != WEBP_MUX_OK) { \
fprintf(stderr, ERR_MSG, FORMAT_STR); \
return err; \
}
#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \ #define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
if (err != WEBP_MUX_OK) { \ if (err != WEBP_MUX_OK) { \
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \ fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
@ -173,81 +172,66 @@ static const char* ErrorString(WebPMuxError err) {
} while (0) } while (0)
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \ #define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
do { \ do { \
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \ fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
ok = 0; \ ok = 0; \
goto LABEL; \ goto LABEL; \
} while (0) } while (0)
static WebPMuxError DisplayInfo(const WebPMux* mux) { static WebPMuxError DisplayInfo(const WebPMux* mux) {
int width, height;
uint32_t flag; uint32_t flag;
WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height); WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
assert(err == WEBP_MUX_OK); // As WebPMuxCreate() was successful earlier. #ifndef WEBP_EXPERIMENTAL_FEATURES
printf("Canvas size: %d x %d\n", width, height); if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
#endif
err = WebPMuxGetFeatures(mux, &flag);
RETURN_IF_ERROR("Failed to retrieve features\n"); RETURN_IF_ERROR("Failed to retrieve features\n");
if (flag == 0) { if (flag == 0) {
printf("No features present.\n"); fprintf(stderr, "No features present.\n");
return err; return err;
} }
// Print the features present. // Print the features present.
printf("Features present:"); printf("Features present:");
if (flag & ANIMATION_FLAG) printf(" animation"); if (flag & ANIMATION_FLAG) printf(" animation");
if (flag & FRAGMENTS_FLAG) printf(" image fragments");
if (flag & ICCP_FLAG) printf(" ICC profile"); if (flag & ICCP_FLAG) printf(" ICC profile");
if (flag & EXIF_FLAG) printf(" EXIF metadata"); if (flag & EXIF_FLAG) printf(" EXIF metadata");
if (flag & XMP_FLAG) printf(" XMP metadata"); if (flag & XMP_FLAG) printf(" XMP metadata");
if (flag & ALPHA_FLAG) printf(" transparency"); if (flag & ALPHA_FLAG) printf(" transparency");
printf("\n"); printf("\n");
if (flag & ANIMATION_FLAG) { if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
const WebPChunkId id = WEBP_CHUNK_ANMF; const int is_anim = !!(flag & ANIMATION_FLAG);
const char* const type_str = "frame"; const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
const char* const type_str = is_anim ? "frame" : "fragment";
int nFrames; int nFrames;
WebPMuxAnimParams params; if (is_anim) {
err = WebPMuxGetAnimationParams(mux, &params); WebPMuxAnimParams params;
assert(err == WEBP_MUX_OK); err = WebPMuxGetAnimationParams(mux, &params);
printf("Background color : 0x%.8X Loop Count : %d\n", RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
params.bgcolor, params.loop_count); printf("Background color : 0x%.8X Loop Count : %d\n",
params.bgcolor, params.loop_count);
}
err = WebPMuxNumChunks(mux, id, &nFrames); err = WebPMuxNumChunks(mux, id, &nFrames);
assert(err == WEBP_MUX_OK); RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str);
printf("Number of %ss: %d\n", type_str, nFrames); printf("Number of %ss: %d\n", type_str, nFrames);
if (nFrames > 0) { if (nFrames > 0) {
int i; int i;
printf("No.: width height alpha x_offset y_offset "); printf("No.: x_offset y_offset ");
printf("duration dispose blend "); if (is_anim) printf("duration dispose ");
printf("image_size compression\n"); printf("image_size\n");
for (i = 1; i <= nFrames; i++) { for (i = 1; i <= nFrames; i++) {
WebPMuxFrameInfo frame; WebPMuxFrameInfo frame;
err = WebPMuxGetFrame(mux, i, &frame); err = WebPMuxGetFrame(mux, i, &frame);
if (err == WEBP_MUX_OK) { if (err == WEBP_MUX_OK) {
WebPBitstreamFeatures features; printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
const VP8StatusCode status = WebPGetFeatures( if (is_anim) printf("%8d %7d ", frame.duration, frame.dispose_method);
frame.bitstream.bytes, frame.bitstream.size, &features); printf("%10d\n", (int)frame.bitstream.size);
assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate().
(void)status;
printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
features.height, features.has_alpha ? "yes" : "no",
frame.x_offset, frame.y_offset);
{
const char* const dispose =
(frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
: "background";
const char* const blend =
(frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
printf("%8d %10s %5s ", frame.duration, dispose, blend);
}
printf("%10d %11s\n", (int)frame.bitstream.size,
(features.format == 1) ? "lossy" :
(features.format == 2) ? "lossless" :
"undefined");
} }
WebPDataClear(&frame.bitstream); WebPDataClear(&frame.bitstream);
RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i); RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
@ -258,25 +242,25 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
if (flag & ICCP_FLAG) { if (flag & ICCP_FLAG) {
WebPData icc_profile; WebPData icc_profile;
err = WebPMuxGetChunk(mux, "ICCP", &icc_profile); err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
assert(err == WEBP_MUX_OK); RETURN_IF_ERROR("Failed to retrieve the ICC profile\n");
printf("Size of the ICC profile data: %d\n", (int)icc_profile.size); printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
} }
if (flag & EXIF_FLAG) { if (flag & EXIF_FLAG) {
WebPData exif; WebPData exif;
err = WebPMuxGetChunk(mux, "EXIF", &exif); err = WebPMuxGetChunk(mux, "EXIF", &exif);
assert(err == WEBP_MUX_OK); RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n");
printf("Size of the EXIF metadata: %d\n", (int)exif.size); printf("Size of the EXIF metadata: %d\n", (int)exif.size);
} }
if (flag & XMP_FLAG) { if (flag & XMP_FLAG) {
WebPData xmp; WebPData xmp;
err = WebPMuxGetChunk(mux, "XMP ", &xmp); err = WebPMuxGetChunk(mux, "XMP ", &xmp);
assert(err == WEBP_MUX_OK); RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
printf("Size of the XMP metadata: %d\n", (int)xmp.size); printf("Size of the XMP metadata: %d\n", (int)xmp.size);
} }
if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) { if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
WebPMuxFrameInfo image; WebPMuxFrameInfo image;
err = WebPMuxGetFrame(mux, 1, &image); err = WebPMuxGetFrame(mux, 1, &image);
if (err == WEBP_MUX_OK) { if (err == WEBP_MUX_OK) {
@ -292,9 +276,10 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
static void PrintHelp(void) { static void PrintHelp(void) {
printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n"); printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n"); printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n");
printf(" INPUT -o OUTPUT\n");
printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n"); printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
#ifdef WEBP_EXPERIMENTAL_FEATURES
printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
#endif
printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]" printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
"\n"); "\n");
printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n"); printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
@ -304,51 +289,50 @@ static void PrintHelp(void) {
printf("\n"); printf("\n");
printf("GET_OPTIONS:\n"); printf("GET_OPTIONS:\n");
printf(" Extract relevant data:\n"); printf(" Extract relevant data.\n");
printf(" icc get ICC profile\n"); printf(" icc Get ICC profile.\n");
printf(" exif get EXIF metadata\n"); printf(" exif Get EXIF metadata.\n");
printf(" xmp get XMP metadata\n"); printf(" xmp Get XMP metadata.\n");
printf(" frame n get nth frame\n"); #ifdef WEBP_EXPERIMENTAL_FEATURES
printf(" frgm n Get nth fragment.\n");
#endif
printf(" frame n Get nth frame.\n");
printf("\n"); printf("\n");
printf("SET_OPTIONS:\n"); printf("SET_OPTIONS:\n");
printf(" Set color profile/metadata:\n"); printf(" Set color profile/metadata.\n");
printf(" icc file.icc set ICC profile\n"); printf(" icc file.icc Set ICC profile.\n");
printf(" exif file.exif set EXIF metadata\n"); printf(" exif file.exif Set EXIF metadata.\n");
printf(" xmp file.xmp set XMP metadata\n"); printf(" xmp file.xmp Set XMP metadata.\n");
printf(" where: 'file.icc' contains the ICC profile to be set,\n"); printf(" where: 'file.icc' contains the ICC profile to be set,\n");
printf(" 'file.exif' contains the EXIF metadata to be set\n"); printf(" 'file.exif' contains the EXIF metadata to be set\n");
printf(" 'file.xmp' contains the XMP metadata to be set\n"); printf(" 'file.xmp' contains the XMP metadata to be set\n");
printf("\n");
printf("DURATION_OPTIONS:\n");
printf(" Set duration of selected frames:\n");
printf(" duration set duration for each frames\n");
printf(" duration,frame set duration of a particular frame\n");
printf(" duration,start,end set duration of frames in the\n");
printf(" interval [start,end])\n");
printf(" where: 'duration' is the duration in milliseconds\n");
printf(" 'start' is the start frame index\n");
printf(" 'end' is the inclusive end frame index\n");
printf(" The special 'end' value '0' means: last frame.\n");
printf("\n"); printf("\n");
printf("STRIP_OPTIONS:\n"); printf("STRIP_OPTIONS:\n");
printf(" Strip color profile/metadata:\n"); printf(" Strip color profile/metadata.\n");
printf(" icc strip ICC profile\n"); printf(" icc Strip ICC profile.\n");
printf(" exif strip EXIF metadata\n"); printf(" exif Strip EXIF metadata.\n");
printf(" xmp strip XMP metadata\n"); printf(" xmp Strip XMP metadata.\n");
#ifdef WEBP_EXPERIMENTAL_FEATURES
printf("\n");
printf("FRAGMENT_OPTIONS(i):\n");
printf(" Create fragmented image.\n");
printf(" file_i +xi+yi\n");
printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
printf(" 'xi','yi' specify the image offset for this fragment."
"\n");
#endif
printf("\n"); printf("\n");
printf("FRAME_OPTIONS(i):\n"); printf("FRAME_OPTIONS(i):\n");
printf(" Create animation:\n"); printf(" Create animation.\n");
printf(" file_i +di+[xi+yi[+mi[bi]]]\n"); printf(" file_i +di+xi+yi+mi\n");
printf(" where: 'file_i' is the i'th animation frame (WebP format),\n"); printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
printf(" 'di' is the pause duration before next frame,\n"); printf(" 'di' is the pause duration before next frame.\n");
printf(" 'xi','yi' specify the image offset for this frame,\n"); printf(" 'xi','yi' specify the image offset for this frame.\n");
printf(" 'mi' is the dispose method for this frame (0 or 1),\n"); printf(" 'mi' is the dispose method for this frame (0 or 1).\n");
printf(" 'bi' is the blending method for this frame (+b or -b)"
"\n");
printf("\n"); printf("\n");
printf("LOOP_COUNT:\n"); printf("LOOP_COUNT:\n");
@ -363,7 +347,7 @@ static void PrintHelp(void) {
"specifying\n"); "specifying\n");
printf(" the Alpha, Red, Green and Blue component values " printf(" the Alpha, Red, Green and Blue component values "
"respectively\n"); "respectively\n");
printf(" [Default: 255,255,255,255]\n"); printf(" [Default: 255,255,255,255].\n");
printf("\nINPUT & OUTPUT are in WebP format.\n"); printf("\nINPUT & OUTPUT are in WebP format.\n");
@ -371,19 +355,11 @@ static void PrintHelp(void) {
printf(" and is assumed to be\nvalid.\n"); printf(" and is assumed to be\nvalid.\n");
} }
static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
if ((info->x_offset | info->y_offset) & 1) {
fprintf(stderr, "Warning: odd offsets will be snapped to even values"
" (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
info->x_offset & ~1, info->y_offset & ~1);
}
}
static int ReadFileToWebPData(const char* const filename, static int ReadFileToWebPData(const char* const filename,
WebPData* const webp_data) { WebPData* const webp_data) {
const uint8_t* data; const uint8_t* data;
size_t size; size_t size;
if (!ImgIoUtilReadFile(filename, &data, &size)) return 0; if (!ExUtilReadFile(filename, &data, &size)) return 0;
webp_data->bytes = data; webp_data->bytes = data;
webp_data->size = size; webp_data->size = size;
return 1; return 1;
@ -402,9 +378,8 @@ static int CreateMux(const char* const filename, WebPMux** mux) {
static int WriteData(const char* filename, const WebPData* const webpdata) { static int WriteData(const char* filename, const WebPData* const webpdata) {
int ok = 0; int ok = 0;
FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
: ImgIoUtilSetBinaryMode(stdout); if (!fout) {
if (fout == NULL) {
fprintf(stderr, "Error opening output WebP file %s!\n", filename); fprintf(stderr, "Error opening output WebP file %s!\n", filename);
return 0; return 0;
} }
@ -432,80 +407,31 @@ static int WriteWebP(WebPMux* const mux, const char* filename) {
return ok; return ok;
} }
static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
WebPMux* new_mux = WebPMuxNew();
WebPMuxAnimParams p;
WebPMuxError err;
int i;
int ok = 1;
if (new_mux == NULL) return NULL;
err = WebPMuxGetAnimationParams(mux, &p);
if (err == WEBP_MUX_OK) {
err = WebPMuxSetAnimationParams(new_mux, &p);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("Error (%s) handling animation params.\n",
ErrorString(err), End);
}
} else {
/* it might not be an animation. Just keep moving. */
}
for (i = 1; i <= 3; ++i) {
WebPData metadata;
err = WebPMuxGetChunk(mux, kFourccList[i], &metadata);
if (err == WEBP_MUX_OK && metadata.size > 0) {
err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
if (err != WEBP_MUX_OK) {
ERROR_GOTO1("Error transferring metadata in DuplicateMux().", End);
}
}
}
End:
if (!ok) {
WebPMuxDelete(new_mux);
new_mux = NULL;
}
return new_mux;
}
static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) { static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
int dispose_method, dummy; int dispose_method, dummy;
char plus_minus, blend_method; const int num_args = sscanf(args, "+%d+%d+%d+%d+%d",
const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration, &info->duration, &info->x_offset, &info->y_offset,
&info->x_offset, &info->y_offset, &dispose_method, &dispose_method, &dummy);
&plus_minus, &blend_method, &dummy);
switch (num_args) { switch (num_args) {
case 1: case 1:
info->x_offset = info->y_offset = 0; // fall through info->x_offset = info->y_offset = 0; // fall through
case 3: case 3:
dispose_method = 0; // fall through dispose_method = 0; // fall through
case 4: case 4:
plus_minus = '+';
blend_method = 'b'; // fall through
case 6:
break; break;
case 2:
case 5:
default: default:
return 0; return 0;
} }
WarnAboutOddOffset(info);
// Note: The sanity of the following conversion is checked by // Note: The sanity of the following conversion is checked by
// WebPMuxPushFrame(). // WebPMuxSetAnimationParams().
info->dispose_method = (WebPMuxAnimDispose)dispose_method; info->dispose_method = (WebPMuxAnimDispose)dispose_method;
if (blend_method != 'b') return 0;
if (plus_minus != '-' && plus_minus != '+') return 0;
info->blend_method =
(plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
return 1; return 1;
} }
static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
}
static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) { static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
uint32_t a, r, g, b; uint32_t a, r, g, b;
if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0; if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
@ -520,7 +446,7 @@ static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
static void DeleteConfig(WebPMuxConfig* config) { static void DeleteConfig(WebPMuxConfig* config) {
if (config != NULL) { if (config != NULL) {
free(config->feature_.args_); free(config->feature_.args_);
memset(config, 0, sizeof(*config)); free(config);
} }
} }
@ -534,9 +460,9 @@ static void DeleteConfig(WebPMuxConfig* config) {
static int ValidateCommandLine(int argc, const char* argv[], static int ValidateCommandLine(int argc, const char* argv[],
int* num_feature_args) { int* num_feature_args) {
int num_frame_args; int num_frame_args;
int num_frgm_args;
int num_loop_args; int num_loop_args;
int num_bgcolor_args; int num_bgcolor_args;
int num_durations_args;
int ok = 1; int ok = 1;
assert(num_feature_args != NULL); assert(num_feature_args != NULL);
@ -561,9 +487,9 @@ static int ValidateCommandLine(int argc, const char* argv[],
// Compound checks. // Compound checks.
num_frame_args = CountOccurrences(argv, argc, "-frame"); num_frame_args = CountOccurrences(argv, argc, "-frame");
num_frgm_args = CountOccurrences(argv, argc, "-frgm");
num_loop_args = CountOccurrences(argv, argc, "-loop"); num_loop_args = CountOccurrences(argv, argc, "-loop");
num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor"); num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
num_durations_args = CountOccurrences(argv, argc, "-duration");
if (num_loop_args > 1) { if (num_loop_args > 1) {
ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate); ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
@ -576,20 +502,22 @@ static int ValidateCommandLine(int argc, const char* argv[],
ERROR_GOTO1("ERROR: Loop count and background color are relevant only in " ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
"case of animation.\n", ErrValidate); "case of animation.\n", ErrValidate);
} }
if (num_durations_args > 0 && num_frame_args != 0) { if (num_frame_args > 0 && num_frgm_args > 0) {
ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n", ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
ErrValidate); "time.\n", ErrValidate);
} }
assert(ok == 1); assert(ok == 1);
if (num_durations_args > 0) { if (num_frame_args == 0 && num_frgm_args == 0) {
*num_feature_args = num_durations_args;
} else if (num_frame_args == 0) {
// Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action). // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
*num_feature_args = 1; *num_feature_args = 1;
} else { } else {
// Multiple arguments ('set' action for animation) // Multiple arguments ('set' action for animation or fragmented image).
*num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args; if (num_frame_args > 0) {
*num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
} else {
*num_feature_args = num_frgm_args;
}
} }
ErrValidate: ErrValidate:
@ -631,21 +559,6 @@ static int ParseCommandLine(int argc, const char* argv[],
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
} }
++i; ++i;
} else if (!strcmp(argv[i], "-duration")) {
CHECK_NUM_ARGS_LESS(2, ErrParse);
if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
config->action_type_ = ACTION_DURATION;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_DURATION) {
feature->type_ = FEATURE_DURATION;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
arg->params_ = argv[i + 1];
++feature_arg_index;
i += 2;
} else if (!strcmp(argv[i], "-get")) { } else if (!strcmp(argv[i], "-get")) {
if (ACTION_IS_NIL) { if (ACTION_IS_NIL) {
config->action_type_ = ACTION_GET; config->action_type_ = ACTION_GET;
@ -695,6 +608,24 @@ static int ParseCommandLine(int argc, const char* argv[],
arg->params_ = argv[i + 1]; arg->params_ = argv[i + 1];
++feature_arg_index; ++feature_arg_index;
i += 2; i += 2;
#ifdef WEBP_EXPERIMENTAL_FEATURES
} else if (!strcmp(argv[i], "-frgm")) {
CHECK_NUM_ARGS_LESS(3, ErrParse);
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
config->action_type_ = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
feature->type_ = FEATURE_FRGM;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
arg->filename_ = argv[i + 1];
arg->params_ = argv[i + 2];
++feature_arg_index;
i += 3;
#endif
} else if (!strcmp(argv[i], "-o")) { } else if (!strcmp(argv[i], "-o")) {
CHECK_NUM_ARGS_LESS(2, ErrParse); CHECK_NUM_ARGS_LESS(2, ErrParse);
config->output_ = argv[i + 1]; config->output_ = argv[i + 1];
@ -719,17 +650,6 @@ static int ParseCommandLine(int argc, const char* argv[],
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
DeleteConfig(config); DeleteConfig(config);
exit(0); exit(0);
} else if (!strcmp(argv[i], "--")) {
if (i < argc - 1) {
++i;
if (config->input_ == NULL) {
config->input_ = argv[i];
} else {
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
argv[i], ErrParse);
}
}
break;
} else { } else {
ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse); ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
} }
@ -754,10 +674,16 @@ static int ParseCommandLine(int argc, const char* argv[],
} else { } else {
++i; ++i;
} }
#ifdef WEBP_EXPERIMENTAL_FEATURES
} else if ((!strcmp(argv[i], "frame") ||
!strcmp(argv[i], "frgm")) &&
#else
} else if (!strcmp(argv[i], "frame") && } else if (!strcmp(argv[i], "frame") &&
(config->action_type_ == ACTION_GET)) { #endif
(config->action_type_ == ACTION_GET)) {
CHECK_NUM_ARGS_LESS(2, ErrParse); CHECK_NUM_ARGS_LESS(2, ErrParse);
feature->type_ = FEATURE_ANMF; feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
FEATURE_FRGM;
arg->params_ = argv[i + 1]; arg->params_ = argv[i + 1];
++feature_arg_index; ++feature_arg_index;
i += 2; i += 2;
@ -795,7 +721,8 @@ static int ValidateConfig(WebPMuxConfig* config) {
if (config->input_ == NULL) { if (config->input_ == NULL) {
if (config->action_type_ != ACTION_SET) { if (config->action_type_ != ACTION_SET) {
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2); ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
} else if (feature->type_ != FEATURE_ANMF) { } else if (feature->type_ != FEATURE_ANMF &&
feature->type_ != FEATURE_FRGM) {
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2); ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
} }
} }
@ -811,27 +738,33 @@ static int ValidateConfig(WebPMuxConfig* config) {
// Create config object from command-line arguments. // Create config object from command-line arguments.
static int InitializeConfig(int argc, const char* argv[], static int InitializeConfig(int argc, const char* argv[],
WebPMuxConfig* config) { WebPMuxConfig** config) {
int num_feature_args = 0; int num_feature_args = 0;
int ok = 1; int ok = 1;
assert(config != NULL); assert(config != NULL);
memset(config, 0, sizeof(*config)); *config = NULL;
// Validate command-line arguments. // Validate command-line arguments.
if (!ValidateCommandLine(argc, argv, &num_feature_args)) { if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1); ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
} }
config->feature_.arg_count_ = num_feature_args; // Allocate memory.
config->feature_.args_ = *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
(FeatureArg*)calloc(num_feature_args, sizeof(*config->feature_.args_)); if (*config == NULL) {
if (config->feature_.args_ == NULL) { ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
}
(*config)->feature_.arg_count_ = num_feature_args;
(*config)->feature_.args_ =
(FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
if ((*config)->feature_.args_ == NULL) {
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1); ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
} }
// Parse command-line. // Parse command-line.
if (!ParseCommandLine(argc, argv, config) || !ValidateConfig(config)) { if (!ParseCommandLine(argc, argv, *config) ||
!ValidateConfig(*config)) {
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1); ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
} }
@ -847,26 +780,25 @@ static int InitializeConfig(int argc, const char* argv[],
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Processing. // Processing.
static int GetFrame(const WebPMux* mux, const WebPMuxConfig* config) { static int GetFrameFragment(const WebPMux* mux,
const WebPMuxConfig* config, int is_frame) {
WebPMuxError err = WEBP_MUX_OK; WebPMuxError err = WEBP_MUX_OK;
WebPMux* mux_single = NULL; WebPMux* mux_single = NULL;
int num = 0; long num = 0;
int ok = 1; int ok = 1;
int parse_error = 0; const WebPChunkId id = is_frame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
const WebPChunkId id = WEBP_CHUNK_ANMF;
WebPMuxFrameInfo info; WebPMuxFrameInfo info;
WebPDataInit(&info.bitstream); WebPDataInit(&info.bitstream);
num = ExUtilGetInt(config->feature_.args_[0].params_, 10, &parse_error); num = strtol(config->feature_.args_[0].params_, NULL, 10);
if (num < 0) { if (num < 0) {
ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet); ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
} }
if (parse_error) goto ErrGet;
err = WebPMuxGetFrame(mux, num, &info); err = WebPMuxGetFrame(mux, num, &info);
if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND; if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
if (err != WEBP_MUX_OK) { if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n", ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
ErrorString(err), num, ErrGet); ErrorString(err), num, ErrGet);
} }
@ -887,7 +819,7 @@ static int GetFrame(const WebPMux* mux, const WebPMuxConfig* config) {
ErrGet: ErrGet:
WebPDataClear(&info.bitstream); WebPDataClear(&info.bitstream);
WebPMuxDelete(mux_single); WebPMuxDelete(mux_single);
return ok && !parse_error; return ok;
} }
// Read and process config. // Read and process config.
@ -904,7 +836,9 @@ static int Process(const WebPMuxConfig* config) {
if (!ok) goto Err2; if (!ok) goto Err2;
switch (feature->type_) { switch (feature->type_) {
case FEATURE_ANMF: case FEATURE_ANMF:
ok = GetFrame(mux, config); case FEATURE_FRGM:
ok = GetFrameFragment(mux, config,
(feature->type_ == FEATURE_ANMF) ? 1 : 0);
break; break;
case FEATURE_ICCP: case FEATURE_ICCP:
@ -947,19 +881,16 @@ static int Process(const WebPMuxConfig* config) {
break; break;
} }
case SUBTYPE_LOOP: { case SUBTYPE_LOOP: {
int parse_error = 0; const long loop_count =
const int loop_count = strtol(feature->args_[i].params_, NULL, 10);
ExUtilGetInt(feature->args_[i].params_, 10, &parse_error); if (loop_count != (int)loop_count) {
if (loop_count < 0 || loop_count > 65535) {
// Note: This is only a 'necessary' condition for loop_count // Note: This is only a 'necessary' condition for loop_count
// to be valid. The 'sufficient' conditioned in checked in // to be valid. The 'sufficient' conditioned in checked in
// WebPMuxSetAnimationParams() method called later. // WebPMuxSetAnimationParams() method called later.
ERROR_GOTO1("ERROR: Loop count must be in the range 0 to " ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
"65535.\n", Err2); "65535.\n", Err2);
} }
ok = !parse_error; params.loop_count = (int)loop_count;
if (!ok) goto Err2;
params.loop_count = loop_count;
break; break;
} }
case SUBTYPE_ANMF: { case SUBTYPE_ANMF: {
@ -996,6 +927,35 @@ static int Process(const WebPMuxConfig* config) {
break; break;
} }
case FEATURE_FRGM: {
int i;
mux = WebPMuxNew();
if (mux == NULL) {
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
}
for (i = 0; i < feature->arg_count_; ++i) {
WebPMuxFrameInfo frgm;
frgm.id = WEBP_CHUNK_FRGM;
ok = ReadFileToWebPData(feature->args_[i].filename_,
&frgm.bitstream);
if (!ok) goto Err2;
ok = ParseFragmentArgs(feature->args_[i].params_, &frgm);
if (!ok) {
WebPDataClear(&frgm.bitstream);
ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
Err2);
}
err = WebPMuxPushFrame(mux, &frgm, 1);
WebPDataClear(&frgm.bitstream);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
ErrorString(err), i, Err2);
}
}
break;
}
case FEATURE_ICCP: case FEATURE_ICCP:
case FEATURE_EXIF: case FEATURE_EXIF:
case FEATURE_XMP: { case FEATURE_XMP: {
@ -1019,89 +979,6 @@ static int Process(const WebPMuxConfig* config) {
ok = WriteWebP(mux, config->output_); ok = WriteWebP(mux, config->output_);
break; break;
} }
case ACTION_DURATION: {
int num_frames;
ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2;
err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
ok = (err == WEBP_MUX_OK);
if (!ok) {
ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
}
if (num_frames == 0) {
fprintf(stderr, "Doesn't look like the source is animated. "
"Skipping duration setting.\n");
ok = WriteWebP(mux, config->output_);
if (!ok) goto Err2;
} else {
int i;
int* durations = NULL;
WebPMux* new_mux = DuplicateMuxHeader(mux);
if (new_mux == NULL) goto Err2;
durations = (int*)malloc((size_t)num_frames * sizeof(*durations));
if (durations == NULL) goto Err2;
for (i = 0; i < num_frames; ++i) durations[i] = -1;
// Parse intervals to process.
for (i = 0; i < feature->arg_count_; ++i) {
int k;
int args[3];
int duration, start, end;
const int nb_args = ExUtilGetInts(feature->args_[i].params_,
10, 3, args);
ok = (nb_args >= 1);
if (!ok) goto Err3;
duration = args[0];
if (duration < 0) {
ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
}
if (nb_args == 1) { // only duration is present -> use full interval
start = 1;
end = num_frames;
} else {
start = args[1];
if (start <= 0) {
start = 1;
} else if (start > num_frames) {
start = num_frames;
}
end = (nb_args >= 3) ? args[2] : start;
if (end == 0 || end > num_frames) end = num_frames;
}
for (k = start; k <= end; ++k) {
assert(k >= 1 && k <= num_frames);
durations[k - 1] = duration;
}
}
// Apply non-negative durations to their destination frames.
for (i = 1; i <= num_frames; ++i) {
WebPMuxFrameInfo frame;
err = WebPMuxGetFrame(mux, i, &frame);
if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) {
ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3);
}
if (durations[i - 1] >= 0) frame.duration = durations[i - 1];
err = WebPMuxPushFrame(new_mux, &frame, 1);
if (err != WEBP_MUX_OK) {
ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3);
}
WebPDataClear(&frame.bitstream);
}
WebPMuxDelete(mux);
ok = WriteWebP(new_mux, config->output_);
mux = new_mux; // transfer for the WebPMuxDelete() call
new_mux = NULL;
Err3:
free(durations);
WebPMuxDelete(new_mux);
if (!ok) goto Err2;
}
break;
}
case ACTION_STRIP: { case ACTION_STRIP: {
ok = CreateMux(config->input_, &mux); ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2; if (!ok) goto Err2;
@ -1113,8 +990,8 @@ static int Process(const WebPMuxConfig* config) {
ErrorString(err), kDescriptions[feature->type_], Err2); ErrorString(err), kDescriptions[feature->type_], Err2);
} }
} else { } else {
ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2); ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
break; break;
} }
ok = WriteWebP(mux, config->output_); ok = WriteWebP(mux, config->output_);
break; break;
@ -1140,14 +1017,14 @@ static int Process(const WebPMuxConfig* config) {
// Main. // Main.
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
WebPMuxConfig config; WebPMuxConfig* config;
int ok = InitializeConfig(argc - 1, argv + 1, &config); int ok = InitializeConfig(argc - 1, argv + 1, &config);
if (ok) { if (ok) {
ok = Process(&config); ok = Process(config);
} else { } else {
PrintHelp(); PrintHelp();
} }
DeleteConfig(&config); DeleteConfig(config);
return !ok; return !ok;
} }

View File

@ -12,12 +12,10 @@
#include "./wicdec.h" #include "./wicdec.h"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "webp/config.h" #include "config.h"
#endif #endif
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#ifdef HAVE_WINCODEC_H #ifdef HAVE_WINCODEC_H
#ifdef __MINGW32__ #ifdef __MINGW32__
@ -27,13 +25,11 @@
#define COBJMACROS #define COBJMACROS
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++ #define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
// code with COBJMACROS. // code with COBJMACROS.
#include <ole2.h> // CreateStreamOnHGlobal()
#include <shlwapi.h> #include <shlwapi.h>
#include <windows.h> #include <windows.h>
#include <wincodec.h> #include <wincodec.h>
#include "webp/encode.h" #include "webp/encode.h"
#include "./imageio_util.h"
#include "./metadata.h" #include "./metadata.h"
#define IFS(fn) \ #define IFS(fn) \
@ -76,41 +72,10 @@ WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_, WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
0xf5c7ad2d, 0x6a8d, 0x43dd, 0xf5c7ad2d, 0x6a8d, 0x43dd,
0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9); 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
0x1562ff7c, 0xd352, 0x46f9,
0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
0x6fddc324, 0x4e03, 0x4bfe,
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
static HRESULT OpenInputStream(const char* filename, IStream** stream) { static HRESULT OpenInputStream(const char* filename, IStream** stream) {
HRESULT hr = S_OK; HRESULT hr = S_OK;
if (!strcmp(filename, "-")) { IFS(SHCreateStreamOnFileA(filename, STGM_READ, stream));
const uint8_t* data = NULL;
size_t data_size = 0;
const int ok = ImgIoUtilReadFile(filename, &data, &data_size);
if (ok) {
HGLOBAL image = GlobalAlloc(GMEM_MOVEABLE, data_size);
if (image != NULL) {
void* const image_mem = GlobalLock(image);
if (image_mem != NULL) {
memcpy(image_mem, data, data_size);
GlobalUnlock(image);
IFS(CreateStreamOnHGlobal(image, TRUE, stream));
} else {
hr = E_FAIL;
}
} else {
hr = E_OUTOFMEMORY;
}
free((void*)data);
} else {
hr = E_FAIL;
}
} else {
IFS(SHCreateStreamOnFileA(filename, STGM_READ, stream));
}
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "Error opening input file %s (%08lx)\n", filename, hr); fprintf(stderr, "Error opening input file %s (%08lx)\n", filename, hr);
} }
@ -144,7 +109,6 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
IFS(IWICBitmapFrameDecode_GetColorContexts(frame, IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
count, color_contexts, count, color_contexts,
&num_color_contexts)); &num_color_contexts));
assert(FAILED(hr) || num_color_contexts <= count);
for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) { for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
WICColorContextType type; WICColorContextType type;
IFS(IWICColorContext_GetType(color_contexts[i], &type)); IFS(IWICColorContext_GetType(color_contexts[i], &type));
@ -152,7 +116,7 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
UINT size; UINT size;
IFS(IWICColorContext_GetProfileBytes(color_contexts[i], IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
0, NULL, &size)); 0, NULL, &size));
if (SUCCEEDED(hr) && size > 0) { if (size > 0) {
iccp->bytes = (uint8_t*)malloc(size); iccp->bytes = (uint8_t*)malloc(size);
if (iccp->bytes == NULL) { if (iccp->bytes == NULL) {
hr = E_OUTOFMEMORY; hr = E_OUTOFMEMORY;
@ -230,11 +194,7 @@ static int HasAlpha(IWICImagingFactory* const factory,
has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format), has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) || MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
IsEqualGUID(MAKE_REFGUID(pixel_format), IsEqualGUID(MAKE_REFGUID(pixel_format),
MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) || MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_));
IsEqualGUID(MAKE_REFGUID(pixel_format),
MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) ||
IsEqualGUID(MAKE_REFGUID(pixel_format),
MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_));
} }
return has_alpha; return has_alpha;
} }
@ -274,9 +234,7 @@ int ReadPictureWithWIC(const char* const filename,
NULL NULL
}; };
int has_alpha = 0; int has_alpha = 0;
int64_t stride; int stride;
if (filename == NULL || pic == NULL) return 0;
IFS(CoInitialize(NULL)); IFS(CoInitialize(NULL));
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
@ -303,7 +261,7 @@ int ReadPictureWithWIC(const char* const filename,
IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format)); IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format)); IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
if (SUCCEEDED(hr) && keep_alpha) { if (keep_alpha) {
const GUID** guid; const GUID** guid;
for (guid = kAlphaContainers; *guid != NULL; ++guid) { for (guid = kAlphaContainers; *guid != NULL; ++guid) {
if (IsEqualGUID(MAKE_REFGUID(src_container_format), if (IsEqualGUID(MAKE_REFGUID(src_container_format),
@ -336,27 +294,21 @@ int ReadPictureWithWIC(const char* const filename,
// Decode. // Decode.
IFS(IWICFormatConverter_GetSize(converter, &width, &height)); IFS(IWICFormatConverter_GetSize(converter, &width, &height));
stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb); stride = importer->bytes_per_pixel * width * sizeof(*rgb);
if (stride != (int)stride ||
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
hr = E_FAIL;
}
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
rgb = (BYTE*)malloc((size_t)stride * height); rgb = (BYTE*)malloc(stride * height);
if (rgb == NULL) if (rgb == NULL)
hr = E_OUTOFMEMORY; hr = E_OUTOFMEMORY;
} }
IFS(IWICFormatConverter_CopyPixels(converter, NULL, IFS(IWICFormatConverter_CopyPixels(converter, NULL,
(UINT)stride, (UINT)stride * height, rgb)); stride, stride * height, rgb));
// WebP conversion. // WebP conversion.
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
int ok; int ok;
pic->width = width; pic->width = width;
pic->height = height; 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 (!ok) hr = E_FAIL;
} }
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {

View File

@ -9,10 +9,10 @@
// //
// Windows Imaging Component (WIC) decode. // Windows Imaging Component (WIC) decode.
#ifndef WEBP_IMAGEIO_WICDEC_H_ #ifndef WEBP_EXAMPLES_WICDEC_H_
#define WEBP_IMAGEIO_WICDEC_H_ #define WEBP_EXAMPLES_WICDEC_H_
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
@ -21,14 +21,14 @@ struct WebPPicture;
// Reads an image from 'filename', returning the decoded output in 'pic'. // Reads an image from 'filename', returning the decoded output in 'pic'.
// If 'keep_alpha' is true and the image has an alpha channel, the output is // If 'keep_alpha' is true and the image has an alpha channel, the output is
// RGBA otherwise it will be RGB. pic->use_argb is always forced to true. // RGBA otherwise it will be RGB.
// Returns true on success. // Returns true on success.
int ReadPictureWithWIC(const char* const filename, int ReadPictureWithWIC(const char* const filename,
struct WebPPicture* const pic, int keep_alpha, struct WebPPicture* const pic, int keep_alpha,
struct Metadata* const metadata); struct Metadata* const metadata);
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_IMAGEIO_WICDEC_H_ #endif // WEBP_EXAMPLES_WICDEC_H_

View File

@ -1,26 +0,0 @@
AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
noinst_LTLIBRARIES = libwebpextras.la
noinst_HEADERS =
noinst_HEADERS += ../src/webp/types.h
libwebpextras_la_SOURCES =
libwebpextras_la_SOURCES += extras.c extras.h quality_estimate.c
libwebpextras_la_CPPFLAGS = $(AM_CPPFLAGS)
libwebpextras_la_LDFLAGS = -lm
libwebpextras_la_LIBADD = ../src/libwebp.la
noinst_PROGRAMS = get_disto webp_quality
get_disto_SOURCES = get_disto.c
get_disto_CPPFLAGS = $(AM_CPPFLAGS)
get_disto_LDADD = ../imageio/libimageio_util.la ../imageio/libimagedec.la
get_disto_LDADD += ../src/libwebp.la
get_disto_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS)
webp_quality_SOURCES = webp_quality.c
webp_quality_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
webp_quality_LDADD = ../imageio/libimageio_util.la
webp_quality_LDADD += ./libwebpextras.la
webp_quality_LDADD += ../src/libwebp.la

View File

@ -1,142 +0,0 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Additional WebP utilities.
//
#include "./extras.h"
#include "webp/format_constants.h"
#include <assert.h>
#include <string.h>
#define XTRA_MAJ_VERSION 0
#define XTRA_MIN_VERSION 1
#define XTRA_REV_VERSION 0
//------------------------------------------------------------------------------
int WebPGetExtrasVersion(void) {
return (XTRA_MAJ_VERSION << 16) | (XTRA_MIN_VERSION << 8) | XTRA_REV_VERSION;
}
//------------------------------------------------------------------------------
int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) {
int y, width, uv_width;
if (pic == NULL || gray_data == NULL) return 0;
pic->colorspace = WEBP_YUV420;
if (!WebPPictureAlloc(pic)) return 0;
width = pic->width;
uv_width = (width + 1) >> 1;
for (y = 0; y < pic->height; ++y) {
memcpy(pic->y + y * pic->y_stride, gray_data, width);
gray_data += width; // <- we could use some 'data_stride' here if needed
if ((y & 1) == 0) {
memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width);
memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width);
}
}
return 1;
}
int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic) {
int x, y;
if (pic == NULL || rgb565 == NULL) return 0;
pic->colorspace = WEBP_YUV420;
pic->use_argb = 1;
if (!WebPPictureAlloc(pic)) return 0;
for (y = 0; y < pic->height; ++y) {
const int width = pic->width;
uint32_t* dst = pic->argb + y * pic->argb_stride;
for (x = 0; x < width; ++x) {
#ifdef WEBP_SWAP_16BIT_CSP
const uint32_t rg = rgb565[2 * x + 1];
const uint32_t gb = rgb565[2 * x + 0];
#else
const uint32_t rg = rgb565[2 * x + 0];
const uint32_t gb = rgb565[2 * x + 1];
#endif
uint32_t r = rg & 0xf8;
uint32_t g = ((rg << 5) | (gb >> 3)) & 0xfc;
uint32_t b = (gb << 5);
// dithering
r = r | (r >> 5);
g = g | (g >> 6);
b = b | (b >> 5);
dst[x] = (r << 16) | (g << 8) | b;
}
rgb565 += 2 * width;
}
return 1;
}
int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic) {
int x, y;
if (pic == NULL || rgb4444 == NULL) return 0;
pic->colorspace = WEBP_YUV420;
pic->use_argb = 1;
if (!WebPPictureAlloc(pic)) return 0;
for (y = 0; y < pic->height; ++y) {
const int width = pic->width;
uint32_t* dst = pic->argb + y * pic->argb_stride;
for (x = 0; x < width; ++x) {
#ifdef WEBP_SWAP_16BIT_CSP
const uint32_t rg = rgb4444[2 * x + 1];
const uint32_t ba = rgb4444[2 * x + 0];
#else
const uint32_t rg = rgb4444[2 * x + 0];
const uint32_t ba = rgb4444[2 * x + 1];
#endif
uint32_t r = rg & 0xf0;
uint32_t g = (rg << 4);
uint32_t b = (ba & 0xf0);
uint32_t a = (ba << 4);
// dithering
r = r | (r >> 4);
g = g | (g >> 4);
b = b | (b >> 4);
a = a | (a >> 4);
dst[x] = (a << 24) | (r << 16) | (g << 8) | b;
}
rgb4444 += 2 * width;
}
return 1;
}
int WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
const uint32_t palette[], int palette_size,
WebPPicture* pic) {
int x, y;
uint32_t* dst;
// 256 as the input buffer is uint8_t.
assert(MAX_PALETTE_SIZE <= 256);
if (pic == NULL || indexed == NULL || indexed_stride < pic->width ||
palette == NULL || palette_size > MAX_PALETTE_SIZE || palette_size <= 0) {
return 0;
}
pic->use_argb = 1;
if (!WebPPictureAlloc(pic)) return 0;
dst = pic->argb;
for (y = 0; y < pic->height; ++y) {
for (x = 0; x < pic->width; ++x) {
// Make sure we are within the palette.
if (indexed[x] >= palette_size) {
WebPPictureFree(pic);
return 0;
}
dst[x] = palette[indexed[x]];
}
indexed += indexed_stride;
dst += pic->argb_stride;
}
return 1;
}
//------------------------------------------------------------------------------

View File

@ -1,70 +0,0 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
#ifndef WEBP_EXTRAS_EXTRAS_H_
#define WEBP_EXTRAS_EXTRAS_H_
#include "webp/types.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "webp/encode.h"
#define WEBP_EXTRAS_ABI_VERSION 0x0001 // MAJOR(8b) + MINOR(8b)
//------------------------------------------------------------------------------
// Returns the version number of the extras library, packed in hexadecimal using
// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
WEBP_EXTERN(int) WebPGetExtrasVersion(void);
//------------------------------------------------------------------------------
// Ad-hoc colorspace importers.
// Import luma sample (gray scale image) into 'picture'. The 'picture'
// width and height must be set prior to calling this function.
WEBP_EXTERN(int) WebPImportGray(const uint8_t* gray, WebPPicture* picture);
// Import rgb sample in RGB565 packed format into 'picture'. The 'picture'
// width and height must be set prior to calling this function.
WEBP_EXTERN(int) WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic);
// Import rgb sample in RGB4444 packed format into 'picture'. The 'picture'
// width and height must be set prior to calling this function.
WEBP_EXTERN(int) WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic);
// Import a color mapped image. The number of colors is less or equal to
// MAX_PALETTE_SIZE. 'pic' must have been initialized. Its content, if any,
// will be discarded. Returns 'false' in case of error, or if indexed[] contains
// invalid indices.
WEBP_EXTERN(int)
WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
const uint32_t palette[], int palette_size,
WebPPicture* pic);
//------------------------------------------------------------------------------
// Parse a bitstream, search for VP8 (lossy) header and report a
// rough estimation of the quality factor used for compressing the bitstream.
// If the bitstream is in lossless format, the special value '101' is returned.
// Otherwise (lossy bitstream), the returned value is in the range [0..100].
// Any error (invalid bitstream, animated WebP, incomplete header, etc.)
// will return a value of -1.
WEBP_EXTERN(int) VP8EstimateQuality(const uint8_t* const data, size_t size);
//------------------------------------------------------------------------------
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* WEBP_EXTRAS_EXTRAS_H_ */

View File

@ -1,343 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Simple tool to load two webp/png/jpg/tiff files and compute PSNR/SSIM.
// This is mostly a wrapper around WebPPictureDistortion().
//
/*
gcc -o get_disto get_disto.c -O3 -I../ -L../examples -L../imageio \
-lexample_util -limageio_util -limagedec -lwebp -L/opt/local/lib \
-lpng -lz -ljpeg -ltiff -lm -lpthread
*/
//
// Author: Skal (pascal.massimino@gmail.com)
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "webp/encode.h"
#include "../imageio/image_dec.h"
#include "../imageio/imageio_util.h"
static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
int keep_alpha) {
const uint8_t* data = NULL;
size_t data_size = 0;
WebPImageReader reader = NULL;
int ok = ImgIoUtilReadFile(filename, &data, &data_size);
if (!ok) goto End;
pic->use_argb = 1; // force ARGB
#ifdef HAVE_WINCODEC_H
// Try to decode the file using WIC falling back to the other readers for
// e.g., WebP.
ok = ReadPictureWithWIC(filename, pic, keep_alpha, NULL);
if (ok) goto End;
#endif
reader = WebPGuessImageReader(data, data_size);
ok = reader(data, data_size, pic, keep_alpha, NULL);
End:
if (!ok) {
fprintf(stderr, "Error! Could not process file %s\n", filename);
}
free((void*)data);
return ok ? data_size : 0;
}
static void RescalePlane(uint8_t* plane, int width, int height,
int x_stride, int y_stride, int max) {
const uint32_t factor = (max > 0) ? (255u << 16) / max : 0;
int x, y;
for (y = 0; y < height; ++y) {
uint8_t* const ptr = plane + y * y_stride;
for (x = 0; x < width * x_stride; x += x_stride) {
const uint32_t diff = (ptr[x] * factor + (1 << 15)) >> 16;
ptr[x] = diff;
}
}
}
// Return the max absolute difference.
static int DiffScaleChannel(uint8_t* src1, int stride1,
const uint8_t* src2, int stride2,
int x_stride, int w, int h, int do_scaling) {
int x, y;
int max = 0;
for (y = 0; y < h; ++y) {
uint8_t* const ptr1 = src1 + y * stride1;
const uint8_t* const ptr2 = src2 + y * stride2;
for (x = 0; x < w * x_stride; x += x_stride) {
const int diff = abs(ptr1[x] - ptr2[x]);
if (diff > max) max = diff;
ptr1[x] = diff;
}
}
if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max);
return max;
}
//------------------------------------------------------------------------------
// SSIM calculation. We re-implement these functions here, out of dsp/, to avoid
// breaking the library's hidden visibility. This code duplication avoids the
// bigger annoyance of having to open up internal details of libdsp...
#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1
// struct for accumulating statistical moments
typedef struct {
uint32_t w; // sum(w_i) : sum of weights
uint32_t xm, ym; // sum(w_i * x_i), sum(w_i * y_i)
uint32_t xxm, xym, yym; // sum(w_i * x_i * x_i), etc.
} DistoStats;
// hat-shaped filter. Sum of coefficients is equal to 16.
static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = { 1, 2, 3, 4, 3, 2, 1 };
static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) {
const uint32_t N = stats->w;
const uint32_t w2 = N * N;
const uint32_t C1 = 20 * w2;
const uint32_t C2 = 60 * w2;
const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
const uint64_t xmxm = (uint64_t)stats->xm * stats->xm;
const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
if (xmxm + ymym >= C3) {
const int64_t xmym = (int64_t)stats->xm * stats->ym;
const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
const uint64_t syy = (uint64_t)stats->yym * N - ymym;
// we descale by 8 to prevent overflow during the fnum/fden multiply.
const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8;
const uint64_t den_S = (sxx + syy + C2) >> 8;
const uint64_t fnum = (2 * xmym + C1) * num_S;
const uint64_t fden = (xmxm + ymym + C1) * den_S;
const double r = (double)fnum / fden;
assert(r >= 0. && r <= 1.0);
return r;
}
return 1.; // area is too dark to contribute meaningfully
}
static double SSIMGetClipped(const uint8_t* src1, int stride1,
const uint8_t* src2, int stride2,
int xo, int yo, int W, int H) {
DistoStats stats = { 0, 0, 0, 0, 0, 0 };
const int ymin = (yo - SSIM_KERNEL < 0) ? 0 : yo - SSIM_KERNEL;
const int ymax = (yo + SSIM_KERNEL > H - 1) ? H - 1 : yo + SSIM_KERNEL;
const int xmin = (xo - SSIM_KERNEL < 0) ? 0 : xo - SSIM_KERNEL;
const int xmax = (xo + SSIM_KERNEL > W - 1) ? W - 1 : xo + SSIM_KERNEL;
int x, y;
src1 += ymin * stride1;
src2 += ymin * stride2;
for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
for (x = xmin; x <= xmax; ++x) {
const uint32_t w = kWeight[SSIM_KERNEL + x - xo]
* kWeight[SSIM_KERNEL + y - yo];
const uint32_t s1 = src1[x];
const uint32_t s2 = src2[x];
stats.w += w;
stats.xm += w * s1;
stats.ym += w * s2;
stats.xxm += w * s1 * s1;
stats.xym += w * s1 * s2;
stats.yym += w * s2 * s2;
}
}
return SSIMCalculation(&stats);
}
// Compute SSIM-score map. Return -1 in case of error, max diff otherwise.
static int SSIMScaleChannel(uint8_t* src1, int stride1,
const uint8_t* src2, int stride2,
int x_stride, int w, int h, int do_scaling) {
int x, y;
int max = 0;
uint8_t* const plane1 = (uint8_t*)malloc(2 * w * h * sizeof(*plane1));
uint8_t* const plane2 = plane1 + w * h;
if (plane1 == NULL) return -1;
// extract plane
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
plane1[x + y * w] = src1[x * x_stride + y * stride1];
plane2[x + y * w] = src2[x * x_stride + y * stride2];
}
}
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
const double ssim = SSIMGetClipped(plane1, w, plane2, w, x, y, w, h);
int diff = (int)(255 * (1. - ssim));
if (diff < 0) {
diff = 0;
} else if (diff > max) {
max = diff;
}
src1[x * x_stride + y * stride1] = (diff > 255) ? 255u : (uint8_t)diff;
}
}
free(plane1);
if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max);
return max;
}
// Convert an argb picture to luminance.
static void ConvertToGray(WebPPicture* const pic) {
int x, y;
assert(pic != NULL);
assert(pic->use_argb);
for (y = 0; y < pic->height; ++y) {
uint32_t* const row = &pic->argb[y * pic->argb_stride];
for (x = 0; x < pic->width; ++x) {
const uint32_t argb = row[x];
const uint32_t r = (argb >> 16) & 0xff;
const uint32_t g = (argb >> 8) & 0xff;
const uint32_t b = (argb >> 0) & 0xff;
// We use BT.709 for converting to luminance.
const uint32_t Y = (uint32_t)(0.2126 * r + 0.7152 * g + 0.0722 * b + .5);
row[x] = (argb & 0xff000000u) | (Y * 0x010101u);
}
}
}
static void Help(void) {
fprintf(stderr,
"Usage: get_disto [-ssim][-psnr][-alpha] compressed.webp orig.webp\n"
" -ssim ..... print SSIM distortion\n"
" -psnr ..... print PSNR distortion (default)\n"
" -alpha .... preserve alpha plane\n"
" -h ........ this message\n"
" -o <file> . save the diff map as a WebP lossless file\n"
" -scale .... scale the difference map to fit [0..255] range\n"
" -gray ..... use grayscale for difference map (-scale)\n"
" Also handles PNG, JPG and TIFF files, in addition to WebP.\n");
}
int main(int argc, const char *argv[]) {
WebPPicture pic1, pic2;
size_t size1 = 0, size2 = 0;
int ret = 1;
float disto[5];
int type = 0;
int c;
int help = 0;
int keep_alpha = 0;
int scale = 0;
int use_gray = 0;
const char* name1 = NULL;
const char* name2 = NULL;
const char* output = NULL;
if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) {
fprintf(stderr, "Can't init pictures\n");
return 1;
}
for (c = 1; c < argc; ++c) {
if (!strcmp(argv[c], "-ssim")) {
type = 1;
} else if (!strcmp(argv[c], "-psnr")) {
type = 0;
} else if (!strcmp(argv[c], "-alpha")) {
keep_alpha = 1;
} else if (!strcmp(argv[c], "-scale")) {
scale = 1;
} else if (!strcmp(argv[c], "-gray")) {
use_gray = 1;
} else if (!strcmp(argv[c], "-h")) {
help = 1;
ret = 0;
} else if (!strcmp(argv[c], "-o")) {
if (++c == argc) {
fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]);
goto End;
}
output = argv[c];
} else if (name1 == NULL) {
name1 = argv[c];
} else {
name2 = argv[c];
}
}
if (help || name1 == NULL || name2 == NULL) {
if (!help) {
fprintf(stderr, "Error: missing arguments.\n");
}
Help();
goto End;
}
size1 = ReadPicture(name1, &pic1, 1);
size2 = ReadPicture(name1, &pic2, 1);
if (size1 == 0 || size2 == 0) goto End;
if (!keep_alpha) {
WebPBlendAlpha(&pic1, 0x00000000);
WebPBlendAlpha(&pic2, 0x00000000);
}
if (!WebPPictureDistortion(&pic1, &pic2, type, disto)) {
fprintf(stderr, "Error while computing the distortion.\n");
goto End;
}
printf("%u %.2f %.2f %.2f %.2f %.2f\n",
(unsigned int)size1,
disto[4], disto[0], disto[1], disto[2], disto[3]);
if (output != NULL) {
uint8_t* data = NULL;
size_t data_size = 0;
if (pic1.use_argb != pic2.use_argb) {
fprintf(stderr, "Pictures are not in the same argb format. "
"Can't save the difference map.\n");
goto End;
}
if (pic1.use_argb) {
int n;
fprintf(stderr, "max differences per channel: ");
for (n = 0; n < 3; ++n) { // skip the alpha channel
const int range = (type == 1) ?
SSIMScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
4, pic1.width, pic1.height, scale) :
DiffScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
4, pic1.width, pic1.height, scale);
if (range < 0) fprintf(stderr, "\nError computing diff map\n");
fprintf(stderr, "[%d]", range);
}
fprintf(stderr, "\n");
if (use_gray) ConvertToGray(&pic1);
} else {
fprintf(stderr, "Can only compute the difference map in ARGB format.\n");
goto End;
}
data_size = WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb,
pic1.width, pic1.height,
pic1.argb_stride * 4,
&data);
if (data_size == 0) {
fprintf(stderr, "Error during lossless encoding.\n");
goto End;
}
ret = ImgIoUtilWriteFile(output, data, data_size) ? 0 : 1;
WebPFree(data);
if (ret) goto End;
}
ret = 0;
End:
WebPPictureFree(&pic1);
WebPPictureFree(&pic2);
return ret;
}

View File

@ -1,129 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// VP8EstimateQuality(): rough encoding quality estimate
//
// Author: Skal (pascal.massimino@gmail.com)
#include "./extras.h"
#include "webp/decode.h"
#include <math.h>
//------------------------------------------------------------------------------
#define INVALID_BIT_POS (1ull << 63)
// In most cases, we don't need to use a full arithmetic decoder, since
// all the header's bits are written using a uniform probability of 128.
// We can just parse the header as if it was bits (works in 99.999% cases).
static WEBP_INLINE uint32_t GetBit(const uint8_t* const data, size_t nb,
uint64_t max_size, uint64_t* const bit_pos) {
uint32_t val = 0;
if (*bit_pos + nb <= 8 * max_size) {
while (nb-- > 0) {
const uint64_t p = (*bit_pos)++;
const int bit = !!(data[p >> 3] & (128 >> ((p & 7))));
val = (val << 1) | bit;
}
} else {
*bit_pos = INVALID_BIT_POS;
}
return val;
}
#define GET_BIT(n) GetBit(data, (n), size, &bit_pos)
#define CONDITIONAL_SKIP(n) (GET_BIT(1) ? GET_BIT((n)) : 0)
int VP8EstimateQuality(const uint8_t* const data, size_t size) {
size_t pos = 0;
uint64_t bit_pos;
uint64_t sig = 0x00;
int ok = 0;
int Q = -1;
WebPBitstreamFeatures features;
if (data == NULL) return -1;
if (WebPGetFeatures(data, size, &features) != VP8_STATUS_OK) {
return -1; // invalid file
}
if (features.format == 2) return 101; // lossless
if (features.format == 0 || features.has_animation) return -1; // mixed
while (pos < size) {
sig = (sig >> 8) | ((uint64_t)data[pos++] << 40);
if ((sig >> 24) == 0x2a019dull) {
ok = 1;
break;
}
}
if (!ok) return -1;
if (pos + 4 > size) return -1;
// Skip main Header
// width = (data[pos + 0] | (data[pos + 1] << 8)) & 0x3fff;
// height = (data[pos + 2] | (data[pos + 3] << 8)) & 0x3fff;
pos += 4;
bit_pos = pos * 8;
GET_BIT(2); // color_space + clamp type
// Segment header
if (GET_BIT(1)) { // use_segment_
int s;
const int update_map = GET_BIT(1);
if (GET_BIT(1)) { // update data
const int absolute_delta = GET_BIT(1);
int q[4] = { 0, 0, 0, 0 };
for (s = 0; s < 4; ++s) {
if (GET_BIT(1)) {
q[s] = GET_BIT(7);
if (GET_BIT(1)) q[s] = -q[s]; // sign
}
}
if (absolute_delta) Q = q[0]; // just use the first segment's quantizer
for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength
}
if (update_map) {
for (s = 0; s < 3; ++s) CONDITIONAL_SKIP(8);
}
}
// Filter header
GET_BIT(1 + 6 + 3); // simple + level + sharpness
if (GET_BIT(1)) { // use_lf_delta
if (GET_BIT(1)) { // update lf_delta?
int n;
for (n = 0; n < 4 + 4; ++n) CONDITIONAL_SKIP(6);
}
}
// num partitions
GET_BIT(2);
// ParseQuant
{
const int base_q = GET_BIT(7);
/* dqy1_dc = */ CONDITIONAL_SKIP(5);
/* dqy2_dc = */ CONDITIONAL_SKIP(5);
/* dqy2_ac = */ CONDITIONAL_SKIP(5);
/* dquv_dc = */ CONDITIONAL_SKIP(5);
/* dquv_ac = */ CONDITIONAL_SKIP(5);
if (Q < 0) Q = base_q;
}
if (bit_pos == INVALID_BIT_POS) return -1;
// base mapping
Q = (127 - Q) * 100 / 127;
// correction for power-law behavior in low range
if (Q < 80) {
Q = (int)(pow(Q / 80., 1. / 0.38) * 80);
}
return Q;
}

View File

@ -1,50 +0,0 @@
// Simple tool to roughly evaluate the quality encoding of a webp bitstream
//
// Result is a *rough* estimation of the quality. You should just consider
// the bucket it's in (q > 80? > 50? > 20?) and not take it for face value.
/*
gcc -o webp_quality webp_quality.c -O3 -I../ -L. -L../imageio \
-limageio_util -lwebpextras -lwebp -lm -lpthread
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./extras.h"
#include "../imageio/imageio_util.h"
int main(int argc, const char *argv[]) {
int c;
int quiet = 0;
int ok = 1;
for (c = 1; ok && c < argc; ++c) {
if (!strcmp(argv[c], "-quiet")) {
quiet = 1;
} else if (!strcmp(argv[c], "-help") || !strcmp(argv[c], "-h")) {
printf("webp_quality [-h][-quiet] webp_files...\n");
return 0;
} else {
const char* const filename = argv[c];
const uint8_t* data = NULL;
size_t data_size = 0;
int q;
ok = ImgIoUtilReadFile(filename, &data, &data_size);
if (!ok) break;
q = VP8EstimateQuality(data, data_size);
if (!quiet) printf("[%s] ", filename);
if (q < 0) {
fprintf(stderr, "Not a WebP file, or not a lossy WebP file.\n");
ok = 0;
} else {
if (!quiet) {
printf("Estimated quality factor: %d\n", q);
} else {
printf("%d\n", q); // just print the number
}
}
free((void*)data);
}
}
return ok ? 0 : 1;
}

View File

@ -1,14 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Versions for gradle
BUILD_TOOLS_VERSION=23.0.3
COMPILE_SDK_VERSION=23
ANDROID_GRADLE_PLUGIN_VERSION=1.5.0
GRADLE_DOWNLOAD_TASK_VERSION=2.1.0

Binary file not shown.

View File

@ -1,6 +0,0 @@
#Thu May 12 17:06:25 CEST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip

164
gradlew vendored
View File

@ -1,164 +0,0 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
gradlew.bat vendored
View File

@ -1,90 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -1,53 +0,0 @@
LOCAL_PATH := $(call my-dir)
################################################################################
# libimageio_util
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
imageio_util.c \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
LOCAL_MODULE := imageio_util
include $(BUILD_STATIC_LIBRARY)
################################################################################
# libimagedec
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
image_dec.c \
jpegdec.c \
metadata.c \
pngdec.c \
tiffdec.c \
webpdec.c \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
LOCAL_STATIC_LIBRARIES := imageio_util
LOCAL_MODULE := imagedec
include $(BUILD_STATIC_LIBRARY)
################################################################################
# libimageenc
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
image_enc.c \
LOCAL_CFLAGS := $(WEBP_CFLAGS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
LOCAL_STATIC_LIBRARIES := imageio_util
LOCAL_MODULE := imageenc
include $(BUILD_STATIC_LIBRARY)

View File

@ -1,22 +0,0 @@
AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
noinst_LTLIBRARIES = libimageio_util.la libimagedec.la libimageenc.la
noinst_HEADERS =
noinst_HEADERS += ../src/webp/decode.h
noinst_HEADERS += ../src/webp/types.h
libimageio_util_la_SOURCES = imageio_util.c imageio_util.h
libimagedec_la_SOURCES = image_dec.c image_dec.h
libimagedec_la_SOURCES += jpegdec.c jpegdec.h
libimagedec_la_SOURCES += metadata.c metadata.h
libimagedec_la_SOURCES += pngdec.c pngdec.h
libimagedec_la_SOURCES += tiffdec.c tiffdec.h
libimagedec_la_SOURCES += webpdec.c webpdec.h
libimagedec_la_SOURCES += wicdec.c wicdec.h
libimagedec_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
libimagedec_la_CPPFLAGS += $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
libimageenc_la_SOURCES = image_enc.c image_enc.h
libimageenc_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
libimageenc_la_CPPFLAGS += $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)

View File

@ -1,61 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Generic image-type guessing.
#include "./image_dec.h"
static WEBP_INLINE uint32_t GetBE32(const uint8_t buf[]) {
return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
size_t data_size) {
WebPInputFileFormat format = WEBP_UNSUPPORTED_FORMAT;
if (data != NULL && data_size >= 12) {
const uint32_t magic1 = GetBE32(data + 0);
const uint32_t magic2 = GetBE32(data + 8);
if (magic1 == 0x89504E47U) {
format = WEBP_PNG_FORMAT;
} else if (magic1 >= 0xFFD8FF00U && magic1 <= 0xFFD8FFFFU) {
format = WEBP_JPEG_FORMAT;
} else if (magic1 == 0x49492A00 || magic1 == 0x4D4D002A) {
format = WEBP_TIFF_FORMAT;
} else if (magic1 == 0x52494646 && magic2 == 0x57454250) {
format = WEBP_WEBP_FORMAT;
}
}
return format;
}
static int FailReader(const uint8_t* const data, size_t data_size,
struct WebPPicture* const pic,
int keep_alpha, struct Metadata* const metadata) {
(void)data;
(void)data_size;
(void)pic;
(void)keep_alpha;
(void)metadata;
return 0;
}
WebPImageReader WebPGetImageReader(WebPInputFileFormat format) {
switch (format) {
case WEBP_PNG_FORMAT: return ReadPNG;
case WEBP_JPEG_FORMAT: return ReadJPEG;
case WEBP_TIFF_FORMAT: return ReadTIFF;
case WEBP_WEBP_FORMAT: return ReadWebP;
default: return FailReader;
}
}
WebPImageReader WebPGuessImageReader(const uint8_t* const data,
size_t data_size) {
return WebPGetImageReader(WebPGuessImageType(data, data_size));
}

View File

@ -1,65 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// All-in-one library to decode PNG/JPEG/WebP/TIFF/WIC input images.
//
// Author: Skal (pascal.massimino@gmail.com)
#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_
#define WEBP_IMAGEIO_IMAGE_DEC_H_
#include "webp/types.h"
#ifdef HAVE_CONFIG_H
#include "webp/config.h"
#endif
#include "./metadata.h"
#include "./jpegdec.h"
#include "./pngdec.h"
#include "./tiffdec.h"
#include "./webpdec.h"
#include "./wicdec.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
WEBP_PNG_FORMAT = 0,
WEBP_JPEG_FORMAT,
WEBP_TIFF_FORMAT,
WEBP_WEBP_FORMAT,
WEBP_UNSUPPORTED_FORMAT
} WebPInputFileFormat;
// Try to infer the image format. 'data_size' should be larger than 12.
// Returns WEBP_UNSUPPORTED_FORMAT if format can't be guess safely.
WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
size_t data_size);
// Signature for common image-reading functions (ReadPNG, ReadJPEG, ...)
typedef int (*WebPImageReader)(const uint8_t* const data, size_t data_size,
struct WebPPicture* const pic,
int keep_alpha, struct Metadata* const metadata);
// Return the reader associated to a given file format.
WebPImageReader WebPGetImageReader(WebPInputFileFormat format);
// This function is similar to WebPGuessImageType(), but returns a
// suitable reader function. The returned reader is never NULL, but
// unknown formats will return an always-failing valid reader.
WebPImageReader WebPGuessImageReader(const uint8_t* const data,
size_t data_size);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WEBP_IMAGEIO_IMAGE_DEC_H_

View File

@ -1,591 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Save image
#include "./image_enc.h"
#include <assert.h>
#include <string.h>
#ifdef WEBP_HAVE_PNG
#include <png.h>
#include <setjmp.h> // note: this must be included *after* png.h
#endif
#ifdef HAVE_WINCODEC_H
#ifdef __MINGW32__
#define INITGUID // Without this GUIDs are declared extern and fail to link
#endif
#define CINTERFACE
#define COBJMACROS
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
// code with COBJMACROS.
#include <ole2.h> // CreateStreamOnHGlobal()
#include <shlwapi.h>
#include <windows.h>
#include <wincodec.h>
#endif
#include "./imageio_util.h"
//------------------------------------------------------------------------------
// PNG
#ifdef HAVE_WINCODEC_H
#define IFS(fn) \
do { \
if (SUCCEEDED(hr)) { \
hr = (fn); \
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
} \
} while (0)
#ifdef __cplusplus
#define MAKE_REFGUID(x) (x)
#else
#define MAKE_REFGUID(x) &(x)
#endif
static HRESULT CreateOutputStream(const char* out_file_name,
int write_to_mem, IStream** stream) {
HRESULT hr = S_OK;
if (write_to_mem) {
// Output to a memory buffer. This is freed when 'stream' is released.
IFS(CreateStreamOnHGlobal(NULL, TRUE, stream));
} else {
IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream));
}
if (FAILED(hr)) {
fprintf(stderr, "Error opening output file %s (%08lx)\n",
out_file_name, hr);
}
return hr;
}
static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
REFGUID container_guid,
uint8_t* rgb, int stride,
uint32_t width, uint32_t height, int has_alpha) {
HRESULT hr = S_OK;
IWICImagingFactory* factory = NULL;
IWICBitmapFrameEncode* frame = NULL;
IWICBitmapEncoder* encoder = NULL;
IStream* stream = NULL;
WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
: GUID_WICPixelFormat24bppBGR;
if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG;
IFS(CoInitialize(NULL));
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
CLSCTX_INPROC_SERVER,
MAKE_REFGUID(IID_IWICImagingFactory),
(LPVOID*)&factory));
if (hr == REGDB_E_CLASSNOTREG) {
fprintf(stderr,
"Couldn't access Windows Imaging Component (are you running "
"Windows XP SP3 or newer?). PNG support not available. "
"Use -ppm or -pgm for available PPM and PGM formats.\n");
}
IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
&encoder));
IFS(IWICBitmapEncoder_Initialize(encoder, stream,
WICBitmapEncoderNoCache));
IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
height * stride, rgb));
IFS(IWICBitmapFrameEncode_Commit(frame));
IFS(IWICBitmapEncoder_Commit(encoder));
if (SUCCEEDED(hr) && use_stdout) {
HGLOBAL image;
IFS(GetHGlobalFromStream(stream, &image));
if (SUCCEEDED(hr)) {
HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD mode;
const BOOL update_mode = GetConsoleMode(std_output, &mode);
const void* const image_mem = GlobalLock(image);
DWORD bytes_written = 0;
// Clear output processing if necessary, then output the image.
if (update_mode) SetConsoleMode(std_output, 0);
if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image),
&bytes_written, NULL) ||
bytes_written != GlobalSize(image)) {
hr = E_FAIL;
}
if (update_mode) SetConsoleMode(std_output, mode);
GlobalUnlock(image);
}
}
if (frame != NULL) IUnknown_Release(frame);
if (encoder != NULL) IUnknown_Release(encoder);
if (factory != NULL) IUnknown_Release(factory);
if (stream != NULL) IUnknown_Release(stream);
return hr;
}
int WebPWritePNG(const char* out_file_name, int use_stdout,
const WebPDecBuffer* const buffer) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
uint8_t* const rgb = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
MAKE_REFGUID(GUID_ContainerFormatPng),
rgb, stride, width, height, has_alpha));
}
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp dummy) {
(void)dummy; // remove variable-unused warning
longjmp(png_jmpbuf(png), 1);
}
int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
png_bytep row = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
volatile png_structp png;
volatile png_infop info;
png_uint_32 y;
if (out_file == NULL || buffer == NULL) return 0;
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, PNGErrorFunction, NULL);
if (png == NULL) {
return 0;
}
info = png_create_info_struct(png);
if (info == NULL) {
png_destroy_write_struct((png_structpp)&png, NULL);
return 0;
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
return 0;
}
png_init_io(png, out_file);
png_set_IHDR(png, info, width, height, 8,
has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(png, info);
for (y = 0; y < height; ++y) {
png_write_rows(png, &row, 1);
row += stride;
}
png_write_end(png, info);
png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
return 1;
}
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) {
if (fout == NULL || buffer == NULL) return 0;
fprintf(stderr, "PNG support not compiled. Please install the libpng "
"development package before building.\n");
fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
return 0;
}
#endif
//------------------------------------------------------------------------------
// PPM / PAM
static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer,
int alpha) {
if (fout == NULL || buffer == NULL) {
return 0;
} else {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const uint8_t* row = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const size_t bytes_per_px = alpha ? 4 : 3;
uint32_t y;
if (row == NULL) return 0;
if (alpha) {
fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
} else {
fprintf(fout, "P6\n%u %u\n255\n", width, height);
}
for (y = 0; y < height; ++y) {
if (fwrite(row, width, bytes_per_px, fout) != bytes_per_px) {
return 0;
}
row += stride;
}
}
return 1;
}
int WebPWritePPM(FILE* fout, const WebPDecBuffer* const buffer) {
return WritePPMPAM(fout, buffer, 0);
}
int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) {
return WritePPMPAM(fout, buffer, 1);
}
//------------------------------------------------------------------------------
// Raw PGM
// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose.
int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const uint8_t* rgba = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const uint32_t bytes_per_px = 2;
uint32_t y;
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height);
for (y = 0; y < height; ++y) {
if (fwrite(rgba, width, bytes_per_px, fout) != bytes_per_px) {
return 0;
}
rgba += stride;
}
return 1;
}
//------------------------------------------------------------------------------
// BMP
static void PutLE16(uint8_t* const dst, uint32_t value) {
dst[0] = (value >> 0) & 0xff;
dst[1] = (value >> 8) & 0xff;
}
static void PutLE32(uint8_t* const dst, uint32_t value) {
PutLE16(dst + 0, (value >> 0) & 0xffff);
PutLE16(dst + 2, (value >> 16) & 0xffff);
}
#define BMP_HEADER_SIZE 54
int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const uint8_t* rgba = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const uint32_t bytes_per_px = has_alpha ? 4 : 3;
uint32_t y;
const uint32_t line_size = bytes_per_px * width;
const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4
const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE;
uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 };
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
// bitmap file header
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
PutLE32(bmp_header + 2, total_size); // size including header
PutLE32(bmp_header + 6, 0); // reserved
PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array
// bitmap info header
PutLE32(bmp_header + 14, 40); // DIB header size
PutLE32(bmp_header + 18, width); // dimensions
PutLE32(bmp_header + 22, -(int)height); // vertical flip!
PutLE16(bmp_header + 26, 1); // number of planes
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
PutLE32(bmp_header + 30, 0); // no compression (BI_RGB)
PutLE32(bmp_header + 34, 0); // image size (dummy)
PutLE32(bmp_header + 38, 2400); // x pixels/meter
PutLE32(bmp_header + 42, 2400); // y pixels/meter
PutLE32(bmp_header + 46, 0); // number of palette colors
PutLE32(bmp_header + 50, 0); // important color count
// TODO(skal): color profile
// write header
if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) {
return 0;
}
// write pixel array
for (y = 0; y < height; ++y) {
if (fwrite(rgba, line_size, 1, fout) != 1) {
return 0;
}
// write padding zeroes
if (bmp_stride != line_size) {
const uint8_t zeroes[3] = { 0 };
if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
return 0;
}
}
rgba += stride;
}
return 1;
}
#undef BMP_HEADER_SIZE
//------------------------------------------------------------------------------
// TIFF
#define NUM_IFD_ENTRIES 15
#define EXTRA_DATA_SIZE 16
// 10b for signature/header + n * 12b entries + 4b for IFD terminator:
#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const uint8_t* rgba = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
: NUM_IFD_ENTRIES - 1;
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
0x49, 0x49, 0x2a, 0x00, // little endian signature
8, 0, 0, 0, // offset to the unique IFD that follows
// IFD (offset = 8). Entries must be written in increasing tag order.
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
EXTRA_DATA_OFFSET + 0, 0, 0, 0,
0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
bytes_per_px, 0, 0, 0,
0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA
0, 0, 0, 0, // 190: IFD terminator
// EXTRA_DATA_OFFSET:
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
};
uint32_t y;
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
// Fill placeholders in IFD:
PutLE32(tiff_header + 10 + 8, width);
PutLE32(tiff_header + 22 + 8, height);
PutLE32(tiff_header + 106 + 8, height);
PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator
// write header
if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
return 0;
}
// write pixel values
for (y = 0; y < height; ++y) {
if (fwrite(rgba, bytes_per_px, width, fout) != width) {
return 0;
}
rgba += stride;
}
return 1;
}
#undef TIFF_HEADER_SIZE
#undef EXTRA_DATA_OFFSET
#undef EXTRA_DATA_SIZE
#undef NUM_IFD_ENTRIES
//------------------------------------------------------------------------------
// Raw Alpha
int WebPWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
if (fout == NULL || buffer == NULL) {
return 0;
} else {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const uint8_t* a = buffer->u.YUVA.a;
const int a_stride = buffer->u.YUVA.a_stride;
uint32_t y;
if (a == NULL) return 0;
fprintf(fout, "P5\n%u %u\n255\n", width, height);
for (y = 0; y < height; ++y) {
if (fwrite(a, width, 1, fout) != 1) return 0;
a += a_stride;
}
return 1;
}
}
//------------------------------------------------------------------------------
// PGM with IMC4 layout
int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
if (fout == NULL || buffer == NULL) {
return 0;
} else {
const int width = buffer->width;
const int height = buffer->height;
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
const uint8_t* src_y = yuv->y;
const uint8_t* src_u = yuv->u;
const uint8_t* src_v = yuv->v;
const uint8_t* src_a = yuv->a;
const int uv_width = (width + 1) / 2;
const int uv_height = (height + 1) / 2;
const int a_height = (src_a != NULL) ? height : 0;
int ok = 1;
int y;
if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
fprintf(fout, "P5\n%d %d\n255\n",
(width + 1) & ~1, height + uv_height + a_height);
for (y = 0; ok && y < height; ++y) {
ok &= (fwrite(src_y, width, 1, fout) == 1);
if (width & 1) fputc(0, fout); // padding byte
src_y += yuv->y_stride;
}
for (y = 0; ok && y < uv_height; ++y) {
ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
src_u += yuv->u_stride;
src_v += yuv->v_stride;
}
for (y = 0; ok && y < a_height; ++y) {
ok &= (fwrite(src_a, width, 1, fout) == 1);
if (width & 1) fputc(0, fout); // padding byte
src_a += yuv->a_stride;
}
return ok;
}
}
//------------------------------------------------------------------------------
// Raw YUV(A) planes
int WebPWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) {
if (fout == NULL || buffer == NULL) {
return 0;
} else {
const int width = buffer->width;
const int height = buffer->height;
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
const uint8_t* src_y = yuv->y;
const uint8_t* src_u = yuv->u;
const uint8_t* src_v = yuv->v;
const uint8_t* src_a = yuv->a;
const int uv_width = (width + 1) / 2;
const int uv_height = (height + 1) / 2;
const int a_height = (src_a != NULL) ? height : 0;
int ok = 1;
int y;
if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
for (y = 0; ok && y < height; ++y) {
ok &= (fwrite(src_y, width, 1, fout) == 1);
src_y += yuv->y_stride;
}
for (y = 0; ok && y < uv_height; ++y) {
ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
src_u += yuv->u_stride;
}
for (y = 0; ok && y < uv_height; ++y) {
ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
src_v += yuv->v_stride;
}
for (y = 0; ok && y < a_height; ++y) {
ok &= (fwrite(src_a, width, 1, fout) == 1);
src_a += yuv->a_stride;
}
return ok;
}
}
//------------------------------------------------------------------------------
// Generic top-level call
int WebPSaveImage(const WebPDecBuffer* const buffer,
WebPOutputFileFormat format, const char* const out_file) {
FILE* fout = NULL;
int needs_open_file = 1;
const int use_stdout = (out_file != NULL) && !strcmp(out_file, "-");
int ok = 1;
if (buffer == NULL || out_file == NULL) return 0;
#ifdef HAVE_WINCODEC_H
needs_open_file = (format != PNG);
#endif
if (needs_open_file) {
fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(out_file, "wb");
if (fout == NULL) {
fprintf(stderr, "Error opening output file %s\n", out_file);
return 0;
}
}
if (format == PNG ||
format == RGBA || format == BGRA || format == ARGB ||
format == rgbA || format == bgrA || format == Argb) {
#ifdef HAVE_WINCODEC_H
ok &= WebPWritePNG(out_file, use_stdout, buffer);
#else
ok &= WebPWritePNG(fout, buffer);
#endif
} else if (format == PAM) {
ok &= WebPWritePAM(fout, buffer);
} else if (format == PPM || format == RGB || format == BGR) {
ok &= WebPWritePPM(fout, buffer);
} else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) {
ok &= WebPWrite16bAsPGM(fout, buffer);
} else if (format == BMP) {
ok &= WebPWriteBMP(fout, buffer);
} else if (format == TIFF) {
ok &= WebPWriteTIFF(fout, buffer);
} else if (format == RAW_YUV) {
ok &= WebPWriteYUV(fout, buffer);
} else if (format == PGM || format == YUV || format == YUVA) {
ok &= WebPWritePGM(fout, buffer);
} else if (format == ALPHA_PLANE_ONLY) {
ok &= WebPWriteAlphaPlane(fout, buffer);
}
if (fout != NULL && fout != stdout) {
fclose(fout);
}
return ok;
}

View File

@ -1,96 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// All-in-one library to save PNG/JPEG/WebP/TIFF/WIC images.
//
// Author: Skal (pascal.massimino@gmail.com)
#ifndef WEBP_IMAGEIO_IMAGE_ENC_H_
#define WEBP_IMAGEIO_IMAGE_ENC_H_
#include <stdio.h>
#ifdef HAVE_CONFIG_H
#include "webp/config.h"
#endif
#include "webp/types.h"
#include "webp/decode.h"
#ifdef __cplusplus
extern "C" {
#endif
// Output types
typedef enum {
PNG = 0,
PAM,
PPM,
PGM,
BMP,
TIFF,
RAW_YUV,
ALPHA_PLANE_ONLY, // this is for experimenting only
// forced colorspace output (for testing, mostly)
RGB, RGBA, BGR, BGRA, ARGB,
RGBA_4444, RGB_565,
rgbA, bgrA, Argb, rgbA_4444,
YUV, YUVA
} WebPOutputFileFormat;
// General all-purpose call.
// Most formats expect a 'buffer' containing RGBA-like samples, except
// RAW_YUV, YUV and YUVA formats.
// If 'out_file_name' is "-", data is saved to stdout.
// Returns false if an error occurred, true otherwise.
int WebPSaveImage(const WebPDecBuffer* const buffer,
WebPOutputFileFormat format, const char* const out_file_name);
// Save to PNG.
#ifdef HAVE_WINCODEC_H
int WebPWritePNG(const char* out_file_name, int use_stdout,
const struct WebPDecBuffer* const buffer);
#else
int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer);
#endif
// Save to PPM format (RGB, no alpha)
int WebPWritePPM(FILE* fout, const struct WebPDecBuffer* const buffer);
// Save to PAM format (= PPM + alpha)
int WebPWritePAM(FILE* fout, const struct WebPDecBuffer* const buffer);
// Save 16b mode (RGBA4444, RGB565, ...) for debugging purposes.
int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
// Save as BMP
int WebPWriteBMP(FILE* fout, const struct WebPDecBuffer* const buffer);
// Save as TIFF
int WebPWriteTIFF(FILE* fout, const struct WebPDecBuffer* const buffer);
// Save the ALPHA plane (only) as a PGM
int WebPWriteAlphaPlane(FILE* fout, const struct WebPDecBuffer* const buffer);
// Save as YUV samples as PGM format (using IMC4 layout).
// See: http://www.fourcc.org/yuv.php#IMC4.
// (very convenient format for viewing the samples, esp. for odd dimensions).
int WebPWritePGM(FILE* fout, const struct WebPDecBuffer* const buffer);
// Save YUV(A) planes sequentially (raw dump)
int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer);
// Save 16b mode (RGBA4444, RGB565, ...) as PGM format, for debugging purposes.
int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WEBP_IMAGEIO_IMAGE_ENC_H_

View File

@ -1,143 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Utility functions used by the image decoders.
//
#include "./imageio_util.h"
#if defined(_WIN32)
#include <fcntl.h> // for _O_BINARY
#include <io.h> // for _setmode()
#endif
#include <stdlib.h>
#include <string.h>
// -----------------------------------------------------------------------------
// File I/O
FILE* ImgIoUtilSetBinaryMode(FILE* file) {
#if defined(_WIN32)
if (_setmode(_fileno(file), _O_BINARY) == -1) {
fprintf(stderr, "Failed to reopen file in O_BINARY mode.\n");
return NULL;
}
#endif
return file;
}
int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
static const size_t kBlockSize = 16384; // default initial size
size_t max_size = 0;
size_t size = 0;
uint8_t* input = NULL;
if (data == NULL || data_size == NULL) return 0;
*data = NULL;
*data_size = 0;
if (!ImgIoUtilSetBinaryMode(stdin)) return 0;
while (!feof(stdin)) {
// We double the buffer size each time and read as much as possible.
const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
void* const new_data = realloc(input, max_size + extra_size);
if (new_data == NULL) goto Error;
input = (uint8_t*)new_data;
max_size += extra_size;
size += fread(input + size, 1, extra_size, stdin);
if (size < max_size) break;
}
if (ferror(stdin)) goto Error;
*data = input;
*data_size = size;
return 1;
Error:
free(input);
fprintf(stderr, "Could not read from stdin\n");
return 0;
}
int ImgIoUtilReadFile(const char* const file_name,
const uint8_t** data, size_t* data_size) {
int ok;
void* file_data;
size_t file_size;
FILE* in;
const int from_stdin = (file_name == NULL) || !strcmp(file_name, "-");
if (from_stdin) return ImgIoUtilReadFromStdin(data, data_size);
if (data == NULL || data_size == NULL) return 0;
*data = NULL;
*data_size = 0;
in = fopen(file_name, "rb");
if (in == NULL) {
fprintf(stderr, "cannot open input file '%s'\n", file_name);
return 0;
}
fseek(in, 0, SEEK_END);
file_size = ftell(in);
fseek(in, 0, SEEK_SET);
file_data = malloc(file_size);
if (file_data == NULL) return 0;
ok = (fread(file_data, file_size, 1, in) == 1);
fclose(in);
if (!ok) {
fprintf(stderr, "Could not read %d bytes of data from file %s\n",
(int)file_size, file_name);
free(file_data);
return 0;
}
*data = (uint8_t*)file_data;
*data_size = file_size;
return 1;
}
int ImgIoUtilWriteFile(const char* const file_name,
const uint8_t* data, size_t data_size) {
int ok;
FILE* out;
const int to_stdout = (file_name == NULL) || !strcmp(file_name, "-");
if (data == NULL) {
return 0;
}
out = to_stdout ? stdout : fopen(file_name, "wb");
if (out == NULL) {
fprintf(stderr, "Error! Cannot open output file '%s'\n", file_name);
return 0;
}
ok = (fwrite(data, data_size, 1, out) == 1);
if (out != stdout) fclose(out);
return ok;
}
// -----------------------------------------------------------------------------
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
uint8_t* dst, int dst_stride, int width, int height) {
while (height-- > 0) {
memcpy(dst, src, width * sizeof(*dst));
src += src_stride;
dst += dst_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

@ -1,61 +0,0 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Utility functions used by the image decoders.
//
#ifndef WEBP_IMAGEIO_IMAGEIO_UTIL_H_
#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_
#include <stdio.h>
#include "webp/types.h"
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------------------------------------------------------
// File I/O
// Reopen file in binary (O_BINARY) mode.
// Returns 'file' on success, NULL otherwise.
FILE* ImgIoUtilSetBinaryMode(FILE* file);
// Allocates storage for entire file 'file_name' and returns contents and size
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
// be deleted using free().
// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
// the function ImgIoUtilReadFromStdin().
int ImgIoUtilReadFile(const char* const file_name,
const uint8_t** data, size_t* data_size);
// Same as ImgIoUtilReadFile(), but reads until EOF from stdin instead.
int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size);
// Write a data segment into a file named 'file_name'. Returns true if ok.
// If 'file_name' is NULL or equal to "-", output is written to stdout.
int ImgIoUtilWriteFile(const char* const file_name,
const uint8_t* data, size_t data_size);
//------------------------------------------------------------------------------
// Copy width x height pixels from 'src' to 'dst' honoring the strides.
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
uint8_t* dst, int dst_stride, int width, int height);
//------------------------------------------------------------------------------
// Returns 0 in case of overflow of nmemb * size.
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t nmemb, size_t size);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WEBP_IMAGEIO_IMAGEIO_UTIL_H_

View File

@ -1,190 +0,0 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// WebP decode.
#include "./webpdec.h"
#include <stdio.h>
#include <stdlib.h>
#include "webp/decode.h"
#include "webp/encode.h"
#include "./imageio_util.h"
#include "./metadata.h"
//------------------------------------------------------------------------------
// WebP decoding
static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
"OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
"UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
};
static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
if (config->input.has_animation) {
fprintf(stderr,
"Error! Decoding of an animated WebP file is not supported.\n"
" Use webpmux to extract the individual frames or\n"
" vwebp to view this image.\n");
}
}
void PrintWebPError(const char* const in_file, int status) {
fprintf(stderr, "Decoding of %s failed.\n", in_file);
fprintf(stderr, "Status: %d", status);
if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
fprintf(stderr, "(%s)", kStatusMessages[status]);
}
fprintf(stderr, "\n");
}
int LoadWebP(const char* const in_file,
const uint8_t** data, size_t* data_size,
WebPBitstreamFeatures* bitstream) {
VP8StatusCode status;
WebPBitstreamFeatures local_features;
if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0;
if (bitstream == NULL) {
bitstream = &local_features;
}
status = WebPGetFeatures(*data, *data_size, bitstream);
if (status != VP8_STATUS_OK) {
free((void*)*data);
*data = NULL;
*data_size = 0;
PrintWebPError(in_file, status);
return 0;
}
return 1;
}
//------------------------------------------------------------------------------
VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
WebPDecoderConfig* const config) {
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
PrintAnimationWarning(config);
return WebPDecode(data, data_size, config);
}
VP8StatusCode DecodeWebPIncremental(
const uint8_t* const data, size_t data_size,
WebPDecoderConfig* const config) {
VP8StatusCode status = VP8_STATUS_OK;
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
PrintAnimationWarning(config);
// Decoding call.
{
WebPIDecoder* const idec = WebPIDecode(data, data_size, config);
if (idec == NULL) {
fprintf(stderr, "Failed during WebPINewDecoder().\n");
return VP8_STATUS_OUT_OF_MEMORY;
} else {
#ifdef WEBP_EXPERIMENTAL_FEATURES
size_t size = 0;
const size_t incr = 2 + (data_size / 20);
while (size < data_size) {
size_t next_size = size + (rand() % incr);
if (next_size > data_size) next_size = data_size;
status = WebPIUpdate(idec, data, next_size);
if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) break;
size = next_size;
}
#else
status = WebPIUpdate(idec, data, data_size);
#endif
WebPIDelete(idec);
}
}
return status;
}
// -----------------------------------------------------------------------------
int ReadWebP(const uint8_t* const data, size_t data_size,
WebPPicture* const pic,
int keep_alpha, Metadata* const metadata) {
int ok = 0;
VP8StatusCode status = VP8_STATUS_OK;
WebPDecoderConfig config;
WebPDecBuffer* const output_buffer = &config.output;
WebPBitstreamFeatures* const bitstream = &config.input;
if (data == NULL || data_size == 0 || pic == NULL) return 0;
// TODO(jzern): add Exif/XMP/ICC extraction.
if (metadata != NULL) {
fprintf(stderr, "Warning: metadata extraction from WebP is unsupported.\n");
}
if (!WebPInitDecoderConfig(&config)) {
fprintf(stderr, "Library version mismatch!\n");
return 0;
}
status = WebPGetFeatures(data, data_size, bitstream);
if (status != VP8_STATUS_OK) {
PrintWebPError("input data", status);
return 0;
}
{
const int has_alpha = keep_alpha && bitstream->has_alpha;
if (pic->use_argb) {
output_buffer->colorspace = has_alpha ? MODE_RGBA : MODE_RGB;
} else {
output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
}
status = DecodeWebP(data, data_size, &config);
if (status == VP8_STATUS_OK) {
pic->width = output_buffer->width;
pic->height = output_buffer->height;
if (pic->use_argb) {
const uint8_t* const rgba = output_buffer->u.RGBA.rgba;
const int stride = output_buffer->u.RGBA.stride;
ok = has_alpha ? WebPPictureImportRGBA(pic, rgba, stride)
: WebPPictureImportRGB(pic, rgba, stride);
} else {
pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
ok = WebPPictureAlloc(pic);
if (!ok) {
status = VP8_STATUS_OUT_OF_MEMORY;
} else {
const WebPYUVABuffer* const yuva = &output_buffer->u.YUVA;
const int uv_width = (pic->width + 1) >> 1;
const int uv_height = (pic->height + 1) >> 1;
ImgIoUtilCopyPlane(yuva->y, yuva->y_stride,
pic->y, pic->y_stride, pic->width, pic->height);
ImgIoUtilCopyPlane(yuva->u, yuva->u_stride,
pic->u, pic->uv_stride, uv_width, uv_height);
ImgIoUtilCopyPlane(yuva->v, yuva->v_stride,
pic->v, pic->uv_stride, uv_width, uv_height);
if (has_alpha) {
ImgIoUtilCopyPlane(yuva->a, yuva->a_stride,
pic->a, pic->a_stride, pic->width, pic->height);
}
}
}
}
}
if (status != VP8_STATUS_OK) {
PrintWebPError("input data", status);
}
WebPFreeDecBuffer(output_buffer);
return ok;
}
// -----------------------------------------------------------------------------

View File

@ -1,67 +0,0 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// WebP decode.
#ifndef WEBP_IMAGEIO_WEBPDEC_H_
#define WEBP_IMAGEIO_WEBPDEC_H_
#include "webp/decode.h"
#ifdef __cplusplus
extern "C" {
#endif
struct Metadata;
struct WebPPicture;
//------------------------------------------------------------------------------
// WebP decoding
// Prints an informative error message regarding decode failure of 'in_file'.
// 'status' is treated as a VP8StatusCode and if valid will be printed as a
// text string.
void PrintWebPError(const char* const in_file, int status);
// Reads a WebP from 'in_file', returning the contents and size in 'data' and
// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures().
// Returns true on success.
int LoadWebP(const char* const in_file,
const uint8_t** data, size_t* data_size,
WebPBitstreamFeatures* bitstream);
// Decodes the WebP contained in 'data'.
// 'config' is a structure previously initialized by WebPInitDecoderConfig().
// 'config->output' should have the desired colorspace selected.
// Returns the decoder status. On success 'config->output' will contain the
// decoded picture.
VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
WebPDecoderConfig* const config);
// Same as DecodeWebP(), but using the incremental decoder.
VP8StatusCode DecodeWebPIncremental(
const uint8_t* const data, size_t data_size,
WebPDecoderConfig* const config);
//------------------------------------------------------------------------------
// Reads a WebP from 'in_file', returning the decoded output in 'pic'.
// Output is RGBA or YUVA, depending on pic->use_argb value.
// If 'keep_alpha' is true and the WebP has an alpha channel, the output is RGBA
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
// Returns true on success.
int ReadWebP(const uint8_t* const data, size_t data_size,
struct WebPPicture* const pic,
int keep_alpha, struct Metadata* const metadata);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WEBP_IMAGEIO_WEBPDEC_H_

View File

@ -1,11 +1,10 @@
#!/bin/bash #!/bin/bash
# #
# This script generates 'WebP.framework' and 'WebPDecoder.framework'. An iOS # This script generates 'WebP.framework'. An iOS app can decode WebP images
# app can decode WebP images by including 'WebPDecoder.framework' and both # by including 'WebP.framework'.
# encode and decode WebP images by including 'WebP.framework'.
# #
# Run ./iosbuild.sh to generate the frameworks under the current directory # Run ./iosbuild.sh to generate 'WebP.framework' under the current directory
# (the previous build will be erased if it exists). # (previous build will be erased if it exists).
# #
# This script is inspired by the build script written by Carson McDonald. # 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/). # (http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/).
@ -13,68 +12,42 @@
set -e set -e
# Extract the latest SDK version from the final field of the form: iphoneosX.Y # Extract the latest SDK version from the final field of the form: iphoneosX.Y
readonly SDK=$(xcodebuild -showsdks \ declare -r SDK=$(xcodebuild -showsdks \
| grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}' | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
) )
# Extract Xcode version. declare -r OLDPATH=${PATH}
readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
if [[ -z "${XCODE}" ]]; then
echo "Xcode not available"
exit 1
fi
readonly OLDPATH=${PATH}
# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support. # Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
# Note that iPhoneOS-V6 support is not available with the iOS6 SDK. # Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
PLATFORMS="iPhoneSimulator iPhoneSimulator64" declare -r PLATFORMS="iPhoneSimulator iPhoneOS-V7 iPhoneOS-V7s"
PLATFORMS+=" iPhoneOS-V7 iPhoneOS-V7s iPhoneOS-V7-arm64" declare -r SRCDIR=$(dirname $0)
readonly PLATFORMS declare -r TOPDIR=$(pwd)
readonly SRCDIR=$(dirname $0) declare -r BUILDDIR="${TOPDIR}/iosbuild"
readonly TOPDIR=$(pwd) declare -r TARGETDIR="${TOPDIR}/WebP.framework"
readonly BUILDDIR="${TOPDIR}/iosbuild" declare -r DEVELOPER=$(xcode-select --print-path)
readonly TARGETDIR="${TOPDIR}/WebP.framework" declare -r PLATFORMSROOT="${DEVELOPER}/Platforms"
readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.framework" declare -r LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
readonly DEVELOPER=$(xcode-select --print-path)
readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
readonly LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
LIBLIST='' LIBLIST=''
DECLIBLIST=''
if [[ -z "${SDK}" ]]; then if [[ -z "${SDK}" ]]; then
echo "iOS SDK not available" echo "iOS SDK not available"
exit 1 exit 1
elif [[ ${SDK%%.*} -gt 8 ]]; then elif [[ ${SDK} < 4.0 ]]; then
EXTRA_CFLAGS="-fembed-bitcode" echo "You need iOS SDK version 4.0 or above"
elif [[ ${SDK} < 6.0 ]]; then
echo "You need iOS SDK version 6.0 or above"
exit 1 exit 1
else else
echo "iOS SDK Version ${SDK}" echo "iOS SDK Version ${SDK}"
fi fi
rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} rm -rf ${BUILDDIR}
mkdir -p ${BUILDDIR} ${TARGETDIR}/Headers/ ${DECTARGETDIR}/Headers/ rm -rf ${TARGETDIR}
mkdir -p ${BUILDDIR}
mkdir -p ${TARGETDIR}/Headers/
if [[ ! -e ${SRCDIR}/configure ]]; then [[ -e ${SRCDIR}/configure ]] || (cd ${SRCDIR} && sh autogen.sh)
if ! (cd ${SRCDIR} && sh autogen.sh); then
cat <<EOT
Error creating configure script!
This script requires the autoconf/automake and libtool to build. MacPorts can
be used to obtain these:
http://www.macports.org/install.php
EOT
exit 1
fi
fi
for PLATFORM in ${PLATFORMS}; do for PLATFORM in ${PLATFORMS}; do
ARCH2="" if [[ "${PLATFORM}" == "iPhoneOS-V7s" ]]; then
if [[ "${PLATFORM}" == "iPhoneOS-V7-arm64" ]]; then
PLATFORM="iPhoneOS"
ARCH="aarch64"
ARCH2="arm64"
elif [[ "${PLATFORM}" == "iPhoneOS-V7s" ]]; then
PLATFORM="iPhoneOS" PLATFORM="iPhoneOS"
ARCH="armv7s" ARCH="armv7s"
elif [[ "${PLATFORM}" == "iPhoneOS-V7" ]]; then elif [[ "${PLATFORM}" == "iPhoneOS-V7" ]]; then
@ -83,9 +56,6 @@ for PLATFORM in ${PLATFORMS}; do
elif [[ "${PLATFORM}" == "iPhoneOS-V6" ]]; then elif [[ "${PLATFORM}" == "iPhoneOS-V6" ]]; then
PLATFORM="iPhoneOS" PLATFORM="iPhoneOS"
ARCH="armv6" ARCH="armv6"
elif [[ "${PLATFORM}" == "iPhoneSimulator64" ]]; then
PLATFORM="iPhoneSimulator"
ARCH="x86_64"
else else
ARCH="i386" ARCH="i386"
fi fi
@ -93,28 +63,25 @@ for PLATFORM in ${PLATFORMS}; do
ROOTDIR="${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}" ROOTDIR="${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}"
mkdir -p "${ROOTDIR}" mkdir -p "${ROOTDIR}"
DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain" export DEVROOT="${PLATFORMSROOT}/${PLATFORM}.platform/Developer"
SDKROOT="${PLATFORMSROOT}/" export SDKROOT="${DEVROOT}/SDKs/${PLATFORM}${SDK}.sdk"
SDKROOT+="${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/"
CFLAGS="-arch ${ARCH2:-${ARCH}} -pipe -isysroot ${SDKROOT} -O3 -DNDEBUG"
CFLAGS+=" -miphoneos-version-min=6.0 ${EXTRA_CFLAGS}"
set -x export CFLAGS="-arch ${ARCH} -pipe -isysroot ${SDKROOT}"
export CXXFLAGS=${CFLAGS}
export LDFLAGS="-arch ${ARCH} -pipe -isysroot ${SDKROOT}"
export PATH="${DEVROOT}/usr/bin:${OLDPATH}" export PATH="${DEVROOT}/usr/bin:${OLDPATH}"
${SRCDIR}/configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} \ ${SRCDIR}/configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} \
--build=$(${SRCDIR}/config.guess) \ --build=$(${SRCDIR}/config.guess) \
--disable-shared --enable-static \ --disable-shared --enable-static \
--enable-libwebpdecoder --enable-swap-16bit-csp \ --enable-libwebpdecoder --enable-swap-16bit-csp
CFLAGS="${CFLAGS}"
set +x
# run make only in the src/ directory to create libwebp.a/libwebpdecoder.a # run make only in the src/ directory to create libwebpdecoder.a
cd src/ cd src/
make V=0 make V=0
make install make install
LIBLIST+=" ${ROOTDIR}/lib/libwebp.a" LIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
DECLIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
make clean make clean
cd .. cd ..
@ -122,8 +89,5 @@ for PLATFORM in ${PLATFORMS}; do
export PATH=${OLDPATH} export PATH=${OLDPATH}
done done
cp -a ${SRCDIR}/src/webp/{decode,encode,types}.h ${TARGETDIR}/Headers/ cp -a ${SRCDIR}/src/webp/* ${TARGETDIR}/Headers/
${LIPO} -create ${LIBLIST} -output ${TARGETDIR}/WebP ${LIPO} -create ${LIBLIST} -output ${TARGETDIR}/WebP
cp -a ${SRCDIR}/src/webp/{decode,types}.h ${DECTARGETDIR}/Headers/
${LIPO} -create ${DECLIBLIST} -output ${DECTARGETDIR}/WebPDecoder

View File

@ -82,7 +82,7 @@
# modified version of the Autoconf Macro, you may extend this special # modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well. # exception to the GPL to apply to your modified version as well.
#serial 21 #serial 18
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [ AC_DEFUN([AX_PTHREAD], [
@ -103,8 +103,8 @@ if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_LIBS="$LIBS" save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS" LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
AC_MSG_RESULT([$ax_pthread_ok]) AC_MSG_RESULT($ax_pthread_ok)
if test x"$ax_pthread_ok" = xno; then if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS="" PTHREAD_LIBS=""
PTHREAD_CFLAGS="" PTHREAD_CFLAGS=""
@ -164,20 +164,6 @@ case ${host_os} in
;; ;;
esac esac
# Clang doesn't consider unrecognized options an error unless we specify
# -Werror. We throw in some extra Clang-specific options to ensure that
# this doesn't happen for GCC, which also accepts -Werror.
AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags])
save_CFLAGS="$CFLAGS"
ax_pthread_extra_flags="-Werror"
CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
[AC_MSG_RESULT([yes])],
[ax_pthread_extra_flags=
AC_MSG_RESULT([no])])
CFLAGS="$save_CFLAGS"
if test x"$ax_pthread_ok" = xno; then if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do for flag in $ax_pthread_flags; do
@ -192,7 +178,7 @@ for flag in $ax_pthread_flags; do
;; ;;
pthread-config) pthread-config)
AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
if test x"$ax_pthread_config" = xno; then continue; fi if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
@ -207,7 +193,7 @@ for flag in $ax_pthread_flags; do
save_LIBS="$LIBS" save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS" save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS" LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h, # Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we # since some functions may be macros. (On the Sequent, we
@ -233,7 +219,7 @@ for flag in $ax_pthread_flags; do
LIBS="$save_LIBS" LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS" CFLAGS="$save_CFLAGS"
AC_MSG_RESULT([$ax_pthread_ok]) AC_MSG_RESULT($ax_pthread_ok)
if test "x$ax_pthread_ok" = xyes; then if test "x$ax_pthread_ok" = xyes; then
break; break;
fi fi
@ -259,9 +245,9 @@ if test "x$ax_pthread_ok" = xyes; then
[attr_name=$attr; break], [attr_name=$attr; break],
[]) [])
done done
AC_MSG_RESULT([$attr_name]) AC_MSG_RESULT($attr_name)
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
[Define to necessary symbol if this constant [Define to necessary symbol if this constant
uses a non-standard name on your system.]) uses a non-standard name on your system.])
fi fi
@ -275,54 +261,45 @@ if test "x$ax_pthread_ok" = xyes; then
if test "$GCC" = "yes"; then if test "$GCC" = "yes"; then
flag="-D_REENTRANT" flag="-D_REENTRANT"
else else
# TODO: What about Clang on Solaris?
flag="-mt -D_REENTRANT" flag="-mt -D_REENTRANT"
fi fi
;; ;;
esac esac
AC_MSG_RESULT([$flag]) AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi fi
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
[ax_cv_PTHREAD_PRIO_INHERIT], [ ax_cv_PTHREAD_PRIO_INHERIT, [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], AC_LINK_IFELSE([
[[int i = PTHREAD_PRIO_INHERIT;]])], AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
[ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=yes],
[ax_cv_PTHREAD_PRIO_INHERIT=no]) [ax_cv_PTHREAD_PRIO_INHERIT=no])
]) ])
AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
[AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
LIBS="$save_LIBS" LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS" CFLAGS="$save_CFLAGS"
# More AIX lossage: compile with *_r variant # More AIX lossage: must compile with xlc_r or cc_r
if test "x$GCC" != xyes; then if test x"$GCC" != xyes; then
case $host_os in AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
aix*) else
AS_CASE(["x/$CC"], PTHREAD_CC=$CC
[x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
[#handle absolute path differently from PATH based program lookup
AS_CASE(["x$CC"],
[x/*],
[AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
[AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
;;
esac
fi fi
else
PTHREAD_CC="$CC"
fi fi
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST([PTHREAD_LIBS]) AC_SUBST(PTHREAD_CC)
AC_SUBST([PTHREAD_CFLAGS])
AC_SUBST([PTHREAD_CC])
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then if test x"$ax_pthread_ok" = xyes; then
ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
: :
else else
ax_pthread_ok=no ax_pthread_ok=no

View File

@ -2,8 +2,8 @@
# system, for simple local building of the libraries and tools. # system, for simple local building of the libraries and tools.
# It will not install the libraries system-wide, but just create the 'cwebp' # It will not install the libraries system-wide, but just create the 'cwebp'
# and 'dwebp' tools in the examples/ directory, along with the static # and 'dwebp' tools in the examples/ directory, along with the static
# libraries 'src/libwebp.a', 'src/libwebpdecoder.a', 'src/mux/libwebpmux.a', # libraries 'src/libwebp.a', 'src/libwebpdecoder.a', 'src/mux/libwebpmux.a' and
# 'src/demux/libwebpdemux.a' and 'extras/libwebpextras.a'. # 'src/demux/libwebpdemux.a'.
# #
# To build the library and examples, use: # To build the library and examples, use:
# make -f makefile.unix # make -f makefile.unix
@ -61,215 +61,111 @@ endif
EXTRA_FLAGS += -DWEBP_USE_THREAD EXTRA_FLAGS += -DWEBP_USE_THREAD
EXTRA_LIBS += -lpthread EXTRA_LIBS += -lpthread
# Control symbol visibility. Comment out if your compiler doesn't support it.
EXTRA_FLAGS += -fvisibility=hidden
# Extra flags to emulate C89 strictness with the full ANSI # Extra flags to emulate C89 strictness with the full ANSI
EXTRA_FLAGS += -Wextra -Wold-style-definition EXTRA_FLAGS += -Wextra -Wold-style-definition
EXTRA_FLAGS += -Wmissing-prototypes EXTRA_FLAGS += -Wmissing-prototypes
EXTRA_FLAGS += -Wmissing-declarations EXTRA_FLAGS += -Wmissing-declarations
EXTRA_FLAGS += -Wdeclaration-after-statement EXTRA_FLAGS += -Wdeclaration-after-statement
EXTRA_FLAGS += -Wshadow EXTRA_FLAGS += -Wshadow
EXTRA_FLAGS += -Wformat-security -Wformat-nonliteral
# EXTRA_FLAGS += -Wvla # EXTRA_FLAGS += -Wvla
# SSE4.1-specific flags:
ifeq ($(HAVE_SSE41), 1)
EXTRA_FLAGS += -DWEBP_HAVE_SSE41
src/dsp/%_sse41.o: EXTRA_FLAGS += -msse4.1
endif
# AVX2-specific flags:
ifeq ($(HAVE_AVX2), 1)
EXTRA_FLAGS += -DWEBP_HAVE_AVX2
src/dsp/%_avx2.o: EXTRA_FLAGS += -mavx2
endif
# NEON-specific flags:
# EXTRA_FLAGS += -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8
# -> seems to make the overall lib slower: -fno-split-wide-types
# MIPS (MSA) 32-bit build specific flags for mips32r5 (p5600):
# EXTRA_FLAGS += -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64
# EXTRA_FLAGS += -msched-weight -mload-store-pairs
# MIPS (MSA) 64-bit build specific flags for mips64r6 (i6400):
# EXTRA_FLAGS += -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64
# EXTRA_FLAGS += -msched-weight -mload-store-pairs
#### Nothing should normally be changed below this line #### #### Nothing should normally be changed below this line ####
AR = ar AR = ar
ARFLAGS = r ARFLAGS = r
CC = gcc
CPPFLAGS = -Isrc/ -Wall CPPFLAGS = -Isrc/ -Wall
CFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS) CFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS)
CC = gcc
INSTALL = install INSTALL = install
GROFF = /usr/bin/groff GROFF = /usr/bin/groff
COL = /usr/bin/col COL = /usr/bin/col
LDFLAGS = $(EXTRA_LIBS) $(EXTRA_FLAGS) -lm LDFLAGS = $(EXTRA_LIBS) $(EXTRA_FLAGS) -lm
ANIM_UTIL_OBJS = \
examples/anim_util.o \
DEC_OBJS = \ DEC_OBJS = \
src/dec/alpha_dec.o \ src/dec/alpha.o \
src/dec/buffer_dec.o \ src/dec/buffer.o \
src/dec/frame_dec.o \ src/dec/frame.o \
src/dec/idec_dec.o \ src/dec/idec.o \
src/dec/io_dec.o \ src/dec/io.o \
src/dec/quant_dec.o \ src/dec/layer.o \
src/dec/tree_dec.o \ src/dec/quant.o \
src/dec/vp8_dec.o \ src/dec/tree.o \
src/dec/vp8l_dec.o \ src/dec/vp8.o \
src/dec/webp_dec.o \ src/dec/vp8l.o \
src/dec/webp.o \
DEMUX_OBJS = \ DEMUX_OBJS = \
src/demux/anim_decode.o \
src/demux/demux.o \ src/demux/demux.o \
DSP_DEC_OBJS = \ DSP_DEC_OBJS = \
src/dsp/alpha_processing.o \
src/dsp/alpha_processing_mips_dsp_r2.o \
src/dsp/alpha_processing_neon.o \
src/dsp/alpha_processing_sse2.o \
src/dsp/alpha_processing_sse41.o \
src/dsp/cpu.o \ src/dsp/cpu.o \
src/dsp/dec.o \ src/dsp/dec.o \
src/dsp/dec_clip_tables.o \
src/dsp/dec_mips32.o \
src/dsp/dec_mips_dsp_r2.o \
src/dsp/dec_msa.o \
src/dsp/dec_neon.o \ src/dsp/dec_neon.o \
src/dsp/dec_sse2.o \ src/dsp/dec_sse2.o \
src/dsp/dec_sse41.o \
src/dsp/filters.o \
src/dsp/filters_mips_dsp_r2.o \
src/dsp/filters_msa.o \
src/dsp/filters_neon.o \
src/dsp/filters_sse2.o \
src/dsp/lossless.o \ src/dsp/lossless.o \
src/dsp/lossless_mips_dsp_r2.o \
src/dsp/lossless_msa.o \
src/dsp/lossless_neon.o \
src/dsp/lossless_sse2.o \
src/dsp/rescaler.o \
src/dsp/rescaler_mips32.o \
src/dsp/rescaler_mips_dsp_r2.o \
src/dsp/rescaler_msa.o \
src/dsp/rescaler_neon.o \
src/dsp/rescaler_sse2.o \
src/dsp/upsampling.o \ src/dsp/upsampling.o \
src/dsp/upsampling_mips_dsp_r2.o \
src/dsp/upsampling_msa.o \
src/dsp/upsampling_neon.o \ src/dsp/upsampling_neon.o \
src/dsp/upsampling_sse2.o \ src/dsp/upsampling_sse2.o \
src/dsp/yuv.o \ src/dsp/yuv.o \
src/dsp/yuv_mips32.o \
src/dsp/yuv_mips_dsp_r2.o \
src/dsp/yuv_sse2.o \
DSP_ENC_OBJS = \ DSP_ENC_OBJS = \
src/dsp/argb.o \
src/dsp/argb_mips_dsp_r2.o \
src/dsp/argb_sse2.o \
src/dsp/cost.o \
src/dsp/cost_mips32.o \
src/dsp/cost_mips_dsp_r2.o \
src/dsp/cost_sse2.o \
src/dsp/enc.o \ src/dsp/enc.o \
src/dsp/enc_avx2.o \
src/dsp/enc_mips32.o \
src/dsp/enc_mips_dsp_r2.o \
src/dsp/enc_msa.o \
src/dsp/enc_neon.o \ src/dsp/enc_neon.o \
src/dsp/enc_sse2.o \ src/dsp/enc_sse2.o \
src/dsp/enc_sse41.o \
src/dsp/lossless_enc.o \
src/dsp/lossless_enc_mips32.o \
src/dsp/lossless_enc_mips_dsp_r2.o \
src/dsp/lossless_enc_msa.o \
src/dsp/lossless_enc_neon.o \
src/dsp/lossless_enc_sse2.o \
src/dsp/lossless_enc_sse41.o \
ENC_OBJS = \ ENC_OBJS = \
src/enc/alpha_enc.o \ src/enc/alpha.o \
src/enc/analysis_enc.o \ src/enc/analysis.o \
src/enc/backward_references_enc.o \ src/enc/backward_references.o \
src/enc/config_enc.o \ src/enc/config.o \
src/enc/cost_enc.o \ src/enc/cost.o \
src/enc/delta_palettization_enc.o \ src/enc/filter.o \
src/enc/filter_enc.o \ src/enc/frame.o \
src/enc/frame_enc.o \ src/enc/histogram.o \
src/enc/histogram_enc.o \ src/enc/iterator.o \
src/enc/iterator_enc.o \ src/enc/layer.o \
src/enc/near_lossless_enc.o \ src/enc/picture.o \
src/enc/picture_enc.o \ src/enc/quant.o \
src/enc/picture_csp_enc.o \ src/enc/syntax.o \
src/enc/picture_psnr_enc.o \ src/enc/token.o \
src/enc/picture_rescale_enc.o \ src/enc/tree.o \
src/enc/picture_tools_enc.o \ src/enc/vp8l.o \
src/enc/predictor_enc.o \ src/enc/webpenc.o \
src/enc/quant_enc.o \
src/enc/syntax_enc.o \
src/enc/token_enc.o \
src/enc/tree_enc.o \
src/enc/vp8l_enc.o \
src/enc/webp_enc.o \
EX_FORMAT_DEC_OBJS = \ EX_FORMAT_DEC_OBJS = \
imageio/image_dec.o \ examples/jpegdec.o \
imageio/jpegdec.o \ examples/metadata.o \
imageio/metadata.o \ examples/pngdec.o \
imageio/pngdec.o \ examples/tiffdec.o \
imageio/tiffdec.o \
imageio/webpdec.o \
EX_FORMAT_ENC_OBJS = \
imageio/image_enc.o \
EX_UTIL_OBJS = \ EX_UTIL_OBJS = \
examples/example_util.o \ examples/example_util.o \
GIFDEC_OBJS = \
examples/gifdec.o \
IMAGE_UTIL_OBJS = \
imageio/imageio_util.o \
MUX_OBJS = \ MUX_OBJS = \
src/mux/anim_encode.o \
src/mux/muxedit.o \ src/mux/muxedit.o \
src/mux/muxinternal.o \ src/mux/muxinternal.o \
src/mux/muxread.o \ src/mux/muxread.o \
UTILS_DEC_OBJS = \ UTILS_DEC_OBJS = \
src/utils/bit_reader_utils.o \ src/utils/bit_reader.o \
src/utils/color_cache_utils.o \ src/utils/color_cache.o \
src/utils/filters_utils.o \ src/utils/filters.o \
src/utils/huffman_utils.o \ src/utils/huffman.o \
src/utils/quant_levels_dec_utils.o \ src/utils/quant_levels_dec.o \
src/utils/random_utils.o \ src/utils/rescaler.o \
src/utils/rescaler_utils.o \ src/utils/thread.o \
src/utils/thread_utils.o \
src/utils/utils.o \ src/utils/utils.o \
UTILS_ENC_OBJS = \ UTILS_ENC_OBJS = \
src/utils/bit_writer_utils.o \ src/utils/bit_writer.o \
src/utils/huffman_encode_utils.o \ src/utils/huffman_encode.o \
src/utils/quant_levels_utils.o \ src/utils/quant_levels.o \
EXTRA_OBJS = \
extras/extras.o \
extras/quality_estimate.o \
LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS) LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \ LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
$(UTILS_ENC_OBJS) $(UTILS_ENC_OBJS)
LIBWEBPMUX_OBJS = $(MUX_OBJS) LIBWEBPMUX_OBJS = $(MUX_OBJS)
LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS)
LIBWEBPEXTRA_OBJS = $(EXTRA_OBJS)
HDRS_INSTALLED = \ HDRS_INSTALLED = \
src/webp/decode.h \ src/webp/decode.h \
@ -280,91 +176,47 @@ HDRS_INSTALLED = \
src/webp/types.h \ src/webp/types.h \
HDRS = \ HDRS = \
src/dec/alphai_dec.h \ src/dec/decode_vp8.h \
src/dec/common_dec.h \ src/dec/vp8i.h \
src/dec/vp8_dec.h \ src/dec/vp8li.h \
src/dec/vp8i_dec.h \ src/dec/webpi.h \
src/dec/vp8li_dec.h \
src/dec/webpi_dec.h \
src/dsp/common_sse2.h \
src/dsp/dsp.h \ src/dsp/dsp.h \
src/dsp/lossless.h \ src/dsp/lossless.h \
src/dsp/lossless_common.h \
src/dsp/mips_macro.h \
src/dsp/msa_macro.h \
src/dsp/neon.h \
src/dsp/yuv.h \ src/dsp/yuv.h \
src/enc/backward_references_enc.h \ src/enc/cost.h \
src/enc/cost_enc.h \ src/enc/vp8enci.h \
src/enc/delta_palettization_enc.h \ src/utils/bit_reader.h \
src/enc/histogram_enc.h \ src/utils/bit_writer.h \
src/enc/vp8i_enc.h \ src/utils/color_cache.h \
src/enc/vp8li_enc.h \ src/utils/filters.h \
src/mux/animi.h \ src/utils/huffman.h \
src/mux/muxi.h \ src/utils/huffman_encode.h \
src/utils/bit_reader_utils.h \ src/utils/quant_levels.h \
src/utils/bit_reader_inl_utils.h \ src/utils/quant_levels_dec.h \
src/utils/bit_writer_utils.h \ src/utils/rescaler.h \
src/utils/color_cache_utils.h \ src/utils/thread.h \
src/utils/endian_inl_utils.h \
src/utils/filters_utils.h \
src/utils/huffman_utils.h \
src/utils/huffman_encode_utils.h \
src/utils/quant_levels_utils.h \
src/utils/quant_levels_dec_utils.h \
src/utils/random_utils.h \
src/utils/rescaler_utils.h \
src/utils/thread_utils.h \
src/utils/utils.h \
src/webp/format_constants.h \ src/webp/format_constants.h \
$(HDRS_INSTALLED) \ $(HDRS_INSTALLED) \
OUT_LIBS = examples/libexample_util.a OUT_LIBS = examples/libexample_util.a src/libwebpdecoder.a src/libwebp.a
OUT_LIBS += imageio/libimageio_util.a
OUT_LIBS += imageio/libimagedec.a
OUT_LIBS += imageio/libimageenc.a
OUT_LIBS += src/libwebpdecoder.a
OUT_LIBS += src/libwebp.a
EXTRA_LIB = extras/libwebpextras.a
OUT_EXAMPLES = examples/cwebp examples/dwebp OUT_EXAMPLES = examples/cwebp examples/dwebp
EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux \ EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux
examples/anim_diff examples/img2webp
OTHER_EXAMPLES = extras/get_disto extras/webp_quality
OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES) OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES)
ifeq ($(MAKECMDGOALS),clean) ifeq ($(MAKECMDGOALS),clean)
OUTPUT += $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES) OUTPUT += $(EXTRA_EXAMPLES)
OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a $(EXTRA_LIB) OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a
OUTPUT += examples/libgifdec.a examples/libanim_util.a
endif endif
ex: $(OUT_EXAMPLES) ex: $(OUT_EXAMPLES)
all: ex $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES) all: ex $(EXTRA_EXAMPLES)
extras: $(EXTRA_LIB)
$(EX_FORMAT_DEC_OBJS): %.o: %.h $(EX_FORMAT_DEC_OBJS): %.o: %.h
# special dependencies:
# tree_dec.c/vp8_dec.c/bit_reader_utils.c <->
# bit_reader_inl_utils.h, endian_inl_utils.h
# bit_writer_utils.c <-> endian_inl_utils.h
src/dec/tree_dec.o: src/utils/bit_reader_inl_utils.h
src/dec/tree_dec.o: src/utils/endian_inl_utils.h
src/dec/vp8_dec.o: src/utils/bit_reader_inl_utils.h src/utils/endian_inl_utils.h
src/utils/bit_reader_utils.o: src/utils/bit_reader_inl_utils.h
src/utils/bit_reader_utils.o: src/utils/endian_inl_utils.h
src/utils/bit_writer_utils.o: src/utils/endian_inl_utils.h
%.o: %.c $(HDRS) %.o: %.c $(HDRS)
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
examples/libanim_util.a: $(ANIM_UTIL_OBJS)
examples/libexample_util.a: $(EX_UTIL_OBJS) examples/libexample_util.a: $(EX_UTIL_OBJS)
examples/libgifdec.a: $(GIFDEC_OBJS)
extras/libwebpextras.a: $(LIBWEBPEXTRA_OBJS)
imageio/libimagedec.a: $(EX_FORMAT_DEC_OBJS)
imageio/libimageenc.a: $(EX_FORMAT_ENC_OBJS)
imageio/libimageio_util.a: $(IMAGE_UTIL_OBJS)
src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS) src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS)
src/libwebp.a: $(LIBWEBP_OBJS) src/libwebp.a: $(LIBWEBP_OBJS)
src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS) src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS)
@ -373,69 +225,39 @@ src/demux/libwebpdemux.a: $(LIBWEBPDEMUX_OBJS)
%.a: %.a:
$(AR) $(ARFLAGS) $@ $^ $(AR) $(ARFLAGS) $@ $^
examples/anim_diff: examples/anim_diff.o $(ANIM_UTIL_OBJS) $(GIFDEC_OBJS) examples/cwebp: examples/cwebp.o $(EX_FORMAT_DEC_OBJS)
examples/cwebp: examples/cwebp.o
examples/dwebp: examples/dwebp.o examples/dwebp: examples/dwebp.o
examples/gif2webp: examples/gif2webp.o $(GIFDEC_OBJS) examples/gif2webp: examples/gif2webp.o
examples/vwebp: examples/vwebp.o examples/vwebp: examples/vwebp.o
examples/webpmux: examples/webpmux.o examples/webpmux: examples/webpmux.o
examples/img2webp: examples/img2webp.o
examples/anim_diff: examples/libanim_util.a examples/libgifdec.a
examples/anim_diff: src/demux/libwebpdemux.a examples/libexample_util.a
examples/anim_diff: imageio/libimageio_util.a src/libwebp.a
examples/anim_diff: EXTRA_LIBS += $(GIF_LIBS)
examples/anim_diff: EXTRA_FLAGS += -DWEBP_HAVE_GIF
examples/cwebp: examples/libexample_util.a
examples/cwebp: imageio/libimagedec.a
examples/cwebp: imageio/libimageio_util.a
examples/cwebp: src/libwebp.a examples/cwebp: src/libwebp.a
examples/cwebp: EXTRA_LIBS += $(CWEBP_LIBS) examples/cwebp: EXTRA_LIBS += $(CWEBP_LIBS)
examples/dwebp: examples/libexample_util.a examples/dwebp: examples/libexample_util.a src/libwebpdecoder.a
examples/dwebp: imageio/libimagedec.a
examples/dwebp: imageio/libimageenc.a
examples/dwebp: imageio/libimageio_util.a
examples/dwebp: src/libwebp.a
examples/dwebp: EXTRA_LIBS += $(DWEBP_LIBS) examples/dwebp: EXTRA_LIBS += $(DWEBP_LIBS)
examples/gif2webp: examples/libexample_util.a imageio/libimageio_util.a examples/gif2webp: examples/libexample_util.a src/mux/libwebpmux.a src/libwebp.a
examples/gif2webp: examples/libgifdec.a src/mux/libwebpmux.a src/libwebp.a
examples/gif2webp: EXTRA_LIBS += $(GIF_LIBS) examples/gif2webp: EXTRA_LIBS += $(GIF_LIBS)
examples/gif2webp: EXTRA_FLAGS += -DWEBP_HAVE_GIF
examples/vwebp: examples/libexample_util.a src/demux/libwebpdemux.a examples/vwebp: examples/libexample_util.a src/demux/libwebpdemux.a
examples/vwebp: imageio/libimageio_util.a src/libwebp.a examples/vwebp: src/libwebp.a
examples/vwebp: EXTRA_LIBS += $(GL_LIBS) examples/vwebp: EXTRA_LIBS += $(GL_LIBS)
examples/vwebp: EXTRA_FLAGS += -DWEBP_HAVE_GL examples/webpmux: examples/libexample_util.a src/mux/libwebpmux.a
examples/webpmux: examples/libexample_util.a imageio/libimageio_util.a examples/webpmux: src/libwebpdecoder.a
examples/webpmux: src/mux/libwebpmux.a src/libwebpdecoder.a
examples/img2webp: examples/libexample_util.a imageio/libimageio_util.a
examples/img2webp: imageio/libimagedec.a
examples/img2webp: src/mux/libwebpmux.a src/libwebp.a
examples/img2webp: EXTRA_LIBS += $(CWEBP_LIBS)
extras/get_disto: extras/get_disto.o $(OUT_EXAMPLES) $(EXTRA_EXAMPLES):
extras/get_disto: imageio/libimagedec.a imageio/libimageio_util.a src/libwebp.a
extras/get_disto: EXTRA_LIBS += $(CWEBP_LIBS)
extras/webp_quality: extras/webp_quality.o
extras/webp_quality: imageio/libimageio_util.a
extras/webp_quality: $(EXTRA_LIB) src/libwebp.a
$(OUT_EXAMPLES) $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES):
$(CC) -o $@ $^ $(LDFLAGS) $(CC) -o $@ $^ $(LDFLAGS)
dist: DESTDIR := dist dist: DESTDIR := dist
dist: OUT_EXAMPLES += $(EXTRA_EXAMPLES) dist: OUT_EXAMPLES += $(EXTRA_EXAMPLES)
dist: all dist: all
$(INSTALL) -m755 -d $(DESTDIR)/include/webp \ $(INSTALL) -m755 -d $(DESTDIR)/include/webp \
$(DESTDIR)/bin $(DESTDIR)/doc $(DESTDIR)/lib $(DESTDIR)/doc $(DESTDIR)/lib
$(INSTALL) -m755 -s $(OUT_EXAMPLES) $(DESTDIR)/bin $(INSTALL) -m755 -s $(OUT_EXAMPLES) $(DESTDIR)
$(INSTALL) -m644 $(HDRS_INSTALLED) $(DESTDIR)/include/webp $(INSTALL) -m644 $(HDRS_INSTALLED) $(DESTDIR)/include/webp
$(INSTALL) -m644 src/libwebp.a $(DESTDIR)/lib $(INSTALL) -m644 src/libwebp.a $(DESTDIR)/lib
$(INSTALL) -m644 src/demux/libwebpdemux.a $(DESTDIR)/lib $(INSTALL) -m644 src/demux/libwebpdemux.a $(DESTDIR)/lib
$(INSTALL) -m644 src/mux/libwebpmux.a $(DESTDIR)/lib $(INSTALL) -m644 src/mux/libwebpmux.a $(DESTDIR)/lib
umask 022; \ umask 022; \
for m in man/[cdv]webp.1 man/gif2webp.1 man/webpmux.1 \ for m in man/[cd]webp.1 man/gif2webp.1 man/webpmux.1; do \
man/img2webp.1; do \
basenam=$$(basename $$m .1); \ basenam=$$(basename $$m .1); \
$(GROFF) -t -e -man -T utf8 $$m \ $(GROFF) -t -e -man -T utf8 $$m \
| $(COL) -bx >$(DESTDIR)/doc/$${basenam}.txt; \ | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.txt; \
@ -446,8 +268,6 @@ dist: all
clean: clean:
$(RM) $(OUTPUT) *~ \ $(RM) $(OUTPUT) *~ \
examples/*.o examples/*~ \ examples/*.o examples/*~ \
extras/*.o extras/*~ \
imageio/*.o imageio/*~ \
src/dec/*.o src/dec/*~ \ src/dec/*.o src/dec/*~ \
src/demux/*.o src/demux/*~ \ src/demux/*.o src/demux/*~ \
src/dsp/*.o src/dsp/*~ \ src/dsp/*.o src/dsp/*~ \
@ -456,5 +276,19 @@ clean:
src/utils/*.o src/utils/*~ \ src/utils/*.o src/utils/*~ \
src/webp/*~ man/*~ doc/*~ swig/*~ \ src/webp/*~ man/*~ doc/*~ swig/*~ \
.PHONY: all clean dist ex superclean: clean
$(RM) -r .git *.log *.cache *~
$(RM) -r .deps */.deps */*/.deps
$(RM) -r .libs */.libs */*/.libs
$(RM) */*.lo */*/*.lo
$(RM) */*.la */*/*.la
$(RM) Makefile */Makefile */*/Makefile
$(RM) Makefile.in */Makefile.in */*/Makefile.in
$(RM) config.log autom4te.cache libtool config.h stamp-h1
$(RM) aclocal.m4 compile
$(RM) config.guess config.h.in config.sub config.status
$(RM) configure depcomp install-sh ltmain.sh missing src/libwebp.pc
$(RM) m4/*
.PHONY: all clean dist ex superclean
.SUFFIXES: .SUFFIXES:

View File

@ -5,7 +5,4 @@ endif
if BUILD_GIF2WEBP if BUILD_GIF2WEBP
man_MANS += gif2webp.1 man_MANS += gif2webp.1
endif endif
if BUILD_VWEBP
man_MANS += vwebp.1
endif
EXTRA_DIST = $(man_MANS) EXTRA_DIST = $(man_MANS)

View File

@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*- .\" Hey, EMACS: -*- nroff -*-
.TH CWEBP 1 "January 20, 2017" .TH CWEBP 1 "March 13, 2013"
.SH NAME .SH NAME
cwebp \- compress an image file to a WebP file cwebp \- compress an image file to a WebP file
.SH SYNOPSIS .SH SYNOPSIS
@ -12,19 +12,13 @@ This manual page documents the
command. command.
.PP .PP
\fBcwebp\fP compresses an image using the WebP format. \fBcwebp\fP compresses an image using the WebP format.
Input format can be either PNG, JPEG, TIFF, WebP or raw Y'CbCr samples. Input format can be either PNG, JPEG, TIFF or raw Y'CbCr samples.
.SH OPTIONS .SH OPTIONS
The basic options are: The basic options are:
.TP .TP
.BI \-o " string .BI \-o " string
Specify the name of the output WebP file. If omitted, \fBcwebp\fP will Specify the name of the output WebP file. If omitted, \fBcwebp\fP will
perform compression but only report statistics. perform compression but only report statistics.
Using "\-" as output name will direct output to 'stdout'.
.TP
.BI \-\- " string
Explicitly specify the input file. This option is useful if the input
file starts with a '\-' for instance. This option must appear \fBlast\fP.
Any other options afterward will be ignored.
.TP .TP
.B \-h, \-help .B \-h, \-help
A short usage summary. A short usage summary.
@ -35,17 +29,6 @@ A summary of all the possible options.
.B \-version .B \-version
Print the version number (as major.minor.revision) and exit. Print the version number (as major.minor.revision) and exit.
.TP .TP
.B \-lossless
Encode the image without any loss. For images with fully transparent area,
the invisible pixel values (R/G/B or Y/U/V) will be preserved only if the
\-exact option is used.
.TP
.BI \-near_lossless " int
Use near\-lossless image preprocessing. This option adjusts pixel values
to help compressibility, but has minimal impact on the visual quality.
It triggers lossless compression mode automatically.
Range is 0 (maximum preprocessing) to 100 (no preprocessing, the default).
.TP
.BI \-q " float .BI \-q " float
Specify the compression factor for RGB channels between 0 and 100. The default Specify the compression factor for RGB channels between 0 and 100. The default
is 75. is 75.
@ -53,52 +36,54 @@ is 75.
In case of lossy compression (default), a small factor produces a smaller file In case of lossy compression (default), a small factor produces a smaller file
with lower quality. Best quality is achieved by using a value of 100. with lower quality. Best quality is achieved by using a value of 100.
.br .br
In case of lossless compression (specified by the \fB\-lossless\fP option), a In case of lossless compression (specified by the \-lossless option), a small
small factor enables faster compression speed, but produces a larger file. factor enables faster compression speed, but produces a larger file. Maximum
Maximum compression is achieved by using a value of 100. compression is achieved by using a value of 100.
.TP
.BI \-z " int
Switch on \fBlossless\fP compression mode with the specified level between 0
and 9, with level 0 being the fastest, 9 being the slowest. Fast mode
produces larger file size than slower ones. A good default is \fB\-z 6\fP.
This option is actually a shortcut for some predefined settings for quality
and method. If options \fB\-q\fP or \fB\-m\fP are subsequently used, they will
invalidate the effect of this option.
.TP .TP
.BI \-alpha_q " int .BI \-alpha_q " int
Specify the compression factor for alpha compression between 0 and 100. Specify the compression factor for alpha compression between 0 and 100.
Lossless compression of alpha is achieved using a value of 100, while the lower Lossless compression of alpha is achieved using a value of 100, while the lower
values result in a lossy compression. The default is 100. values result in a lossy compression. The default is 100.
.TP .TP
.BI \-f " int
Specify the strength of the deblocking filter, between 0 (no filtering)
and 100 (maximum filtering). A value of 0 will turn off any filtering.
Higher value will increase the strength of the filtering process applied
after decoding the picture. The higher the value the smoother the picture will
appear. Typical values are usually in the range of 20 to 50.
.TP
.BI \-preset " string .BI \-preset " string
Specify a set of pre\-defined parameters to suit a particular type of Specify a set of pre-defined parameters to suit a particular type of
source material. Possible values are: \fBdefault\fP, \fBphoto\fP, source material. Possible values are: \fBdefault\fP, \fBphoto\fP,
\fBpicture\fP, \fBdrawing\fP, \fBicon\fP, \fBtext\fP. Since \fBpicture\fP, \fBdrawing\fP, \fBicon\fP, \fBtext\fP. Since
\fB\-preset\fP overwrites the other parameters' values (except the \fB\-preset\fP overwrites the other parameters' values (except the
\fB\-q\fP one), this option should preferably appear first in the \fB\-q\fP one), this option should preferably appear first in the
order of the arguments. order of the arguments.
.TP .TP
.BI \-sns " int
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.
.TP
.BI \-m " int .BI \-m " int
Specify the compression method to use. This parameter controls the Specify the compression method to use. This parameter controls the
trade off between encoding speed and the compressed file size and quality. trade off between encoding speed and the compressed file size and quality.
Possible values range from 0 to 6. Default value is 4. Possible values range from 0 to 6. Default value is 4.
When higher values are used, the encoder will spend more time inspecting When higher values are used, the encoder will spend more time inspecting
additional encoding possibilities and decide on the quality gain. additional encoding possibilities and decide on the quality gain.
Lower value can result in faster processing time at the expense of Lower value can result is faster processing time at the expense of
larger file size and lower compression quality. larger file size and lower compression quality.
.TP .TP
.BI \-resize " width height .B \-jpeg_like
Resize the source to a rectangle with size \fBwidth\fP x \fBheight\fP. Change the internal parameter mapping to better match the expected size
If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0, of JPEG compression. This flag will generally produce an output file of
the value will be calculated preserving the aspect\-ratio. similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but
.TP with less visual distortion.
.BI \-crop " x_position y_position width height
Crop the source to a rectangle with top\-left corner at coordinates
(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
This cropping area must be fully contained within the source rectangle.
.TP .TP
.B \-mt .B \-mt
Use multi\-threading for encoding, if possible. This option is only effective Use multi-threading for encoding, if possible. This option is only effective
when using lossy compression on a source with a transparency channel. when using lossy compression on a source with a transparency channel.
.TP .TP
.B \-low_memory .B \-low_memory
@ -109,50 +94,13 @@ different in size and distortion. This flag is only effective for methods
some side effects on the bitstream: it forces certain bitstream features some side effects on the bitstream: it forces certain bitstream features
like number of partitions (forced to 1). Note that a more detailed report like number of partitions (forced to 1). Note that a more detailed report
of bitstream size is printed by \fBcwebp\fP when using this option. of bitstream size is printed by \fBcwebp\fP when using this option.
.SS LOSSY OPTIONS
These options are only effective when doing lossy encoding (the default, with
or without alpha).
.TP
.BI \-size " int
Specify a target size (in bytes) to try and reach for the compressed output.
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
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.
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
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
options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10, default is 1.
If options \fB\-size\fP or \fB\-psnr\fP were used, but \fB\-pass\fP wasn't
specified, a default value of '6' passes will be used.
.TP .TP
.B \-af .B \-af
Turns auto\-filter on. This algorithm will spend additional time optimizing Turns auto-filter on. This algorithm will spend additional time optimizing
the filtering strength to reach a well\-balanced quality. the filtering strength to reach a well-balanced quality.
.TP
.B \-jpeg_like
Change the internal parameter mapping to better match the expected size
of JPEG compression. This flag will generally produce an output file of
similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but
with less visual distortion.
.TP .SH ADDITIONAL OPTIONS
Advanced options: More advanced options are:
.TP
.BI \-f " int
Specify the strength of the deblocking filter, between 0 (no filtering)
and 100 (maximum filtering). A value of 0 will turn off any filtering.
Higher value will increase the strength of the filtering process applied
after decoding the picture. The higher the value the smoother the picture will
appear. Typical values are usually in the range of 20 to 50.
.TP .TP
.BI \-sharpness " int .BI \-sharpness " int
Specify the sharpness of the filtering (if used). Specify the sharpness of the filtering (if used).
@ -166,17 +114,6 @@ Use strong filtering (if filtering is being used thanks to the
Disable strong filtering (if filtering is being used thanks to the Disable strong filtering (if filtering is being used thanks to the
\fB\-f\fP option) and use simple filtering instead. \fB\-f\fP option) and use simple filtering instead.
.TP .TP
.B \-sharp_yuv
Use more accurate and sharper RGB->YUV conversion if needed. Note that this
process is slower than the default 'fast' RGB->YUV conversion.
.TP
.BI \-sns " int
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 50.
.TP
.BI \-segments " int .BI \-segments " int
Change the number of partitions to use during the segmentation of the Change the number of partitions to use during the segmentation of the
sns algorithm. Segments should be in range 1 to 4. Default value is 4. sns algorithm. Segments should be in range 1 to 4. Default value is 4.
@ -186,30 +123,96 @@ is used.
.BI \-partition_limit " int .BI \-partition_limit " int
Degrade quality by limiting the number of bits used by some macroblocks. Degrade quality by limiting the number of bits used by some macroblocks.
Range is 0 (no degradation, the default) to 100 (full degradation). Range is 0 (no degradation, the default) to 100 (full degradation).
Useful values are usually around 30\-70 for moderately large images. Useful values are usually around 30-70 for moderately large images.
In the VP8 format, the so\-called control partition has a limit of 512k and In the VP8 format, the so-called control partition has a limit of 512k and
is used to store the following information: whether the macroblock is skipped, is used to store the following information: whether the macroblock is skipped,
which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16 which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16
mode, and finally the prediction modes to use for each of the sub\-blocks. mode, and finally the prediction modes to use for each of the sub-blocks.
For a very large image, 512k only leaves room to few bits per 16x16 macroblock. For a very large image, 512k only leaves room to few bits per 16x16 macroblock.
The absolute minimum is 4 bits per macroblock. Skip, segment, and mode The absolute minimum is 4 bits per macroblock. Skip, segment, and mode
information can use up almost all these 4 bits (although the case is unlikely), information can use up almost all these 4 bits (although the case is unlikely),
which is problematic for very large images. The partition_limit factor controls which is problematic for very large images. The partition_limit factor controls
how frequently the most bit\-costly mode (intra 4x4) will be used. This is how frequently the most bit-costly mode (intra 4x4) will be used. This is
useful in case the 512k limit is reached and the following message is displayed: useful in case the 512k limit is reached and the following message is displayed:
\fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP. \fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP.
If using \fB\-partition_limit\fP is not enough to meet the 512k constraint, one If using \fB-partition_limit\fP is not enough to meet the 512k constraint, one
should use less segments in order to save more header bits per macroblock. should use less segments in order to save more header bits per macroblock.
See the \fB\-segments\fP option. See the \fB-segments\fP option.
.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
close as possible to this target.
.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
close as possible to this target.
.TP
.BI \-pass " int
Set a maximum number of passes to use during the dichotomy used by
options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10.
.TP
.BI \-crop " x_position y_position width height
Crop the source to a rectangle with top-left corner at coordinates
(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
This cropping area must be fully contained within the source rectangle.
.TP
.BI \-s " width height
Specify that the input file actually consists of raw Y'CbCr samples following
the ITU-R BT.601 recommendation, in 4:2:0 linear format.
The luma plane has size \fBwidth\fP x \fBheight\fP.
.TP
.BI \-map " int
Output additional ASCII-map of encoding information. Possible map values
range from 1 to 6. This is only meant to help debugging.
.TP
.BI \-pre " int
Specify a pre-processing filter. This option is a placeholder
and has currently no effect.
.TP
.BI \-alpha_filter " string
Specify the predictive filtering method for the alpha plane. One of 'none',
\&'fast' or 'best', in increasing complexity and slowness order. Default is
\&'fast'. Internally, alpha filtering is performed using four possible
predictions (none, horizontal, vertical, gradient). The 'best' mode will try
each mode in turn and pick the one which gives the smaller size. The 'fast'
mode will just try to form an a-priori guess without testing all modes.
.TP
.BI \-alpha_method " int
Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes
no compression, 1 uses WebP lossless format for compression. The default is 1.
.TP
.B \-alpha_cleanup
Modify unseen RGB values under fully transparent area, to help compressibility.
The default is off.
.TP
.B \-noalpha
Using this option will discard the alpha channel.
.TP
.B \-lossless
Encode the image without any loss.
.TP
.BI \-hint " string
Specify the hint about input image type. Possible values are:
\fBphoto\fP, \fBpicture\fP or \fBgraph\fP.
.TP
.BI \-metadata " string
A comma separated list of metadata to copy from the input to the output if
present.
Valid values: \fBall\fP, \fBnone\fP, \fBexif\fP, \fBicc\fP, \fBxmp\fP.
The default is \fBnone\fP.
.SS LOGGING OPTIONS Note: each input format may not support all combinations.
These options control the level of output: .TP
.B \-noasm
Disable all assembly optimizations.
.TP .TP
.B \-v .B \-v
Print extra information (encoding time in particular). Print extra information (encoding time in particular).
.TP .TP
.B \-print_psnr .B \-print_psnr
Compute and report average PSNR (Peak\-Signal\-To\-Noise ratio). Compute and report average PSNR (Peak-Signal-To-Noise ratio).
.TP .TP
.B \-print_ssim .B \-print_ssim
Compute and report average SSIM (structural similarity Compute and report average SSIM (structural similarity
@ -226,70 +229,14 @@ Report encoding progress in percent.
Do not print anything. Do not print anything.
.TP .TP
.B \-short .B \-short
Only print brief information (output file size and PSNR) for testing purposes. Only print brief information (output file size and PSNR) for testing purpose.
.TP
.BI \-map " int
Output additional ASCII\-map of encoding information. Possible map values
range from 1 to 6. This is only meant to help debugging.
.SS ADDITIONAL OPTIONS
More advanced options are:
.TP
.BI \-s " width height
Specify that the input file actually consists of raw Y'CbCr samples following
the ITU\-R BT.601 recommendation, in 4:2:0 linear format.
The luma plane has size \fBwidth\fP x \fBheight\fP.
.TP
.BI \-pre " int
Specify some preprocessing steps. Using a value of '2' will trigger
quality\-dependent pseudo\-random dithering during RGBA\->YUVA conversion
(lossy compression only).
.TP
.BI \-alpha_filter " string
Specify the predictive filtering method for the alpha plane. One of 'none',
\&'fast' or 'best', in increasing complexity and slowness order. Default is
\&'fast'. Internally, alpha filtering is performed using four possible
predictions (none, horizontal, vertical, gradient). The 'best' mode will try
each mode in turn and pick the one which gives the smaller size. The 'fast'
mode will just try to form an a priori guess without testing all modes.
.TP
.BI \-alpha_method " int
Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes
no compression, 1 uses WebP lossless format for compression. The default is 1.
.TP
.B \-exact
Preserve RGB values in transparent area. The default is off, to help
compressibility.
.TP
.BI \-blend_alpha " int
This option blends the alpha channel (if present) with the source using the
background color specified in hexadecimal as 0xrrggbb. The alpha channel is
afterward reset to the opaque value 255.
.TP
.B \-noalpha
Using this option will discard the alpha channel.
.TP
.BI \-hint " string
Specify the hint about input image type. Possible values are:
\fBphoto\fP, \fBpicture\fP or \fBgraph\fP.
.TP
.BI \-metadata " string
A comma separated list of metadata to copy from the input to the output if
present.
Valid values: \fBall\fP, \fBnone\fP, \fBexif\fP, \fBicc\fP, \fBxmp\fP.
The default is \fBnone\fP.
Note: each input format may not support all combinations.
.TP
.B \-noasm
Disable all assembly optimizations.
.SH BUGS .SH BUGS
Please report all bugs to the issue tracker: Please report all bugs to our issue tracker:
https://bugs.chromium.org/p/webp http://code.google.com/p/webp/issues
.br .br
Patches welcome! See this page to get started: Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting\-patches/ http://www.webmproject.org/code/contribute/submitting-patches/
.SH EXAMPLES .SH EXAMPLES
cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
@ -297,21 +244,18 @@ cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
cwebp \-q 70 picture_with_alpha.png \-o picture_with_alpha.webp cwebp \-q 70 picture_with_alpha.png \-o picture_with_alpha.webp
.br .br
cwebp \-sns 70 \-f 50 \-size 60000 picture.png \-o picture.webp cwebp \-sns 70 \-f 50 \-size 60000 picture.png \-o picture.webp
.br
cwebp \-o picture.webp \-\- \-\-\-picture.png
.SH AUTHORS .SH AUTHORS
\fBcwebp\fP is a part of libwebp and was written by the WebP team. \fBcwebp\fP was written by the WebP team.
.br .br
The latest source tree is available at The latest source tree is available at http://www.webmproject.org/code
https://chromium.googlesource.com/webm/libwebp
.PP .PP
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>, This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
for the Debian project (and may be used by others). for the Debian project (and may be used by others).
.SH SEE ALSO .SH SEE ALSO
.BR dwebp (1), .BR dwebp (1),
.BR gif2webp (1) .BR gif2webp (1).
.br .br
Please refer to http://developers.google.com/speed/webp/ for additional Please refer to http://developers.google.com/speed/webp/ for additional
information. information.

View File

@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*- .\" Hey, EMACS: -*- nroff -*-
.TH DWEBP 1 "June 23, 2016" .TH DWEBP 1 "February 01, 2013"
.SH NAME .SH NAME
dwebp \- decompress a WebP file to an image file dwebp \- decompress a WebP file to an image file
.SH SYNOPSIS .SH SYNOPSIS
@ -23,19 +23,6 @@ Print the version number (as major.minor.revision) and exit.
.TP .TP
.BI \-o " string .BI \-o " string
Specify the name of the output file (as PNG format by default). Specify the name of the output file (as PNG format by default).
Using "-" as output name will direct output to 'stdout'.
.TP
.BI \-\- " string
Explicitly specify the input file. This option is useful if the input
file starts with an '\-' for instance. This option must appear \fBlast\fP.
Any other options afterward will be ignored. If the input file is "\-",
the data will be read from \fIstdin\fP instead of a file.
.TP
.B \-bmp
Change the output format to uncompressed BMP.
.TP
.B \-tiff
Change the output format to uncompressed TIFF.
.TP .TP
.B \-pam .B \-pam
Change the output format to PAM (retains alpha). Change the output format to PAM (retains alpha).
@ -45,7 +32,7 @@ Change the output format to PPM (discards alpha).
.TP .TP
.B \-pgm .B \-pgm
Change the output format to PGM. The output consists of luma/chroma Change the output format to PGM. The output consists of luma/chroma
samples instead of RGB, using the IMC4 layout. This option is mainly samples instead of RGB, using the ICM4 layout. This option is mainly
for verification and debugging purposes. for verification and debugging purposes.
.TP .TP
.B \-yuv .B \-yuv
@ -61,20 +48,7 @@ edges (especially the red ones), but should be faster.
.B \-nofilter .B \-nofilter
Don't use the in-loop filtering process even if it is required by Don't use the in-loop filtering process even if it is required by
the bitstream. This may produce visible blocks on the non-compliant output, the bitstream. This may produce visible blocks on the non-compliant output,
but it will make the decoding faster. but will make the decoding faster.
.TP
.BI \-dither " strength
Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
post-processing effect applied to chroma components in lossy compression.
It helps by smoothing gradients and avoiding banding artifacts.
.TP
.BI \-alpha_dither
If the compressed file contains a transparency plane that was quantized
during compression, this flag will allow dithering the reconstructed plane
in order to generate smoother transparency gradients.
.TP
.B \-nodither
Disable all dithering (default).
.TP .TP
.B \-mt .B \-mt
Use multi-threading for decoding, if possible. Use multi-threading for decoding, if possible.
@ -87,19 +61,11 @@ The top-left corner will be snapped to even coordinates if needed.
This option is meant to reduce the memory needed for cropping large images. This option is meant to reduce the memory needed for cropping large images.
Note: the cropping is applied \fIbefore\fP any scaling. Note: the cropping is applied \fIbefore\fP any scaling.
.TP .TP
.B \-flip .BI \-scale " width height
Flip decoded image vertically (can be useful for OpenGL textures for instance).
.TP
\fB\-resize\fR, \fB\-scale\fI width height\fR
Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This
option is mostly intended to reducing the memory needed to decode large images, option is mostly intended to reducing the memory needed to decode large images,
when only a small version is needed (thumbnail, preview, etc.). Note: scaling when only a small version is needed (thumbnail, preview, etc.). Note: scaling
is applied \fIafter\fP cropping. is applied \fIafter\fP cropping.
If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
the value will be calculated preserving the aspect-ratio.
.TP
.B \-quiet
Do not print anything.
.TP .TP
.B \-v .B \-v
Print extra information (decoding time in particular). Print extra information (decoding time in particular).
@ -108,8 +74,8 @@ Print extra information (decoding time in particular).
Disable all assembly optimizations. Disable all assembly optimizations.
.SH BUGS .SH BUGS
Please report all bugs to the issue tracker: Please report all bugs to our issue tracker:
https://bugs.chromium.org/p/webp http://code.google.com/p/webp/issues
.br .br
Patches welcome! See this page to get started: Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/ http://www.webmproject.org/code/contribute/submitting-patches/
@ -118,24 +84,19 @@ http://www.webmproject.org/code/contribute/submitting-patches/
dwebp picture.webp \-o output.png dwebp picture.webp \-o output.png
.br .br
dwebp picture.webp \-ppm \-o output.ppm dwebp picture.webp \-ppm \-o output.ppm
.br
dwebp \-o output.ppm \-\- \-\-\-picture.webp
.br
cat picture.webp | dwebp \-o \- \-\- \- > output.ppm
.SH AUTHORS .SH AUTHORS
\fBdwebp\fP is a part of libwebp and was written by the WebP team. \fBdwebp\fP was written by the WebP team.
.br .br
The latest source tree is available at The latest source tree is available at http://www.webmproject.org/code
https://chromium.googlesource.com/webm/libwebp
.PP .PP
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>, This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
for the Debian project (and may be used by others). for the Debian project (and may be used by others).
.SH SEE ALSO .SH SEE ALSO
.BR cwebp (1), .BR cwebp (1),
.BR gif2webp (1), .BR webpmux (1),
.BR webpmux (1) .BR gif2webp (1).
.br .br
Please refer to http://developers.google.com/speed/webp/ for additional Please refer to http://developers.google.com/speed/webp/ for additional
information. information.

View File

@ -1,5 +1,5 @@
.\" Hey, EMACS: -*- nroff -*- .\" Hey, EMACS: -*- nroff -*-
.TH GIF2WEBP 1 "January 25, 2017" .TH GIF2WEBP 1 "February 01, 2013"
.SH NAME .SH NAME
gif2webp \- Convert a GIF image to WebP gif2webp \- Convert a GIF image to WebP
.SH SYNOPSIS .SH SYNOPSIS
@ -18,7 +18,6 @@ The basic options are:
.BI \-o " string .BI \-o " string
Specify the name of the output WebP file. If omitted, \fBgif2webp\fP will Specify the name of the output WebP file. If omitted, \fBgif2webp\fP will
perform conversion but only report statistics. perform conversion but only report statistics.
Using "\-" as output name will direct output to 'stdout'.
.TP .TP
.B \-h, \-help .B \-h, \-help
Usage information. Usage information.
@ -29,10 +28,6 @@ Print the version number (as major.minor.revision) and exit.
.B \-lossy .B \-lossy
Encode the image using lossy compression. Encode the image using lossy compression.
.TP .TP
.B \-mixed
Mixed compression mode: optimize compression of the image by picking either
lossy or lossless compression for each frame heuristically.
.TP
.BI \-q " float .BI \-q " float
Specify the compression factor for RGB channels between 0 and 100. The default Specify the compression factor for RGB channels between 0 and 100. The default
is 75. is 75.
@ -54,51 +49,6 @@ additional encoding possibilities and decide on the quality gain.
Lower value can result is faster processing time at the expense of Lower value can result is faster processing time at the expense of
larger file size and lower compression quality. larger file size and lower compression quality.
.TP .TP
.BI \-min_size
Encode image to achieve smallest size. This disables key frame insertion and
picks the dispose method resulting in smallest output for each frame. It uses
lossless compression by default, but can be combined with \-q, \-m, \-lossy or
\-mixed options.
.TP
.BI \-kmin " int
.TP
.BI \-kmax " int
Specify the minimum and maximum distance between consecutive key frames
(independently decodable frames) in the output animation. The tool will insert
some key frames into the output animation as needed so that this criteria is
satisfied.
.br
A 'kmax' value of 0 will turn off insertion of key frames. A 'kmax' value of 1
will result in all frames being key frames. 'kmin' value is not taken into
account in both these special cases.
Typical values are in the range 3 to 30. Default values are kmin = 9,
kmax = 17 for lossless compression and kmin = 3, kmax = 5 for lossy compression.
.br
These two options are relevant only for animated images with large number of
frames (>50).
.br
When lower values are used, more frames will be converted to key frames. This
may lead to smaller number of frames required to decode a frame on average,
thereby improving the decoding performance. But this may lead to slightly bigger
file sizes.
Higher values may lead to worse decoding performance, but smaller file sizes.
.br
Some restrictions:
.br
(i) kmin < kmax,
.br
(ii) kmin >= kmax / 2 + 1 and
.br
(iii) kmax - kmin <= 30.
.br
If any of these restrictions are not met, they will be enforced automatically.
.TP
.BI \-metadata " string
A comma separated list of metadata to copy from the input to the output if
present.
Valid values: \fBall\fP, \fBnone\fP, \fBicc\fP, \fBxmp\fP.
The default is \fBxmp\fP.
.TP
.BI \-f " int .BI \-f " int
For lossy encoding only (specified by the \-lossy option). Specify the strength For lossy encoding only (specified by the \-lossy option). Specify the strength
of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering). of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).
@ -107,10 +57,6 @@ strength of the filtering process applied after decoding the picture. The higher
the value the smoother the picture will appear. Typical values are usually in the value the smoother the picture will appear. Typical values are usually in
the range of 20 to 50. the range of 20 to 50.
.TP .TP
.B \-mt
Use multi-threading for encoding, if possible. This option is only effective
when using lossy compression.
.TP
.B \-v .B \-v
Print extra information. Print extra information.
.TP .TP
@ -118,8 +64,8 @@ Print extra information.
Do not print anything. Do not print anything.
.SH BUGS .SH BUGS
Please report all bugs to the issue tracker: Please report all bugs to our issue tracker:
https://bugs.chromium.org/p/webp http://code.google.com/p/webp/issues
.br .br
Patches welcome! See this page to get started: Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/ http://www.webmproject.org/code/contribute/submitting-patches/
@ -132,22 +78,19 @@ gif2webp \-q 70 picture.gif \-o picture.webp
gif2webp \-lossy \-m 3 picture.gif \-o picture_lossy.webp gif2webp \-lossy \-m 3 picture.gif \-o picture_lossy.webp
.br .br
gif2webp \-lossy \-f 50 picture.gif \-o picture.webp gif2webp \-lossy \-f 50 picture.gif \-o picture.webp
.br
gif2webp \-q 70 \-o picture.webp \-\- \-\-\-picture.gif
.SH AUTHORS .SH AUTHORS
\fBgif2webp\fP is a part of libwebp and was written by the WebP team. \fBgif2webp\fP was written by the WebP team.
.br .br
The latest source tree is available at The latest source tree is available at http://www.webmproject.org/code
https://chromium.googlesource.com/webm/libwebp
.PP .PP
This manual page was written by Urvang Joshi <urvang@google.com>, for the This manual page was written by Urvang Joshi <urvang@google.com>, for the
Debian project (and may be used by others). Debian project (and may be used by others).
.SH SEE ALSO .SH SEE ALSO
.BR cwebp (1),
.BR dwebp (1), .BR dwebp (1),
.BR webpmux (1) .BR cwebp (1),
.BR webpmux (1).
.br .br
Please refer to http://developers.google.com/speed/webp/ for additional Please refer to http://developers.google.com/speed/webp/ for additional
information. information.

View File

@ -1,97 +0,0 @@
.\" Hey, EMACS: -*- nroff -*-
.TH IMG2WEBP 1 "January 23, 2017"
.SH NAME
img2webp \- create animated WebP file from a sequence of input images.
.SH SYNOPSIS
.B img2webp
[file_level_options] [files] [per_frame_options...]
.br
.SH DESCRIPTION
This manual page documents the
.B img2webp
command.
.PP
\fBimg2webp\fP compresses a sequence of images using the animated WebP format.
Input images can either be PNG, JPEG, TIFF or WebP.
.SH FILE-LEVEL OPTIONS
The file-level options are applied at the beginning of the compression process,
before the input frames are read.
.TP
.BI \-o " string
Specify the name of the output WebP file.
.TP
.BI \-min_size
Encode images to achieve smallest size. This disables key frame insertion and
picks the parameters resulting in smallest output for each frame. It uses
lossless compression by default, but can be combined with \-q, \-m, \-lossy or
\-mixed options.
.TP
.BI \-kmin " int
.TP
.BI \-kmax " int
Specify the minimum and maximum distance between consecutive key frames
(independently decodable frames) in the output animation. The tool will insert
some key frames into the output animation as needed so that this criteria is
satisfied.
.br
.B \-mixed
Mixed compression mode: optimize compression of the image by picking either
lossy or lossless compression for each frame heuristically. This global
option disables the local option \fB-lossy\fP and \fB-lossless\fP .
.TP
.BI \-loop " int
Specifies the number of times the animation should loop. Using '0' means
'loop indefinitely'.
.TP
.BI \-v
Be more verbose.
.TP
.B \-h, \-help
A short usage summary.
.SH PER-FRAME OPTIONS
The per-frame options are applied for the images following as arguments in the
command line. They can be modified any number of times preceding each particular
input image.
.TP
.BI \-d " int
Specify the image duration in milliseconds.
.TP
.B \-lossless, \-lossy
Compress the next image(s) using lossless or lossy compression mode. The
default mode is lossless.
.TP
.BI \-q " float
Specify the compression factor between 0 and 100. The default is 75.
.TP
.BI \-m " int
Specify the compression method to use. This parameter controls the
trade off between encoding speed and the compressed file size and quality.
Possible values range from 0 to 6. Default value is 4.
.SH EXAMPLE
img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp
.br
.SH BUGS
Please report all bugs to the issue tracker:
https://bugs.chromium.org/p/webp
.br
Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting\-patches/
.SH AUTHORS
\fBimg2webp\fP is a part of libwebp and was written by the WebP team.
.br
The latest source tree is available at
https://chromium.googlesource.com/webm/libwebp
.PP
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
for the Debian project (and may be used by others).
.SH SEE ALSO
.BR webpmux (1),
.BR gif2webp (1)
.br
Please refer to http://developers.google.com/speed/webp/ for additional
information.

View File

@ -1,94 +0,0 @@
.\" Hey, EMACS: -*- nroff -*-
.TH VWEBP 1 "November 25, 2016"
.SH NAME
vwebp \- decompress a WebP file and display it in a window
.SH SYNOPSIS
.B vwebp
.RI [ options ] " input_file.webp
.br
.SH DESCRIPTION
This manual page documents the
.B vwebp
command.
.PP
\fBvwebp\fP decompresses a WebP file and displays it in a window using OpenGL.
.SH OPTIONS
.TP
.B \-h
Print usage summary.
.TP
.B \-version
Print version number and exit.
.TP
.B \-noicc
Don't use the ICC profile if present.
.TP
.B \-nofancy
Don't use the fancy YUV420 upscaler.
.TP
.B \-nofilter
Disable in-loop filtering.
.TP
.BI \-dither " strength
Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
post-processing effect applied to chroma components in lossy compression.
It helps by smoothing gradients and avoiding banding artifacts. Default: 50.
.TP
.BI \-noalphadither
By default, quantized transparency planes are dithered during decompression,
to smooth the gradients. This flag will prevent this dithering.
.TP
.B \-mt
Use multi-threading for decoding, if possible.
.TP
.B \-info
Display image information on top of the decoded image.
.TP
.BI \-\- " string
Explicitly specify the input file. This option is useful if the input
file starts with an '\-' for instance. This option must appear \fBlast\fP.
Any other options afterward will be ignored. If the input file is "\-",
the data will be read from \fIstdin\fP instead of a file.
.TP
.SH KEYBOARD SHORTCUTS
.TP
.B 'c'
Toggle use of color profile.
.TP
.B 'i'
Overlay file information.
.TP
.B 'd'
Disable blending and disposal process, for debugging purposes.
.TP
.B 'q' / 'Q' / ESC
Quit.
.SH BUGS
Please report all bugs to the issue tracker:
https://bugs.chromium.org/p/webp
.br
Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting-patches/
.SH EXAMPLES
vwebp picture.webp
.br
vwebp picture.webp -mt -dither 0
.br
vwebp \-\- \-\-\-picture.webp
.SH AUTHORS
\fBvwebp\fP is a part of libwebp and was written by the WebP team.
.br
The latest source tree is available at
https://chromium.googlesource.com/webm/libwebp
.PP
This manual page was written for the Debian project (and may be used by others).
.SH SEE ALSO
.BR dwebp (1)
.br
Please refer to http://developers.google.com/speed/webp/ for additional
information.

View File

@ -1,8 +1,7 @@
.\" Hey, EMACS: -*- nroff -*- .\" Hey, EMACS: -*- nroff -*-
.TH WEBPMUX 1 "November 10, 2016" .TH WEBPMUX 1 "March 16, 2013"
.SH NAME .SH NAME
webpmux \- create animated WebP files from non\-animated WebP images, extract webpmux \- command line tool to create WebP Mux/container file.
frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
.SH SYNOPSIS .SH SYNOPSIS
.B webpmux \-get .B webpmux \-get
.I GET_OPTIONS .I GET_OPTIONS
@ -35,13 +34,6 @@ frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
.I OUTPUT .I OUTPUT
.RE .RE
.br .br
.B webpmux \-duration
.I DURATION OPTIONS
.B [ \-duration ... ]
.I INPUT
.B \-o
.I OUTPUT
.br
.B webpmux \-info .B webpmux \-info
.I INPUT .I INPUT
.br .br
@ -53,8 +45,8 @@ This manual page documents the
.B webpmux .B webpmux
command. command.
.PP .PP
\fBwebpmux\fP can be used to create/extract from animated WebP files, as well as \fBwebpmux\fP can be used to create a WebP container file
to add/extract/strip XMP/EXIF metadata and ICC profile. and extract/strip relevant data from the container file.
.SH OPTIONS .SH OPTIONS
.SS GET_OPTIONS (\-get): .SS GET_OPTIONS (\-get):
.TP .TP
@ -68,7 +60,7 @@ Get EXIF metadata.
Get XMP metadata. Get XMP metadata.
.TP .TP
.BI frame " n .BI frame " n
Get nth frame from an animated image. (n = 0 has a special meaning: last frame). Get nth frame.
.SS SET_OPTIONS (\-set) .SS SET_OPTIONS (\-set)
.TP .TP
@ -98,53 +90,14 @@ Strip EXIF metadata.
.B xmp .B xmp
Strip XMP metadata. Strip XMP metadata.
.SS DURATION_OPTIONS (\-duration)
Amend the duration of a specific interval of frames. This option is only
effective on animated WebP and has no effect on a single-frame file.
.TP
.I duration[,start[,end]]
Where:
.br
.B duration
is the duration for the interval in milliseconds (mandatory).
Must be non-negative.
.br
.B start
is the starting frame index of the interval (optional).
.br
.B end
is the ending frame index (inclusive) of the interval (optional).
.TP
The three typical usages of this option are:
.br
.B -duration d
set the duration to 'd' for the whole animation.
.br
.B -duration d,f
set the duration of frame 'f' to 'd'.
.br
.B -duration d,start,end
set the duration to 'd' for the whole [start,end] interval.
.TP
.P
Note that the frames outside of the [start, end] interval will remain untouched.
The 'end' value '0' has the special meaning 'last frame of the animation'.
.TP
.I Reminder:
frame indexing starts at '1'.
.br
.SS FRAME_OPTIONS (\-frame) .SS FRAME_OPTIONS (\-frame)
Create an animated WebP file from multiple (non\-animated) WebP images.
.TP .TP
.I file_i +di[+xi+yi[+mi[bi]]] .I file_i +di[+xi+yi[+mi]]
Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image
offset for this frame, 'di' is the pause duration before next frame, 'mi' is offset for this frame, 'di' is the pause duration before next frame and 'mi' is
the dispose method for this frame (0 for NONE or 1 for BACKGROUND) and 'bi' is the dispose method for this frame (0 for NONE or 1 for BACKGROUND).
the blending method for this frame (+b for BLEND or \-b for NO_BLEND). 'mi' can be omitted and will default to 0 (NONE).
Argument 'bi' can be omitted and will default to +b (BLEND). Additionally, if 'mi' is ommitted then'xi' and 'yi' can be omitted and will
Also, 'mi' can be omitted if 'bi' is omitted and will default to 0 (NONE).
Finally, if 'mi' and 'bi' are omitted then 'xi' and 'yi' can be omitted and will
default to +0+0. default to +0+0.
.TP .TP
.BI \-loop " n .BI \-loop " n
@ -171,85 +124,51 @@ Output file in WebP format.
The nature of EXIF, XMP and ICC data is not checked and is assumed to be valid. The nature of EXIF, XMP and ICC data is not checked and is assumed to be valid.
.SH BUGS .SH BUGS
Please report all bugs to the issue tracker: Please report all bugs to our issue tracker:
https://bugs.chromium.org/p/webp http://code.google.com/p/webp/issues
.br .br
Patches welcome! See this page to get started: Patches welcome! See this page to get started:
http://www.webmproject.org/code/contribute/submitting\-patches/ http://www.webmproject.org/code/contribute/submitting-patches/
.SH EXAMPLES .SH EXAMPLES
.P
Add ICC profile:
.br
webpmux \-set icc image_profile.icc in.webp \-o icc_container.webp webpmux \-set icc image_profile.icc in.webp \-o icc_container.webp
.P
Extract ICC profile:
.br .br
webpmux \-get icc icc_container.webp \-o image_profile.icc webpmux \-get icc icc_container.webp \-o image_profile.icc
.P
Strip ICC profile:
.br .br
webpmux \-strip icc icc_container.webp \-o without_icc.webp webpmux \-strip icc icc_container.webp \-o without_icc.webp
.P
Add XMP metadata:
.br .br
webpmux \-set xmp image_metadata.xmp in.webp \-o xmp_container.webp webpmux \-set xmp image_metadata.xmp in.webp \-o xmp_container.webp
.P
Extract XMP metadata:
.br .br
webpmux \-get xmp xmp_container.webp \-o image_metadata.xmp webpmux \-get xmp xmp_container.webp \-o image_metadata.xmp
.P
Strip XMP metadata:
.br .br
webpmux \-strip xmp xmp_container.webp \-o without_xmp.webp webpmux \-strip xmp xmp_container.webp \-o without_xmp.webp
.P
Add EXIF metadata:
.br .br
webpmux \-set exif image_metadata.exif in.webp \-o exif_container.webp webpmux \-set exif image_metadata.exif in.webp \-o exif_container.webp
.P
Extract EXIF metadata:
.br .br
webpmux \-get exif exif_container.webp \-o image_metadata.exif webpmux \-get exif exif_container.webp \-o image_metadata.exif
.P
Strip EXIF metadata:
.br .br
webpmux \-strip exif exif_container.webp \-o without_exif.webp webpmux \-strip exif exif_container.webp \-o without_exif.webp
.P
Create an animated WebP file from 3 (non\-animated) WebP images:
.br .br
webpmux \-frame 1.webp +100 \-frame 2.webp +100+50+50 webpmux \-frame anim_1.webp +100 \-frame anim_2.webp +100+50+50 \-loop 10
.br .br
.RS 8 .RS 8
\-frame 3.webp +100+50+50+1+b \-loop 10 \-bgcolor 255,255,255,255 \-bgcolor 255,255,255,255 \-o anim_container.webp
.br
\-o anim_container.webp
.RE .RE
.P
Get the 2nd frame from an animated WebP file:
.br .br
webpmux \-get frame 2 anim_container.webp \-o frame_2.webp webpmux \-get frame 2 anim_container.webp \-o frame_2.webp
.P
Using \-get/\-set/\-strip with input file name starting with '\-':
.br
webpmux \-set icc image_profile.icc \-o icc_container.webp \-\- \-\-\-in.webp
.br
webpmux \-get icc \-o image_profile.icc \-\- \-\-\-icc_container.webp
.br
webpmux \-strip icc \-o without_icc.webp \-\- \-\-\-icc_container.webp
.SH AUTHORS .SH AUTHORS
\fBwebpmux\fP is a part of libwebp and was written by the WebP team. \fBwebpmux\fP is written by the WebP team.
.br .br
The latest source tree is available at The latest source tree is available at http://www.webmproject.org/code
https://chromium.googlesource.com/webm/libwebp
.PP .PP
This manual page was written by Vikas Arora <vikaas.arora@gmail.com>, This manual page was written by Vikas Arora <vikaas.arora@gmail.com>,
for the Debian project (and may be used by others). for the Debian project (and may be used by others).
.SH SEE ALSO .SH SEE ALSO
.BR cwebp (1),
.BR dwebp (1), .BR dwebp (1),
.BR gif2webp (1) .BR cwebp (1),
.BR gif2webp (1).
.br .br
Please refer to http://developers.google.com/speed/webp/ for additional Please refer to http://developers.google.com/speed/webp/ for additional
information. information.

1
src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/*.pc

View File

@ -1,5 +1,5 @@
# The mux and demux libraries depend on libwebp, thus the '.' to force # The mux and demux libraries depend on libwebp, thus the '.' to force the
# the build order so it's available to them. # build order so it's available to them.
SUBDIRS = dec enc dsp utils . SUBDIRS = dec enc dsp utils .
if WANT_MUX if WANT_MUX
SUBDIRS += mux SUBDIRS += mux
@ -8,6 +8,7 @@ if WANT_DEMUX
SUBDIRS += demux SUBDIRS += demux
endif endif
AM_CPPFLAGS = -I$(top_srcdir)/src
lib_LTLIBRARIES = libwebp.la lib_LTLIBRARIES = libwebp.la
if BUILD_LIBWEBPDECODER if BUILD_LIBWEBPDECODER
@ -35,7 +36,7 @@ libwebp_la_LIBADD += utils/libwebputils.la
# other than the ones listed on the command line, i.e., after linking, it will # 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 # not have unresolved symbols. Some platforms (Windows among them) require all
# symbols in shared libraries to be resolved at library creation. # symbols in shared libraries to be resolved at library creation.
libwebp_la_LDFLAGS = -no-undefined -version-info 7:0:0 libwebp_la_LDFLAGS = -no-undefined -version-info 4:3:0
libwebpincludedir = $(includedir)/webp libwebpincludedir = $(includedir)/webp
pkgconfig_DATA = libwebp.pc pkgconfig_DATA = libwebp.pc
@ -47,7 +48,7 @@ if BUILD_LIBWEBPDECODER
libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 3:0:0 libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 0:1:0
pkgconfig_DATA += libwebpdecoder.pc pkgconfig_DATA += libwebpdecoder.pc
endif endif

View File

@ -1,22 +1,22 @@
AM_CPPFLAGS = -I$(top_srcdir)/src
noinst_LTLIBRARIES = libwebpdecode.la noinst_LTLIBRARIES = libwebpdecode.la
libwebpdecode_la_SOURCES = libwebpdecode_la_SOURCES =
libwebpdecode_la_SOURCES += alpha_dec.c libwebpdecode_la_SOURCES += alpha.c
libwebpdecode_la_SOURCES += alphai_dec.h libwebpdecode_la_SOURCES += buffer.c
libwebpdecode_la_SOURCES += buffer_dec.c libwebpdecode_la_SOURCES += decode_vp8.h
libwebpdecode_la_SOURCES += common_dec.h libwebpdecode_la_SOURCES += frame.c
libwebpdecode_la_SOURCES += vp8_dec.h libwebpdecode_la_SOURCES += idec.c
libwebpdecode_la_SOURCES += frame_dec.c libwebpdecode_la_SOURCES += io.c
libwebpdecode_la_SOURCES += idec_dec.c libwebpdecode_la_SOURCES += layer.c
libwebpdecode_la_SOURCES += io_dec.c libwebpdecode_la_SOURCES += quant.c
libwebpdecode_la_SOURCES += quant_dec.c libwebpdecode_la_SOURCES += tree.c
libwebpdecode_la_SOURCES += tree_dec.c libwebpdecode_la_SOURCES += vp8.c
libwebpdecode_la_SOURCES += vp8_dec.c libwebpdecode_la_SOURCES += vp8i.h
libwebpdecode_la_SOURCES += vp8i_dec.h libwebpdecode_la_SOURCES += vp8l.c
libwebpdecode_la_SOURCES += vp8l_dec.c libwebpdecode_la_SOURCES += vp8li.h
libwebpdecode_la_SOURCES += vp8li_dec.h libwebpdecode_la_SOURCES += webp.c
libwebpdecode_la_SOURCES += webp_dec.c libwebpdecode_la_SOURCES += webpi.h
libwebpdecode_la_SOURCES += webpi_dec.h
libwebpdecodeinclude_HEADERS = libwebpdecodeinclude_HEADERS =
libwebpdecodeinclude_HEADERS += ../webp/decode.h libwebpdecodeinclude_HEADERS += ../webp/decode.h
@ -24,5 +24,5 @@ libwebpdecodeinclude_HEADERS += ../webp/types.h
noinst_HEADERS = noinst_HEADERS =
noinst_HEADERS += ../webp/format_constants.h noinst_HEADERS += ../webp/format_constants.h
libwebpdecode_la_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) libwebpdecode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
libwebpdecodeincludedir = $(includedir)/webp libwebpdecodeincludedir = $(includedir)/webp

115
src/dec/alpha.c Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Alpha-plane decompression.
//
// Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h>
#include "./vp8i.h"
#include "./vp8li.h"
#include "../utils/filters.h"
#include "../utils/quant_levels_dec.h"
#include "../webp/format_constants.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
// The 'output' buffer should be pre-allocated and must be of the same
// dimension 'height'x'width', as that of the image.
//
// Returns 1 on successfully decoding the compressed alpha and
// 0 if either:
// error in bit-stream header (invalid compression mode or filter), or
// error returned by appropriate compression method.
static int DecodeAlpha(const uint8_t* data, size_t data_size,
int width, int height, uint8_t* output) {
WEBP_FILTER_TYPE filter;
int pre_processing;
int rsrv;
int ok = 0;
int method;
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
assert(width > 0 && height > 0);
assert(data != NULL && output != NULL);
if (data_size <= ALPHA_HEADER_LEN) {
return 0;
}
method = (data[0] >> 0) & 0x03;
filter = (data[0] >> 2) & 0x03;
pre_processing = (data[0] >> 4) & 0x03;
rsrv = (data[0] >> 6) & 0x03;
if (method < ALPHA_NO_COMPRESSION ||
method > ALPHA_LOSSLESS_COMPRESSION ||
filter >= WEBP_FILTER_LAST ||
pre_processing > ALPHA_PREPROCESSED_LEVELS ||
rsrv != 0) {
return 0;
}
if (method == ALPHA_NO_COMPRESSION) {
const size_t alpha_decoded_size = height * width;
ok = (alpha_data_size >= alpha_decoded_size);
if (ok) memcpy(output, alpha_data, alpha_decoded_size);
} else {
ok = VP8LDecodeAlphaImageStream(width, height, alpha_data, alpha_data_size,
output);
}
if (ok) {
WebPUnfilterFunc unfilter_func = WebPUnfilters[filter];
if (unfilter_func != NULL) {
// TODO(vikas): Implement on-the-fly decoding & filter mechanism to decode
// and apply filter per image-row.
unfilter_func(width, height, width, output);
}
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
ok = DequantizeLevels(output, width, height);
}
}
return ok;
}
//------------------------------------------------------------------------------
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
int row, int num_rows) {
const int width = dec->pic_hdr_.width_;
const int height = dec->pic_hdr_.height_;
if (row < 0 || num_rows < 0 || row + num_rows > height) {
return NULL; // sanity check.
}
if (row == 0) {
// Decode everything during the first call.
assert(!dec->is_alpha_decoded_);
if (!DecodeAlpha(dec->alpha_data_, (size_t)dec->alpha_data_size_,
width, height, dec->alpha_plane_)) {
return NULL; // Error.
}
dec->is_alpha_decoded_ = 1;
}
// Return a pointer to the current decoded row.
return dec->alpha_plane_ + row * width;
}
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -1,232 +0,0 @@
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Alpha-plane decompression.
//
// Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h>
#include "./alphai_dec.h"
#include "./vp8i_dec.h"
#include "./vp8li_dec.h"
#include "../dsp/dsp.h"
#include "../utils/quant_levels_dec_utils.h"
#include "../utils/utils.h"
#include "../webp/format_constants.h"
//------------------------------------------------------------------------------
// ALPHDecoder object.
// Allocates a new alpha decoder instance.
static ALPHDecoder* ALPHNew(void) {
ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
return dec;
}
// Clears and deallocates an alpha decoder instance.
static void ALPHDelete(ALPHDecoder* const dec) {
if (dec != NULL) {
VP8LDelete(dec->vp8l_dec_);
dec->vp8l_dec_ = NULL;
WebPSafeFree(dec);
}
}
//------------------------------------------------------------------------------
// Decoding.
// Initialize alpha decoding by parsing the alpha header and decoding the image
// header for alpha data stored using lossless compression.
// Returns false in case of error in alpha header (data too short, invalid
// compression method or filter, error in lossless header data etc).
static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
size_t data_size, const VP8Io* const src_io,
uint8_t* output) {
int ok = 0;
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
int rsrv;
VP8Io* const io = &dec->io_;
assert(data != NULL && output != NULL && src_io != NULL);
VP8FiltersInit();
dec->output_ = output;
dec->width_ = src_io->width;
dec->height_ = src_io->height;
assert(dec->width_ > 0 && dec->height_ > 0);
if (data_size <= ALPHA_HEADER_LEN) {
return 0;
}
dec->method_ = (data[0] >> 0) & 0x03;
dec->filter_ = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03);
dec->pre_processing_ = (data[0] >> 4) & 0x03;
rsrv = (data[0] >> 6) & 0x03;
if (dec->method_ < ALPHA_NO_COMPRESSION ||
dec->method_ > ALPHA_LOSSLESS_COMPRESSION ||
dec->filter_ >= WEBP_FILTER_LAST ||
dec->pre_processing_ > ALPHA_PREPROCESSED_LEVELS ||
rsrv != 0) {
return 0;
}
// Copy the necessary parameters from src_io to io
VP8InitIo(io);
WebPInitCustomIo(NULL, io);
io->opaque = dec;
io->width = src_io->width;
io->height = src_io->height;
io->use_cropping = src_io->use_cropping;
io->crop_left = src_io->crop_left;
io->crop_right = src_io->crop_right;
io->crop_top = src_io->crop_top;
io->crop_bottom = src_io->crop_bottom;
// No need to copy the scaling parameters.
if (dec->method_ == ALPHA_NO_COMPRESSION) {
const size_t alpha_decoded_size = dec->width_ * dec->height_;
ok = (alpha_data_size >= alpha_decoded_size);
} else {
assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION);
ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size);
}
return ok;
}
// Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha
// starting from row number 'row'. It assumes that rows up to (row - 1) have
// already been decoded.
// Returns false in case of bitstream error.
static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) {
ALPHDecoder* const alph_dec = dec->alph_dec_;
const int width = alph_dec->width_;
const int height = alph_dec->io_.crop_bottom;
if (alph_dec->method_ == ALPHA_NO_COMPRESSION) {
int y;
const uint8_t* prev_line = dec->alpha_prev_line_;
const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width;
uint8_t* dst = dec->alpha_plane_ + row * width;
assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]);
if (alph_dec->filter_ != WEBP_FILTER_NONE) {
assert(WebPUnfilters[alph_dec->filter_] != NULL);
for (y = 0; y < num_rows; ++y) {
WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width);
prev_line = dst;
dst += width;
deltas += width;
}
} else {
for (y = 0; y < num_rows; ++y) {
memcpy(dst, deltas, width * sizeof(*dst));
prev_line = dst;
dst += width;
deltas += width;
}
}
dec->alpha_prev_line_ = prev_line;
} else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION
assert(alph_dec->vp8l_dec_ != NULL);
if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) {
return 0;
}
}
if (row + num_rows >= height) {
dec->is_alpha_decoded_ = 1;
}
return 1;
}
static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) {
const int stride = io->width;
const int height = io->crop_bottom;
const uint64_t alpha_size = (uint64_t)stride * height;
assert(dec->alpha_plane_mem_ == NULL);
dec->alpha_plane_mem_ =
(uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_));
if (dec->alpha_plane_mem_ == NULL) {
return 0;
}
dec->alpha_plane_ = dec->alpha_plane_mem_;
dec->alpha_prev_line_ = NULL;
return 1;
}
void WebPDeallocateAlphaMemory(VP8Decoder* const dec) {
assert(dec != NULL);
WebPSafeFree(dec->alpha_plane_mem_);
dec->alpha_plane_mem_ = NULL;
dec->alpha_plane_ = NULL;
ALPHDelete(dec->alph_dec_);
dec->alph_dec_ = NULL;
}
//------------------------------------------------------------------------------
// Main entry point.
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
const VP8Io* const io,
int row, int num_rows) {
const int width = io->width;
const int height = io->crop_bottom;
assert(dec != NULL && io != NULL);
if (row < 0 || num_rows <= 0 || row + num_rows > height) {
return NULL; // sanity check.
}
if (!dec->is_alpha_decoded_) {
if (dec->alph_dec_ == NULL) { // Initialize decoder.
dec->alph_dec_ = ALPHNew();
if (dec->alph_dec_ == NULL) return NULL;
if (!AllocateAlphaPlane(dec, io)) goto Error;
if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_,
io, dec->alpha_plane_)) {
goto Error;
}
// if we allowed use of alpha dithering, check whether it's needed at all
if (dec->alph_dec_->pre_processing_ != ALPHA_PREPROCESSED_LEVELS) {
dec->alpha_dithering_ = 0; // disable dithering
} else {
num_rows = height - row; // decode everything in one pass
}
}
assert(dec->alph_dec_ != NULL);
assert(row + num_rows <= height);
if (!ALPHDecode(dec, row, num_rows)) goto Error;
if (dec->is_alpha_decoded_) { // finished?
ALPHDelete(dec->alph_dec_);
dec->alph_dec_ = NULL;
if (dec->alpha_dithering_ > 0) {
uint8_t* const alpha = dec->alpha_plane_ + io->crop_top * width
+ io->crop_left;
if (!WebPDequantizeLevels(alpha,
io->crop_right - io->crop_left,
io->crop_bottom - io->crop_top,
width, dec->alpha_dithering_)) {
goto Error;
}
}
}
}
// Return a pointer to the current decoded row.
return dec->alpha_plane_ + row * width;
Error:
WebPDeallocateAlphaMemory(dec);
return NULL;
}

View File

@ -1,54 +0,0 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Alpha decoder: internal header.
//
// Author: Urvang (urvang@google.com)
#ifndef WEBP_DEC_ALPHAI_H_
#define WEBP_DEC_ALPHAI_H_
#include "./webpi_dec.h"
#include "../utils/filters_utils.h"
#ifdef __cplusplus
extern "C" {
#endif
struct VP8LDecoder; // Defined in dec/vp8li.h.
typedef struct ALPHDecoder ALPHDecoder;
struct ALPHDecoder {
int width_;
int height_;
int method_;
WEBP_FILTER_TYPE filter_;
int pre_processing_;
struct VP8LDecoder* vp8l_dec_;
VP8Io io_;
int use_8b_decode_; // Although alpha channel requires only 1 byte per
// pixel, sometimes VP8LDecoder may need to allocate
// 4 bytes per pixel internally during decode.
uint8_t* output_;
const uint8_t* prev_line_; // last output row (or NULL)
};
//------------------------------------------------------------------------------
// internal functions. Not public.
// Deallocate memory associated to dec->alpha_plane_ decoding
void WebPDeallocateAlphaMemory(VP8Decoder* const dec);
//------------------------------------------------------------------------------
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* WEBP_DEC_ALPHAI_H_ */

View File

@ -13,10 +13,14 @@
#include <stdlib.h> #include <stdlib.h>
#include "./vp8i_dec.h" #include "./vp8i.h"
#include "./webpi_dec.h" #include "./webpi.h"
#include "../utils/utils.h" #include "../utils/utils.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// WebPDecBuffer // WebPDecBuffer
@ -33,11 +37,6 @@ static int IsValidColorspace(int webp_csp_mode) {
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST); return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
} }
// strictly speaking, the very last (or first, if flipped) row
// doesn't require padding.
#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
(uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH)
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) { static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
int ok = 1; int ok = 1;
const WEBP_CSP_MODE mode = buffer->colorspace; const WEBP_CSP_MODE mode = buffer->colorspace;
@ -47,41 +46,33 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
ok = 0; ok = 0;
} else if (!WebPIsRGBMode(mode)) { // YUV checks } else if (!WebPIsRGBMode(mode)) { // YUV checks
const WebPYUVABuffer* const buf = &buffer->u.YUVA; const WebPYUVABuffer* const buf = &buffer->u.YUVA;
const int uv_width = (width + 1) / 2; const uint64_t y_size = (uint64_t)buf->y_stride * height;
const int uv_height = (height + 1) / 2; const uint64_t u_size = (uint64_t)buf->u_stride * ((height + 1) / 2);
const int y_stride = abs(buf->y_stride); const uint64_t v_size = (uint64_t)buf->v_stride * ((height + 1) / 2);
const int u_stride = abs(buf->u_stride); const uint64_t a_size = (uint64_t)buf->a_stride * height;
const int v_stride = abs(buf->v_stride);
const int a_stride = abs(buf->a_stride);
const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride);
const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride);
const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride);
const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride);
ok &= (y_size <= buf->y_size); ok &= (y_size <= buf->y_size);
ok &= (u_size <= buf->u_size); ok &= (u_size <= buf->u_size);
ok &= (v_size <= buf->v_size); ok &= (v_size <= buf->v_size);
ok &= (y_stride >= width); ok &= (buf->y_stride >= width);
ok &= (u_stride >= uv_width); ok &= (buf->u_stride >= (width + 1) / 2);
ok &= (v_stride >= uv_width); ok &= (buf->v_stride >= (width + 1) / 2);
ok &= (buf->y != NULL); ok &= (buf->y != NULL);
ok &= (buf->u != NULL); ok &= (buf->u != NULL);
ok &= (buf->v != NULL); ok &= (buf->v != NULL);
if (mode == MODE_YUVA) { if (mode == MODE_YUVA) {
ok &= (a_stride >= width); ok &= (buf->a_stride >= width);
ok &= (a_size <= buf->a_size); ok &= (a_size <= buf->a_size);
ok &= (buf->a != NULL); ok &= (buf->a != NULL);
} }
} else { // RGB checks } else { // RGB checks
const WebPRGBABuffer* const buf = &buffer->u.RGBA; const WebPRGBABuffer* const buf = &buffer->u.RGBA;
const int stride = abs(buf->stride); const uint64_t size = (uint64_t)buf->stride * height;
const uint64_t size = MIN_BUFFER_SIZE(width, height, stride);
ok &= (size <= buf->size); ok &= (size <= buf->size);
ok &= (stride >= width * kModeBpp[mode]); ok &= (buf->stride >= width * kModeBpp[mode]);
ok &= (buf->rgba != NULL); ok &= (buf->rgba != NULL);
} }
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM; return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
} }
#undef MIN_BUFFER_SIZE
static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
const int w = buffer->width; const int w = buffer->width;
@ -92,7 +83,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
} }
if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) { if (!buffer->is_external_memory && buffer->private_memory == NULL) {
uint8_t* output; uint8_t* output;
int uv_stride = 0, a_stride = 0; int uv_stride = 0, a_stride = 0;
uint64_t uv_size = 0, a_size = 0, total_size; uint64_t uv_size = 0, a_size = 0, total_size;
@ -144,35 +135,9 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
return CheckDecBuffer(buffer); return CheckDecBuffer(buffer);
} }
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) {
if (buffer == NULL) {
return VP8_STATUS_INVALID_PARAM;
}
if (WebPIsRGBMode(buffer->colorspace)) {
WebPRGBABuffer* const buf = &buffer->u.RGBA;
buf->rgba += (buffer->height - 1) * buf->stride;
buf->stride = -buf->stride;
} else {
WebPYUVABuffer* const buf = &buffer->u.YUVA;
const int H = buffer->height;
buf->y += (H - 1) * buf->y_stride;
buf->y_stride = -buf->y_stride;
buf->u += ((H - 1) >> 1) * buf->u_stride;
buf->u_stride = -buf->u_stride;
buf->v += ((H - 1) >> 1) * buf->v_stride;
buf->v_stride = -buf->v_stride;
if (buf->a != NULL) {
buf->a += (H - 1) * buf->a_stride;
buf->a_stride = -buf->a_stride;
}
}
return VP8_STATUS_OK;
}
VP8StatusCode WebPAllocateDecBuffer(int w, int h, VP8StatusCode WebPAllocateDecBuffer(int w, int h,
const WebPDecoderOptions* const options, const WebPDecoderOptions* const options,
WebPDecBuffer* const out) { WebPDecBuffer* const out) {
VP8StatusCode status;
if (out == NULL || w <= 0 || h <= 0) { if (out == NULL || w <= 0 || h <= 0) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
} }
@ -189,28 +154,18 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h,
h = ch; h = ch;
} }
if (options->use_scaling) { if (options->use_scaling) {
int scaled_width = options->scaled_width; if (options->scaled_width <= 0 || options->scaled_height <= 0) {
int scaled_height = options->scaled_height;
if (!WebPRescalerGetScaledDimensions(
w, h, &scaled_width, &scaled_height)) {
return VP8_STATUS_INVALID_PARAM; return VP8_STATUS_INVALID_PARAM;
} }
w = scaled_width; w = options->scaled_width;
h = scaled_height; h = options->scaled_height;
} }
} }
out->width = w; out->width = w;
out->height = h; out->height = h;
// Then, allocate buffer for real. // Then, allocate buffer for real
status = AllocateBuffer(out); return AllocateBuffer(out);
if (status != VP8_STATUS_OK) return status;
// Use the stride trick if vertical flip is needed.
if (options != NULL && options->flip) {
status = WebPFlipBuffer(out);
}
return status;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -227,9 +182,8 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
void WebPFreeDecBuffer(WebPDecBuffer* buffer) { void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
if (buffer != NULL) { if (buffer != NULL) {
if (buffer->is_external_memory <= 0) { if (!buffer->is_external_memory)
WebPSafeFree(buffer->private_memory); free(buffer->private_memory);
}
buffer->private_memory = NULL; buffer->private_memory = NULL;
} }
} }
@ -256,45 +210,8 @@ void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
} }
} }
VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf,
WebPDecBuffer* const dst_buf) {
assert(src_buf != NULL && dst_buf != NULL);
assert(src_buf->colorspace == dst_buf->colorspace);
dst_buf->width = src_buf->width;
dst_buf->height = src_buf->height;
if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) {
return VP8_STATUS_INVALID_PARAM;
}
if (WebPIsRGBMode(src_buf->colorspace)) {
const WebPRGBABuffer* const src = &src_buf->u.RGBA;
const WebPRGBABuffer* const dst = &dst_buf->u.RGBA;
WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride,
src_buf->width * kModeBpp[src_buf->colorspace],
src_buf->height);
} else {
const WebPYUVABuffer* const src = &src_buf->u.YUVA;
const WebPYUVABuffer* const dst = &dst_buf->u.YUVA;
WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride,
src_buf->width, src_buf->height);
WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride,
(src_buf->width + 1) / 2, (src_buf->height + 1) / 2);
WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride,
(src_buf->width + 1) / 2, (src_buf->height + 1) / 2);
if (WebPIsAlphaMode(src_buf->colorspace)) {
WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride,
src_buf->width, src_buf->height);
}
}
return VP8_STATUS_OK;
}
int WebPAvoidSlowMemory(const WebPDecBuffer* const output,
const WebPBitstreamFeatures* const features) {
assert(output != NULL);
return (output->is_external_memory >= 2) &&
WebPIsPremultipliedMode(output->colorspace) &&
(features != NULL && features->has_alpha);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -1,54 +0,0 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Definitions and macros common to encoding and decoding
//
// Author: Skal (pascal.massimino@gmail.com)
#ifndef WEBP_DEC_COMMON_H_
#define WEBP_DEC_COMMON_H_
// intra prediction modes
enum { B_DC_PRED = 0, // 4x4 modes
B_TM_PRED = 1,
B_VE_PRED = 2,
B_HE_PRED = 3,
B_RD_PRED = 4,
B_VR_PRED = 5,
B_LD_PRED = 6,
B_VL_PRED = 7,
B_HD_PRED = 8,
B_HU_PRED = 9,
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
// Luma16 or UV modes
DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
B_PRED = NUM_BMODES, // refined I4x4 mode
NUM_PRED_MODES = 4,
// special modes
B_DC_PRED_NOTOP = 4,
B_DC_PRED_NOLEFT = 5,
B_DC_PRED_NOTOPLEFT = 6,
NUM_B_DC_MODES = 7 };
enum { MB_FEATURE_TREE_PROBS = 3,
NUM_MB_SEGMENTS = 4,
NUM_REF_LF_DELTAS = 4,
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
MAX_NUM_PARTITIONS = 8,
// Probabilities
NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
NUM_BANDS = 8,
NUM_CTX = 3,
NUM_PROBAS = 11
};
#endif // WEBP_DEC_COMMON_H_

View File

@ -16,7 +16,7 @@
#include "../webp/decode.h" #include "../webp/decode.h"
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
@ -132,8 +132,7 @@ static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION); return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
} }
// Decode the VP8 frame header. Returns true if ok. // Start decoding a new picture. Returns true if ok.
// Note: 'io->data' must be pointing to the start of the VP8 frame header.
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
// Decode a picture. Will call VP8GetHeaders() if it wasn't done already. // Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
@ -178,7 +177,7 @@ WEBP_EXTERN(int) VP8LGetInfo(
const uint8_t* data, size_t data_size, // data available so far const uint8_t* data, size_t data_size, // data available so far
int* const width, int* const height, int* const has_alpha); int* const width, int* const height, int* const has_alpha);
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

View File

@ -12,183 +12,14 @@
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h> #include <stdlib.h>
#include "./vp8i_dec.h" #include "./vp8i.h"
#include "../utils/utils.h" #include "../utils/utils.h"
//------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus)
// Main reconstruction function. extern "C" {
#endif
static const int kScan[16] = { #define ALIGN_MASK (32 - 1)
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
};
static int CheckMode(int mb_x, int mb_y, int mode) {
if (mode == B_DC_PRED) {
if (mb_x == 0) {
return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
} else {
return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
}
}
return mode;
}
static void Copy32b(uint8_t* const dst, const uint8_t* const src) {
memcpy(dst, src, 4);
}
static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
uint8_t* const dst) {
switch (bits >> 30) {
case 3:
VP8Transform(src, dst, 0);
break;
case 2:
VP8TransformAC3(src, dst);
break;
case 1:
VP8TransformDC(src, dst);
break;
default:
break;
}
}
static void DoUVTransform(uint32_t bits, const int16_t* const src,
uint8_t* const dst) {
if (bits & 0xff) { // any non-zero coeff at all?
if (bits & 0xaa) { // any non-zero AC coefficient?
VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V
} else {
VP8TransformDCUV(src, dst);
}
}
}
static void ReconstructRow(const VP8Decoder* const dec,
const VP8ThreadContext* ctx) {
int j;
int mb_x;
const int mb_y = ctx->mb_y_;
const int cache_id = ctx->id_;
uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
// Initialize left-most block.
for (j = 0; j < 16; ++j) {
y_dst[j * BPS - 1] = 129;
}
for (j = 0; j < 8; ++j) {
u_dst[j * BPS - 1] = 129;
v_dst[j * BPS - 1] = 129;
}
// Init top-left sample on left column too.
if (mb_y > 0) {
y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
} else {
// we only need to do this init once at block (0,0).
// Afterward, it remains valid for the whole topmost row.
memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
memset(u_dst - BPS - 1, 127, 8 + 1);
memset(v_dst - BPS - 1, 127, 8 + 1);
}
// Reconstruct one row.
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
const VP8MBData* const block = ctx->mb_data_ + mb_x;
// Rotate in the left samples from previously decoded block. We move four
// pixels at a time for alignment reason, and because of in-loop filter.
if (mb_x > 0) {
for (j = -1; j < 16; ++j) {
Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
}
for (j = -1; j < 8; ++j) {
Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
}
}
{
// bring top samples into the cache
VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x;
const int16_t* const coeffs = block->coeffs_;
uint32_t bits = block->non_zero_y_;
int n;
if (mb_y > 0) {
memcpy(y_dst - BPS, top_yuv[0].y, 16);
memcpy(u_dst - BPS, top_yuv[0].u, 8);
memcpy(v_dst - BPS, top_yuv[0].v, 8);
}
// predict and add residuals
if (block->is_i4x4_) { // 4x4
uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
if (mb_y > 0) {
if (mb_x >= dec->mb_w_ - 1) { // on rightmost border
memset(top_right, top_yuv[0].y[15], sizeof(*top_right));
} else {
memcpy(top_right, top_yuv[1].y, sizeof(*top_right));
}
}
// replicate the top-right pixels below
top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
// predict and add residuals for all 4x4 blocks in turn.
for (n = 0; n < 16; ++n, bits <<= 2) {
uint8_t* const dst = y_dst + kScan[n];
VP8PredLuma4[block->imodes_[n]](dst);
DoTransform(bits, coeffs + n * 16, dst);
}
} else { // 16x16
const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]);
VP8PredLuma16[pred_func](y_dst);
if (bits != 0) {
for (n = 0; n < 16; ++n, bits <<= 2) {
DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]);
}
}
}
{
// Chroma
const uint32_t bits_uv = block->non_zero_uv_;
const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_);
VP8PredChroma8[pred_func](u_dst);
VP8PredChroma8[pred_func](v_dst);
DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst);
DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst);
}
// stash away top samples for next block
if (mb_y < dec->mb_h_ - 1) {
memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16);
memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8);
memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8);
}
}
// Transfer reconstructed samples from yuv_b_ cache to final destination.
{
const int y_offset = cache_id * 16 * dec->cache_y_stride_;
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset;
uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset;
uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset;
for (j = 0; j < 16; ++j) {
memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16);
}
for (j = 0; j < 8; ++j) {
memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8);
memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8);
}
}
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Filtering // Filtering
@ -200,18 +31,25 @@ static void ReconstructRow(const VP8Decoder* const dec,
// U/V, so it's 8 samples total (because of the 2x upsampling). // U/V, so it's 8 samples total (because of the 2x upsampling).
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 }; static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
static WEBP_INLINE int hev_thresh_from_level(int level, int keyframe) {
if (keyframe) {
return (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
} else {
return (level >= 40) ? 3 : (level >= 20) ? 2 : (level >= 15) ? 1 : 0;
}
}
static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) { static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
const VP8ThreadContext* const ctx = &dec->thread_ctx_; const VP8ThreadContext* const ctx = &dec->thread_ctx_;
const int cache_id = ctx->id_;
const int y_bps = dec->cache_y_stride_; const int y_bps = dec->cache_y_stride_;
const VP8FInfo* const f_info = ctx->f_info_ + mb_x; VP8FInfo* const f_info = ctx->f_info_ + mb_x;
uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16; uint8_t* const y_dst = dec->cache_y_ + ctx->id_ * 16 * y_bps + mb_x * 16;
const int level = f_info->f_level_;
const int ilevel = f_info->f_ilevel_; const int ilevel = f_info->f_ilevel_;
const int limit = f_info->f_limit_; const int limit = 2 * level + ilevel;
if (limit == 0) { if (level == 0) {
return; return;
} }
assert(limit >= 3);
if (dec->filter_type_ == 1) { // simple if (dec->filter_type_ == 1) { // simple
if (mb_x > 0) { if (mb_x > 0) {
VP8SimpleHFilter16(y_dst, y_bps, limit + 4); VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
@ -227,9 +65,10 @@ static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
} }
} else { // complex } else { // complex
const int uv_bps = dec->cache_uv_stride_; const int uv_bps = dec->cache_uv_stride_;
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8; uint8_t* const u_dst = dec->cache_u_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8; uint8_t* const v_dst = dec->cache_v_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
const int hev_thresh = f_info->hev_thresh_; const int hev_thresh =
hev_thresh_from_level(level, dec->frm_hdr_.key_frame_);
if (mb_x > 0) { if (mb_x > 0) {
VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
@ -282,110 +121,32 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
VP8FInfo* const info = &dec->fstrengths_[s][i4x4]; VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
int level = base_level; int level = base_level;
if (hdr->use_lf_delta_) { if (hdr->use_lf_delta_) {
// TODO(skal): only CURRENT is handled for now.
level += hdr->ref_lf_delta_[0]; level += hdr->ref_lf_delta_[0];
if (i4x4) { if (i4x4) {
level += hdr->mode_lf_delta_[0]; level += hdr->mode_lf_delta_[0];
} }
} }
level = (level < 0) ? 0 : (level > 63) ? 63 : level; level = (level < 0) ? 0 : (level > 63) ? 63 : level;
if (level > 0) { info->f_level_ = level;
int ilevel = level;
if (hdr->sharpness_ > 0) { if (hdr->sharpness_ > 0) {
if (hdr->sharpness_ > 4) { if (hdr->sharpness_ > 4) {
ilevel >>= 2; level >>= 2;
} else { } else {
ilevel >>= 1; level >>= 1;
} }
if (ilevel > 9 - hdr->sharpness_) { if (level > 9 - hdr->sharpness_) {
ilevel = 9 - hdr->sharpness_; level = 9 - hdr->sharpness_;
}
} }
if (ilevel < 1) ilevel = 1;
info->f_ilevel_ = ilevel;
info->f_limit_ = 2 * level + ilevel;
info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
} else {
info->f_limit_ = 0; // no filtering
} }
info->f_inner_ = i4x4; info->f_ilevel_ = (level < 1) ? 1 : level;
info->f_inner_ = 0;
} }
} }
} }
} }
//------------------------------------------------------------------------------
// Dithering
// minimal amp that will provide a non-zero dithering effect
#define MIN_DITHER_AMP 4
#define DITHER_AMP_TAB_SIZE 12
static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
// roughly, it's dqm->uv_mat_[1]
8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1
};
void VP8InitDithering(const WebPDecoderOptions* const options,
VP8Decoder* const dec) {
assert(dec != NULL);
if (options != NULL) {
const int d = options->dithering_strength;
const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1;
const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100);
if (f > 0) {
int s;
int all_amp = 0;
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
VP8QuantMatrix* const dqm = &dec->dqm_[s];
if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) {
// TODO(skal): should we specially dither more for uv_quant_ < 0?
const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_;
dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3;
}
all_amp |= dqm->dither_;
}
if (all_amp != 0) {
VP8InitRandom(&dec->dithering_rg_, 1.0f);
dec->dither_ = 1;
}
}
// potentially allow alpha dithering
dec->alpha_dithering_ = options->alpha_dithering_strength;
if (dec->alpha_dithering_ > 100) {
dec->alpha_dithering_ = 100;
} else if (dec->alpha_dithering_ < 0) {
dec->alpha_dithering_ = 0;
}
}
}
// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100
static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
uint8_t dither[64];
int i;
for (i = 0; i < 8 * 8; ++i) {
dither[i] = VP8RandomBits2(rg, VP8_DITHER_AMP_BITS + 1, amp);
}
VP8DitherCombine8x8(dither, dst, bps);
}
static void DitherRow(VP8Decoder* const dec) {
int mb_x;
assert(dec->dither_);
for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
const VP8MBData* const data = ctx->mb_data_ + mb_x;
const int cache_id = ctx->id_;
const int uv_bps = dec->cache_uv_stride_;
if (data->dither_ >= MIN_DITHER_AMP) {
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_);
Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_);
}
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// This function is called after a row of macroblocks is finished decoding. // This function is called after a row of macroblocks is finished decoding.
// It also takes into account the following restrictions: // It also takes into account the following restrictions:
@ -403,35 +164,25 @@ static void DitherRow(VP8Decoder* const dec) {
static int FinishRow(VP8Decoder* const dec, VP8Io* const io) { static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
int ok = 1; int ok = 1;
const VP8ThreadContext* const ctx = &dec->thread_ctx_; const VP8ThreadContext* const ctx = &dec->thread_ctx_;
const int cache_id = ctx->id_;
const int extra_y_rows = kFilterExtraRows[dec->filter_type_]; const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
const int ysize = extra_y_rows * dec->cache_y_stride_; const int ysize = extra_y_rows * dec->cache_y_stride_;
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_; const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
const int y_offset = cache_id * 16 * dec->cache_y_stride_; const int y_offset = ctx->id_ * 16 * dec->cache_y_stride_;
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_; const int uv_offset = ctx->id_ * 8 * dec->cache_uv_stride_;
uint8_t* const ydst = dec->cache_y_ - ysize + y_offset; uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset; uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset; uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
const int mb_y = ctx->mb_y_; const int first_row = (ctx->mb_y_ == 0);
const int is_first_row = (mb_y == 0); const int last_row = (ctx->mb_y_ >= dec->br_mb_y_ - 1);
const int is_last_row = (mb_y >= dec->br_mb_y_ - 1); int y_start = MACROBLOCK_VPOS(ctx->mb_y_);
int y_end = MACROBLOCK_VPOS(ctx->mb_y_ + 1);
if (dec->mt_method_ == 2) {
ReconstructRow(dec, ctx);
}
if (ctx->filter_row_) { if (ctx->filter_row_) {
FilterRow(dec); FilterRow(dec);
} }
if (dec->dither_) { if (io->put) {
DitherRow(dec); if (!first_row) {
}
if (io->put != NULL) {
int y_start = MACROBLOCK_VPOS(mb_y);
int y_end = MACROBLOCK_VPOS(mb_y + 1);
if (!is_first_row) {
y_start -= extra_y_rows; y_start -= extra_y_rows;
io->y = ydst; io->y = ydst;
io->u = udst; io->u = udst;
@ -442,7 +193,7 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
io->v = dec->cache_v_ + uv_offset; io->v = dec->cache_v_ + uv_offset;
} }
if (!is_last_row) { if (!last_row) {
y_end -= extra_y_rows; y_end -= extra_y_rows;
} }
if (y_end > io->crop_bottom) { if (y_end > io->crop_bottom) {
@ -450,9 +201,12 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
} }
io->a = NULL; io->a = NULL;
if (dec->alpha_data_ != NULL && y_start < y_end) { if (dec->alpha_data_ != NULL && y_start < y_end) {
// TODO(skal): testing presence of alpha with dec->alpha_data_ is not a // TODO(skal): several things to correct here:
// good idea. // * testing presence of alpha with dec->alpha_data_ is not a good idea
io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start); // * we're actually decompressing the full plane only once. It should be
// more obvious from signature.
// * we could free alpha_data_ right after this call, but we don't own.
io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
if (io->a == NULL) { if (io->a == NULL) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"Could not decode alpha data."); "Could not decode alpha data.");
@ -483,8 +237,8 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
} }
} }
// rotate top samples if needed // rotate top samples if needed
if (cache_id + 1 == dec->num_caches_) { if (ctx->id_ + 1 == dec->num_caches_) {
if (!is_last_row) { if (!last_row) {
memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize); memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize); memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize); memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
@ -501,40 +255,27 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) { int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
int ok = 1; int ok = 1;
VP8ThreadContext* const ctx = &dec->thread_ctx_; VP8ThreadContext* const ctx = &dec->thread_ctx_;
const int filter_row = if (!dec->use_threads_) {
(dec->filter_type_ > 0) &&
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
if (dec->mt_method_ == 0) {
// ctx->id_ and ctx->f_info_ are already set // ctx->id_ and ctx->f_info_ are already set
ctx->mb_y_ = dec->mb_y_; ctx->mb_y_ = dec->mb_y_;
ctx->filter_row_ = filter_row; ctx->filter_row_ = dec->filter_row_;
ReconstructRow(dec, ctx);
ok = FinishRow(dec, io); ok = FinishRow(dec, io);
} else { } else {
WebPWorker* const worker = &dec->worker_; WebPWorker* const worker = &dec->worker_;
// Finish previous job *before* updating context // Finish previous job *before* updating context
ok &= WebPGetWorkerInterface()->Sync(worker); ok &= WebPWorkerSync(worker);
assert(worker->status_ == OK); assert(worker->status_ == OK);
if (ok) { // spawn a new deblocking/output job if (ok) { // spawn a new deblocking/output job
ctx->io_ = *io; ctx->io_ = *io;
ctx->id_ = dec->cache_id_; ctx->id_ = dec->cache_id_;
ctx->mb_y_ = dec->mb_y_; ctx->mb_y_ = dec->mb_y_;
ctx->filter_row_ = filter_row; ctx->filter_row_ = dec->filter_row_;
if (dec->mt_method_ == 2) { // swap macroblock data if (ctx->filter_row_) { // just swap filter info
VP8MBData* const tmp = ctx->mb_data_;
ctx->mb_data_ = dec->mb_data_;
dec->mb_data_ = tmp;
} else {
// perform reconstruction directly in main thread
ReconstructRow(dec, ctx);
}
if (filter_row) { // swap filter info
VP8FInfo* const tmp = ctx->f_info_; VP8FInfo* const tmp = ctx->f_info_;
ctx->f_info_ = dec->f_info_; ctx->f_info_ = dec->f_info_;
dec->f_info_ = tmp; dec->f_info_ = tmp;
} }
// (reconstruct)+filter in parallel WebPWorkerLaunch(worker);
WebPGetWorkerInterface()->Launch(worker);
if (++dec->cache_id_ == dec->num_caches_) { if (++dec->cache_id_ == dec->num_caches_) {
dec->cache_id_ = 0; dec->cache_id_ = 0;
} }
@ -548,8 +289,8 @@ int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) { VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
// Call setup() first. This may trigger additional decoding features on 'io'. // Call setup() first. This may trigger additional decoding features on 'io'.
// Note: Afterward, we must call teardown() no matter what. // Note: Afterward, we must call teardown() not matter what.
if (io->setup != NULL && !io->setup(io)) { if (io->setup && !io->setup(io)) {
VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed"); VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
return dec->status_; return dec->status_;
} }
@ -562,7 +303,7 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
// Define the area where we can skip in-loop filtering, in case of cropping. // Define the area where we can skip in-loop filtering, in case of cropping.
// //
// 'Simple' filter reads two luma samples outside of the macroblock // 'Simple' filter reads two luma samples outside of the macroblock and
// and filters one. It doesn't filter the chroma samples. Hence, we can // and filters one. It doesn't filter the chroma samples. Hence, we can
// avoid doing the in-loop filtering before crop_top/crop_left position. // avoid doing the in-loop filtering before crop_top/crop_left position.
// For the 'Complex' filter, 3 samples are read and up to 3 are filtered. // For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
@ -603,11 +344,11 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) { int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
int ok = 1; int ok = 1;
if (dec->mt_method_ > 0) { if (dec->use_threads_) {
ok = WebPGetWorkerInterface()->Sync(&dec->worker_); ok = WebPWorkerSync(&dec->worker_);
} }
if (io->teardown != NULL) { if (io->teardown) {
io->teardown(io); io->teardown(io);
} }
return ok; return ok;
@ -643,9 +384,9 @@ int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
// Initialize multi/single-thread worker // Initialize multi/single-thread worker
static int InitThreadContext(VP8Decoder* const dec) { static int InitThreadContext(VP8Decoder* const dec) {
dec->cache_id_ = 0; dec->cache_id_ = 0;
if (dec->mt_method_ > 0) { if (dec->use_threads_) {
WebPWorker* const worker = &dec->worker_; WebPWorker* const worker = &dec->worker_;
if (!WebPGetWorkerInterface()->Reset(worker)) { if (!WebPWorkerReset(worker)) {
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
"thread initialization failed."); "thread initialization failed.");
} }
@ -660,28 +401,6 @@ static int InitThreadContext(VP8Decoder* const dec) {
return 1; return 1;
} }
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
const WebPHeaderStructure* const headers,
int width, int height) {
if (options == NULL || options->use_threads == 0) {
return 0;
}
(void)headers;
(void)width;
(void)height;
assert(headers == NULL || !headers->is_lossless);
#if defined(WEBP_USE_THREAD)
if (width < MIN_WIDTH_FOR_THREADS) return 0;
// TODO(skal): tune the heuristic further
#if 0
if (height < 2 * width) return 2;
#endif
return 2;
#else // !WEBP_USE_THREAD
return 0;
#endif
}
#undef MT_CACHE_LINES #undef MT_CACHE_LINES
#undef ST_CACHE_LINES #undef ST_CACHE_LINES
@ -693,15 +412,14 @@ static int AllocateMemory(VP8Decoder* const dec) {
const int mb_w = dec->mb_w_; const int mb_w = dec->mb_w_;
// Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise. // Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t); const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
const size_t top_size = sizeof(VP8TopSamples) * mb_w; const size_t top_size = (16 + 8 + 8) * mb_w;
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB); const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
const size_t f_info_size = const size_t f_info_size =
(dec->filter_type_ > 0) ? (dec->filter_type_ > 0) ?
mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo) mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
: 0; : 0;
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_); const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
const size_t mb_data_size = const size_t coeffs_size = 384 * sizeof(*dec->coeffs_);
(dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_);
const size_t cache_height = (16 * num_caches const size_t cache_height = (16 * num_caches
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2; + kFilterExtraRows[dec->filter_type_]) * 3 / 2;
const size_t cache_size = top_size * cache_height; const size_t cache_size = top_size * cache_height;
@ -710,20 +428,20 @@ static int AllocateMemory(VP8Decoder* const dec) {
(uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL; (uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
const uint64_t needed = (uint64_t)intra_pred_mode_size const uint64_t needed = (uint64_t)intra_pred_mode_size
+ top_size + mb_info_size + f_info_size + top_size + mb_info_size + f_info_size
+ yuv_size + mb_data_size + yuv_size + coeffs_size
+ cache_size + alpha_size + WEBP_ALIGN_CST; + cache_size + alpha_size + ALIGN_MASK;
uint8_t* mem; uint8_t* mem;
if (needed != (size_t)needed) return 0; // check for overflow if (needed != (size_t)needed) return 0; // check for overflow
if (needed > dec->mem_size_) { if (needed > dec->mem_size_) {
WebPSafeFree(dec->mem_); free(dec->mem_);
dec->mem_size_ = 0; dec->mem_size_ = 0;
dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t)); dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
if (dec->mem_ == NULL) { if (dec->mem_ == NULL) {
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
"no memory during frame initialization."); "no memory during frame initialization.");
} }
// down-cast is ok, thanks to WebPSafeMalloc() above. // down-cast is ok, thanks to WebPSafeAlloc() above.
dec->mem_size_ = (size_t)needed; dec->mem_size_ = (size_t)needed;
} }
@ -731,8 +449,12 @@ static int AllocateMemory(VP8Decoder* const dec) {
dec->intra_t_ = (uint8_t*)mem; dec->intra_t_ = (uint8_t*)mem;
mem += intra_pred_mode_size; mem += intra_pred_mode_size;
dec->yuv_t_ = (VP8TopSamples*)mem; dec->y_t_ = (uint8_t*)mem;
mem += top_size; mem += 16 * mb_w;
dec->u_t_ = (uint8_t*)mem;
mem += 8 * mb_w;
dec->v_t_ = (uint8_t*)mem;
mem += 8 * mb_w;
dec->mb_info_ = ((VP8MB*)mem) + 1; dec->mb_info_ = ((VP8MB*)mem) + 1;
mem += mb_info_size; mem += mb_info_size;
@ -741,24 +463,20 @@ static int AllocateMemory(VP8Decoder* const dec) {
mem += f_info_size; mem += f_info_size;
dec->thread_ctx_.id_ = 0; dec->thread_ctx_.id_ = 0;
dec->thread_ctx_.f_info_ = dec->f_info_; dec->thread_ctx_.f_info_ = dec->f_info_;
if (dec->mt_method_ > 0) { if (dec->use_threads_) {
// secondary cache line. The deblocking process need to make use of the // secondary cache line. The deblocking process need to make use of the
// filtering strength from previous macroblock row, while the new ones // filtering strength from previous macroblock row, while the new ones
// are being decoded in parallel. We'll just swap the pointers. // are being decoded in parallel. We'll just swap the pointers.
dec->thread_ctx_.f_info_ += mb_w; dec->thread_ctx_.f_info_ += mb_w;
} }
mem = (uint8_t*)WEBP_ALIGN(mem); mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
assert((yuv_size & WEBP_ALIGN_CST) == 0); assert((yuv_size & ALIGN_MASK) == 0);
dec->yuv_b_ = (uint8_t*)mem; dec->yuv_b_ = (uint8_t*)mem;
mem += yuv_size; mem += yuv_size;
dec->mb_data_ = (VP8MBData*)mem; dec->coeffs_ = (int16_t*)mem;
dec->thread_ctx_.mb_data_ = (VP8MBData*)mem; mem += coeffs_size;
if (dec->mt_method_ == 2) {
dec->thread_ctx_.mb_data_ += mb_w;
}
mem += mb_data_size;
dec->cache_y_stride_ = 16 * mb_w; dec->cache_y_stride_ = 16 * mb_w;
dec->cache_uv_stride_ = 8 * mb_w; dec->cache_uv_stride_ = 8 * mb_w;
@ -780,9 +498,8 @@ static int AllocateMemory(VP8Decoder* const dec) {
mem += alpha_size; mem += alpha_size;
assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_); assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_);
// note: left/top-info is initialized once for all. // note: left-info is initialized once for all.
memset(dec->mb_info_ - 1, 0, mb_info_size); memset(dec->mb_info_ - 1, 0, mb_info_size);
VP8InitScanline(dec); // initialize left too.
// initialize top // initialize top
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size); memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
@ -801,7 +518,7 @@ static void InitIo(VP8Decoder* const dec, VP8Io* io) {
io->a = NULL; io->a = NULL;
} }
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) { int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_. if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
if (!AllocateMemory(dec)) return 0; if (!AllocateMemory(dec)) return 0;
InitIo(dec, io); InitIo(dec, io);
@ -810,3 +527,168 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Main reconstruction function.
static const int kScan[16] = {
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
};
static WEBP_INLINE int CheckMode(VP8Decoder* const dec, int mode) {
if (mode == B_DC_PRED) {
if (dec->mb_x_ == 0) {
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
} else {
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
}
}
return mode;
}
static WEBP_INLINE void Copy32b(uint8_t* dst, uint8_t* src) {
*(uint32_t*)dst = *(uint32_t*)src;
}
void VP8ReconstructBlock(VP8Decoder* const dec) {
int j;
uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
// Rotate in the left samples from previously decoded block. We move four
// pixels at a time for alignment reason, and because of in-loop filter.
if (dec->mb_x_ > 0) {
for (j = -1; j < 16; ++j) {
Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
}
for (j = -1; j < 8; ++j) {
Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
}
} else {
for (j = 0; j < 16; ++j) {
y_dst[j * BPS - 1] = 129;
}
for (j = 0; j < 8; ++j) {
u_dst[j * BPS - 1] = 129;
v_dst[j * BPS - 1] = 129;
}
// Init top-left sample on left column too
if (dec->mb_y_ > 0) {
y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
}
}
{
// bring top samples into the cache
uint8_t* const top_y = dec->y_t_ + dec->mb_x_ * 16;
uint8_t* const top_u = dec->u_t_ + dec->mb_x_ * 8;
uint8_t* const top_v = dec->v_t_ + dec->mb_x_ * 8;
const int16_t* coeffs = dec->coeffs_;
int n;
if (dec->mb_y_ > 0) {
memcpy(y_dst - BPS, top_y, 16);
memcpy(u_dst - BPS, top_u, 8);
memcpy(v_dst - BPS, top_v, 8);
} else if (dec->mb_x_ == 0) {
// we only need to do this init once at block (0,0).
// Afterward, it remains valid for the whole topmost row.
memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
memset(u_dst - BPS - 1, 127, 8 + 1);
memset(v_dst - BPS - 1, 127, 8 + 1);
}
// predict and add residuals
if (dec->is_i4x4_) { // 4x4
uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
if (dec->mb_y_ > 0) {
if (dec->mb_x_ >= dec->mb_w_ - 1) { // on rightmost border
top_right[0] = top_y[15] * 0x01010101u;
} else {
memcpy(top_right, top_y + 16, sizeof(*top_right));
}
}
// replicate the top-right pixels below
top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
// predict and add residues for all 4x4 blocks in turn.
for (n = 0; n < 16; n++) {
uint8_t* const dst = y_dst + kScan[n];
VP8PredLuma4[dec->imodes_[n]](dst);
if (dec->non_zero_ac_ & (1 << n)) {
VP8Transform(coeffs + n * 16, dst, 0);
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
VP8TransformDC(coeffs + n * 16, dst);
}
}
} else { // 16x16
const int pred_func = CheckMode(dec, dec->imodes_[0]);
VP8PredLuma16[pred_func](y_dst);
if (dec->non_zero_) {
for (n = 0; n < 16; n++) {
uint8_t* const dst = y_dst + kScan[n];
if (dec->non_zero_ac_ & (1 << n)) {
VP8Transform(coeffs + n * 16, dst, 0);
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
VP8TransformDC(coeffs + n * 16, dst);
}
}
}
}
{
// Chroma
const int pred_func = CheckMode(dec, dec->uvmode_);
VP8PredChroma8[pred_func](u_dst);
VP8PredChroma8[pred_func](v_dst);
if (dec->non_zero_ & 0x0f0000) { // chroma-U
const int16_t* const u_coeffs = dec->coeffs_ + 16 * 16;
if (dec->non_zero_ac_ & 0x0f0000) {
VP8TransformUV(u_coeffs, u_dst);
} else {
VP8TransformDCUV(u_coeffs, u_dst);
}
}
if (dec->non_zero_ & 0xf00000) { // chroma-V
const int16_t* const v_coeffs = dec->coeffs_ + 20 * 16;
if (dec->non_zero_ac_ & 0xf00000) {
VP8TransformUV(v_coeffs, v_dst);
} else {
VP8TransformDCUV(v_coeffs, v_dst);
}
}
// stash away top samples for next block
if (dec->mb_y_ < dec->mb_h_ - 1) {
memcpy(top_y, y_dst + 15 * BPS, 16);
memcpy(top_u, u_dst + 7 * BPS, 8);
memcpy(top_v, v_dst + 7 * BPS, 8);
}
}
}
// Transfer reconstructed samples from yuv_b_ cache to final destination.
{
const int y_offset = dec->cache_id_ * 16 * dec->cache_y_stride_;
const int uv_offset = dec->cache_id_ * 8 * dec->cache_uv_stride_;
uint8_t* const y_out = dec->cache_y_ + dec->mb_x_ * 16 + y_offset;
uint8_t* const u_out = dec->cache_u_ + dec->mb_x_ * 8 + uv_offset;
uint8_t* const v_out = dec->cache_v_ + dec->mb_x_ * 8 + uv_offset;
for (j = 0; j < 16; ++j) {
memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16);
}
for (j = 0; j < 8; ++j) {
memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8);
memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8);
}
}
}
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -15,11 +15,14 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "./alphai_dec.h" #include "./webpi.h"
#include "./webpi_dec.h" #include "./vp8i.h"
#include "./vp8i_dec.h"
#include "../utils/utils.h" #include "../utils/utils.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
// In append mode, buffer allocations increase as multiples of this value. // In append mode, buffer allocations increase as multiples of this value.
// Needs to be a power of 2. // Needs to be a power of 2.
#define CHUNK_SIZE 4096 #define CHUNK_SIZE 4096
@ -28,13 +31,11 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Data structures for memory and states // Data structures for memory and states
// Decoding states. State normally flows as: // Decoding states. State normally flows like HEADER->PARTS0->DATA->DONE.
// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and
// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.
// If there is any error the decoder goes into state ERROR. // If there is any error the decoder goes into state ERROR.
typedef enum { typedef enum {
STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk. STATE_PRE_VP8, // All data before that of the first VP8 chunk.
STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk). STATE_VP8_FRAME_HEADER, // For VP8 Frame header (within VP8 chunk).
STATE_VP8_PARTS0, STATE_VP8_PARTS0,
STATE_VP8_DATA, STATE_VP8_DATA,
STATE_VP8L_HEADER, STATE_VP8L_HEADER,
@ -70,24 +71,30 @@ struct WebPIDecoder {
VP8Io io_; VP8Io io_;
MemBuffer mem_; // input memory buffer. MemBuffer mem_; // input memory buffer.
WebPDecBuffer output_; // output buffer (when no external one is supplied, WebPDecBuffer output_; // output buffer (when no external one is supplied)
// or if the external one has slow-memory)
WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually.
size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header. size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
int last_mb_y_; // last row reached for intra-mode decoding
}; };
// MB context to restore in case VP8DecodeMB() fails // MB context to restore in case VP8DecodeMB() fails
typedef struct { typedef struct {
VP8MB left_; VP8MB left_;
VP8MB info_; VP8MB info_;
uint8_t intra_t_[4];
uint8_t intra_l_[4];
VP8BitReader br_;
VP8BitReader token_br_; VP8BitReader token_br_;
} MBContext; } MBContext;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// MemBuffer: incoming data handling // MemBuffer: incoming data handling
static void RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {
if (br->buf_ != NULL) {
br->buf_ += offset;
br->buf_end_ += offset;
}
}
static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) { static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
return (mem->end_ - mem->start_); return (mem->end_ - mem->start_);
} }
@ -95,7 +102,7 @@ static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
// Check if we need to preserve the compressed alpha data, as it may not have // Check if we need to preserve the compressed alpha data, as it may not have
// been decoded yet. // been decoded yet.
static int NeedCompressedAlpha(const WebPIDecoder* const idec) { static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
if (idec->state_ == STATE_WEBP_HEADER) { if (idec->state_ == STATE_PRE_VP8) {
// We haven't parsed the headers yet, so we don't know whether the image is // We haven't parsed the headers yet, so we don't know whether the image is
// lossy or lossless. This also means that we haven't parsed the ALPH chunk. // lossy or lossless. This also means that we haven't parsed the ALPH chunk.
return 0; return 0;
@ -104,7 +111,7 @@ static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
return 0; // ALPH chunk is not present for lossless images. return 0; // ALPH chunk is not present for lossless images.
} else { } else {
const VP8Decoder* const dec = (VP8Decoder*)idec->dec_; const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER. assert(dec != NULL); // Must be true as idec->state_ != STATE_PRE_VP8.
return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_; return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_;
} }
} }
@ -120,39 +127,21 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
if (idec->dec_ != NULL) { if (idec->dec_ != NULL) {
if (!idec->is_lossless_) { if (!idec->is_lossless_) {
VP8Decoder* const dec = (VP8Decoder*)idec->dec_; VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
const uint32_t last_part = dec->num_parts_minus_one_; const int last_part = dec->num_parts_ - 1;
if (offset != 0) { if (offset != 0) {
uint32_t p; int p;
for (p = 0; p <= last_part; ++p) { for (p = 0; p <= last_part; ++p) {
VP8RemapBitReader(dec->parts_ + p, offset); RemapBitReader(dec->parts_ + p, offset);
} }
// Remap partition #0 data pointer to new offset, but only in MAP // Remap partition #0 data pointer to new offset, but only in MAP
// mode (in APPEND mode, partition #0 is copied into a fixed memory). // mode (in APPEND mode, partition #0 is copied into a fixed memory).
if (mem->mode_ == MEM_MODE_MAP) { if (mem->mode_ == MEM_MODE_MAP) {
VP8RemapBitReader(&dec->br_, offset); RemapBitReader(&dec->br_, offset);
}
}
{
const uint8_t* const last_start = dec->parts_[last_part].buf_;
VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start,
mem->buf_ + mem->end_ - last_start);
}
if (NeedCompressedAlpha(idec)) {
ALPHDecoder* const alph_dec = dec->alph_dec_;
dec->alpha_data_ += offset;
if (alph_dec != NULL) {
if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {
VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;
assert(alph_vp8l_dec != NULL);
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);
VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,
dec->alpha_data_ + ALPHA_HEADER_LEN,
dec->alpha_data_size_ - ALPHA_HEADER_LEN);
} else { // alph_dec->method_ == ALPHA_NO_COMPRESSION
// Nothing special to do in this case.
}
} }
} }
assert(last_part >= 0);
dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
if (NeedCompressedAlpha(idec)) dec->alpha_data_ += offset;
} else { // Resize lossless bitreader } else { // Resize lossless bitreader
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem)); VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
@ -186,7 +175,7 @@ static int AppendToMemBuffer(WebPIDecoder* const idec,
(uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf)); (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
if (new_buf == NULL) return 0; if (new_buf == NULL) return 0;
memcpy(new_buf, old_base, current_size); memcpy(new_buf, old_base, current_size);
WebPSafeFree(mem->buf_); free(mem->buf_);
mem->buf_ = new_buf; mem->buf_ = new_buf;
mem->buf_size_ = (size_t)extra_size; mem->buf_size_ = (size_t)extra_size;
mem->start_ = new_mem_start; mem->start_ = new_mem_start;
@ -228,8 +217,8 @@ static void InitMemBuffer(MemBuffer* const mem) {
static void ClearMemBuffer(MemBuffer* const mem) { static void ClearMemBuffer(MemBuffer* const mem) {
assert(mem); assert(mem);
if (mem->mode_ == MEM_MODE_APPEND) { if (mem->mode_ == MEM_MODE_APPEND) {
WebPSafeFree(mem->buf_); free(mem->buf_);
WebPSafeFree((void*)mem->part0_buf_); free((void*)mem->part0_buf_);
} }
} }
@ -243,40 +232,35 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
return 1; return 1;
} }
// To be called last.
static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
const WebPDecoderOptions* const options = idec->params_.options;
WebPDecBuffer* const output = idec->params_.output;
idec->state_ = STATE_DONE;
if (options != NULL && options->flip) {
const VP8StatusCode status = WebPFlipBuffer(output);
if (status != VP8_STATUS_OK) return status;
}
if (idec->final_output_ != NULL) {
WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy
WebPFreeDecBuffer(&idec->output_);
*output = *idec->final_output_;
idec->final_output_ = NULL;
}
return VP8_STATUS_OK;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Macroblock-decoding contexts // Macroblock-decoding contexts
static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br, static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
MBContext* const context) { MBContext* const context) {
context->left_ = dec->mb_info_[-1]; const VP8BitReader* const br = &dec->br_;
context->info_ = dec->mb_info_[dec->mb_x_]; const VP8MB* const left = dec->mb_info_ - 1;
const VP8MB* const info = dec->mb_info_ + dec->mb_x_;
context->left_ = *left;
context->info_ = *info;
context->br_ = *br;
context->token_br_ = *token_br; context->token_br_ = *token_br;
memcpy(context->intra_t_, dec->intra_t_ + 4 * dec->mb_x_, 4);
memcpy(context->intra_l_, dec->intra_l_, 4);
} }
static void RestoreContext(const MBContext* context, VP8Decoder* const dec, static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
VP8BitReader* const token_br) { VP8BitReader* const token_br) {
dec->mb_info_[-1] = context->left_; VP8BitReader* const br = &dec->br_;
dec->mb_info_[dec->mb_x_] = context->info_; VP8MB* const left = dec->mb_info_ - 1;
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
*left = context->left_;
*info = context->info_;
*br = context->br_;
*token_br = context->token_br_; *token_br = context->token_br_;
memcpy(dec->intra_t_ + 4 * dec->mb_x_, context->intra_t_, 4);
memcpy(dec->intra_l_, context->intra_l_, 4);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -284,7 +268,7 @@ static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) { static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
if (idec->state_ == STATE_VP8_DATA) { if (idec->state_ == STATE_VP8_DATA) {
VP8Io* const io = &idec->io_; VP8Io* const io = &idec->io_;
if (io->teardown != NULL) { if (io->teardown) {
io->teardown(io); io->teardown(io);
} }
} }
@ -312,7 +296,6 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
headers.data = data; headers.data = data;
headers.data_size = curr_size; headers.data_size = curr_size;
headers.have_all_data = 0;
status = WebPParseHeaders(&headers); status = WebPParseHeaders(&headers);
if (status == VP8_STATUS_NOT_ENOUGH_DATA) { if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
@ -328,9 +311,15 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
return VP8_STATUS_OUT_OF_MEMORY; return VP8_STATUS_OUT_OF_MEMORY;
} }
idec->dec_ = dec; idec->dec_ = dec;
#ifdef WEBP_USE_THREAD
dec->use_threads_ = (idec->params_.options != NULL) &&
(idec->params_.options->use_threads > 0);
#else
dec->use_threads_ = 0;
#endif
dec->alpha_data_ = headers.alpha_data; dec->alpha_data_ = headers.alpha_data;
dec->alpha_data_size_ = headers.alpha_data_size; dec->alpha_data_size_ = headers.alpha_data_size;
ChangeState(idec, STATE_VP8_HEADER, headers.offset); ChangeState(idec, STATE_VP8_FRAME_HEADER, headers.offset);
} else { } else {
VP8LDecoder* const dec = VP8LNew(); VP8LDecoder* const dec = VP8LNew();
if (dec == NULL) { if (dec == NULL) {
@ -345,14 +334,13 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) { static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_; const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
const size_t curr_size = MemDataSize(&idec->mem_); const size_t curr_size = MemDataSize(&idec->mem_);
int width, height;
uint32_t bits; uint32_t bits;
if (curr_size < VP8_FRAME_HEADER_SIZE) { if (curr_size < VP8_FRAME_HEADER_SIZE) {
// Not enough data bytes to extract VP8 Frame Header. // Not enough data bytes to extract VP8 Frame Header.
return VP8_STATUS_SUSPENDED; return VP8_STATUS_SUSPENDED;
} }
if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) { if (!VP8GetInfo(data, curr_size, idec->chunk_size_, NULL, NULL)) {
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
} }
@ -366,32 +354,30 @@ static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
} }
// Partition #0 // Partition #0
static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) { static int CopyParts0Data(WebPIDecoder* const idec) {
VP8Decoder* const dec = (VP8Decoder*)idec->dec_; VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
VP8BitReader* const br = &dec->br_; VP8BitReader* const br = &dec->br_;
const size_t part_size = br->buf_end_ - br->buf_; const size_t psize = br->buf_end_ - br->buf_;
MemBuffer* const mem = &idec->mem_; MemBuffer* const mem = &idec->mem_;
assert(!idec->is_lossless_); assert(!idec->is_lossless_);
assert(mem->part0_buf_ == NULL); assert(mem->part0_buf_ == NULL);
// the following is a format limitation, no need for runtime check: assert(psize > 0);
assert(part_size <= mem->part0_size_); assert(psize <= mem->part0_size_); // Format limit: no need for runtime check
if (part_size == 0) { // can't have zero-size partition #0
return VP8_STATUS_BITSTREAM_ERROR;
}
if (mem->mode_ == MEM_MODE_APPEND) { if (mem->mode_ == MEM_MODE_APPEND) {
// We copy and grab ownership of the partition #0 data. // We copy and grab ownership of the partition #0 data.
uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size); uint8_t* const part0_buf = (uint8_t*)malloc(psize);
if (part0_buf == NULL) { if (part0_buf == NULL) {
return VP8_STATUS_OUT_OF_MEMORY; return 0;
} }
memcpy(part0_buf, br->buf_, part_size); memcpy(part0_buf, br->buf_, psize);
mem->part0_buf_ = part0_buf; mem->part0_buf_ = part0_buf;
VP8BitReaderSetBuffer(br, part0_buf, part_size); br->buf_ = part0_buf;
br->buf_end_ = part0_buf + psize;
} else { } else {
// Else: just keep pointers to the partition #0's data in dec_->br_. // Else: just keep pointers to the partition #0's data in dec_->br_.
} }
mem->start_ += part_size; mem->start_ += psize;
return VP8_STATUS_OK; return 1;
} }
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) { static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
@ -421,14 +407,9 @@ static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
if (dec->status_ != VP8_STATUS_OK) { if (dec->status_ != VP8_STATUS_OK) {
return IDecError(idec, dec->status_); return IDecError(idec, dec->status_);
} }
// This change must be done before calling VP8InitFrame()
dec->mt_method_ = VP8GetThreadMethod(params->options, NULL,
io->width, io->height);
VP8InitDithering(params->options, dec);
dec->status_ = CopyParts0Data(idec); if (!CopyParts0Data(idec)) {
if (dec->status_ != VP8_STATUS_OK) { return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY);
return IDecError(idec, dec->status_);
} }
// Finish setting up the decoding parameters. Will call io->setup(). // Finish setting up the decoding parameters. Will call io->setup().
@ -452,53 +433,49 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
VP8Io* const io = &idec->io_; VP8Io* const io = &idec->io_;
assert(dec->ready_); assert(dec->ready_);
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) { for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
if (idec->last_mb_y_ != dec->mb_y_) { VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
if (!VP8ParseIntraModeRow(&dec->br_, dec)) { if (dec->mb_x_ == 0) {
// note: normally, error shouldn't occur since we already have the whole VP8InitScanline(dec);
// partition0 available here in DecodeRemaining(). Reaching EOF while
// reading intra modes really means a BITSTREAM_ERROR.
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
}
idec->last_mb_y_ = dec->mb_y_;
} }
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { for (; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
VP8BitReader* const token_br =
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
MBContext context; MBContext context;
SaveContext(dec, token_br, &context); SaveContext(dec, token_br, &context);
if (!VP8DecodeMB(dec, token_br)) { if (!VP8DecodeMB(dec, token_br)) {
RestoreContext(&context, dec, token_br);
// We shouldn't fail when MAX_MB data was available // We shouldn't fail when MAX_MB data was available
if (dec->num_parts_minus_one_ == 0 && if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
} }
RestoreContext(&context, dec, token_br);
return VP8_STATUS_SUSPENDED; return VP8_STATUS_SUSPENDED;
} }
// Reconstruct and emit samples.
VP8ReconstructBlock(dec);
// Release buffer only if there is only one partition // Release buffer only if there is only one partition
if (dec->num_parts_minus_one_ == 0) { if (dec->num_parts_ == 1) {
idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_; idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
assert(idec->mem_.start_ <= idec->mem_.end_); assert(idec->mem_.start_ <= idec->mem_.end_);
} }
} }
VP8InitScanline(dec); // Prepare for next scanline
// Reconstruct, filter and emit the row.
if (!VP8ProcessRow(dec, io)) { if (!VP8ProcessRow(dec, io)) {
return IDecError(idec, VP8_STATUS_USER_ABORT); return IDecError(idec, VP8_STATUS_USER_ABORT);
} }
dec->mb_x_ = 0;
} }
// Synchronize the thread and check for errors. // Synchronize the thread and check for errors.
if (!VP8ExitCritical(dec, io)) { if (!VP8ExitCritical(dec, io)) {
return IDecError(idec, VP8_STATUS_USER_ABORT); return IDecError(idec, VP8_STATUS_USER_ABORT);
} }
dec->ready_ = 0; dec->ready_ = 0;
return FinishDecoding(idec); idec->state_ = STATE_DONE;
return VP8_STATUS_OK;
} }
static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec, static int ErrorStatusLossless(WebPIDecoder* const idec, VP8StatusCode status) {
VP8StatusCode status) {
if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) { if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
return VP8_STATUS_SUSPENDED; return VP8_STATUS_SUSPENDED;
} }
@ -515,15 +492,9 @@ static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
// Wait until there's enough data for decoding header. // Wait until there's enough data for decoding header.
if (curr_size < (idec->chunk_size_ >> 3)) { if (curr_size < (idec->chunk_size_ >> 3)) {
dec->status_ = VP8_STATUS_SUSPENDED; return VP8_STATUS_SUSPENDED;
return ErrorStatusLossless(idec, dec->status_);
} }
if (!VP8LDecodeHeader(dec, io)) { if (!VP8LDecodeHeader(dec, io)) {
if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR &&
curr_size < idec->chunk_size_) {
dec->status_ = VP8_STATUS_SUSPENDED;
}
return ErrorStatusLossless(idec, dec->status_); return ErrorStatusLossless(idec, dec->status_);
} }
// Allocate/verify output buffer now. // Allocate/verify output buffer now.
@ -542,29 +513,33 @@ static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
const size_t curr_size = MemDataSize(&idec->mem_); const size_t curr_size = MemDataSize(&idec->mem_);
assert(idec->is_lossless_); assert(idec->is_lossless_);
// Switch to incremental decoding if we don't have all the bytes available. // At present Lossless decoder can't decode image incrementally. So wait till
dec->incremental_ = (curr_size < idec->chunk_size_); // all the image data is aggregated before image can be decoded.
if (curr_size < idec->chunk_size_) {
return VP8_STATUS_SUSPENDED;
}
if (!VP8LDecodeImage(dec)) { if (!VP8LDecodeImage(dec)) {
return ErrorStatusLossless(idec, dec->status_); return ErrorStatusLossless(idec, dec->status_);
} }
assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED);
return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_ idec->state_ = STATE_DONE;
: FinishDecoding(idec);
return VP8_STATUS_OK;
} }
// Main decoding loop // Main decoding loop
static VP8StatusCode IDecode(WebPIDecoder* idec) { static VP8StatusCode IDecode(WebPIDecoder* idec) {
VP8StatusCode status = VP8_STATUS_SUSPENDED; VP8StatusCode status = VP8_STATUS_SUSPENDED;
if (idec->state_ == STATE_WEBP_HEADER) { if (idec->state_ == STATE_PRE_VP8) {
status = DecodeWebPHeaders(idec); status = DecodeWebPHeaders(idec);
} else { } else {
if (idec->dec_ == NULL) { if (idec->dec_ == NULL) {
return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder. return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
} }
} }
if (idec->state_ == STATE_VP8_HEADER) { if (idec->state_ == STATE_VP8_FRAME_HEADER) {
status = DecodeVP8FrameHeader(idec); status = DecodeVP8FrameHeader(idec);
} }
if (idec->state_ == STATE_VP8_PARTS0) { if (idec->state_ == STATE_VP8_PARTS0) {
@ -583,65 +558,40 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Internal constructor // Public functions
static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
const WebPBitstreamFeatures* const features) { WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(*idec));
WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
if (idec == NULL) { if (idec == NULL) {
return NULL; return NULL;
} }
idec->state_ = STATE_WEBP_HEADER; idec->state_ = STATE_PRE_VP8;
idec->chunk_size_ = 0; idec->chunk_size_ = 0;
idec->last_mb_y_ = -1;
InitMemBuffer(&idec->mem_); InitMemBuffer(&idec->mem_);
WebPInitDecBuffer(&idec->output_); WebPInitDecBuffer(&idec->output_);
VP8InitIo(&idec->io_); VP8InitIo(&idec->io_);
WebPResetDecParams(&idec->params_); WebPResetDecParams(&idec->params_);
if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { idec->params_.output = output_buffer ? output_buffer : &idec->output_;
idec->params_.output = &idec->output_;
idec->final_output_ = output_buffer;
if (output_buffer != NULL) {
idec->params_.output->colorspace = output_buffer->colorspace;
}
} else {
idec->params_.output = output_buffer;
idec->final_output_ = NULL;
}
WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions. WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
return idec; return idec;
} }
//------------------------------------------------------------------------------
// Public functions
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
return NewDecoder(output_buffer, NULL);
}
WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
WebPDecoderConfig* config) { WebPDecoderConfig* config) {
WebPIDecoder* idec; WebPIDecoder* idec;
WebPBitstreamFeatures tmp_features;
WebPBitstreamFeatures* const features =
(config == NULL) ? &tmp_features : &config->input;
memset(&tmp_features, 0, sizeof(tmp_features));
// Parse the bitstream's features, if requested: // Parse the bitstream's features, if requested:
if (data != NULL && data_size > 0) { if (data != NULL && data_size > 0 && config != NULL) {
if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) {
return NULL; return NULL;
} }
} }
// Create an instance of the incremental decoder // Create an instance of the incremental decoder
idec = (config != NULL) ? NewDecoder(&config->output, features) idec = WebPINewDecoder(config ? &config->output : NULL);
: NewDecoder(NULL, features);
if (idec == NULL) { if (idec == NULL) {
return NULL; return NULL;
} }
@ -658,16 +608,16 @@ void WebPIDelete(WebPIDecoder* idec) {
if (!idec->is_lossless_) { if (!idec->is_lossless_) {
if (idec->state_ == STATE_VP8_DATA) { if (idec->state_ == STATE_VP8_DATA) {
// Synchronize the thread, clean-up and check for errors. // Synchronize the thread, clean-up and check for errors.
VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); VP8ExitCritical(idec->dec_, &idec->io_);
} }
VP8Delete((VP8Decoder*)idec->dec_); VP8Delete(idec->dec_);
} else { } else {
VP8LDelete((VP8LDecoder*)idec->dec_); VP8LDelete(idec->dec_);
} }
} }
ClearMemBuffer(&idec->mem_); ClearMemBuffer(&idec->mem_);
WebPFreeDecBuffer(&idec->output_); WebPFreeDecBuffer(&idec->output_);
WebPSafeFree(idec); free(idec);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -675,11 +625,11 @@ void WebPIDelete(WebPIDecoder* idec) {
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
size_t output_buffer_size, int output_stride) { size_t output_buffer_size, int output_stride) {
const int is_external_memory = (output_buffer != NULL) ? 1 : 0; const int is_external_memory = (output_buffer != NULL);
WebPIDecoder* idec; WebPIDecoder* idec;
if (mode >= MODE_YUV) return NULL; if (mode >= MODE_YUV) return NULL;
if (is_external_memory == 0) { // Overwrite parameters to sane values. if (!is_external_memory) { // Overwrite parameters to sane values.
output_buffer_size = 0; output_buffer_size = 0;
output_stride = 0; output_stride = 0;
} else { // A buffer was passed. Validate the other params. } else { // A buffer was passed. Validate the other params.
@ -701,11 +651,11 @@ WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
uint8_t* u, size_t u_size, int u_stride, uint8_t* u, size_t u_size, int u_stride,
uint8_t* v, size_t v_size, int v_stride, uint8_t* v, size_t v_size, int v_stride,
uint8_t* a, size_t a_size, int a_stride) { uint8_t* a, size_t a_size, int a_stride) {
const int is_external_memory = (luma != NULL) ? 1 : 0; const int is_external_memory = (luma != NULL);
WebPIDecoder* idec; WebPIDecoder* idec;
WEBP_CSP_MODE colorspace; WEBP_CSP_MODE colorspace;
if (is_external_memory == 0) { // Overwrite parameters to sane values. if (!is_external_memory) { // Overwrite parameters to sane values.
luma_size = u_size = v_size = a_size = 0; luma_size = u_size = v_size = a_size = 0;
luma_stride = u_stride = v_stride = a_stride = 0; luma_stride = u_stride = v_stride = a_stride = 0;
u = v = a = NULL; u = v = a = NULL;
@ -813,9 +763,6 @@ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
if (idec->state_ <= STATE_VP8_PARTS0) { if (idec->state_ <= STATE_VP8_PARTS0) {
return NULL; return NULL;
} }
if (idec->final_output_ != NULL) {
return NULL; // not yet slow-copied
}
return idec->params_.output; return idec->params_.output;
} }
@ -825,7 +772,8 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
const WebPDecBuffer* const src = GetOutputBuffer(idec); const WebPDecBuffer* const src = GetOutputBuffer(idec);
if (left != NULL) *left = 0; if (left != NULL) *left = 0;
if (top != NULL) *top = 0; if (top != NULL) *top = 0;
if (src != NULL) { // TODO(skal): later include handling of rotations.
if (src) {
if (width != NULL) *width = src->width; if (width != NULL) *width = src->width;
if (height != NULL) *height = idec->params_.last_y; if (height != NULL) *height = idec->params_.last_y;
} else { } else {
@ -879,7 +827,7 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
VP8IoSetupHook setup, VP8IoSetupHook setup,
VP8IoTeardownHook teardown, VP8IoTeardownHook teardown,
void* user_data) { void* user_data) {
if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) { if (idec == NULL || idec->state_ > STATE_PRE_VP8) {
return 0; return 0;
} }
@ -890,3 +838,7 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
return 1; return 1;
} }
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -13,11 +13,14 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include "../dec/vp8i_dec.h" #include "../dec/vp8i.h"
#include "./webpi_dec.h" #include "./webpi.h"
#include "../dsp/dsp.h" #include "../dsp/dsp.h"
#include "../dsp/yuv.h" #include "../dsp/yuv.h"
#include "../utils/utils.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Main YUV<->RGB conversion functions // Main YUV<->RGB conversion functions
@ -45,16 +48,56 @@ static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
// Point-sampling U/V sampler. // Point-sampling U/V sampler.
static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) { static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
WebPDecBuffer* const output = p->output; WebPDecBuffer* output = p->output;
WebPRGBABuffer* const buf = &output->u.RGBA; const WebPRGBABuffer* const buf = &output->u.RGBA;
uint8_t* const dst = buf->rgba + io->mb_y * buf->stride; uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
WebPSamplerProcessPlane(io->y, io->y_stride, const uint8_t* y_src = io->y;
io->u, io->v, io->uv_stride, const uint8_t* u_src = io->u;
dst, buf->stride, io->mb_w, io->mb_h, const uint8_t* v_src = io->v;
WebPSamplers[output->colorspace]); const WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace];
const int mb_w = io->mb_w;
const int last = io->mb_h - 1;
int j;
for (j = 0; j < last; j += 2) {
sample(y_src, y_src + io->y_stride, u_src, v_src,
dst, dst + buf->stride, mb_w);
y_src += 2 * io->y_stride;
u_src += io->uv_stride;
v_src += io->uv_stride;
dst += 2 * buf->stride;
}
if (j == last) { // Just do the last line twice
sample(y_src, y_src, u_src, v_src, dst, dst, mb_w);
}
return io->mb_h; return io->mb_h;
} }
//------------------------------------------------------------------------------
// YUV444 -> RGB conversion
#if 0 // TODO(skal): this is for future rescaling.
static int EmitRGB(const VP8Io* const io, WebPDecParams* const p) {
WebPDecBuffer* output = p->output;
const WebPRGBABuffer* const buf = &output->u.RGBA;
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
const uint8_t* y_src = io->y;
const uint8_t* u_src = io->u;
const uint8_t* v_src = io->v;
const WebPYUV444Converter convert = WebPYUV444Converters[output->colorspace];
const int mb_w = io->mb_w;
const int last = io->mb_h;
int j;
for (j = 0; j < last; ++j) {
convert(y_src, u_src, v_src, dst, mb_w);
y_src += io->y_stride;
u_src += io->uv_stride;
v_src += io->uv_stride;
dst += buf->stride;
}
return io->mb_h;
}
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Fancy upsampling // Fancy upsampling
@ -76,7 +119,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
if (y == 0) { if (y == 0) {
// First line is special cased. We mirror the u/v samples at boundary. // First line is special cased. We mirror the u/v samples at boundary.
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w); upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
} else { } else {
// We can finish the left-over line from previous call. // We can finish the left-over line from previous call.
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v, upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
@ -119,24 +162,14 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static void FillAlphaPlane(uint8_t* dst, int w, int h, int stride) { static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
int j;
for (j = 0; j < h; ++j) {
memset(dst, 0xff, w * sizeof(*dst));
dst += stride;
}
}
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
int expected_num_lines_out) {
const uint8_t* alpha = io->a; const uint8_t* alpha = io->a;
const WebPYUVABuffer* const buf = &p->output->u.YUVA; const WebPYUVABuffer* const buf = &p->output->u.YUVA;
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
const int mb_h = io->mb_h; const int mb_h = io->mb_h;
uint8_t* dst = buf->a + io->mb_y * buf->a_stride; uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
int j; int j;
(void)expected_num_lines_out;
assert(expected_num_lines_out == mb_h);
if (alpha != NULL) { if (alpha != NULL) {
for (j = 0; j < mb_h; ++j) { for (j = 0; j < mb_h; ++j) {
memcpy(dst, alpha, mb_w * sizeof(*dst)); memcpy(dst, alpha, mb_w * sizeof(*dst));
@ -145,7 +178,10 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
} }
} else if (buf->a != NULL) { } else if (buf->a != NULL) {
// the user requested alpha, but there is none, set it to opaque. // the user requested alpha, but there is none, set it to opaque.
FillAlphaPlane(dst, mb_w, mb_h, buf->a_stride); for (j = 0; j < mb_h; ++j) {
memset(dst, 0xff, mb_w * sizeof(*dst));
dst += buf->a_stride;
}
} }
return 0; return 0;
} }
@ -176,8 +212,7 @@ static int GetAlphaSourceRow(const VP8Io* const io,
return start_y; return start_y;
} }
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p, static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
int expected_num_lines_out) {
const uint8_t* alpha = io->a; const uint8_t* alpha = io->a;
if (alpha != NULL) { if (alpha != NULL) {
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
@ -188,13 +223,21 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
int num_rows; int num_rows;
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3); uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w, uint32_t alpha_mask = 0xff;
num_rows, dst, buf->stride); int i, j;
(void)expected_num_lines_out;
assert(expected_num_lines_out == num_rows); for (j = 0; j < num_rows; ++j) {
// has_alpha is true if there's non-trivial alpha to premultiply with. for (i = 0; i < mb_w; ++i) {
if (has_alpha && WebPIsPremultipliedMode(colorspace)) { const uint32_t alpha_value = alpha[i];
dst[4 * i] = alpha_value;
alpha_mask &= alpha_value;
}
alpha += io->width;
dst += buf->stride;
}
// alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
WebPApplyAlphaMultiply(base_rgba, alpha_first, WebPApplyAlphaMultiply(base_rgba, alpha_first,
mb_w, num_rows, buf->stride); mb_w, num_rows, buf->stride);
} }
@ -202,8 +245,7 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
return 0; return 0;
} }
static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p, static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
int expected_num_lines_out) {
const uint8_t* alpha = io->a; const uint8_t* alpha = io->a;
if (alpha != NULL) { if (alpha != NULL) {
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
@ -212,13 +254,10 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
int num_rows; int num_rows;
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
#ifdef WEBP_SWAP_16BIT_CSP
uint8_t* alpha_dst = base_rgba;
#else
uint8_t* alpha_dst = base_rgba + 1; uint8_t* alpha_dst = base_rgba + 1;
#endif
uint32_t alpha_mask = 0x0f; uint32_t alpha_mask = 0x0f;
int i, j; int i, j;
for (j = 0; j < num_rows; ++j) { for (j = 0; j < num_rows; ++j) {
for (i = 0; i < mb_w; ++i) { for (i = 0; i < mb_w; ++i) {
// Fill in the alpha value (converted to 4 bits). // Fill in the alpha value (converted to 4 bits).
@ -229,8 +268,6 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
alpha += io->width; alpha += io->width;
alpha_dst += buf->stride; alpha_dst += buf->stride;
} }
(void)expected_num_lines_out;
assert(expected_num_lines_out == num_rows);
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) { if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride); WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
} }
@ -256,39 +293,15 @@ static int Rescale(const uint8_t* src, int src_stride,
static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) { static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
const int mb_h = io->mb_h; const int mb_h = io->mb_h;
const int uv_mb_h = (mb_h + 1) >> 1; const int uv_mb_h = (mb_h + 1) >> 1;
WebPRescaler* const scaler = p->scaler_y; const int num_lines_out = Rescale(io->y, io->y_stride, mb_h, &p->scaler_y);
int num_lines_out = 0; Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u);
if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) { Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
// Before rescaling, we premultiply the luma directly into the io->y
// internal buffer. This is OK since these samples are not used for
// intra-prediction (the top samples are saved in cache_y_/u_/v_).
// But we need to cast the const away, though.
WebPMultRows((uint8_t*)io->y, io->y_stride,
io->a, io->width, io->mb_w, mb_h, 0);
}
num_lines_out = Rescale(io->y, io->y_stride, mb_h, scaler);
Rescale(io->u, io->uv_stride, uv_mb_h, p->scaler_u);
Rescale(io->v, io->uv_stride, uv_mb_h, p->scaler_v);
return num_lines_out; return num_lines_out;
} }
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p, static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
int expected_num_lines_out) {
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
uint8_t* const dst_a = buf->a + p->last_y * buf->a_stride;
if (io->a != NULL) { if (io->a != NULL) {
uint8_t* const dst_y = buf->y + p->last_y * buf->y_stride; Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
const int num_lines_out = Rescale(io->a, io->width, io->mb_h, p->scaler_a);
assert(expected_num_lines_out == num_lines_out);
if (num_lines_out > 0) { // unmultiply the Y
WebPMultRows(dst_y, buf->y_stride, dst_a, buf->a_stride,
p->scaler_a->dst_width, num_lines_out, 1);
}
} else if (buf->a != NULL) {
// the user requested alpha, but there is none, set it to opaque.
assert(p->last_y + expected_num_lines_out <= io->scaled_height);
FillAlphaPlane(dst_a, io->scaled_width, expected_num_lines_out,
buf->a_stride);
} }
return 0; return 0;
} }
@ -304,46 +317,40 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
const int uv_in_height = (io->mb_h + 1) >> 1; const int uv_in_height = (io->mb_h + 1) >> 1;
const size_t work_size = 2 * out_width; // scratch memory for luma rescaler const size_t work_size = 2 * out_width; // scratch memory for luma rescaler
const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
size_t tmp_size, rescaler_size; size_t tmp_size;
rescaler_t* work; int32_t* work;
WebPRescaler* scalers;
const int num_rescalers = has_alpha ? 4 : 3;
tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work); tmp_size = work_size + 2 * uv_work_size;
if (has_alpha) { if (has_alpha) {
tmp_size += work_size * sizeof(*work); tmp_size += work_size;
} }
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST; p->memory = calloc(1, tmp_size * sizeof(*work));
p->memory = WebPSafeMalloc(1ULL, tmp_size + rescaler_size);
if (p->memory == NULL) { if (p->memory == NULL) {
return 0; // memory error return 0; // memory error
} }
work = (rescaler_t*)p->memory; work = (int32_t*)p->memory;
WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + tmp_size);
p->scaler_y = &scalers[0];
p->scaler_u = &scalers[1];
p->scaler_v = &scalers[2];
p->scaler_a = has_alpha ? &scalers[3] : NULL;
WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
buf->y, out_width, out_height, buf->y_stride, 1, buf->y, out_width, out_height, buf->y_stride, 1,
io->mb_w, out_width, io->mb_h, out_height,
work); work);
WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height, WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
buf->u, uv_out_width, uv_out_height, buf->u_stride, 1, buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
uv_in_width, uv_out_width,
uv_in_height, uv_out_height,
work + work_size); work + work_size);
WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height, WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
buf->v, uv_out_width, uv_out_height, buf->v_stride, 1, buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
uv_in_width, uv_out_width,
uv_in_height, uv_out_height,
work + work_size + uv_work_size); work + work_size + uv_work_size);
p->emit = EmitRescaledYUV; p->emit = EmitRescaledYUV;
if (has_alpha) { if (has_alpha) {
WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h, WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
buf->a, out_width, out_height, buf->a_stride, 1, buf->a, out_width, out_height, buf->a_stride, 1,
io->mb_w, out_width, io->mb_h, out_height,
work + work_size + 2 * uv_work_size); work + work_size + 2 * uv_work_size);
p->emit_alpha = EmitRescaledAlphaYUV; p->emit_alpha = EmitRescaledAlphaYUV;
WebPInitAlphaProcessing();
} }
return 1; return 1;
} }
@ -355,19 +362,19 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) {
const WebPYUV444Converter convert = const WebPYUV444Converter convert =
WebPYUV444Converters[p->output->colorspace]; WebPYUV444Converters[p->output->colorspace];
const WebPRGBABuffer* const buf = &p->output->u.RGBA; const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* dst = buf->rgba + y_pos * buf->stride; uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
int num_lines_out = 0; int num_lines_out = 0;
// For RGB rescaling, because of the YUV420, current scan position // For RGB rescaling, because of the YUV420, current scan position
// U/V can be +1/-1 line from the Y one. Hence the double test. // U/V can be +1/-1 line from the Y one. Hence the double test.
while (WebPRescalerHasPendingOutput(p->scaler_y) && while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
WebPRescalerHasPendingOutput(p->scaler_u)) { WebPRescalerHasPendingOutput(&p->scaler_u)) {
assert(y_pos + num_lines_out < p->output->height); assert(p->last_y + y_pos + num_lines_out < p->output->height);
assert(p->scaler_u->y_accum == p->scaler_v->y_accum); assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
WebPRescalerExportRow(p->scaler_y); WebPRescalerExportRow(&p->scaler_y);
WebPRescalerExportRow(p->scaler_u); WebPRescalerExportRow(&p->scaler_u);
WebPRescalerExportRow(p->scaler_v); WebPRescalerExportRow(&p->scaler_v);
convert(p->scaler_y->dst, p->scaler_u->dst, p->scaler_v->dst, convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
dst, p->scaler_y->dst_width); dst, p->scaler_y.dst_width);
dst += buf->stride; dst += buf->stride;
++num_lines_out; ++num_lines_out;
} }
@ -381,75 +388,71 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
int num_lines_out = 0; int num_lines_out = 0;
while (j < mb_h) { while (j < mb_h) {
const int y_lines_in = const int y_lines_in =
WebPRescalerImport(p->scaler_y, mb_h - j, WebPRescalerImport(&p->scaler_y, mb_h - j,
io->y + j * io->y_stride, io->y_stride); io->y + j * io->y_stride, io->y_stride);
const int u_lines_in =
WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
io->u + uv_j * io->uv_stride, io->uv_stride);
const int v_lines_in =
WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
io->v + uv_j * io->uv_stride, io->uv_stride);
(void)v_lines_in; // remove a gcc warning
assert(u_lines_in == v_lines_in);
j += y_lines_in; j += y_lines_in;
if (WebPRescaleNeededLines(p->scaler_u, uv_mb_h - uv_j)) { uv_j += u_lines_in;
const int u_lines_in = num_lines_out += ExportRGB(p, num_lines_out);
WebPRescalerImport(p->scaler_u, uv_mb_h - uv_j,
io->u + uv_j * io->uv_stride, io->uv_stride);
const int v_lines_in =
WebPRescalerImport(p->scaler_v, uv_mb_h - uv_j,
io->v + uv_j * io->uv_stride, io->uv_stride);
(void)v_lines_in; // remove a gcc warning
assert(u_lines_in == v_lines_in);
uv_j += u_lines_in;
}
num_lines_out += ExportRGB(p, p->last_y + num_lines_out);
} }
return num_lines_out; return num_lines_out;
} }
static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) { static int ExportAlpha(WebPDecParams* const p, int y_pos) {
const WebPRGBABuffer* const buf = &p->output->u.RGBA; const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride; uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
const WEBP_CSP_MODE colorspace = p->output->colorspace; const WEBP_CSP_MODE colorspace = p->output->colorspace;
const int alpha_first = const int alpha_first =
(colorspace == MODE_ARGB || colorspace == MODE_Argb); (colorspace == MODE_ARGB || colorspace == MODE_Argb);
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
int num_lines_out = 0; int num_lines_out = 0;
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
uint32_t non_opaque = 0; uint32_t alpha_mask = 0xff;
const int width = p->scaler_a->dst_width; const int width = p->scaler_a.dst_width;
while (WebPRescalerHasPendingOutput(p->scaler_a) && while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
num_lines_out < max_lines_out) { int i;
assert(y_pos + num_lines_out < p->output->height); assert(p->last_y + y_pos + num_lines_out < p->output->height);
WebPRescalerExportRow(p->scaler_a); WebPRescalerExportRow(&p->scaler_a);
non_opaque |= WebPDispatchAlpha(p->scaler_a->dst, 0, width, 1, dst, 0); for (i = 0; i < width; ++i) {
const uint32_t alpha_value = p->scaler_a.dst[i];
dst[4 * i] = alpha_value;
alpha_mask &= alpha_value;
}
dst += buf->stride; dst += buf->stride;
++num_lines_out; ++num_lines_out;
} }
if (is_premult_alpha && non_opaque) { if (is_premult_alpha && alpha_mask != 0xff) {
WebPApplyAlphaMultiply(base_rgba, alpha_first, WebPApplyAlphaMultiply(base_rgba, alpha_first,
width, num_lines_out, buf->stride); width, num_lines_out, buf->stride);
} }
return num_lines_out; return num_lines_out;
} }
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos, static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
int max_lines_out) {
const WebPRGBABuffer* const buf = &p->output->u.RGBA; const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride; uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
#ifdef WEBP_SWAP_16BIT_CSP
uint8_t* alpha_dst = base_rgba;
#else
uint8_t* alpha_dst = base_rgba + 1; uint8_t* alpha_dst = base_rgba + 1;
#endif
int num_lines_out = 0; int num_lines_out = 0;
const WEBP_CSP_MODE colorspace = p->output->colorspace; const WEBP_CSP_MODE colorspace = p->output->colorspace;
const int width = p->scaler_a->dst_width; const int width = p->scaler_a.dst_width;
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
uint32_t alpha_mask = 0x0f; uint32_t alpha_mask = 0x0f;
while (WebPRescalerHasPendingOutput(p->scaler_a) && while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
num_lines_out < max_lines_out) {
int i; int i;
assert(y_pos + num_lines_out < p->output->height); assert(p->last_y + y_pos + num_lines_out < p->output->height);
WebPRescalerExportRow(p->scaler_a); WebPRescalerExportRow(&p->scaler_a);
for (i = 0; i < width; ++i) { for (i = 0; i < width; ++i) {
// Fill in the alpha value (converted to 4 bits). // Fill in the alpha value (converted to 4 bits).
const uint32_t alpha_value = p->scaler_a->dst[i] >> 4; const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value; alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
alpha_mask &= alpha_value; alpha_mask &= alpha_value;
} }
@ -462,17 +465,15 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos,
return num_lines_out; return num_lines_out;
} }
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p, static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
int expected_num_out_lines) {
if (io->a != NULL) { if (io->a != NULL) {
WebPRescaler* const scaler = p->scaler_a; WebPRescaler* const scaler = &p->scaler_a;
int lines_left = expected_num_out_lines; int j = 0;
const int y_end = p->last_y + lines_left; int pos = 0;
while (lines_left > 0) { while (j < io->mb_h) {
const int row_offset = scaler->src_y - io->mb_y; j += WebPRescalerImport(scaler, io->mb_h - j,
WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y, io->a + j * io->width, io->width);
io->a + row_offset * io->width, io->width); pos += p->emit_alpha_row(p, pos);
lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left);
} }
} }
return 0; return 0;
@ -485,11 +486,9 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
const int uv_in_width = (io->mb_w + 1) >> 1; const int uv_in_width = (io->mb_w + 1) >> 1;
const int uv_in_height = (io->mb_h + 1) >> 1; const int uv_in_height = (io->mb_h + 1) >> 1;
const size_t work_size = 2 * out_width; // scratch memory for one rescaler const size_t work_size = 2 * out_width; // scratch memory for one rescaler
rescaler_t* work; // rescalers work area int32_t* work; // rescalers work area
uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
size_t tmp_size1, tmp_size2, total_size, rescaler_size; size_t tmp_size1, tmp_size2;
WebPRescaler* scalers;
const int num_rescalers = has_alpha ? 4 : 3;
tmp_size1 = 3 * work_size; tmp_size1 = 3 * work_size;
tmp_size2 = 3 * out_width; tmp_size2 = 3 * out_width;
@ -497,37 +496,30 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
tmp_size1 += work_size; tmp_size1 += work_size;
tmp_size2 += out_width; tmp_size2 += out_width;
} }
total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp); p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
p->memory = WebPSafeMalloc(1ULL, total_size + rescaler_size);
if (p->memory == NULL) { if (p->memory == NULL) {
return 0; // memory error return 0; // memory error
} }
work = (rescaler_t*)p->memory; work = (int32_t*)p->memory;
tmp = (uint8_t*)(work + tmp_size1); tmp = (uint8_t*)(work + tmp_size1);
WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size);
p->scaler_y = &scalers[0];
p->scaler_u = &scalers[1];
p->scaler_v = &scalers[2];
p->scaler_a = has_alpha ? &scalers[3] : NULL;
WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
tmp + 0 * out_width, out_width, out_height, 0, 1, tmp + 0 * out_width, out_width, out_height, 0, 1,
io->mb_w, out_width, io->mb_h, out_height,
work + 0 * work_size); work + 0 * work_size);
WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height, WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
tmp + 1 * out_width, out_width, out_height, 0, 1, tmp + 1 * out_width, out_width, out_height, 0, 1,
io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
work + 1 * work_size); work + 1 * work_size);
WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height, WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
tmp + 2 * out_width, out_width, out_height, 0, 1, tmp + 2 * out_width, out_width, out_height, 0, 1,
io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
work + 2 * work_size); work + 2 * work_size);
p->emit = EmitRescaledRGB; p->emit = EmitRescaledRGB;
WebPInitYUV444Converters();
if (has_alpha) { if (has_alpha) {
WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h, WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
tmp + 3 * out_width, out_width, out_height, 0, 1, tmp + 3 * out_width, out_width, out_height, 0, 1,
io->mb_w, out_width, io->mb_h, out_height,
work + 3 * work_size); work + 3 * work_size);
p->emit_alpha = EmitRescaledAlphaRGB; p->emit_alpha = EmitRescaledAlphaRGB;
if (p->output->colorspace == MODE_RGBA_4444 || if (p->output->colorspace == MODE_RGBA_4444 ||
@ -536,7 +528,6 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
} else { } else {
p->emit_alpha_row = ExportAlpha; p->emit_alpha_row = ExportAlpha;
} }
WebPInitAlphaProcessing();
} }
return 1; return 1;
} }
@ -557,9 +548,7 @@ static int CustomSetup(VP8Io* io) {
if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) { if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
return 0; return 0;
} }
if (is_alpha && WebPIsPremultipliedMode(colorspace)) {
WebPInitUpsamplers();
}
if (io->use_scaling) { if (io->use_scaling) {
const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p); const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
if (!ok) { if (!ok) {
@ -567,12 +556,11 @@ static int CustomSetup(VP8Io* io) {
} }
} else { } else {
if (is_rgb) { if (is_rgb) {
WebPInitSamplers();
p->emit = EmitSampledRGB; // default p->emit = EmitSampledRGB; // default
if (io->fancy_upsampling) {
#ifdef FANCY_UPSAMPLING #ifdef FANCY_UPSAMPLING
if (io->fancy_upsampling) {
const int uv_width = (io->mb_w + 1) >> 1; const int uv_width = (io->mb_w + 1) >> 1;
p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width)); p->memory = malloc(io->mb_w + 2 * uv_width);
if (p->memory == NULL) { if (p->memory == NULL) {
return 0; // memory error. return 0; // memory error.
} }
@ -581,20 +569,18 @@ static int CustomSetup(VP8Io* io) {
p->tmp_v = p->tmp_u + uv_width; p->tmp_v = p->tmp_u + uv_width;
p->emit = EmitFancyRGB; p->emit = EmitFancyRGB;
WebPInitUpsamplers(); WebPInitUpsamplers();
#endif
} }
#endif
} else { } else {
p->emit = EmitYUV; p->emit = EmitYUV;
} }
if (is_alpha) { // need transparency output if (is_alpha) { // need transparency output
if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
p->emit_alpha = p->emit_alpha =
(colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ? (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
EmitAlphaRGBA4444 EmitAlphaRGBA4444
: is_rgb ? EmitAlphaRGB : is_rgb ? EmitAlphaRGB
: EmitAlphaYUV; : EmitAlphaYUV;
if (is_rgb) {
WebPInitAlphaProcessing();
}
} }
} }
@ -617,8 +603,8 @@ static int CustomPut(const VP8Io* io) {
return 0; return 0;
} }
num_lines_out = p->emit(io, p); num_lines_out = p->emit(io, p);
if (p->emit_alpha != NULL) { if (p->emit_alpha) {
p->emit_alpha(io, p, num_lines_out); p->emit_alpha(io, p);
} }
p->last_y += num_lines_out; p->last_y += num_lines_out;
return 1; return 1;
@ -628,7 +614,7 @@ static int CustomPut(const VP8Io* io) {
static void CustomTeardown(const VP8Io* io) { static void CustomTeardown(const VP8Io* io) {
WebPDecParams* const p = (WebPDecParams*)io->opaque; WebPDecParams* const p = (WebPDecParams*)io->opaque;
WebPSafeFree(p->memory); free(p->memory);
p->memory = NULL; p->memory = NULL;
} }
@ -643,3 +629,7 @@ void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -7,26 +7,31 @@
// be found in the AUTHORS file in the root of the source tree. // be found in the AUTHORS file in the root of the source tree.
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// Spatial prediction using various filters // Enhancement layer (for YUV444/422)
// //
// Author: Urvang (urvang@google.com) // Author: Skal (pascal.massimino@gmail.com)
#ifndef WEBP_UTILS_FILTERS_H_ #include <assert.h>
#define WEBP_UTILS_FILTERS_H_ #include <stdlib.h>
#include "../webp/types.h" #include "./vp8i.h"
#include "../dsp/dsp.h"
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
// Fast estimate of a potentially good filter. //------------------------------------------------------------------------------
WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data,
int width, int height, int stride);
#ifdef __cplusplus int VP8DecodeLayer(VP8Decoder* const dec) {
assert(dec);
assert(dec->layer_data_size_ > 0);
(void)dec;
// TODO: handle enhancement layer here.
return 1;
}
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif /* WEBP_UTILS_FILTERS_H_ */

View File

@ -11,7 +11,11 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include "./vp8i_dec.h" #include "./vp8i.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
static WEBP_INLINE int clip(int v, int M) { static WEBP_INLINE int clip(int v, int M) {
return v < 0 ? 0 : v > M ? M : v; return v < 0 ? 0 : v > M ? M : v;
@ -100,11 +104,12 @@ void VP8ParseQuant(VP8Decoder* const dec) {
m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)]; m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];
m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)]; m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)];
m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation
} }
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -11,12 +11,12 @@
// //
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include "./vp8i_dec.h" #include "vp8i.h"
#include "../utils/bit_reader_inl_utils.h"
#if !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__)
// using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then.
#define USE_GENERIC_TREE #define USE_GENERIC_TREE
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif #endif
#ifdef USE_GENERIC_TREE #ifdef USE_GENERIC_TREE
@ -33,12 +33,61 @@ static const int8_t kYModesIntra4[18] = {
}; };
#endif #endif
#ifndef ONLY_KEYFRAME_CODE
// inter prediction modes
enum {
LEFT4 = 0, ABOVE4 = 1, ZERO4 = 2, NEW4 = 3,
NEARESTMV, NEARMV, ZEROMV, NEWMV, SPLITMV };
static const int8_t kYModesInter[8] = {
-DC_PRED, 1,
2, 3,
-V_PRED, -H_PRED,
-TM_PRED, -B_PRED
};
static const int8_t kMBSplit[6] = {
-3, 1,
-2, 2,
-0, -1
};
static const int8_t kMVRef[8] = {
-ZEROMV, 1,
-NEARESTMV, 2,
-NEARMV, 3,
-NEWMV, -SPLITMV
};
static const int8_t kMVRef4[6] = {
-LEFT4, 1,
-ABOVE4, 2,
-ZERO4, -NEW4
};
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Default probabilities // Default probabilities
// Inter
#ifndef ONLY_KEYFRAME_CODE
static const uint8_t kYModeProbaInter0[4] = { 112, 86, 140, 37 };
static const uint8_t kUVModeProbaInter0[3] = { 162, 101, 204 };
static const uint8_t kMVProba0[2][NUM_MV_PROBAS] = {
{ 162, 128, 225, 146, 172, 147, 214, 39,
156, 128, 129, 132, 75, 145, 178, 206,
239, 254, 254 },
{ 164, 128, 204, 170, 119, 235, 140, 230,
228, 128, 130, 130, 74, 148, 180, 203,
236, 254, 254 }
};
#endif
// Paragraph 13.5 // Paragraph 13.5
static const uint8_t static const uint8_t
CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = { CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
// genereated using vp8_default_coef_probs() in entropy.c:129
{ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
@ -279,38 +328,28 @@ static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
void VP8ResetProba(VP8Proba* const proba) { void VP8ResetProba(VP8Proba* const proba) {
memset(proba->segments_, 255u, sizeof(proba->segments_)); memset(proba->segments_, 255u, sizeof(proba->segments_));
// proba->bands_[][] is initialized later memcpy(proba->coeffs_, CoeffsProba0, sizeof(CoeffsProba0));
#ifndef ONLY_KEYFRAME_CODE
memcpy(proba->mv_, kMVProba0, sizeof(kMVProba0));
memcpy(proba->ymode_, kYModeProbaInter0, sizeof(kYModeProbaInter0));
memcpy(proba->uvmode_, kUVModeProbaInter0, sizeof(kUVModeProbaInter0));
#endif
} }
static void ParseIntraMode(VP8BitReader* const br, void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
VP8Decoder* const dec, int mb_x) { uint8_t* const top = dec->intra_t_ + 4 * dec->mb_x_;
uint8_t* const top = dec->intra_t_ + 4 * mb_x;
uint8_t* const left = dec->intra_l_; uint8_t* const left = dec->intra_l_;
VP8MBData* const block = dec->mb_data_ + mb_x; // Hardcoded 16x16 intra-mode decision tree.
dec->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
// Note: we don't save segment map (yet), as we don't expect if (!dec->is_i4x4_) {
// to decode more than 1 keyframe.
if (dec->segment_hdr_.update_map_) {
// Hardcoded tree parsing
block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0])
? VP8GetBit(br, dec->proba_.segments_[1])
: 2 + VP8GetBit(br, dec->proba_.segments_[2]);
} else {
block->segment_ = 0; // default for intra
}
if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_);
block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
if (!block->is_i4x4_) {
// Hardcoded 16x16 intra-mode decision tree.
const int ymode = const int ymode =
VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED) VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED)
: (VP8GetBit(br, 163) ? V_PRED : DC_PRED); : (VP8GetBit(br, 163) ? V_PRED : DC_PRED);
block->imodes_[0] = ymode; dec->imodes_[0] = ymode;
memset(top, ymode, 4 * sizeof(*top)); memset(top, ymode, 4 * sizeof(top[0]));
memset(left, ymode, 4 * sizeof(*left)); memset(left, ymode, 4 * sizeof(left[0]));
} else { } else {
uint8_t* modes = block->imodes_; uint8_t* modes = dec->imodes_;
int y; int y;
for (y = 0; y < 4; ++y) { for (y = 0; y < 4; ++y) {
int ymode = left[y]; int ymode = left[y];
@ -319,10 +358,10 @@ static void ParseIntraMode(VP8BitReader* const br,
const uint8_t* const prob = kBModesProba[top[x]][ymode]; const uint8_t* const prob = kBModesProba[top[x]][ymode];
#ifdef USE_GENERIC_TREE #ifdef USE_GENERIC_TREE
// Generic tree-parsing // Generic tree-parsing
int i = kYModesIntra4[VP8GetBit(br, prob[0])]; int i = 0;
while (i > 0) { do {
i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])]; i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])];
} } while (i > 0);
ymode = -i; ymode = -i;
#else #else
// Hardcoded tree parsing // Hardcoded tree parsing
@ -337,24 +376,15 @@ static void ParseIntraMode(VP8BitReader* const br,
(!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED))); (!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED)));
#endif // USE_GENERIC_TREE #endif // USE_GENERIC_TREE
top[x] = ymode; top[x] = ymode;
*modes++ = ymode;
} }
memcpy(modes, top, 4 * sizeof(*top));
modes += 4;
left[y] = ymode; left[y] = ymode;
} }
} }
// Hardcoded UVMode decision tree // Hardcoded UVMode decision tree
block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED dec->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
: !VP8GetBit(br, 114) ? V_PRED : !VP8GetBit(br, 114) ? V_PRED
: VP8GetBit(br, 183) ? TM_PRED : H_PRED; : VP8GetBit(br, 183) ? TM_PRED : H_PRED;
}
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
int mb_x;
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
ParseIntraMode(br, dec, mb_x);
}
return !dec->br_.eof_;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -496,13 +526,18 @@ static const uint8_t
} }
}; };
// Paragraph 9.9 #ifndef ONLY_KEYFRAME_CODE
static const uint8_t MVUpdateProba[2][NUM_MV_PROBAS] = {
static const int kBands[16 + 1] = { { 237, 246, 253, 253, 254, 254, 254, 254,
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 254, 254, 254, 254, 254, 254, 250, 250,
0 // extra entry as sentinel 252, 254, 254 },
{ 231, 243, 245, 253, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 251, 251,
254, 254, 254 }
}; };
#endif
// Paragraph 9.9
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) { void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
VP8Proba* const proba = &dec->proba_; VP8Proba* const proba = &dec->proba_;
int t, b, c, p; int t, b, c, p;
@ -510,19 +545,47 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
for (b = 0; b < NUM_BANDS; ++b) { for (b = 0; b < NUM_BANDS; ++b) {
for (c = 0; c < NUM_CTX; ++c) { for (c = 0; c < NUM_CTX; ++c) {
for (p = 0; p < NUM_PROBAS; ++p) { for (p = 0; p < NUM_PROBAS; ++p) {
const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ? if (VP8GetBit(br, CoeffsUpdateProba[t][b][c][p])) {
VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p]; proba->coeffs_[t][b][c][p] = VP8GetValue(br, 8);
proba->bands_[t][b].probas_[c][p] = v; }
} }
} }
} }
for (b = 0; b < 16 + 1; ++b) {
proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
}
} }
dec->use_skip_proba_ = VP8Get(br); dec->use_skip_proba_ = VP8Get(br);
if (dec->use_skip_proba_) { if (dec->use_skip_proba_) {
dec->skip_p_ = VP8GetValue(br, 8); dec->skip_p_ = VP8GetValue(br, 8);
} }
#ifndef ONLY_KEYFRAME_CODE
if (!dec->frm_hdr_.key_frame_) {
int i;
dec->intra_p_ = VP8GetValue(br, 8);
dec->last_p_ = VP8GetValue(br, 8);
dec->golden_p_ = VP8GetValue(br, 8);
if (VP8Get(br)) { // update y-mode
for (i = 0; i < 4; ++i) {
proba->ymode_[i] = VP8GetValue(br, 8);
}
}
if (VP8Get(br)) { // update uv-mode
for (i = 0; i < 3; ++i) {
proba->uvmode_[i] = VP8GetValue(br, 8);
}
}
// update MV
for (i = 0; i < 2; ++i) {
int k;
for (k = 0; k < NUM_MV_PROBAS; ++k) {
if (VP8GetBit(br, MVUpdateProba[i][k])) {
const int v = VP8GetValue(br, 7);
proba->mv_[i][k] = v ? v << 1 : 1;
}
}
}
}
#endif
} }
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -13,12 +13,14 @@
#include <stdlib.h> #include <stdlib.h>
#include "./alphai_dec.h" #include "./vp8i.h"
#include "./vp8i_dec.h" #include "./vp8li.h"
#include "./vp8li_dec.h" #include "./webpi.h"
#include "./webpi_dec.h" #include "../utils/bit_reader.h"
#include "../utils/bit_reader_inl_utils.h"
#include "../utils/utils.h" #if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -26,16 +28,6 @@ int WebPGetDecoderVersion(void) {
return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION; return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
} }
//------------------------------------------------------------------------------
// Signature and pointer-to-function for GetCoeffs() variants below.
typedef int (*GetCoeffsFunc)(VP8BitReader* const br,
const VP8BandProbas* const prob[],
int ctx, const quant_t dq, int n, int16_t* out);
static volatile GetCoeffsFunc GetCoeffs = NULL;
static void InitGetCoeffs(void);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// VP8Decoder // VP8Decoder
@ -55,13 +47,12 @@ int VP8InitIoInternal(VP8Io* const io, int version) {
} }
VP8Decoder* VP8New(void) { VP8Decoder* VP8New(void) {
VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(*dec));
if (dec != NULL) { if (dec != NULL) {
SetOk(dec); SetOk(dec);
WebPGetWorkerInterface()->Init(&dec->worker_); WebPWorkerInit(&dec->worker_);
dec->ready_ = 0; dec->ready_ = 0;
dec->num_parts_minus_one_ = 0; dec->num_parts_ = 1;
InitGetCoeffs();
} }
return dec; return dec;
} }
@ -80,13 +71,16 @@ const char* VP8StatusMessage(VP8Decoder* const dec) {
void VP8Delete(VP8Decoder* const dec) { void VP8Delete(VP8Decoder* const dec) {
if (dec != NULL) { if (dec != NULL) {
VP8Clear(dec); VP8Clear(dec);
WebPSafeFree(dec); free(dec);
} }
} }
int VP8SetError(VP8Decoder* const dec, int VP8SetError(VP8Decoder* const dec,
VP8StatusCode error, const char* const msg) { VP8StatusCode error, const char* const msg) {
// The oldest error reported takes precedence over the new one. // TODO This check would be unnecessary if alpha decompression was separated
// from VP8ProcessRow/FinishRow. This avoids setting 'dec->status_' to
// something other than VP8_STATUS_BITSTREAM_ERROR on alpha decompression
// failure.
if (dec->status_ == VP8_STATUS_OK) { if (dec->status_ == VP8_STATUS_OK) {
dec->status_ = error; dec->status_ = error;
dec->error_msg_ = msg; dec->error_msg_ = msg;
@ -129,9 +123,6 @@ int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
if (((bits >> 5)) >= chunk_size) { // partition_length if (((bits >> 5)) >= chunk_size) { // partition_length
return 0; // inconsistent size information. return 0; // inconsistent size information.
} }
if (w == 0 || h == 0) {
return 0; // We don't support both width and height to be zero.
}
if (width) { if (width) {
*width = w; *width = w;
@ -201,27 +192,25 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
const uint8_t* sz = buf; const uint8_t* sz = buf;
const uint8_t* buf_end = buf + size; const uint8_t* buf_end = buf + size;
const uint8_t* part_start; const uint8_t* part_start;
size_t size_left = size; int last_part;
size_t last_part; int p;
size_t p;
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1; dec->num_parts_ = 1 << VP8GetValue(br, 2);
last_part = dec->num_parts_minus_one_; last_part = dec->num_parts_ - 1;
if (size < 3 * last_part) { part_start = buf + last_part * 3;
if (buf_end < part_start) {
// we can't even read the sizes with sz[]! That's a failure. // we can't even read the sizes with sz[]! That's a failure.
return VP8_STATUS_NOT_ENOUGH_DATA; return VP8_STATUS_NOT_ENOUGH_DATA;
} }
part_start = buf + last_part * 3;
size_left -= last_part * 3;
for (p = 0; p < last_part; ++p) { for (p = 0; p < last_part; ++p) {
size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16); const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
if (psize > size_left) psize = size_left; const uint8_t* part_end = part_start + psize;
VP8InitBitReader(dec->parts_ + p, part_start, psize); if (part_end > buf_end) part_end = buf_end;
part_start += psize; VP8InitBitReader(dec->parts_ + p, part_start, part_end);
size_left -= psize; part_start = part_end;
sz += 3; sz += 3;
} }
VP8InitBitReader(dec->parts_ + last_part, part_start, size_left); VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end);
return (part_start < buf_end) ? VP8_STATUS_OK : return (part_start < buf_end) ? VP8_STATUS_OK :
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
} }
@ -260,6 +249,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
VP8PictureHeader* pic_hdr; VP8PictureHeader* pic_hdr;
VP8BitReader* br; VP8BitReader* br;
VP8StatusCode status; VP8StatusCode status;
WebPHeaderStructure headers;
if (dec == NULL) { if (dec == NULL) {
return 0; return 0;
@ -269,8 +259,33 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
"null VP8Io passed to VP8GetHeaders()"); "null VP8Io passed to VP8GetHeaders()");
} }
buf = io->data;
buf_size = io->data_size; // Process Pre-VP8 chunks.
headers.data = io->data;
headers.data_size = io->data_size;
status = WebPParseHeaders(&headers);
if (status != VP8_STATUS_OK) {
return VP8SetError(dec, status, "Incorrect/incomplete header.");
}
if (headers.is_lossless) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"Unexpected lossless format encountered.");
}
if (dec->alpha_data_ == NULL) {
assert(dec->alpha_data_size_ == 0);
// We have NOT set alpha data yet. Set it now.
// (This is to ensure that dec->alpha_data_ is NOT reset to NULL if
// WebPParseHeaders() is called more than once, as in incremental decoding
// case.)
dec->alpha_data_ = headers.alpha_data;
dec->alpha_data_size_ = headers.alpha_data_size;
}
// Process the VP8 frame header.
buf = headers.data + headers.offset;
buf_size = headers.data_size - headers.offset;
assert(headers.data_size >= headers.offset); // WebPParseHeaders' guarantee
if (buf_size < 4) { if (buf_size < 4) {
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
"Truncated header."); "Truncated header.");
@ -284,14 +299,12 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
frm_hdr->profile_ = (bits >> 1) & 7; frm_hdr->profile_ = (bits >> 1) & 7;
frm_hdr->show_ = (bits >> 4) & 1; frm_hdr->show_ = (bits >> 4) & 1;
frm_hdr->partition_length_ = (bits >> 5); frm_hdr->partition_length_ = (bits >> 5);
if (frm_hdr->profile_ > 3) { if (frm_hdr->profile_ > 3)
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"Incorrect keyframe parameters."); "Incorrect keyframe parameters.");
} if (!frm_hdr->show_)
if (!frm_hdr->show_) {
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
"Frame not displayable."); "Frame not displayable.");
}
buf += 3; buf += 3;
buf_size -= 3; buf_size -= 3;
} }
@ -316,27 +329,21 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
dec->mb_w_ = (pic_hdr->width_ + 15) >> 4; dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
dec->mb_h_ = (pic_hdr->height_ + 15) >> 4; dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
// Setup default output area (can be later modified during io->setup()) // Setup default output area (can be later modified during io->setup())
io->width = pic_hdr->width_; io->width = pic_hdr->width_;
io->height = pic_hdr->height_; io->height = pic_hdr->height_;
// IMPORTANT! use some sane dimensions in crop_* and scaled_* fields. io->use_scaling = 0;
// So they can be used interchangeably without always testing for
// 'use_cropping'.
io->use_cropping = 0; io->use_cropping = 0;
io->crop_top = 0; io->crop_top = 0;
io->crop_left = 0; io->crop_left = 0;
io->crop_right = io->width; io->crop_right = io->width;
io->crop_bottom = io->height; io->crop_bottom = io->height;
io->use_scaling = 0;
io->scaled_width = io->width;
io->scaled_height = io->height;
io->mb_w = io->width; // sanity check io->mb_w = io->width; // sanity check
io->mb_h = io->height; // ditto io->mb_h = io->height; // ditto
VP8ResetProba(&dec->proba_); VP8ResetProba(&dec->proba_);
ResetSegmentHeader(&dec->segment_hdr_); ResetSegmentHeader(&dec->segment_hdr_);
dec->segment_ = 0; // default for intra
} }
// Check if we have all the partition #0 available, and initialize dec->br_ // Check if we have all the partition #0 available, and initialize dec->br_
@ -347,7 +354,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
} }
br = &dec->br_; br = &dec->br_;
VP8InitBitReader(br, buf, frm_hdr->partition_length_); VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
buf += frm_hdr->partition_length_; buf += frm_hdr->partition_length_;
buf_size -= frm_hdr->partition_length_; buf_size -= frm_hdr->partition_length_;
@ -374,14 +381,63 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
// Frame buffer marking // Frame buffer marking
if (!frm_hdr->key_frame_) { if (!frm_hdr->key_frame_) {
// Paragraph 9.7
#ifndef ONLY_KEYFRAME_CODE
dec->buffer_flags_ = VP8Get(br) << 0; // update golden
dec->buffer_flags_ |= VP8Get(br) << 1; // update alt ref
if (!(dec->buffer_flags_ & 1)) {
dec->buffer_flags_ |= VP8GetValue(br, 2) << 2;
}
if (!(dec->buffer_flags_ & 2)) {
dec->buffer_flags_ |= VP8GetValue(br, 2) << 4;
}
dec->buffer_flags_ |= VP8Get(br) << 6; // sign bias golden
dec->buffer_flags_ |= VP8Get(br) << 7; // sign bias alt ref
#else
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
"Not a key frame."); "Not a key frame.");
#endif
} else {
dec->buffer_flags_ = 0x003 | 0x100;
} }
VP8Get(br); // ignore the value of update_proba_ // Paragraph 9.8
#ifndef ONLY_KEYFRAME_CODE
dec->update_proba_ = VP8Get(br);
if (!dec->update_proba_) { // save for later restore
dec->proba_saved_ = dec->proba_;
}
dec->buffer_flags_ &= 1 << 8;
dec->buffer_flags_ |=
(frm_hdr->key_frame_ || VP8Get(br)) << 8; // refresh last frame
#else
VP8Get(br); // just ignore the value of update_proba_
#endif
VP8ParseProba(br, dec); VP8ParseProba(br, dec);
#ifdef WEBP_EXPERIMENTAL_FEATURES
// Extensions
if (dec->pic_hdr_.colorspace_) {
const size_t kTrailerSize = 8;
const uint8_t kTrailerMarker = 0x01;
const uint8_t* ext_buf = buf - kTrailerSize;
size_t size;
if (frm_hdr->partition_length_ < kTrailerSize ||
ext_buf[kTrailerSize - 1] != kTrailerMarker) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"RIFF: Inconsistent extra information.");
}
// Layer
size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16);
dec->layer_data_size_ = size;
dec->layer_data_ = NULL; // will be set later
dec->layer_colorspace_ = ext_buf[3];
}
#endif
// sanitized state // sanitized state
dec->ready_ = 1; dec->ready_ = 1;
return 1; return 1;
@ -390,6 +446,11 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Residual decoding (Paragraph 13.2 / 13.3) // Residual decoding (Paragraph 13.2 / 13.3)
static const int kBands[16 + 1] = {
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
0 // extra entry as sentinel
};
static const uint8_t kCat3[] = { 173, 148, 140, 0 }; static const uint8_t kCat3[] = { 173, 148, 140, 0 };
static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 }; static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 };
static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 }; static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 };
@ -400,6 +461,9 @@ static const uint8_t kZigzag[16] = {
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
}; };
typedef const uint8_t (*ProbaArray)[NUM_CTX][NUM_PROBAS]; // for const-casting
typedef const uint8_t (*ProbaCtxArray)[NUM_PROBAS];
// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2 // See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) { static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
int v; int v;
@ -433,20 +497,19 @@ static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
} }
// Returns the position of the last non-zero coeff plus one // Returns the position of the last non-zero coeff plus one
static int GetCoeffsFast(VP8BitReader* const br, // (and 0 if there's no coeff at all)
const VP8BandProbas* const prob[], static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
int ctx, const quant_t dq, int n, int16_t* out) { int ctx, const quant_t dq, int n, int16_t* out) {
const uint8_t* p = prob[n]->probas_[ctx]; // n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'.
const uint8_t* p = prob[n][ctx];
if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit.
return 0;
}
for (; n < 16; ++n) { for (; n < 16; ++n) {
if (!VP8GetBit(br, p[0])) { const ProbaCtxArray p_ctx = prob[kBands[n + 1]];
return n; // previous coeff was last non-zero coeff if (!VP8GetBit(br, p[1])) {
} p = p_ctx[0];
while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs } else { // non zero coeff
p = prob[++n]->probas_[0];
if (n == 16) return 16;
}
{ // non zero coeff
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
int v; int v;
if (!VP8GetBit(br, p[2])) { if (!VP8GetBit(br, p[2])) {
v = 1; v = 1;
@ -456,212 +519,205 @@ static int GetCoeffsFast(VP8BitReader* const br,
p = p_ctx[2]; p = p_ctx[2];
} }
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0]; out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
} if (n < 15 && !VP8GetBit(br, p[0])) { // EOB
} return n + 1;
return 16;
}
// This version of GetCoeffs() uses VP8GetBitAlt() which is an alternate version
// of VP8GetBitAlt() targeting specific platforms.
static int GetCoeffsAlt(VP8BitReader* const br,
const VP8BandProbas* const prob[],
int ctx, const quant_t dq, int n, int16_t* out) {
const uint8_t* p = prob[n]->probas_[ctx];
for (; n < 16; ++n) {
if (!VP8GetBitAlt(br, p[0])) {
return n; // previous coeff was last non-zero coeff
}
while (!VP8GetBitAlt(br, p[1])) { // sequence of zero coeffs
p = prob[++n]->probas_[0];
if (n == 16) return 16;
}
{ // non zero coeff
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
int v;
if (!VP8GetBitAlt(br, p[2])) {
v = 1;
p = p_ctx[1];
} else {
v = GetLargeValue(br, p);
p = p_ctx[2];
} }
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
} }
} }
return 16; return 16;
} }
WEBP_TSAN_IGNORE_FUNCTION static void InitGetCoeffs(void) { // Alias-safe way of converting 4bytes to 32bits.
if (GetCoeffs == NULL) { typedef union {
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) { uint8_t i8[4];
GetCoeffs = GetCoeffsAlt; uint32_t i32;
} else { } PackedNz;
GetCoeffs = GetCoeffsFast;
}
}
}
static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) { // Table to unpack four bits into four bytes
nz_coeffs <<= 2; static const PackedNz kUnpackTab[16] = {
nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz; {{0, 0, 0, 0}}, {{1, 0, 0, 0}}, {{0, 1, 0, 0}}, {{1, 1, 0, 0}},
return nz_coeffs; {{0, 0, 1, 0}}, {{1, 0, 1, 0}}, {{0, 1, 1, 0}}, {{1, 1, 1, 0}},
} {{0, 0, 0, 1}}, {{1, 0, 0, 1}}, {{0, 1, 0, 1}}, {{1, 1, 0, 1}},
{{0, 0, 1, 1}}, {{1, 0, 1, 1}}, {{0, 1, 1, 1}}, {{1, 1, 1, 1}} };
static int ParseResiduals(VP8Decoder* const dec, // Macro to pack four LSB of four bytes into four bits.
VP8MB* const mb, VP8BitReader* const token_br) { #if defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || \
const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_; defined(__BIG_ENDIAN__)
const VP8BandProbas* const * ac_proba; #define PACK_CST 0x08040201U
VP8MBData* const block = dec->mb_data_ + dec->mb_x_; #else
const VP8QuantMatrix* const q = &dec->dqm_[block->segment_]; #define PACK_CST 0x01020408U
int16_t* dst = block->coeffs_; #endif
#define PACK(X, S) ((((X).i32 * PACK_CST) & 0xff000000) >> (S))
static void ParseResiduals(VP8Decoder* const dec,
VP8MB* const mb, VP8BitReader* const token_br) {
int out_t_nz, out_l_nz, first;
ProbaArray ac_prob;
const VP8QuantMatrix* q = &dec->dqm_[dec->segment_];
int16_t* dst = dec->coeffs_;
VP8MB* const left_mb = dec->mb_info_ - 1; VP8MB* const left_mb = dec->mb_info_ - 1;
uint8_t tnz, lnz; PackedNz nz_ac, nz_dc;
uint32_t non_zero_y = 0; PackedNz tnz, lnz;
uint32_t non_zero_uv = 0; uint32_t non_zero_ac = 0;
uint32_t non_zero_dc = 0;
int x, y, ch; int x, y, ch;
uint32_t out_t_nz, out_l_nz;
int first;
nz_dc.i32 = nz_ac.i32 = 0;
memset(dst, 0, 384 * sizeof(*dst)); memset(dst, 0, 384 * sizeof(*dst));
if (!block->is_i4x4_) { // parse DC if (!dec->is_i4x4_) { // parse DC
int16_t dc[16] = { 0 }; int16_t dc[16] = { 0 };
const int ctx = mb->nz_dc_ + left_mb->nz_dc_; const int ctx = mb->dc_nz_ + left_mb->dc_nz_;
const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc); mb->dc_nz_ = left_mb->dc_nz_ =
mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0); (GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[1],
if (nz > 1) { // more than just the DC -> perform the full transform ctx, q->y2_mat_, 0, dc) > 0);
VP8TransformWHT(dc, dst);
} else { // only DC is non-zero -> inlined simplified transform
int i;
const int dc0 = (dc[0] + 3) >> 3;
for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0;
}
first = 1; first = 1;
ac_proba = bands[0]; ac_prob = (ProbaArray)dec->proba_.coeffs_[0];
VP8TransformWHT(dc, dst);
} else { } else {
first = 0; first = 0;
ac_proba = bands[3]; ac_prob = (ProbaArray)dec->proba_.coeffs_[3];
} }
tnz = mb->nz_ & 0x0f; tnz = kUnpackTab[mb->nz_ & 0xf];
lnz = left_mb->nz_ & 0x0f; lnz = kUnpackTab[left_mb->nz_ & 0xf];
for (y = 0; y < 4; ++y) { for (y = 0; y < 4; ++y) {
int l = lnz & 1; int l = lnz.i8[y];
uint32_t nz_coeffs = 0;
for (x = 0; x < 4; ++x) { for (x = 0; x < 4; ++x) {
const int ctx = l + (tnz & 1); const int ctx = l + tnz.i8[x];
const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst); const int nz = GetCoeffs(token_br, ac_prob, ctx,
l = (nz > first); q->y1_mat_, first, dst);
tnz = (tnz >> 1) | (l << 7); tnz.i8[x] = l = (nz > 0);
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0); nz_dc.i8[x] = (dst[0] != 0);
nz_ac.i8[x] = (nz > 1);
dst += 16; dst += 16;
} }
tnz >>= 4; lnz.i8[y] = l;
lnz = (lnz >> 1) | (l << 7); non_zero_dc |= PACK(nz_dc, 24 - y * 4);
non_zero_y = (non_zero_y << 8) | nz_coeffs; non_zero_ac |= PACK(nz_ac, 24 - y * 4);
} }
out_t_nz = tnz; out_t_nz = PACK(tnz, 24);
out_l_nz = lnz >> 4; out_l_nz = PACK(lnz, 24);
tnz = kUnpackTab[mb->nz_ >> 4];
lnz = kUnpackTab[left_mb->nz_ >> 4];
for (ch = 0; ch < 4; ch += 2) { for (ch = 0; ch < 4; ch += 2) {
uint32_t nz_coeffs = 0;
tnz = mb->nz_ >> (4 + ch);
lnz = left_mb->nz_ >> (4 + ch);
for (y = 0; y < 2; ++y) { for (y = 0; y < 2; ++y) {
int l = lnz & 1; int l = lnz.i8[ch + y];
for (x = 0; x < 2; ++x) { for (x = 0; x < 2; ++x) {
const int ctx = l + (tnz & 1); const int ctx = l + tnz.i8[ch + x];
const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst); const int nz =
l = (nz > 0); GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2],
tnz = (tnz >> 1) | (l << 3); ctx, q->uv_mat_, 0, dst);
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0); tnz.i8[ch + x] = l = (nz > 0);
nz_dc.i8[y * 2 + x] = (dst[0] != 0);
nz_ac.i8[y * 2 + x] = (nz > 1);
dst += 16; dst += 16;
} }
tnz >>= 2; lnz.i8[ch + y] = l;
lnz = (lnz >> 1) | (l << 5);
} }
// Note: we don't really need the per-4x4 details for U/V blocks. non_zero_dc |= PACK(nz_dc, 8 - ch * 2);
non_zero_uv |= nz_coeffs << (4 * ch); non_zero_ac |= PACK(nz_ac, 8 - ch * 2);
out_t_nz |= (tnz << 4) << ch;
out_l_nz |= (lnz & 0xf0) << ch;
} }
out_t_nz |= PACK(tnz, 20);
out_l_nz |= PACK(lnz, 20);
mb->nz_ = out_t_nz; mb->nz_ = out_t_nz;
left_mb->nz_ = out_l_nz; left_mb->nz_ = out_l_nz;
block->non_zero_y_ = non_zero_y; dec->non_zero_ac_ = non_zero_ac;
block->non_zero_uv_ = non_zero_uv; dec->non_zero_ = non_zero_ac | non_zero_dc;
mb->skip_ = !dec->non_zero_;
// We look at the mode-code of each block and check if some blocks have less
// than three non-zero coeffs (code < 2). This is to avoid dithering flat and
// empty blocks.
block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_;
return !(non_zero_y | non_zero_uv); // will be used for further optimization
} }
#undef PACK
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Main loop // Main loop
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) { int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
VP8BitReader* const br = &dec->br_;
VP8MB* const left = dec->mb_info_ - 1; VP8MB* const left = dec->mb_info_ - 1;
VP8MB* const mb = dec->mb_info_ + dec->mb_x_; VP8MB* const info = dec->mb_info_ + dec->mb_x_;
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
int skip = dec->use_skip_proba_ ? block->skip_ : 0;
if (!skip) { // Note: we don't save segment map (yet), as we don't expect
skip = ParseResiduals(dec, mb, token_br); // to decode more than 1 keyframe.
if (dec->segment_hdr_.update_map_) {
// Hardcoded tree parsing
dec->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ?
VP8GetBit(br, dec->proba_.segments_[1]) :
2 + VP8GetBit(br, dec->proba_.segments_[2]);
}
info->skip_ = dec->use_skip_proba_ ? VP8GetBit(br, dec->skip_p_) : 0;
VP8ParseIntraMode(br, dec);
if (br->eof_) {
return 0;
}
if (!info->skip_) {
ParseResiduals(dec, info, token_br);
} else { } else {
left->nz_ = mb->nz_ = 0; left->nz_ = info->nz_ = 0;
if (!block->is_i4x4_) { if (!dec->is_i4x4_) {
left->nz_dc_ = mb->nz_dc_ = 0; left->dc_nz_ = info->dc_nz_ = 0;
} }
block->non_zero_y_ = 0; dec->non_zero_ = 0;
block->non_zero_uv_ = 0; dec->non_zero_ac_ = 0;
block->dither_ = 0;
} }
if (dec->filter_type_ > 0) { // store filter info if (dec->filter_type_ > 0) { // store filter info
VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_; VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_;
*finfo = dec->fstrengths_[block->segment_][block->is_i4x4_]; *finfo = dec->fstrengths_[dec->segment_][dec->is_i4x4_];
finfo->f_inner_ |= !skip; finfo->f_inner_ = (!info->skip_ || dec->is_i4x4_);
} }
return !token_br->eof_; return (!token_br->eof_);
} }
void VP8InitScanline(VP8Decoder* const dec) { void VP8InitScanline(VP8Decoder* const dec) {
VP8MB* const left = dec->mb_info_ - 1; VP8MB* const left = dec->mb_info_ - 1;
left->nz_ = 0; left->nz_ = 0;
left->nz_dc_ = 0; left->dc_nz_ = 0;
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_)); memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
dec->mb_x_ = 0; dec->filter_row_ =
(dec->filter_type_ > 0) &&
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
} }
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) { for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
// Parse bitstream for this row.
VP8BitReader* const token_br = VP8BitReader* const token_br =
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
if (!VP8ParseIntraModeRow(&dec->br_, dec)) { VP8InitScanline(dec);
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
"Premature end-of-partition0 encountered.");
}
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
if (!VP8DecodeMB(dec, token_br)) { if (!VP8DecodeMB(dec, token_br)) {
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
"Premature end-of-file encountered."); "Premature end-of-file encountered.");
} }
// Reconstruct and emit samples.
VP8ReconstructBlock(dec);
} }
VP8InitScanline(dec); // Prepare for next scanline
// Reconstruct, filter and emit the row.
if (!VP8ProcessRow(dec, io)) { if (!VP8ProcessRow(dec, io)) {
return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted."); return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
} }
} }
if (dec->mt_method_ > 0) { if (dec->use_threads_ && !WebPWorkerSync(&dec->worker_)) {
if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0; return 0;
} }
// Finish
#ifndef ONLY_KEYFRAME_CODE
if (!dec->update_proba_) {
dec->proba_ = dec->proba_saved_;
}
#endif
#ifdef WEBP_EXPERIMENTAL_FEATURES
if (dec->layer_data_size_ > 0) {
if (!VP8DecodeLayer(dec)) {
return 0;
}
}
#endif
return 1; return 1;
} }
@ -709,9 +765,12 @@ void VP8Clear(VP8Decoder* const dec) {
if (dec == NULL) { if (dec == NULL) {
return; return;
} }
WebPGetWorkerInterface()->End(&dec->worker_); if (dec->use_threads_) {
WebPDeallocateAlphaMemory(dec); WebPWorkerEnd(&dec->worker_);
WebPSafeFree(dec->mem_); }
if (dec->mem_) {
free(dec->mem_);
}
dec->mem_ = NULL; dec->mem_ = NULL;
dec->mem_size_ = 0; dec->mem_size_ = 0;
memset(&dec->br_, 0, sizeof(dec->br_)); memset(&dec->br_, 0, sizeof(dec->br_));
@ -719,3 +778,7 @@ void VP8Clear(VP8Decoder* const dec) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -15,14 +15,12 @@
#define WEBP_DEC_VP8I_H_ #define WEBP_DEC_VP8I_H_
#include <string.h> // for memcpy() #include <string.h> // for memcpy()
#include "./common_dec.h" #include "./vp8li.h"
#include "./vp8li_dec.h" #include "../utils/bit_reader.h"
#include "../utils/bit_reader_utils.h" #include "../utils/thread.h"
#include "../utils/random_utils.h"
#include "../utils/thread_utils.h"
#include "../dsp/dsp.h" #include "../dsp/dsp.h"
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
@ -31,10 +29,48 @@ extern "C" {
// version numbers // version numbers
#define DEC_MAJ_VERSION 0 #define DEC_MAJ_VERSION 0
#define DEC_MIN_VERSION 6 #define DEC_MIN_VERSION 3
#define DEC_REV_VERSION 0 #define DEC_REV_VERSION 1
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). #define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames
// intra prediction modes
enum { B_DC_PRED = 0, // 4x4 modes
B_TM_PRED,
B_VE_PRED,
B_HE_PRED,
B_RD_PRED,
B_VR_PRED,
B_LD_PRED,
B_VL_PRED,
B_HD_PRED,
B_HU_PRED,
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
// Luma16 or UV modes
DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
B_PRED = NUM_BMODES, // refined I4x4 mode
// special modes
B_DC_PRED_NOTOP = 4,
B_DC_PRED_NOLEFT = 5,
B_DC_PRED_NOTOPLEFT = 6,
NUM_B_DC_MODES = 7 };
enum { MB_FEATURE_TREE_PROBS = 3,
NUM_MB_SEGMENTS = 4,
NUM_REF_LF_DELTAS = 4,
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
MAX_NUM_PARTITIONS = 8,
// Probabilities
NUM_TYPES = 4,
NUM_BANDS = 8,
NUM_CTX = 3,
NUM_PROBAS = 11,
NUM_MV_PROBAS = 19 };
// YUV-cache parameters.
// Constraints are: We need to store one 16x16 block of luma samples (y), // Constraints are: We need to store one 16x16 block of luma samples (y),
// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned, // and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned,
// in order to be SIMD-friendly. We also need to store the top, left and // in order to be SIMD-friendly. We also need to store the top, left and
@ -56,15 +92,14 @@ extern "C" {
// 'y' = y-samples 'u' = u-samples 'v' = u-samples // 'y' = y-samples 'u' = u-samples 'v' = u-samples
// '|' = left sample, '-' = top sample, '+' = top-left sample // '|' = left sample, '-' = top sample, '+' = top-left sample
// 't' = extra top-right sample for 4x4 modes // 't' = extra top-right sample for 4x4 modes
// With this layout, BPS (=Bytes Per Scan-line) is one cacheline size.
#define BPS 32 // this is the common stride used by yuv[]
#define YUV_SIZE (BPS * 17 + BPS * 9) #define YUV_SIZE (BPS * 17 + BPS * 9)
#define Y_SIZE (BPS * 17) #define Y_SIZE (BPS * 17)
#define Y_OFF (BPS * 1 + 8) #define Y_OFF (BPS * 1 + 8)
#define U_OFF (Y_OFF + BPS * 16 + BPS) #define U_OFF (Y_OFF + BPS * 16 + BPS)
#define V_OFF (U_OFF + 16) #define V_OFF (U_OFF + 16)
// minimal width under which lossy multi-threading is always disabled
#define MIN_WIDTH_FOR_THREADS 512
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Headers // Headers
@ -93,19 +128,15 @@ typedef struct {
int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
} VP8SegmentHeader; } VP8SegmentHeader;
// probas associated to one of the contexts
typedef uint8_t VP8ProbaArray[NUM_PROBAS];
typedef struct { // all the probas associated to one band
VP8ProbaArray probas_[NUM_CTX];
} VP8BandProbas;
// Struct collecting all frame-persistent probabilities. // Struct collecting all frame-persistent probabilities.
typedef struct { typedef struct {
uint8_t segments_[MB_FEATURE_TREE_PROBS]; uint8_t segments_[MB_FEATURE_TREE_PROBS];
// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4 // Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
VP8BandProbas bands_[NUM_TYPES][NUM_BANDS]; uint8_t coeffs_[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1]; #ifndef ONLY_KEYFRAME_CODE
uint8_t ymode_[4], uvmode_[3];
uint8_t mv_[2][NUM_MV_PROBAS];
#endif
} VP8Proba; } VP8Proba;
// Filter parameters // Filter parameters
@ -122,61 +153,32 @@ typedef struct {
// Informations about the macroblocks. // Informations about the macroblocks.
typedef struct { // filter specs typedef struct { // filter specs
uint8_t f_limit_; // filter limit in [3..189], or 0 if no filtering unsigned int f_level_:6; // filter strength: 0..63
uint8_t f_ilevel_; // inner limit in [1..63] unsigned int f_ilevel_:6; // inner limit: 1..63
uint8_t f_inner_; // do inner filtering? unsigned int f_inner_:1; // do inner filtering?
uint8_t hev_thresh_; // high edge variance threshold in [0..2]
} VP8FInfo; } VP8FInfo;
typedef struct { // Top/Left Contexts used for syntax-parsing typedef struct { // used for syntax-parsing
uint8_t nz_; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma) unsigned int nz_:24; // non-zero AC/DC coeffs (24bit)
uint8_t nz_dc_; // non-zero DC coeff (1bit) unsigned int dc_nz_:1; // non-zero DC coeffs
unsigned int skip_:1; // block type
} VP8MB; } VP8MB;
// Dequantization matrices // Dequantization matrices
typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower). typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
typedef struct { typedef struct {
quant_t y1_mat_, y2_mat_, uv_mat_; quant_t y1_mat_, y2_mat_, uv_mat_;
int uv_quant_; // U/V quantizer value
int dither_; // dithering amplitude (0 = off, max=255)
} VP8QuantMatrix; } VP8QuantMatrix;
// Data needed to reconstruct a macroblock
typedef struct {
int16_t coeffs_[384]; // 384 coeffs = (16+4+4) * 4*4
uint8_t is_i4x4_; // true if intra4x4
uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
uint8_t uvmode_; // chroma prediction mode
// bit-wise info about the content of each sub-4x4 blocks (in decoding order).
// Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
// code=0 -> no coefficient
// code=1 -> only DC
// code=2 -> first three coefficients are non-zero
// code=3 -> more than three coefficients are non-zero
// This allows to call specialized transform functions.
uint32_t non_zero_y_;
uint32_t non_zero_uv_;
uint8_t dither_; // local dithering strength (deduced from non_zero_*)
uint8_t skip_;
uint8_t segment_;
} VP8MBData;
// Persistent information needed by the parallel processing // Persistent information needed by the parallel processing
typedef struct { typedef struct {
int id_; // cache row to process (in [0..2]) int id_; // cache row to process (in [0..2])
int mb_y_; // macroblock position of the row int mb_y_; // macroblock position of the row
int filter_row_; // true if row-filtering is needed int filter_row_; // true if row-filtering is needed
VP8FInfo* f_info_; // filter strengths (swapped with dec->f_info_) VP8FInfo* f_info_; // filter strengths
VP8MBData* mb_data_; // reconstruction data (swapped with dec->mb_data_) VP8Io io_; // copy of the VP8Io to pass to put()
VP8Io io_; // copy of the VP8Io to pass to put()
} VP8ThreadContext; } VP8ThreadContext;
// Saved top samples, per macroblock. Fits into a cache-line.
typedef struct {
uint8_t y[16], u[8], v[8];
} VP8TopSamples;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// VP8Decoder: the main opaque structure handed over to user // VP8Decoder: the main opaque structure handed over to user
@ -196,8 +198,7 @@ struct VP8Decoder {
// Worker // Worker
WebPWorker worker_; WebPWorker worker_;
int mt_method_; // multi-thread method: 0=off, 1=[parse+recon][filter] int use_threads_; // use multi-thread
// 2=[parse][recon+filter]
int cache_id_; // current cache row int cache_id_; // current cache row
int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3) int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3)
VP8ThreadContext thread_ctx_; // Thread context VP8ThreadContext thread_ctx_; // Thread context
@ -209,14 +210,17 @@ struct VP8Decoder {
int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered
int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded
// number of partitions minus one. // number of partitions.
uint32_t num_parts_minus_one_; int num_parts_;
// per-partition boolean decoders. // per-partition boolean decoders.
VP8BitReader parts_[MAX_NUM_PARTITIONS]; VP8BitReader parts_[MAX_NUM_PARTITIONS];
// Dithering strength, deduced from decoding options // buffer refresh flags
int dither_; // whether to use dithering or not // bit 0: refresh Gold, bit 1: refresh Alt
VP8Random dithering_rg_; // random generator for dithering // bit 2-3: copy to Gold, bit 4-5: copy to Alt
// bit 6: Gold sign bias, bit 7: Alt sign bias
// bit 8: refresh last frame
uint32_t buffer_flags_;
// dequantization (one set of DC/AC dequant factor per segment) // dequantization (one set of DC/AC dequant factor per segment)
VP8QuantMatrix dqm_[NUM_MB_SEGMENTS]; VP8QuantMatrix dqm_[NUM_MB_SEGMENTS];
@ -225,18 +229,24 @@ struct VP8Decoder {
VP8Proba proba_; VP8Proba proba_;
int use_skip_proba_; int use_skip_proba_;
uint8_t skip_p_; uint8_t skip_p_;
#ifndef ONLY_KEYFRAME_CODE
uint8_t intra_p_, last_p_, golden_p_;
VP8Proba proba_saved_;
int update_proba_;
#endif
// Boundary data cache and persistent buffers. // Boundary data cache and persistent buffers.
uint8_t* intra_t_; // top intra modes values: 4 * mb_w_ uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
uint8_t intra_l_[4]; // left intra modes values uint8_t intra_l_[4]; // left intra modes values
uint8_t* y_t_; // top luma samples: 16 * mb_w_
uint8_t* u_t_, *v_t_; // top u/v samples: 8 * mb_w_ each
VP8TopSamples* yuv_t_; // top y/u/v samples VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
VP8FInfo* f_info_; // filter strength info
uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
int16_t* coeffs_; // 384 coeffs = (16+8+8) * 4*4
VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1) uint8_t* cache_y_; // macroblock row for storing unfiltered samples
VP8FInfo* f_info_; // filter strength info
uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
uint8_t* cache_y_; // macroblock row for storing unfiltered samples
uint8_t* cache_u_; uint8_t* cache_u_;
uint8_t* cache_v_; uint8_t* cache_v_;
int cache_y_stride_; int cache_y_stride_;
@ -248,21 +258,32 @@ struct VP8Decoder {
// Per macroblock non-persistent infos. // Per macroblock non-persistent infos.
int mb_x_, mb_y_; // current position, in macroblock units int mb_x_, mb_y_; // current position, in macroblock units
VP8MBData* mb_data_; // parsed reconstruction data uint8_t is_i4x4_; // true if intra4x4
uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
uint8_t uvmode_; // chroma prediction mode
uint8_t segment_; // block's segment
// bit-wise info about the content of each sub-4x4 blocks: there are 16 bits
// for luma (bits #0->#15), then 4 bits for chroma-u (#16->#19) and 4 bits for
// chroma-v (#20->#23), each corresponding to one 4x4 block in decoding order.
// If the bit is set, the 4x4 block contains some non-zero coefficients.
uint32_t non_zero_;
uint32_t non_zero_ac_;
// Filtering side-info // Filtering side-info
int filter_type_; // 0=off, 1=simple, 2=complex int filter_type_; // 0=off, 1=simple, 2=complex
int filter_row_; // per-row flag
VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type
// Alpha // extensions
struct ALPHDecoder* alph_dec_; // alpha-plane decoder object const uint8_t* alpha_data_; // compressed alpha data (if present)
const uint8_t* alpha_data_; // compressed alpha data (if present)
size_t alpha_data_size_; size_t alpha_data_size_;
int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_ int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_
uint8_t* alpha_plane_mem_; // memory allocated for alpha_plane_ uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
const uint8_t* alpha_prev_line_; // last decoded alpha row (or NULL) int layer_colorspace_;
int alpha_dithering_; // derived from decoding options (0=off, 100=full) const uint8_t* layer_data_; // compressed layer data (if present)
size_t layer_data_size_;
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -275,14 +296,15 @@ int VP8SetError(VP8Decoder* const dec,
// in tree.c // in tree.c
void VP8ResetProba(VP8Proba* const proba); void VP8ResetProba(VP8Proba* const proba);
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec); void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec);
// parses one row of intra mode data in partition 0, returns !eof void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec);
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec);
// in quant.c // in quant.c
void VP8ParseQuant(VP8Decoder* const dec); void VP8ParseQuant(VP8Decoder* const dec);
// in frame.c // in frame.c
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io); int VP8InitFrame(VP8Decoder* const dec, VP8Io* io);
// Predict a block and add residual
void VP8ReconstructBlock(VP8Decoder* const dec);
// Call io->setup() and finish setting up scan parameters. // Call io->setup() and finish setting up scan parameters.
// After this call returns, one must always call VP8ExitCritical() with the // After this call returns, one must always call VP8ExitCritical() with the
// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK // same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
@ -291,15 +313,7 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
// Must always be called in pair with VP8EnterCritical(). // Must always be called in pair with VP8EnterCritical().
// Returns false in case of error. // Returns false in case of error.
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io); int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
// Return the multi-threading method to use (0=off), depending // Process the last decoded row (filtering + output)
// on options and bitstream size. Only for lossy decoding.
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
const WebPHeaderStructure* const headers,
int width, int height);
// Initialize dithering post-process if needed.
void VP8InitDithering(const WebPDecoderOptions* const options,
VP8Decoder* const dec);
// Process the last decoded row (filtering + output).
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io); int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
// To be called at the start of a new scanline, to initialize predictors. // To be called at the start of a new scanline, to initialize predictors.
void VP8InitScanline(VP8Decoder* const dec); void VP8InitScanline(VP8Decoder* const dec);
@ -308,12 +322,14 @@ int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
// in alpha.c // in alpha.c
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
const VP8Io* const io,
int row, int num_rows); int row, int num_rows);
// in layer.c
int VP8DecodeLayer(VP8Decoder* const dec);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

1249
src/dec/vp8l.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -16,12 +16,13 @@
#define WEBP_DEC_VP8LI_H_ #define WEBP_DEC_VP8LI_H_
#include <string.h> // for memcpy() #include <string.h> // for memcpy()
#include "./webpi_dec.h" #include "./webpi.h"
#include "../utils/bit_reader_utils.h" #include "../utils/bit_reader.h"
#include "../utils/color_cache_utils.h" #include "../utils/color_cache.h"
#include "../utils/huffman_utils.h" #include "../utils/huffman.h"
#include "../webp/format_constants.h"
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
@ -40,10 +41,13 @@ struct VP8LTransform {
uint32_t *data_; // transform data. uint32_t *data_; // transform data.
}; };
typedef struct {
HuffmanTree htrees_[HUFFMAN_CODES_PER_META_CODE];
} HTreeGroup;
typedef struct { typedef struct {
int color_cache_size_; int color_cache_size_;
VP8LColorCache color_cache_; VP8LColorCache color_cache_;
VP8LColorCache saved_color_cache_; // for incremental
int huffman_mask_; int huffman_mask_;
int huffman_subsample_bits_; int huffman_subsample_bits_;
@ -51,12 +55,11 @@ typedef struct {
uint32_t *huffman_image_; uint32_t *huffman_image_;
int num_htree_groups_; int num_htree_groups_;
HTreeGroup *htree_groups_; HTreeGroup *htree_groups_;
HuffmanCode *huffman_tables_;
} VP8LMetadata; } VP8LMetadata;
typedef struct VP8LDecoder VP8LDecoder; typedef struct {
struct VP8LDecoder {
VP8StatusCode status_; VP8StatusCode status_;
VP8LDecodeState action_;
VP8LDecodeState state_; VP8LDecodeState state_;
VP8Io *io_; VP8Io *io_;
@ -67,16 +70,10 @@ struct VP8LDecoder {
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage. uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
VP8LBitReader br_; VP8LBitReader br_;
int incremental_; // if true, incremental decoding is expected
VP8LBitReader saved_br_; // note: could be local variables too
int saved_last_pixel_;
int width_; int width_;
int height_; int height_;
int last_row_; // last input row decoded so far. int last_row_; // last input row decoded so far.
int last_pixel_; // last pixel decoded so far. However, it may
// not be transformed, scaled and
// color-converted yet.
int last_out_row_; // last row output so far. int last_out_row_; // last row output so far.
VP8LMetadata hdr_; VP8LMetadata hdr_;
@ -88,26 +85,18 @@ struct VP8LDecoder {
uint8_t *rescaler_memory; // Working memory for rescaling work. uint8_t *rescaler_memory; // Working memory for rescaling work.
WebPRescaler *rescaler; // Common rescaler for all channels. WebPRescaler *rescaler; // Common rescaler for all channels.
}; } VP8LDecoder;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// internal functions. Not public. // internal functions. Not public.
struct ALPHDecoder; // Defined in dec/alphai.h.
// in vp8l.c // in vp8l.c
// Decodes image header for alpha data stored using lossless compression. // Decodes a raw image stream (without header) and store the alpha data
// Returns false in case of error. // into *output, which must be of size width x height. Returns false in case
int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec, // of error.
const uint8_t* const data, size_t data_size); int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
size_t data_size, uint8_t* const output);
// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are
// already decoded in previous call(s), it will resume decoding from where it
// was paused.
// Returns false in case of bitstream error.
int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec,
int last_row);
// Allocates and initialize a new lossless decoder instance. // Allocates and initialize a new lossless decoder instance.
VP8LDecoder* VP8LNew(void); VP8LDecoder* VP8LNew(void);
@ -128,7 +117,7 @@ void VP8LDelete(VP8LDecoder* const dec);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

View File

@ -13,12 +13,15 @@
#include <stdlib.h> #include <stdlib.h>
#include "./vp8i_dec.h" #include "./vp8i.h"
#include "./vp8li_dec.h" #include "./vp8li.h"
#include "./webpi_dec.h" #include "./webpi.h"
#include "../utils/utils.h"
#include "../webp/mux_types.h" // ALPHA_FLAG #include "../webp/mux_types.h" // ALPHA_FLAG
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// RIFF layout is: // RIFF layout is:
// Offset tag // Offset tag
@ -39,20 +42,27 @@
// 20..23 VP8X flags bit-map corresponding to the chunk-types present. // 20..23 VP8X flags bit-map corresponding to the chunk-types present.
// 24..26 Width of the Canvas Image. // 24..26 Width of the Canvas Image.
// 27..29 Height of the Canvas Image. // 27..29 Height of the Canvas Image.
// There can be extra chunks after the "VP8X" chunk (ICCP, ANMF, VP8, VP8L, // There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
// XMP, EXIF ...) // VP8L, XMP, EXIF ...)
// All sizes are in little-endian order. // All sizes are in little-endian order.
// Note: chunk data size must be padded to multiple of 2 when written. // Note: chunk data size must be padded to multiple of 2 when written.
static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) {
return data[0] | (data[1] << 8) | (data[2] << 16);
}
static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) {
return (uint32_t)get_le24(data) | (data[3] << 24);
}
// Validates the RIFF container (if detected) and skips over it. // Validates the RIFF container (if detected) and skips over it.
// If a RIFF container is detected, returns: // If a RIFF container is detected,
// VP8_STATUS_BITSTREAM_ERROR for invalid header, // Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
// VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true, // VP8_STATUS_OK otherwise.
// and VP8_STATUS_OK otherwise.
// In case there are not enough bytes (partial RIFF container), return 0 for // In case there are not enough bytes (partial RIFF container), return 0 for
// *riff_size. Else return the RIFF size extracted from the header. // *riff_size. Else return the RIFF size extracted from the header.
static VP8StatusCode ParseRIFF(const uint8_t** const data, static VP8StatusCode ParseRIFF(const uint8_t** const data,
size_t* const data_size, int have_all_data, size_t* const data_size,
size_t* const riff_size) { size_t* const riff_size) {
assert(data != NULL); assert(data != NULL);
assert(data_size != NULL); assert(data_size != NULL);
@ -63,7 +73,7 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
if (memcmp(*data + 8, "WEBP", TAG_SIZE)) { if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature. return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
} else { } else {
const uint32_t size = GetLE32(*data + TAG_SIZE); const uint32_t size = get_le32(*data + TAG_SIZE);
// Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn"). // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
if (size < TAG_SIZE + CHUNK_HEADER_SIZE) { if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
@ -71,9 +81,6 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
if (size > MAX_CHUNK_PAYLOAD) { if (size > MAX_CHUNK_PAYLOAD) {
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
} }
if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
}
// We have a RIFF container. Skip it. // We have a RIFF container. Skip it.
*riff_size = size; *riff_size = size;
*data += RIFF_HEADER_SIZE; *data += RIFF_HEADER_SIZE;
@ -109,7 +116,7 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
if (!memcmp(*data, "VP8X", TAG_SIZE)) { if (!memcmp(*data, "VP8X", TAG_SIZE)) {
int width, height; int width, height;
uint32_t flags; uint32_t flags;
const uint32_t chunk_size = GetLE32(*data + TAG_SIZE); const uint32_t chunk_size = get_le32(*data + TAG_SIZE);
if (chunk_size != VP8X_CHUNK_SIZE) { if (chunk_size != VP8X_CHUNK_SIZE) {
return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size. return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
} }
@ -118,9 +125,9 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
if (*data_size < vp8x_size) { if (*data_size < vp8x_size) {
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data. return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
} }
flags = GetLE32(*data + 8); flags = get_le32(*data + 8);
width = 1 + GetLE24(*data + 12); width = 1 + get_le24(*data + 12);
height = 1 + GetLE24(*data + 15); height = 1 + get_le24(*data + 15);
if (width * (uint64_t)height >= MAX_IMAGE_AREA) { if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
return VP8_STATUS_BITSTREAM_ERROR; // image is too large return VP8_STATUS_BITSTREAM_ERROR; // image is too large
} }
@ -174,7 +181,7 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
return VP8_STATUS_NOT_ENOUGH_DATA; return VP8_STATUS_NOT_ENOUGH_DATA;
} }
chunk_size = GetLE32(buf + TAG_SIZE); chunk_size = get_le32(buf + TAG_SIZE);
if (chunk_size > MAX_CHUNK_PAYLOAD) { if (chunk_size > MAX_CHUNK_PAYLOAD) {
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size. return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
} }
@ -220,8 +227,9 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
// extracted from the VP8/VP8L chunk header. // extracted from the VP8/VP8L chunk header.
// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
size_t* const data_size, int have_all_data, size_t* const data_size,
size_t riff_size, size_t* const chunk_size, size_t riff_size,
size_t* const chunk_size,
int* const is_lossless) { int* const is_lossless) {
const uint8_t* const data = *data_ptr; const uint8_t* const data = *data_ptr;
const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
@ -240,13 +248,10 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
if (is_vp8 || is_vp8l) { if (is_vp8 || is_vp8l) {
// Bitstream contains VP8/VP8L header. // Bitstream contains VP8/VP8L header.
const uint32_t size = GetLE32(data + TAG_SIZE); const uint32_t size = get_le32(data + TAG_SIZE);
if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
} }
if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
}
// Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
*chunk_size = size; *chunk_size = size;
*data_ptr += CHUNK_HEADER_SIZE; *data_ptr += CHUNK_HEADER_SIZE;
@ -280,17 +285,9 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
int* const height, int* const height,
int* const has_alpha, int* const has_alpha,
int* const has_animation, int* const has_animation,
int* const format,
WebPHeaderStructure* const headers) { WebPHeaderStructure* const headers) {
int canvas_width = 0;
int canvas_height = 0;
int image_width = 0;
int image_height = 0;
int found_riff = 0; int found_riff = 0;
int found_vp8x = 0; int found_vp8x = 0;
int animation_present = 0;
const int have_all_data = (headers != NULL) ? headers->have_all_data : 0;
VP8StatusCode status; VP8StatusCode status;
WebPHeaderStructure hdrs; WebPHeaderStructure hdrs;
@ -302,7 +299,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
hdrs.data_size = data_size; hdrs.data_size = data_size;
// Skip over RIFF header. // Skip over RIFF header.
status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size); status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return status; // Wrong RIFF header / insufficient data. return status; // Wrong RIFF header / insufficient data.
} }
@ -311,33 +308,23 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
// Skip over VP8X. // Skip over VP8X.
{ {
uint32_t flags = 0; uint32_t flags = 0;
status = ParseVP8X(&data, &data_size, &found_vp8x, status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
&canvas_width, &canvas_height, &flags);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return status; // Wrong VP8X / insufficient data. return status; // Wrong VP8X / insufficient data.
} }
animation_present = !!(flags & ANIMATION_FLAG);
if (!found_riff && found_vp8x) { if (!found_riff && found_vp8x) {
// Note: This restriction may be removed in the future, if it becomes // Note: This restriction may be removed in the future, if it becomes
// necessary to send VP8X chunk to the decoder. // necessary to send VP8X chunk to the decoder.
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
} }
if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
if (has_animation != NULL) *has_animation = animation_present; if (has_animation != NULL) *has_animation = !!(flags & ANIMATION_FLAG);
if (format != NULL) *format = 0; // default = undefined if (found_vp8x && headers == NULL) {
return VP8_STATUS_OK; // Return features from VP8X header.
image_width = canvas_width;
image_height = canvas_height;
if (found_vp8x && animation_present && headers == NULL) {
status = VP8_STATUS_OK;
goto ReturnWidthHeight; // Just return features from VP8X header.
} }
} }
if (data_size < TAG_SIZE) { if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;
status = VP8_STATUS_NOT_ENOUGH_DATA;
goto ReturnWidthHeight;
}
// Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
if ((found_riff && found_vp8x) || if ((found_riff && found_vp8x) ||
@ -345,49 +332,43 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
&hdrs.alpha_data, &hdrs.alpha_data_size); &hdrs.alpha_data, &hdrs.alpha_data_size);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
goto ReturnWidthHeight; // Invalid chunk size / insufficient data. return status; // Found an invalid chunk size / insufficient data.
} }
} }
// Skip over VP8/VP8L header. // Skip over VP8/VP8L header.
status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size, status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
&hdrs.compressed_size, &hdrs.is_lossless); &hdrs.compressed_size, &hdrs.is_lossless);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data. return status; // Wrong VP8/VP8L chunk-header / insufficient data.
} }
if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
} }
if (format != NULL && !animation_present) {
*format = hdrs.is_lossless ? 2 : 1;
}
if (!hdrs.is_lossless) { if (!hdrs.is_lossless) {
if (data_size < VP8_FRAME_HEADER_SIZE) { if (data_size < VP8_FRAME_HEADER_SIZE) {
status = VP8_STATUS_NOT_ENOUGH_DATA; return VP8_STATUS_NOT_ENOUGH_DATA;
goto ReturnWidthHeight;
} }
// Validates raw VP8 data. // Validates raw VP8 data.
if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, if (!VP8GetInfo(data, data_size,
&image_width, &image_height)) { (uint32_t)hdrs.compressed_size, width, height)) {
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
} }
} else { } else {
if (data_size < VP8L_FRAME_HEADER_SIZE) { if (data_size < VP8L_FRAME_HEADER_SIZE) {
status = VP8_STATUS_NOT_ENOUGH_DATA; return VP8_STATUS_NOT_ENOUGH_DATA;
goto ReturnWidthHeight;
} }
// Validates raw VP8L data. // Validates raw VP8L data.
if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
return VP8_STATUS_BITSTREAM_ERROR; return VP8_STATUS_BITSTREAM_ERROR;
} }
} }
// Validates image size coherency.
if (found_vp8x) { if (has_alpha != NULL) {
if (canvas_width != image_width || canvas_height != image_height) { // If the data did not contain a VP8X/VP8L chunk the only definitive way
return VP8_STATUS_BITSTREAM_ERROR; // to set this is by looking for alpha data (from an ALPH chunk).
} *has_alpha |= (hdrs.alpha_data != NULL);
} }
if (headers != NULL) { if (headers != NULL) {
*headers = hdrs; *headers = hdrs;
@ -395,31 +376,16 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
assert(headers->offset == headers->data_size - data_size); assert(headers->offset == headers->data_size - data_size);
} }
ReturnWidthHeight: return VP8_STATUS_OK; // Return features from VP8 header.
if (status == VP8_STATUS_OK ||
(status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) {
if (has_alpha != NULL) {
// If the data did not contain a VP8X/VP8L chunk the only definitive way
// to set this is by looking for alpha data (from an ALPH chunk).
*has_alpha |= (hdrs.alpha_data != NULL);
}
if (width != NULL) *width = image_width;
if (height != NULL) *height = image_height;
return VP8_STATUS_OK;
} else {
return status;
}
} }
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
// status is marked volatile as a workaround for a clang-3.8 (aarch64) bug VP8StatusCode status;
volatile VP8StatusCode status;
int has_animation = 0; int has_animation = 0;
assert(headers != NULL); assert(headers != NULL);
// fill out headers, ignore width/height/has_alpha. // fill out headers, ignore width/height/has_alpha.
status = ParseHeadersInternal(headers->data, headers->data_size, status = ParseHeadersInternal(headers->data, headers->data_size,
NULL, NULL, NULL, &has_animation, NULL, NULL, NULL, &has_animation, headers);
NULL, headers);
if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) { if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
// TODO(jzern): full support of animation frames will require API additions. // TODO(jzern): full support of animation frames will require API additions.
if (has_animation) { if (has_animation) {
@ -433,7 +399,7 @@ VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
// WebPDecParams // WebPDecParams
void WebPResetDecParams(WebPDecParams* const params) { void WebPResetDecParams(WebPDecParams* const params) {
if (params != NULL) { if (params) {
memset(params, 0, sizeof(*params)); memset(params, 0, sizeof(*params));
} }
} }
@ -450,7 +416,6 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
headers.data = data; headers.data = data;
headers.data_size = data_size; headers.data_size = data_size;
headers.have_all_data = 1;
status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks. status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return status; return status;
@ -467,6 +432,11 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
if (dec == NULL) { if (dec == NULL) {
return VP8_STATUS_OUT_OF_MEMORY; return VP8_STATUS_OUT_OF_MEMORY;
} }
#ifdef WEBP_USE_THREAD
dec->use_threads_ = params->options && (params->options->use_threads > 0);
#else
dec->use_threads_ = 0;
#endif
dec->alpha_data_ = headers.alpha_data; dec->alpha_data_ = headers.alpha_data;
dec->alpha_data_size_ = headers.alpha_data_size; dec->alpha_data_size_ = headers.alpha_data_size;
@ -478,10 +448,6 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
status = WebPAllocateDecBuffer(io.width, io.height, params->options, status = WebPAllocateDecBuffer(io.width, io.height, params->options,
params->output); params->output);
if (status == VP8_STATUS_OK) { // Decode if (status == VP8_STATUS_OK) { // Decode
// This change must be done before calling VP8Decode()
dec->mt_method_ = VP8GetThreadMethod(params->options, &headers,
io.width, io.height);
VP8InitDithering(params->options, dec);
if (!VP8Decode(dec, &io)) { if (!VP8Decode(dec, &io)) {
status = dec->status_; status = dec->status_;
} }
@ -510,12 +476,6 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
WebPFreeDecBuffer(params->output); WebPFreeDecBuffer(params->output);
} else {
if (params->options != NULL && params->options->flip) {
// This restores the original stride values if options->flip was used
// during the call to WebPAllocateDecBuffer above.
status = WebPFlipBuffer(params->output);
}
} }
return status; return status;
} }
@ -674,6 +634,7 @@ uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
static void DefaultFeatures(WebPBitstreamFeatures* const features) { static void DefaultFeatures(WebPBitstreamFeatures* const features) {
assert(features != NULL); assert(features != NULL);
memset(features, 0, sizeof(*features)); memset(features, 0, sizeof(*features));
features->bitstream_version = 0;
} }
static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
@ -687,7 +648,7 @@ static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
return ParseHeadersInternal(data, data_size, return ParseHeadersInternal(data, data_size,
&features->width, &features->height, &features->width, &features->height,
&features->has_alpha, &features->has_animation, &features->has_alpha, &features->has_animation,
&features->format, NULL); NULL);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -758,24 +719,9 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
} }
WebPResetDecParams(&params); WebPResetDecParams(&params);
params.options = &config->options;
params.output = &config->output; params.output = &config->output;
if (WebPAvoidSlowMemory(params.output, &config->input)) { params.options = &config->options;
// decoding to slow memory: use a temporary in-mem buffer to decode into. status = DecodeInto(data, data_size, &params);
WebPDecBuffer in_mem_buffer;
WebPInitDecBuffer(&in_mem_buffer);
in_mem_buffer.colorspace = config->output.colorspace;
in_mem_buffer.width = config->input.width;
in_mem_buffer.height = config->input.height;
params.output = &in_mem_buffer;
status = DecodeInto(data, data_size, &params);
if (status == VP8_STATUS_OK) { // do the slow-copy
status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output);
}
WebPFreeDecBuffer(&in_mem_buffer);
} else {
status = DecodeInto(data, data_size, &params);
}
return status; return status;
} }
@ -796,9 +742,9 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
h = options->crop_height; h = options->crop_height;
x = options->crop_left; x = options->crop_left;
y = options->crop_top; y = options->crop_top;
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 or YUV422
x &= ~1; x &= ~1;
y &= ~1; y &= ~1; // TODO(later): only for YUV420, not YUV422.
} }
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) { if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
return 0; // out of frame boundary error return 0; // out of frame boundary error
@ -814,17 +760,15 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
// Scaling // Scaling
io->use_scaling = (options != NULL) && (options->use_scaling > 0); io->use_scaling = (options != NULL) && (options->use_scaling > 0);
if (io->use_scaling) { if (io->use_scaling) {
int scaled_width = options->scaled_width; if (options->scaled_width <= 0 || options->scaled_height <= 0) {
int scaled_height = options->scaled_height;
if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) {
return 0; return 0;
} }
io->scaled_width = scaled_width; io->scaled_width = options->scaled_width;
io->scaled_height = scaled_height; io->scaled_height = options->scaled_height;
} }
// Filter // Filter
io->bypass_filtering = (options != NULL) && options->bypass_filtering; io->bypass_filtering = options && options->bypass_filtering;
// Fancy upsampler // Fancy upsampler
#ifdef FANCY_UPSAMPLING #ifdef FANCY_UPSAMPLING
@ -841,3 +785,7 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif

View File

@ -14,22 +14,19 @@
#ifndef WEBP_DEC_WEBPI_H_ #ifndef WEBP_DEC_WEBPI_H_
#define WEBP_DEC_WEBPI_H_ #define WEBP_DEC_WEBPI_H_
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
#include "../utils/rescaler_utils.h" #include "../utils/rescaler.h"
#include "./vp8_dec.h" #include "./decode_vp8.h"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// WebPDecParams: Decoding output parameters. Transient internal object. // WebPDecParams: Decoding output parameters. Transient internal object.
typedef struct WebPDecParams WebPDecParams; typedef struct WebPDecParams WebPDecParams;
typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p); typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
typedef int (*OutputAlphaFunc)(const VP8Io* const io, WebPDecParams* const p, typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos);
int expected_num_out_lines);
typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos,
int max_out_lines);
struct WebPDecParams { struct WebPDecParams {
WebPDecBuffer* output; // output buffer. WebPDecBuffer* output; // output buffer.
@ -38,12 +35,12 @@ struct WebPDecParams {
int last_y; // coordinate of the line that was last output int last_y; // coordinate of the line that was last output
const WebPDecoderOptions* options; // if not NULL, use alt decoding features const WebPDecoderOptions* options; // if not NULL, use alt decoding features
// rescalers
WebPRescaler* scaler_y, *scaler_u, *scaler_v, *scaler_a; // rescalers WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a;
void* memory; // overall scratch memory for the output work. void* memory; // overall scratch memory for the output work.
OutputFunc emit; // output RGB or YUV samples OutputFunc emit; // output RGB or YUV samples
OutputAlphaFunc emit_alpha; // output alpha channel OutputFunc emit_alpha; // output alpha channel
OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
}; };
@ -57,7 +54,6 @@ void WebPResetDecParams(WebPDecParams* const params);
typedef struct { typedef struct {
const uint8_t* data; // input buffer const uint8_t* data; // input buffer
size_t data_size; // input buffer size size_t data_size; // input buffer size
int have_all_data; // true if all data is known to be available
size_t offset; // offset to main data chunk (VP8 or VP8L) size_t offset; // offset to main data chunk (VP8 or VP8L)
const uint8_t* alpha_data; // points to alpha chunk (if present) const uint8_t* alpha_data; // points to alpha chunk (if present)
size_t alpha_data_size; // alpha chunk size size_t alpha_data_size; // alpha chunk size
@ -97,36 +93,23 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
// dimension / etc.). If *options is not NULL, also verify that the options' // dimension / etc.). If *options is not NULL, also verify that the options'
// parameters are valid and apply them to the width/height dimensions of the // parameters are valid and apply them to the width/height dimensions of the
// output buffer. This takes cropping / scaling / rotation into account. // output buffer. This takes cropping / scaling / rotation into account.
// Also incorporates the options->flip flag to flip the buffer parameters if
// needed.
VP8StatusCode WebPAllocateDecBuffer(int width, int height, VP8StatusCode WebPAllocateDecBuffer(int width, int height,
const WebPDecoderOptions* const options, const WebPDecoderOptions* const options,
WebPDecBuffer* const buffer); WebPDecBuffer* const buffer);
// Flip buffer vertically by negating the various strides.
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer);
// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the // Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the
// memory (still held by 'src'). No pixels are copied. // memory (still held by 'src').
void WebPCopyDecBuffer(const WebPDecBuffer* const src, void WebPCopyDecBuffer(const WebPDecBuffer* const src,
WebPDecBuffer* const dst); WebPDecBuffer* const dst);
// Copy and transfer ownership from src to dst (beware of parameter order!) // Copy and transfer ownership from src to dst (beware of parameter order!)
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst);
// Copy pixels from 'src' into a *preallocated* 'dst' buffer. Returns
// VP8_STATUS_INVALID_PARAM if the 'dst' is not set up correctly for the copy.
VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src,
WebPDecBuffer* const dst);
// Returns true if decoding will be slow with the current configuration
// and bitstream features.
int WebPAvoidSlowMemory(const WebPDecBuffer* const output,
const WebPBitstreamFeatures* const features);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifdef __cplusplus #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif

View File

@ -1,7 +1,8 @@
AM_CPPFLAGS = -I$(top_srcdir)/src
lib_LTLIBRARIES = libwebpdemux.la lib_LTLIBRARIES = libwebpdemux.la
libwebpdemux_la_SOURCES = libwebpdemux_la_SOURCES =
libwebpdemux_la_SOURCES += anim_decode.c demux.c libwebpdemux_la_SOURCES += demux.c
libwebpdemuxinclude_HEADERS = libwebpdemuxinclude_HEADERS =
libwebpdemuxinclude_HEADERS += ../webp/demux.h libwebpdemuxinclude_HEADERS += ../webp/demux.h
@ -9,6 +10,6 @@ libwebpdemuxinclude_HEADERS += ../webp/mux_types.h
libwebpdemuxinclude_HEADERS += ../webp/types.h libwebpdemuxinclude_HEADERS += ../webp/types.h
libwebpdemux_la_LIBADD = ../libwebp.la libwebpdemux_la_LIBADD = ../libwebp.la
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:2:0 libwebpdemux_la_LDFLAGS = -no-undefined -version-info 0:1:0
libwebpdemuxincludedir = $(includedir)/webp libwebpdemuxincludedir = $(includedir)/webp
pkgconfig_DATA = libwebpdemux.pc pkgconfig_DATA = libwebpdemux.pc

Some files were not shown because too many files have changed in this diff Show More