mirror of
https://github.com/webmproject/libwebp.git
synced 2025-12-24 14:06:27 +01:00
Compare commits
267 Commits
cea684626d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b78419750 | ||
|
|
d078f7d201 | ||
|
|
a9f3b447ae | ||
|
|
d67d73bf5a | ||
|
|
d7af7dd7cb | ||
|
|
934b7d7448 | ||
|
|
2d5c82155a | ||
|
|
507c372276 | ||
|
|
4c7aaa92e4 | ||
|
|
b81f462050 | ||
|
|
f776da44f4 | ||
|
|
4fc426c461 | ||
|
|
770ca097e7 | ||
|
|
488f26bc16 | ||
|
|
3532891de4 | ||
|
|
a3dddea9b5 | ||
|
|
5465220b03 | ||
|
|
4ebf0b0ac8 | ||
|
|
df24b5632e | ||
|
|
3697df15d7 | ||
|
|
acedec6860 | ||
|
|
41c2a8d2f5 | ||
|
|
05934f93b9 | ||
|
|
2760d87827 | ||
|
|
2d16e4ac95 | ||
|
|
2a755e7eec | ||
|
|
d1e9c93961 | ||
|
|
160ad07632 | ||
|
|
4efd97e825 | ||
|
|
ea3f3f7c00 | ||
|
|
3eb58b56d9 | ||
|
|
ed05414168 | ||
|
|
9f14c2605b | ||
|
|
0ee1a5f4b0 | ||
|
|
3066c7ea59 | ||
|
|
23359a1039 | ||
|
|
61791c774a | ||
|
|
e40787da71 | ||
|
|
9636d8e04f | ||
|
|
bbbf29a9b6 | ||
|
|
7a155ce913 | ||
|
|
64dce5d826 | ||
|
|
1f0a494e80 | ||
|
|
0e5f4ee3de | ||
|
|
158b533d3e | ||
|
|
13f42ea2d2 | ||
|
|
74f6afd3e6 | ||
|
|
0d14d84bdb | ||
|
|
c00d83f664 | ||
|
|
d5b3883812 | ||
|
|
aae8a3da33 | ||
|
|
b4dbec562f | ||
|
|
d16489f66c | ||
|
|
0fd008f832 | ||
|
|
3779daa97f | ||
|
|
f2372fba3b | ||
|
|
fd2c2cc05b | ||
|
|
fdc81cebda | ||
|
|
ed8b34cf16 | ||
|
|
94bfff3ffe | ||
|
|
1ba05593d0 | ||
|
|
b3f8ce7015 | ||
|
|
2074cb4ba1 | ||
|
|
1fdd4ef501 | ||
|
|
235286fd78 | ||
|
|
dab2cf21fa | ||
|
|
b7d30cfd94 | ||
|
|
17ba97c149 | ||
|
|
4cdb42070f | ||
|
|
baf42a5876 | ||
|
|
7ee251d3fd | ||
|
|
69c8056c7a | ||
|
|
5d3a9fc55b | ||
|
|
52135b8e00 | ||
|
|
ddcdfa6a42 | ||
|
|
ddabb66f23 | ||
|
|
f66f1ee95c | ||
|
|
f2061209d0 | ||
|
|
fde90a49e4 | ||
|
|
2be405c472 | ||
|
|
ed02bfa963 | ||
|
|
9d690dbf06 | ||
|
|
603d4055bc | ||
|
|
69b9b8525e | ||
|
|
9b3fc5f5e8 | ||
|
|
30b2c593c9 | ||
|
|
46d65e4a19 | ||
|
|
10d81e1ef0 | ||
|
|
101e2b303f | ||
|
|
7903644f24 | ||
|
|
8a2b400352 | ||
|
|
fb656b44f3 | ||
|
|
ac865676a9 | ||
|
|
fa93a9bb35 | ||
|
|
15b1de13da | ||
|
|
59dae3a508 | ||
|
|
3f96cbffa2 | ||
|
|
903b6d816f | ||
|
|
cdaac01490 | ||
|
|
456e2cbce1 | ||
|
|
de6aee46e1 | ||
|
|
f52d646039 | ||
|
|
9ab7a640e4 | ||
|
|
011c600dca | ||
|
|
9769bb767c | ||
|
|
47a744d582 | ||
|
|
7d857a1edc | ||
|
|
19f28b7889 | ||
|
|
4cc27eb808 | ||
|
|
6805c246e3 | ||
|
|
61e5c391d6 | ||
|
|
2246828be3 | ||
|
|
4bcea04803 | ||
|
|
6913049d6e | ||
|
|
6680723e66 | ||
|
|
0ce96e30e0 | ||
|
|
ac2795e904 | ||
|
|
c41d168d25 | ||
|
|
019ce505e9 | ||
|
|
e329ca4c26 | ||
|
|
ff87eeecc9 | ||
|
|
b0e8039062 | ||
|
|
0a6869b1bc | ||
|
|
54f23b049e | ||
|
|
db608edd71 | ||
|
|
313692d51e | ||
|
|
484991ce3f | ||
|
|
67cd2fc03a | ||
|
|
0de0a62305 | ||
|
|
f3109bd6c8 | ||
|
|
44257cb826 | ||
|
|
b569988d3f | ||
|
|
5531b1e7b7 | ||
|
|
b1feaa40b8 | ||
|
|
e721627c25 | ||
|
|
86924ed5cd | ||
|
|
3a55b076c9 | ||
|
|
bfea600a5f | ||
|
|
ab0a07fcf5 | ||
|
|
9aebd2a64e | ||
|
|
8c815d82d7 | ||
|
|
a81af56db9 | ||
|
|
4e63ff1eb6 | ||
|
|
a7002d2089 | ||
|
|
08b51dd130 | ||
|
|
4fa2191233 | ||
|
|
370aa5817e | ||
|
|
f83c6b328f | ||
|
|
6a3e656b6d | ||
|
|
fa6f56496a | ||
|
|
bf0bf1e749 | ||
|
|
e8ae210d0b | ||
|
|
ce53efd7ae | ||
|
|
1c3331702f | ||
|
|
85e098e58d | ||
|
|
418340d85b | ||
|
|
23ce76fa37 | ||
|
|
bbf3cbb1be | ||
|
|
f6b87e03fc | ||
|
|
8852f89ab5 | ||
|
|
e015dcc0b9 | ||
|
|
753ed11ef8 | ||
|
|
0cd0b7a701 | ||
|
|
2209ffba39 | ||
|
|
15e2e1ee3b | ||
|
|
98c2780100 | ||
|
|
eb3ff78159 | ||
|
|
57e324e2eb | ||
|
|
7191a602b0 | ||
|
|
19696e0a6f | ||
|
|
89b01eccca | ||
|
|
52a430a7b6 | ||
|
|
e53e213091 | ||
|
|
f8b360c419 | ||
|
|
eb4f813761 | ||
|
|
ad52d5fc7e | ||
|
|
ed7cd6a7f3 | ||
|
|
3a23b0f008 | ||
|
|
a99d0e6f04 | ||
|
|
1ed4654dc0 | ||
|
|
f0689e48cb | ||
|
|
24262266d0 | ||
|
|
3f54b1aa12 | ||
|
|
295804e4b9 | ||
|
|
5225592f6b | ||
|
|
00338240c1 | ||
|
|
44f91b0ddd | ||
|
|
ee8e8c620f | ||
|
|
a1ad3f1e37 | ||
|
|
321561b41f | ||
|
|
e0ae21d231 | ||
|
|
a4183d94c7 | ||
|
|
f2b3f52733 | ||
|
|
7c70ff7a3b | ||
|
|
9dd5ae819b | ||
|
|
613be8fc61 | ||
|
|
1d86819f49 | ||
|
|
743a5f092d | ||
|
|
565da14882 | ||
|
|
319860e919 | ||
|
|
815fc1e110 | ||
|
|
980b708e2c | ||
|
|
73b728cbb9 | ||
|
|
6a22b6709c | ||
|
|
7ed2b10ef0 | ||
|
|
654bfb040c | ||
|
|
f8f2410710 | ||
|
|
2af6c034ac | ||
|
|
a4d7a71533 | ||
|
|
c3d85ce4cf | ||
|
|
ad14e811cf | ||
|
|
74cd026edb | ||
|
|
a027aa93de | ||
|
|
25e17c686f | ||
|
|
aa2684fccc | ||
|
|
369238461b | ||
|
|
ceea8ff6b3 | ||
|
|
e4f7a9f0c7 | ||
|
|
1b4c967fbb | ||
|
|
9e5ecfaf00 | ||
|
|
da0d9c7d4e | ||
|
|
fcff86c71b | ||
|
|
b76c4a8416 | ||
|
|
306335198d | ||
|
|
4c85d860ea | ||
|
|
0ab789e067 | ||
|
|
0323645066 | ||
|
|
61e2cfdadd | ||
|
|
7bda3deb89 | ||
|
|
2ddaaf0aa5 | ||
|
|
a3ba6f19e9 | ||
|
|
f999d94f4a | ||
|
|
dfdcb7f95c | ||
|
|
78ed683978 | ||
|
|
d516a68e54 | ||
|
|
874069042e | ||
|
|
fdb229ea3a | ||
|
|
0c3cd9cc2c | ||
|
|
169dfbf931 | ||
|
|
2dd5eb9862 | ||
|
|
23bbafbeb8 | ||
|
|
35915b389e | ||
|
|
a32b436bd5 | ||
|
|
04d4b4f387 | ||
|
|
b1cb37e659 | ||
|
|
201894ef24 | ||
|
|
02eac8a741 | ||
|
|
84b118c9c3 | ||
|
|
052cf42f1a | ||
|
|
220ee52967 | ||
|
|
7861947813 | ||
|
|
14f09ab75b | ||
|
|
a78c5356ba | ||
|
|
bc49176355 | ||
|
|
34f9223829 | ||
|
|
367ca938f1 | ||
|
|
a582b53b74 | ||
|
|
0fd25d8406 | ||
|
|
f888291359 | ||
|
|
40e4ca60ea | ||
|
|
57883c78ed | ||
|
|
1c8eba978b | ||
|
|
2e81017c7a | ||
|
|
94de6c7fed | ||
|
|
51d9832a36 | ||
|
|
7bcb36b884 | ||
|
|
8e0cc14c3e |
4
.clang-format
Normal file
4
.clang-format
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
DerivePointerAlignment: false
|
||||
1
.mailmap
1
.mailmap
@@ -17,3 +17,4 @@ Roberto Alanis <alanisbaez@google.com>
|
||||
Brian Ledger <brianpl@google.com>
|
||||
Maryla Ustarroz-Calonge <maryla@google.com>
|
||||
Yannis Guyon <yguyon@google.com>
|
||||
Henner Zeller <hzeller@google.com> <h.zeller@acm.org>
|
||||
|
||||
5
AUTHORS
5
AUTHORS
@@ -10,12 +10,16 @@ Contributors:
|
||||
- Christian Duvivier (cduvivier at google dot com)
|
||||
- Christopher Degawa (ccom at randomderp dot com)
|
||||
- Clement Courbet (courbet at google dot com)
|
||||
- devtools-clrobot at google dot com (devtools-clrobot@google dot com)
|
||||
- Djordje Pesut (djordje dot pesut at imgtec dot com)
|
||||
- Frank (1433351828 at qq dot com)
|
||||
- Frank Barchard (fbarchard at google dot com)
|
||||
- Henner Zeller (hzeller at google dot com)
|
||||
- Hui Su (huisu at google dot com)
|
||||
- H. Vetinari (h dot vetinari at gmx dot com)
|
||||
- Ilya Kurdyukov (jpegqs at gmail dot com)
|
||||
- Ingvar Stepanyan (rreverser at google dot com)
|
||||
- Istvan Stefan (Istvan dot Stefan at arm dot com)
|
||||
- James Zern (jzern at google dot com)
|
||||
- Jan Engelhardt (jengelh at medozas dot de)
|
||||
- Jehan (jehan at girinstud dot io)
|
||||
@@ -62,6 +66,7 @@ Contributors:
|
||||
- Vincent Rabaud (vrabaud at google dot com)
|
||||
- Vlad Tsyrklevich (vtsyrklevich at chromium dot org)
|
||||
- Wan-Teh Chang (wtc at google dot com)
|
||||
- wrv (wrv at utexas dot edu)
|
||||
- Yang Zhang (yang dot zhang at arm dot com)
|
||||
- Yannis Guyon (yguyon at google dot com)
|
||||
- Zhi An Ng (zhin at chromium dot org)
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
if(APPLE)
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0072)
|
||||
cmake_policy(SET CMP0072 NEW)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
endif()
|
||||
|
||||
project(WebP C)
|
||||
@@ -50,6 +46,10 @@ option(WEBP_USE_THREAD "Enable threading support" ON)
|
||||
option(WEBP_NEAR_LOSSLESS "Enable near-lossless encoding" ON)
|
||||
option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces."
|
||||
OFF)
|
||||
option(
|
||||
WEBP_ENABLE_FBOUNDS_SAFETY
|
||||
"Enable -fbounds-safety for the part of the codebase which supports it. This expects an experimental toolchain."
|
||||
OFF)
|
||||
set(WEBP_BITTRACE "0" CACHE STRING "Bit trace mode (0=none, 1=bit, 2=bytes)")
|
||||
set_property(CACHE WEBP_BITTRACE PROPERTY STRINGS 0 1 2)
|
||||
option(WEBP_ENABLE_WUNUSED_RESULT "Add [[nodiscard]] to some functions. \
|
||||
@@ -88,6 +88,15 @@ if(WEBP_BUILD_WEBP_JS)
|
||||
message(NOTICE
|
||||
"wasm2js does not support SIMD, disabling webp.js generation.")
|
||||
endif()
|
||||
|
||||
if(NOT EMSCRIPTEN_VERSION)
|
||||
message(
|
||||
WARNING
|
||||
"EMSCRIPTEN_VERSION not detected!\n"
|
||||
"WEBP_BUILD_WEBP_JS is only supported with emcmake/emmake.\n"
|
||||
"The build may fail if those tools are not used. See webp_js/README.md."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(SHARPYUV_DEP_LIBRARIES)
|
||||
@@ -140,6 +149,16 @@ if(WIN32 AND BUILD_SHARED_LIBS)
|
||||
add_definitions(-DWEBP_DLL)
|
||||
endif()
|
||||
|
||||
# Compatibility with autoconf configure script's way of setting this
|
||||
if(CMAKE_C_BYTE_ORDER STREQUAL "BIG_ENDIAN")
|
||||
set(WORDS_BIGENDIAN TRUE)
|
||||
elseif(CMAKE_C_BYTE_ORDER STREQUAL "LITTLE_ENDIAN")
|
||||
set(WORDS_BIGENDIAN FALSE)
|
||||
else()
|
||||
set(WORDS_BIGENDIAN FALSE)
|
||||
message(WARNING "Endianness could not be determined.")
|
||||
endif()
|
||||
|
||||
# pkg-config variables used by *.pc.in.
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix "\${prefix}")
|
||||
@@ -403,6 +422,15 @@ target_include_directories(webputils PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_library(webp $<TARGET_OBJECTS:webpdecode> $<TARGET_OBJECTS:webpdsp>
|
||||
$<TARGET_OBJECTS:webpencode> $<TARGET_OBJECTS:webputils>)
|
||||
if(WEBP_ENABLE_FBOUNDS_SAFETY)
|
||||
# Enable -fbounds-safety only for webputils and webpdecode(r) for now.
|
||||
add_definitions(-DWEBP_SUPPORT_FBOUNDS_SAFETY=1)
|
||||
target_compile_options(webputils PRIVATE -fbounds-safety)
|
||||
target_compile_options(webputilsdecode PRIVATE -fbounds-safety)
|
||||
target_compile_options(webpdecode PRIVATE -fbounds-safety)
|
||||
target_compile_options(webpdecoder PRIVATE -fbounds-safety)
|
||||
endif()
|
||||
|
||||
target_link_libraries(webp sharpyuv)
|
||||
if(XCODE)
|
||||
libwebp_add_stub_file(webp)
|
||||
@@ -567,7 +595,8 @@ if(WEBP_BUILD_GIF2WEBP)
|
||||
add_executable(gif2webp ${GIF2WEBP_SRCS})
|
||||
target_link_libraries(gif2webp exampleutil imageioutil webp libwebpmux
|
||||
${WEBP_DEP_GIF_LIBRARIES})
|
||||
target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
|
||||
target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
install(TARGETS gif2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
|
||||
@@ -64,8 +64,7 @@ for additional details.
|
||||
|
||||
The C code style is based on the
|
||||
[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) and
|
||||
`clang-format --style=Google`, though this project doesn't use the tool to
|
||||
enforce the formatting.
|
||||
the formatting is enforced using `clang-format --style=Google`.
|
||||
|
||||
CMake files are formatted with
|
||||
[cmake-format](https://cmake-format.readthedocs.io/en/latest/). `cmake-format
|
||||
|
||||
200
ChangeLog
200
ChangeLog
@@ -1,3 +1,203 @@
|
||||
370aa581 webp_js/README.md: add some more code formatting (``)
|
||||
f83c6b32 CMakeLists: add warning for incorrect emscripten config
|
||||
6a3e656b update ChangeLog (tag: v1.6.0-rc1)
|
||||
bf0bf1e7 api.md: add WebPValidateDecoderConfig to pseudocode
|
||||
e8ae210d update NEWS
|
||||
ce53efd7 bump version to 1.6.0
|
||||
1c333170 update AUTHORS
|
||||
85e098e5 webpmux: fix heap overflow w/-get/-set
|
||||
418340d8 Merge "Make histogram allocation and access more readable and type-safe." into main
|
||||
23ce76fa Merge "VP8BitReaderSetBuffer: move NULL check to call site" into main
|
||||
bbf3cbb1 VP8BitReaderSetBuffer: move NULL check to call site
|
||||
f6b87e03 Fix const style guide
|
||||
8852f89a Have lossless return the same results with/without -mt
|
||||
e015dcc0 Make histogram allocation and access more readable and type-safe.
|
||||
753ed11e enc_neon.c: fix aarch64 compilation w/gcc < 8.5.0
|
||||
0cd0b7a7 enc_fuzzer.cc: remove duplicate <cstdlib> include
|
||||
2209ffba swig,cosmetics: normalize includes
|
||||
15e2e1ee analysis_enc.c: remove unused include
|
||||
98c27801 IWYU: Include all headers for symbols used in files.
|
||||
eb3ff781 Only use valid histograms in VP8LHistogramSet
|
||||
57e324e2 Refactor VP8LHistogram histogram_enc.cc
|
||||
7191a602 Merge "Generalize trivial histograms" into main
|
||||
19696e0a Merge "alpha_processing_sse2: quiet signed conv warning" into main
|
||||
89b01ecc Merge "cwebp: add `-resize_mode`" into main
|
||||
52a430a7 Generalize trivial histograms
|
||||
e53e2130 Cache all costs in the histograms
|
||||
f8b360c4 alpha_processing_sse2: quiet signed conv warning
|
||||
eb4f8137 cwebp: add `-resize_mode`
|
||||
ad52d5fc dec/dsp/enc/utils,cosmetics: rm struct member '_' suffix
|
||||
ed7cd6a7 utils.c,cosmetics: rm struct member '_' suffix
|
||||
3a23b0f0 random_utils.[hc],cosmetics: rm struct member '_' suffix
|
||||
a99d0e6f quant_levels_dec_utils.c,cosmetics: rm struct member '_' suffix
|
||||
1ed4654d huffman_encode_utils.[hc],cosmetics: rm struct member '_' suffix
|
||||
f0689e48 config_enc.c,cosmetics: rm struct member '_' suffix
|
||||
24262266 mux,cosmetics: rm struct member '_' suffix
|
||||
3f54b1aa demux,cosmetics: rm struct member '_' suffix
|
||||
295804e4 examples,cosmetics: rm struct member '_' suffix
|
||||
5225592f Refactor VP8LHistogram to hide initializations from the user.
|
||||
00338240 Remove some computations in histogram clustering
|
||||
44f91b0d Speed DispatchAlpha_SSE2 up
|
||||
ee8e8c62 Fix member naming for VP8LHistogram
|
||||
a1ad3f1e Merge "Remove now unused ExtraCostCombined" into main
|
||||
321561b4 Remove now unused ExtraCostCombined
|
||||
e0ae21d2 WebPMemoryWriterClear: use WebPMemoryWriterInit
|
||||
a4183d94 Remove the computation of ExtraCost when comparing histograms
|
||||
f2b3f527 Get AVX2 into WebP lossless
|
||||
7c70ff7a Clean dsp/lossless includes
|
||||
9dd5ae81 Use the full register in PredictorSub13_SSE2
|
||||
613be8fc Makefile.vc: add /MP to CFLAGS
|
||||
1d86819f Merge changes I1437390a,I10a20de5,I1ac777d1 into main
|
||||
743a5f09 enc_neon: enable vld1q_u8_x4 for clang & msvc
|
||||
565da148 pngdec.c: add support for 'eXIf' tag
|
||||
319860e9 pngdec.c: support ImageMagick app1 exif text data
|
||||
815fc1e1 pngdec.c: add missing #ifdef for png_get_iCCP
|
||||
980b708e enc_neon: fix build w/aarch64 gcc < 9.4.0
|
||||
73b728cb cmake: bump minimum version to 3.16
|
||||
6a22b670 Add a function to validate a WebPDecoderConfig
|
||||
7ed2b10e Use consistently signed stride types.
|
||||
654bfb04 Avoid nullptr arithmetic in VP8BitReaderSetBuffer
|
||||
f8f24107 Fix potential "divide by zero" in examples found by coverity
|
||||
2af6c034 Merge tag 'v1.5.0'
|
||||
a4d7a715 update ChangeLog (tag: v1.5.0, origin/1.5.0)
|
||||
c3d85ce4 update NEWS
|
||||
ad14e811 tests/fuzzer/*: add missing <string_view> include
|
||||
74cd026e fuzz_utils.cc: fix build error w/WEBP_REDUCE_SIZE
|
||||
a027aa93 mux_demux_api_fuzzer.cc: fix -Wshadow warning
|
||||
25e17c68 update ChangeLog (tag: v1.5.0-rc1)
|
||||
aa2684fc update NEWS
|
||||
36923846 bump version to 1.5.0
|
||||
ceea8ff6 update AUTHORS
|
||||
e4f7a9f0 img2webp: add a warning for unused options
|
||||
1b4c967f Merge "Properly check the data size against the end of the RIFF chunk" into main
|
||||
9e5ecfaf Properly check the data size against the end of the RIFF chunk
|
||||
da0d9c7d examples: exit w/failure w/no args
|
||||
fcff86c7 {gif,img}2webp: sync -m help w/cwebp
|
||||
b76c4a84 man/img2webp.1: sync -m text w/cwebp.1 & gif2webp.1
|
||||
30633519 muxread: fix reading of buffers > riff size
|
||||
4c85d860 yuv.h: update RGB<->YUV coefficients in comment
|
||||
0ab789e0 Merge changes I6dfedfd5,I2376e2dc into main
|
||||
03236450 {ios,xcframework}build.sh: fix compilation w/Xcode 16
|
||||
61e2cfda rework AddVectorEq_SSE2
|
||||
7bda3deb rework AddVector_SSE2
|
||||
2ddaaf0a Fix variable names in SharpYuvComputeConversionMatrix
|
||||
a3ba6f19 Makefile.vc: fix gif2webp link error
|
||||
f999d94f gif2webp: add -sharp_yuv/-near_lossless
|
||||
dfdcb7f9 Merge "lossless.h: fix function declaration mismatches" into main (tag: webp-rfc9649)
|
||||
78ed6839 fix overread in Intra4Preds_NEON
|
||||
d516a68e lossless.h: fix function declaration mismatches
|
||||
87406904 Merge "Improve documentation of SharpYuvConversionMatrix." into main
|
||||
fdb229ea Merge changes I07a7e36a,Ib29980f7,I2316122d,I2356e314,I32b53dd3, ... into main
|
||||
0c3cd9cc Improve documentation of SharpYuvConversionMatrix.
|
||||
169dfbf9 disable Intra4Preds_NEON
|
||||
2dd5eb98 dsp/yuv*: use WEBP_RESTRICT qualifier
|
||||
23bbafbe dsp/upsampling*: use WEBP_RESTRICT qualifier
|
||||
35915b38 dsp/rescaler*: use WEBP_RESTRICT qualifier
|
||||
a32b436b dsp/lossless*: use WEBP_RESTRICT qualifier
|
||||
04d4b4f3 dsp/filters*: use WEBP_RESTRICT qualifier
|
||||
b1cb37e6 dsp/enc*: use WEBP_RESTRICT qualifier
|
||||
201894ef dsp/dec*: use WEBP_RESTRICT qualifier
|
||||
02eac8a7 dsp/cost*: use WEBP_RESTRICT qualifier
|
||||
84b118c9 Merge "webp-container-spec: normalize notes & unknown chunk link" into main
|
||||
052cf42f webp-container-spec: normalize notes & unknown chunk link
|
||||
220ee529 Search for best predictor transform bits
|
||||
78619478 Try to reduce the sampling for the entropy image
|
||||
14f09ab7 webp-container-spec: reorder chunk size - N text
|
||||
a78c5356 Remove a useless malloc for entropy image
|
||||
bc491763 Merge "Refactor predictor finding" into main
|
||||
34f92238 man/{cwebp,img2webp}.1: rm 'if needed' from -sharp_yuv
|
||||
367ca938 Refactor predictor finding
|
||||
a582b53b webp-lossless-bitstream-spec: clarify some text
|
||||
0fd25d84 Merge "anim_encode.c: fix function ref in comment" into main
|
||||
f8882913 anim_encode.c: fix function ref in comment
|
||||
40e4ca60 specs_generation.md: update kramdown command line
|
||||
57883c78 img2webp: add -exact/-noexact per-frame options
|
||||
1c8eba97 img2webp,cosmetics: add missing '.' spacers to help
|
||||
2e81017c Convert predictor_enc.c to fixed point
|
||||
94de6c7f Merge "Fix fuzztest link errors w/-DBUILD_SHARED_LIBS=1" into main
|
||||
51d9832a Fix fuzztest link errors w/-DBUILD_SHARED_LIBS=1
|
||||
7bcb36b8 Merge "Fix static overflow warning." into main
|
||||
8e0cc14c Fix static overflow warning.
|
||||
cea68462 README.md: add security report note
|
||||
615e5874 Merge "make VP8LPredictor[01]_C() static" into main
|
||||
233e86b9 Merge changes Ie43dc5ef,I94cd8bab into main
|
||||
1a29fd2f make VP8LPredictor[01]_C() static
|
||||
dd9d3770 Do*Filter_*: remove row & num_rows parameters
|
||||
ab451a49 Do*Filter_C: remove dead 'inverse' code paths
|
||||
f9a480f7 {TrueMotion,TM16}_NEON: remove zero extension
|
||||
04834aca Merge changes I25c30a9e,I0a192fc6,I4cf89575 into main
|
||||
39a602af webp-lossless-bitstream-spec: normalize predictor transform ref
|
||||
f28c837d Merge "webp-container-spec: align anim pseudocode w/prose" into main
|
||||
74be8e22 Fix implicit conversion issues
|
||||
0c01db7c Merge "Increase the transform bits if possible." into main
|
||||
f2d6dc1e Increase the transform bits if possible.
|
||||
caa19e5b update link to issue tracker
|
||||
c9dd9bd4 webp-container-spec: align anim pseudocode w/prose
|
||||
8a7c8dc6 WASM: Enable VP8L_USE_FAST_LOAD
|
||||
f0c53cd9 WASM: don't use USE_GENERIC_TREE
|
||||
eef903d0 WASM: Enable 64-bit BITS caching
|
||||
6296cc8d iterator_enc: make VP8IteratorReset() static
|
||||
fbd93896 histogram_enc: make VP8LGetHistogramSize static
|
||||
cc7ff545 cost_enc: make VP8CalculateLevelCosts[] static
|
||||
4e2828ba vp8l_dec: make VP8LClear() static
|
||||
d742b24a Intra16Preds_NEON: fix truemotion saturation
|
||||
c7bb4cb5 Intra4Preds_NEON: fix truemotion saturation
|
||||
952a989b Merge "Remove TODO now that log is using fixed point." into main
|
||||
dde11574 Remove TODO now that log is using fixed point.
|
||||
a1ca153d Fix hidden myerr in my_error_exit
|
||||
3bd94202 Merge changes Iff6e47ed,I24c67cd5,Id781e761 into main
|
||||
d27d246e Merge "Convert VP8LFastSLog2 to fixed point" into main
|
||||
4838611f Disable msg_code use in fuzzing mode
|
||||
314a142a Use QuantizeBlock_NEON for VP8EncQuantizeBlockWHT on Arm
|
||||
3bfb05e3 Add AArch64 Neon implementation of Intra16Preds
|
||||
baa93808 Add AArch64 Neon implementation of Intra4Preds
|
||||
41a5e582 Fix errors when compiling code as C++
|
||||
fb444b69 Convert VP8LFastSLog2 to fixed point
|
||||
c1c89f51 Fix WEBP_NODISCARD comment and C++ version
|
||||
66408c2c Switch the histogram_enc.h API to fixed point
|
||||
ac1e410d Remove leftover tiff dep
|
||||
b78d3957 Disable TIFF on fuzztest.
|
||||
cff21a7d Do not build statically on oss-fuzz.
|
||||
6853a8e5 Merge "Move more internal fuzzers to public." into main
|
||||
9bc09db4 Merge "Convert VP8LFastLog2 to fixed point" into main
|
||||
0a9f1c19 Convert VP8LFastLog2 to fixed point
|
||||
db0cb9c2 Move more internal fuzzers to public.
|
||||
ff2b5b15 Merge "advanced_api_fuzzer.cc: use crop dims in OOM check" into main
|
||||
c4af79d0 Put 0 at the end of a palette and do not store it.
|
||||
0ec80aef Delete last references to delta palettization
|
||||
96d79f84 advanced_api_fuzzer.cc: use crop dims in OOM check
|
||||
c35c7e02 Fix huffman fuzzer to not leak.
|
||||
f2fe8dec Bump fuzztest dependency.
|
||||
9ce982fd Fix fuzz tests to work on oss-fuzz
|
||||
3ba8af1a Do not escape quotes anymore in build.sh
|
||||
ea0e121b Allow centipede to be used as a fuzzing engine.
|
||||
27731afd make VP8I4ModeOffsets & VP8MakeIntra4Preds static
|
||||
ddd6245e oss-fuzz/build.sh: use heredoc for script creation
|
||||
50074930 oss-fuzz/build.sh,cosmetics: fix indent
|
||||
20e92f7d Limit the possible fuzz engines.
|
||||
4f200de5 Switch public fuzz tests to fuzztest.
|
||||
64186bb3 Add huffman_fuzzer to .gitignore
|
||||
0905f61c Move build script from oss-fuzz repo to here.
|
||||
e8678758 Fix link to Javascript documentation
|
||||
5e5b8f0c Fix SSE2 Transform_AC3 function name
|
||||
45129ee0 Revert "Check all the rows."
|
||||
ee26766a Check all the rows.
|
||||
7ec51c59 Increase the transform bits if possible.
|
||||
3cd16fd3 Revert "Increase the transform bits if possible."
|
||||
971a03d8 Increase the transform bits if possible.
|
||||
1bf198a2 Allow transform_bits to be different during encoding.
|
||||
1e462ca8 Define MAX_TRANSFORM_BITS according to the specification.
|
||||
64d1ec23 Use (MIN/NUM)_(TRANSFORM/HUFFMAN)_BITS where appropriate
|
||||
a90160e1 Refactor histograms in predictors.
|
||||
a7aa7525 Fix some function declarations
|
||||
68ff4e1e Merge "jpegdec: add a hint for EOF/READ errors" into main
|
||||
79e7968a jpegdec: add a hint for EOF/READ errors
|
||||
d33455cd man/*: s/BUGS/REPORTING BUGS/
|
||||
a67ff735 normalize example exit status
|
||||
edc28909 upsampling_{neon,sse41}: fix int sanitizer warning
|
||||
3cada4ce ImgIoUtilReadFile: check ftell() return
|
||||
dc950585 Merge tag 'v1.4.0'
|
||||
845d5476 update ChangeLog (tag: v1.4.0, origin/1.4.0)
|
||||
8a6a55bb update NEWS
|
||||
cf7c5a5d provide a way to opt-out/override WEBP_NODISCARD
|
||||
cc34288a update ChangeLog (tag: v1.4.0-rc1)
|
||||
|
||||
@@ -32,7 +32,7 @@ PLATFORM_LDFLAGS = /SAFESEH
|
||||
NOLOGO = /nologo
|
||||
CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG
|
||||
CCDEBUG = cl.exe $(NOLOGO) /Od /Zi /D_DEBUG /RTC1
|
||||
CFLAGS = /I. /Isrc $(NOLOGO) /W3 /EHsc /c
|
||||
CFLAGS = /I. /Isrc $(NOLOGO) /MP /W3 /EHsc /c
|
||||
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
|
||||
LDFLAGS = /LARGEADDRESSAWARE /MANIFEST:EMBED /NXCOMPAT /DYNAMICBASE
|
||||
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
|
||||
@@ -231,6 +231,7 @@ DSP_DEC_OBJS = \
|
||||
$(DIROBJ)\dsp\lossless_neon.obj \
|
||||
$(DIROBJ)\dsp\lossless_sse2.obj \
|
||||
$(DIROBJ)\dsp\lossless_sse41.obj \
|
||||
$(DIROBJ)\dsp\lossless_avx2.obj \
|
||||
$(DIROBJ)\dsp\rescaler.obj \
|
||||
$(DIROBJ)\dsp\rescaler_mips32.obj \
|
||||
$(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
|
||||
@@ -270,6 +271,7 @@ DSP_ENC_OBJS = \
|
||||
$(DIROBJ)\dsp\lossless_enc_neon.obj \
|
||||
$(DIROBJ)\dsp\lossless_enc_sse2.obj \
|
||||
$(DIROBJ)\dsp\lossless_enc_sse41.obj \
|
||||
$(DIROBJ)\dsp\lossless_enc_avx2.obj \
|
||||
$(DIROBJ)\dsp\ssim.obj \
|
||||
$(DIROBJ)\dsp\ssim_sse2.obj \
|
||||
|
||||
@@ -393,7 +395,7 @@ $(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS)
|
||||
$(DIRBIN)\dwebp.exe: $(LIBWEBPDEMUX)
|
||||
$(DIRBIN)\gif2webp.exe: $(DIROBJ)\examples\gif2webp.obj $(EX_GIF_DEC_OBJS)
|
||||
$(DIRBIN)\gif2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBPMUX)
|
||||
$(DIRBIN)\gif2webp.exe: $(LIBWEBP)
|
||||
$(DIRBIN)\gif2webp.exe: $(LIBWEBP) $(LIBSHARPYUV)
|
||||
$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj $(EX_UTIL_OBJS)
|
||||
$(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
|
||||
$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\vwebp_sdl.obj
|
||||
|
||||
35
NEWS
35
NEWS
@@ -1,3 +1,38 @@
|
||||
- 6/30/2025 version 1.6.0
|
||||
This is a binary compatible release.
|
||||
API changes:
|
||||
- libwebp: WebPValidateDecoderConfig
|
||||
* additional x86 (AVX2, SSE2), general optimizations and compression
|
||||
improvements for lossless
|
||||
* `-mt` returns same results as single-threaded lossless (regressed in
|
||||
1.5.0, #426506716)
|
||||
* miscellaneous warning, bug & build fixes (#393104377, #397130631,
|
||||
#398288323, #398066379, #427503509)
|
||||
Tool updates:
|
||||
* cwebp can restrict the use of `-resize` with `-resize_mode` (#405437935)
|
||||
|
||||
- 12/19/2024 version 1.5.0
|
||||
This is a binary compatible release.
|
||||
API changes:
|
||||
- `cross_color_transform_bits` added to WebPAuxStats
|
||||
* minor lossless encoder speed and compression improvements
|
||||
* lossless encoding does not use floats anymore
|
||||
* additional Arm optimizations for lossy & lossless + general code generation
|
||||
improvements
|
||||
* improvements to WASM performance (#643)
|
||||
* improvements and corrections in webp-container-spec.txt and
|
||||
webp-lossless-bitstream-spec.txt (#646, #355607636)
|
||||
* further security related hardening and increased fuzzing coverage w/fuzztest
|
||||
(oss-fuzz: #382816119, #70112, #70102, #69873, #69825, #69508, #69208)
|
||||
* miscellaneous warning, bug & build fixes (#499, #562, #381372617,
|
||||
#381109771, #42340561, #375011696, #372109644, chromium: #334120888)
|
||||
Tool updates:
|
||||
* gif2webp: add -sharp_yuv & -near_lossless
|
||||
* img2webp: add -exact & -noexact
|
||||
* exit codes normalized; running an example program with no
|
||||
arguments will output its help and exit with an error (#42340557,
|
||||
#381372617)
|
||||
|
||||
- 4/12/2024: version 1.4.0
|
||||
This is a binary compatible release.
|
||||
* API changes:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
\__\__/\____/\_____/__/ ____ ___
|
||||
/ _/ / \ \ / _ \/ _/
|
||||
/ \_/ / / \ \ __/ \__
|
||||
\____/____/\_____/_____/____/v1.4.0
|
||||
\____/____/\_____/_____/____/v1.6.0
|
||||
```
|
||||
|
||||
WebP codec is a library to encode and decode images in WebP format. This package
|
||||
|
||||
@@ -94,6 +94,9 @@
|
||||
/* Set to 1 if SSE4.1 is supported */
|
||||
#cmakedefine WEBP_HAVE_SSE41 1
|
||||
|
||||
/* Set to 1 if AVX2 is supported */
|
||||
#cmakedefine WEBP_HAVE_AVX2 1
|
||||
|
||||
/* Set to 1 if TIFF library is installed */
|
||||
#cmakedefine WEBP_HAVE_TIFF 1
|
||||
|
||||
@@ -105,12 +108,4 @@
|
||||
|
||||
/* 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
|
||||
#cmakedefine WORDS_BIGENDIAN 1
|
||||
|
||||
@@ -18,11 +18,36 @@ function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD)
|
||||
unset(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} CACHE)
|
||||
cmake_push_check_state()
|
||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# If building a universal binary on macOS, we need to check if one of the
|
||||
# architectures supports the SIMD flag.
|
||||
set(OSX_CHECK "")
|
||||
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
list(LENGTH CMAKE_OSX_ARCHITECTURES ARCH_COUNT)
|
||||
if(ARCH_COUNT EQUAL 2)
|
||||
set(OSX_CHECK "defined(WEBP_CAN_HAVE_${WEBP_SIMD_FLAG}) &&")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
check_c_source_compiles(
|
||||
"
|
||||
#include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\"
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__x86_64__)
|
||||
#define WEBP_CAN_HAVE_SSE2
|
||||
#define WEBP_CAN_HAVE_SSE41
|
||||
#define WEBP_CAN_HAVE_AVX2
|
||||
#elif defined(__aarch64__)
|
||||
#define WEBP_CAN_HAVE_NEON
|
||||
#endif
|
||||
// MIPS intrinsics are not supported on macOS, but we have to define them
|
||||
// so that the check happens.
|
||||
#define WEBP_CAN_HAVE_MIPS32
|
||||
#define WEBP_CAN_HAVE_MIPS_DSP_R2
|
||||
#define WEBP_CAN_HAVE_MSA
|
||||
#endif
|
||||
int main(void) {
|
||||
#if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
|
||||
#if ${OSX_CHECK} !defined(WEBP_USE_${WEBP_SIMD_FLAG})
|
||||
this is not valid code
|
||||
#endif
|
||||
return 0;
|
||||
@@ -38,9 +63,9 @@ function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD)
|
||||
endfunction()
|
||||
|
||||
# those are included in the names of WEBP_USE_* in c++ code.
|
||||
set(WEBP_SIMD_FLAGS "SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA")
|
||||
set(WEBP_SIMD_FLAGS "AVX2;SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA")
|
||||
set(WEBP_SIMD_FILE_EXTENSIONS
|
||||
"_sse41.c;_sse2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
|
||||
"_avx2.c;_sse41.c;_sse2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
|
||||
if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
# With at least Visual Studio 12 (2013)+ /arch is not necessary to build SSE2
|
||||
# or SSE4 code unless a lesser /arch is forced. MSVC does not have a SSE4
|
||||
@@ -50,12 +75,14 @@ if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if(MSVC_VERSION GREATER_EQUAL 1800 AND NOT CMAKE_C_FLAGS MATCHES "/arch:")
|
||||
set(SIMD_ENABLE_FLAGS)
|
||||
else()
|
||||
set(SIMD_ENABLE_FLAGS "/arch:AVX;/arch:SSE2;;;;")
|
||||
set(SIMD_ENABLE_FLAGS "/arch:AVX2;/arch:AVX;/arch:SSE2;;;;")
|
||||
endif()
|
||||
set(SIMD_DISABLE_FLAGS)
|
||||
else()
|
||||
set(SIMD_ENABLE_FLAGS "-msse4.1;-msse2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
|
||||
set(SIMD_DISABLE_FLAGS "-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa")
|
||||
set(SIMD_ENABLE_FLAGS
|
||||
"-mavx2;-msse4.1;-msse2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
|
||||
set(SIMD_DISABLE_FLAGS
|
||||
"-mno-avx2;-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa")
|
||||
endif()
|
||||
|
||||
set(WEBP_SIMD_FILES_TO_INCLUDE)
|
||||
|
||||
22
configure.ac
22
configure.ac
@@ -1,4 +1,4 @@
|
||||
AC_INIT([libwebp], [1.4.0],
|
||||
AC_INIT([libwebp], [1.6.0],
|
||||
[https://issues.webmproject.org],,
|
||||
[https://developers.google.com/speed/webp])
|
||||
AC_CANONICAL_HOST
|
||||
@@ -114,6 +114,7 @@ TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wparentheses-equality])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wself-assign])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshorten-64-to-32])
|
||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wstrict-prototypes])
|
||||
@@ -161,6 +162,25 @@ AS_IF([test "$GCC" = "yes" ], [
|
||||
AC_SUBST([AM_CFLAGS])
|
||||
|
||||
dnl === Check for machine specific flags
|
||||
AC_ARG_ENABLE([avx2],
|
||||
AS_HELP_STRING([--disable-avx2],
|
||||
[Disable detection of AVX2 support
|
||||
@<:@default=auto@:>@]))
|
||||
|
||||
AS_IF([test "x$enable_avx2" != "xno" -a "x$enable_sse4_1" != "xno"
|
||||
-a "x$enable_sse2" != "xno"], [
|
||||
AVX2_FLAGS="$INTRINSICS_CFLAGS $AVX2_FLAGS"
|
||||
TEST_AND_ADD_CFLAGS([AVX2_FLAGS], [-mavx2])
|
||||
AS_IF([test -n "$AVX2_FLAGS"], [
|
||||
SAVED_CFLAGS=$CFLAGS
|
||||
CFLAGS="$CFLAGS $AVX2_FLAGS"
|
||||
AC_CHECK_HEADER([immintrin.h],
|
||||
[AC_DEFINE(WEBP_HAVE_AVX2, [1],
|
||||
[Set to 1 if AVX2 is supported])],
|
||||
[AVX2_FLAGS=""])
|
||||
CFLAGS=$SAVED_CFLAGS])
|
||||
AC_SUBST([AVX2_FLAGS])])
|
||||
|
||||
AC_ARG_ENABLE([sse4.1],
|
||||
AS_HELP_STRING([--disable-sse4.1],
|
||||
[Disable detection of SSE4.1 support
|
||||
|
||||
@@ -36,6 +36,12 @@ size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
|
||||
Of course in this case, no quality factor is needed since the compression occurs
|
||||
without loss of the input values, at the expense of larger output sizes.
|
||||
|
||||
Note these functions, like the lossy versions, use the library's default
|
||||
settings. For lossless this means 'exact' is disabled. RGB values in fully
|
||||
transparent areas (that is, areas with alpha values equal to `0`) will be
|
||||
modified to improve compression. To avoid this, use `WebPEncode()` and set
|
||||
`WebPConfig::exact` to `1`.
|
||||
|
||||
### Advanced encoding API
|
||||
|
||||
A more advanced API is based on the WebPConfig and WebPPicture structures.
|
||||
@@ -202,6 +208,7 @@ config.output.u.RGBA.rgba = (uint8_t*) memory_buffer;
|
||||
config.output.u.RGBA.stride = scanline_stride;
|
||||
config.output.u.RGBA.size = total_size_of_the_memory_buffer;
|
||||
config.output.is_external_memory = 1;
|
||||
config_error = WebPValidateDecoderConfig(&config); // not mandatory, but useful
|
||||
|
||||
// E) Decode the WebP image. There are two variants w.r.t decoding image.
|
||||
// The first one (E.1) decodes the full image and the second one (E.2) is
|
||||
|
||||
@@ -17,10 +17,11 @@ rubygems will install automatically. The following will apply inline CSS
|
||||
styling; an external stylesheet is not needed.
|
||||
|
||||
```shell
|
||||
$ kramdown doc/webp-lossless-bitstream-spec.txt --template \
|
||||
doc/template.html --coderay-css style --coderay-line-numbers ' ' \
|
||||
--coderay-default-lang c > \
|
||||
doc/output/webp-lossless-bitstream-spec.html
|
||||
$ kramdown doc/webp-lossless-bitstream-spec.txt \
|
||||
--template doc/template.html \
|
||||
-x syntax-coderay --syntax-highlighter coderay \
|
||||
--syntax-highlighter-opts "{default_lang: c, line_numbers: , css: style}" \
|
||||
> doc/output/webp-lossless-bitstream-spec.html
|
||||
```
|
||||
|
||||
Optimally, use kramdown 0.13.7 or newer if syntax highlighting desired.
|
||||
|
||||
16
doc/tools.md
16
doc/tools.md
@@ -65,6 +65,7 @@ Options:
|
||||
(default: 0 100)
|
||||
-crop <x> <y> <w> <h> .. crop picture with the given rectangle
|
||||
-resize <w> <h> ........ resize picture (*after* any cropping)
|
||||
-resize_mode <string> .. one of: up_only, down_only, always (default)
|
||||
-mt .................... use multi-threading if available
|
||||
-low_memory ............ reduce memory usage (slower encoding)
|
||||
-map <int> ............. print map of extra info
|
||||
@@ -321,10 +322,13 @@ Per-frame options (only used for subsequent images input):
|
||||
|
||||
```
|
||||
-d <int> ............. frame duration in ms (default: 100)
|
||||
-lossless ........... use lossless mode (default)
|
||||
-lossy ... ........... use lossy mode
|
||||
-lossless ............ use lossless mode (default)
|
||||
-lossy ............... use lossy mode
|
||||
-q <float> ........... quality
|
||||
-m <int> ............. method to use
|
||||
-m <int> ............. compression method (0=fast, 6=slowest), default=4
|
||||
-exact, -noexact ..... preserve or alter RGB values in transparent area
|
||||
(default: -noexact, may cause artifacts
|
||||
with lossy animations)
|
||||
```
|
||||
|
||||
example: `img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp`
|
||||
@@ -351,8 +355,12 @@ Options:
|
||||
-lossy ................. encode image using lossy compression
|
||||
-mixed ................. for each frame in the image, pick lossy
|
||||
or lossless compression heuristically
|
||||
-near_lossless <int> ... use near-lossless image preprocessing
|
||||
(0..100=off), default=100
|
||||
-sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
|
||||
(lossy only)
|
||||
-q <float> ............. quality factor (0:small..100:big)
|
||||
-m <int> ............... compression method (0=fast, 6=slowest)
|
||||
-m <int> ............... compression method (0=fast, 6=slowest), default=4
|
||||
-min_size .............. minimize output size (default:off)
|
||||
lossless compression by default; can be
|
||||
combined with -q, -m, -lossy or -mixed
|
||||
|
||||
@@ -131,7 +131,7 @@ Chunk Payload: _Chunk Size_ bytes
|
||||
: The data payload. If _Chunk Size_ is odd, a single padding byte -- which MUST
|
||||
be `0` to conform with RIFF -- is added.
|
||||
|
||||
**Note:** RIFF has a convention that all-uppercase chunk FourCCs are standard
|
||||
**Note**: RIFF has a convention that all-uppercase chunk FourCCs are standard
|
||||
chunks that apply to any RIFF file format, while FourCCs specific to a file
|
||||
format are all lowercase. WebP does not follow this convention.
|
||||
|
||||
@@ -220,7 +220,7 @@ use another conversion method, but visual results may differ among decoders.
|
||||
Simple File Format (Lossless)
|
||||
-----------------------------
|
||||
|
||||
**Note:** Older readers may not support files using the lossless format.
|
||||
**Note**: Older readers may not support files using the lossless format.
|
||||
|
||||
This layout SHOULD be used if the image requires _lossless_ encoding (with an
|
||||
optional transparency channel) and does not require advanced features provided
|
||||
@@ -262,7 +262,7 @@ and height of the canvas.
|
||||
Extended File Format
|
||||
--------------------
|
||||
|
||||
**Note:** Older readers may not support files using the extended format.
|
||||
**Note**: Older readers may not support files using the extended format.
|
||||
|
||||
An extended format file consists of:
|
||||
|
||||
@@ -290,12 +290,12 @@ up of:
|
||||
For an _animated image_, the _image data_ consists of multiple frames. More
|
||||
details about frames can be found in the [Animation](#animation) section.
|
||||
|
||||
All chunks necessary for reconstruction and color correction, that is 'VP8X',
|
||||
'ICCP', 'ANIM', 'ANMF', 'ALPH', 'VP8 ' and 'VP8L', MUST appear in the order
|
||||
All chunks necessary for reconstruction and color correction, that is, 'VP8X',
|
||||
'ICCP', 'ANIM', 'ANMF', 'ALPH', 'VP8 ', and 'VP8L', MUST appear in the order
|
||||
described earlier. Readers SHOULD fail when chunks necessary for reconstruction
|
||||
and color correction are out of order.
|
||||
|
||||
[Metadata](#metadata) and [unknown](#unknown-chunks) chunks MAY appear out of
|
||||
[Metadata](#metadata) and [unknown chunks](#unknown-chunks) MAY appear out of
|
||||
order.
|
||||
|
||||
**Rationale:** The chunks necessary for reconstruction should appear first in
|
||||
@@ -401,7 +401,7 @@ Background Color: 32 bits (_uint32_)
|
||||
around the frames, as well as the transparent pixels of the first frame.
|
||||
The background color is also used when the Disposal method is `1`.
|
||||
|
||||
**Note**:
|
||||
**Notes**:
|
||||
|
||||
* The background color MAY contain a non-opaque alpha value, even if the
|
||||
_Alpha_ flag in the ['VP8X' Chunk](#extended_header) is unset.
|
||||
@@ -525,7 +525,7 @@ Disposal method (D): 1 bit
|
||||
not present, standard RGB (sRGB) is to be assumed. (Note that sRGB also
|
||||
needs to be linearized due to a gamma of ~2.2.)
|
||||
|
||||
Frame Data: _Chunk Size_ - `16` bytes
|
||||
Frame Data: _Chunk Size_ bytes - `16`
|
||||
|
||||
: Consists of:
|
||||
|
||||
@@ -616,7 +616,7 @@ Compression method (C): 2 bits
|
||||
* `0`: No compression.
|
||||
* `1`: Compressed using the WebP lossless format.
|
||||
|
||||
Alpha bitstream: _Chunk Size_ - `1` bytes
|
||||
Alpha bitstream: _Chunk Size_ bytes - `1`
|
||||
|
||||
: Encoded alpha bitstream.
|
||||
|
||||
|
||||
@@ -436,8 +436,8 @@ should be interpreted as an 8-bit two's complement number (that is: uint8 range
|
||||
|
||||
The multiplication is to be done using more precision (with at least 16-bit
|
||||
precision). The sign extension property of the shift operation does not matter
|
||||
here; only the lowest 8 bits are used from the result, and there the sign
|
||||
extension shifting and unsigned shifting are consistent with each other.
|
||||
here; only the lowest 8 bits are used from the result, and in these bits, the
|
||||
sign extension shifting and unsigned shifting are consistent with each other.
|
||||
|
||||
Now, we describe the contents of color transform data so that decoding can apply
|
||||
the inverse color transform and recover the original red and blue values. The
|
||||
@@ -613,8 +613,8 @@ We use image data in five different roles:
|
||||
1. Color transform image: Created by `ColorTransformElement` values
|
||||
(defined in ["Color Transform"](#color-transform)) for different blocks of
|
||||
the image.
|
||||
1. Color indexing image: An array of size `color_table_size` (up to 256 ARGB
|
||||
values) storing the metadata for the color indexing transform (see
|
||||
1. Color indexing image: An array of the size of `color_table_size` (up to
|
||||
256 ARGB values) that stores metadata for the color indexing transform (see
|
||||
["Color Indexing Transform"](#color-indexing-transform)).
|
||||
|
||||
### 5.2 Encoding of Image Data
|
||||
|
||||
@@ -67,7 +67,7 @@ dwebp_LDADD += ../src/libwebp.la
|
||||
dwebp_LDADD +=$(PNG_LIBS) $(JPEG_LIBS)
|
||||
|
||||
gif2webp_SOURCES = gif2webp.c gifdec.c gifdec.h
|
||||
gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES)
|
||||
gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES) -I$(top_srcdir)
|
||||
gif2webp_LDADD =
|
||||
gif2webp_LDADD += libexample_util.la
|
||||
gif2webp_LDADD += ../imageio/libimageio_util.la
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "./anim_util.h"
|
||||
#include "./example_util.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@@ -56,8 +57,8 @@ static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
|
||||
}
|
||||
|
||||
static int FramesAreSimilar(const uint8_t* const rgba1,
|
||||
const uint8_t* const rgba2,
|
||||
int width, int height, int max_allowed_diff) {
|
||||
const uint8_t* const rgba2, int width, int height,
|
||||
int max_allowed_diff) {
|
||||
int i, j;
|
||||
assert(max_allowed_diff > 0);
|
||||
for (j = 0; j < height; ++j) {
|
||||
@@ -119,8 +120,7 @@ static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) {
|
||||
if (alpha1 == 0 && alpha2 == 0) return 1;
|
||||
}
|
||||
if (bg1 != bg2) {
|
||||
fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n",
|
||||
bg1, bg2);
|
||||
fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n", bg1, bg2);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -130,18 +130,17 @@ static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) {
|
||||
// is OK for other aspects like offsets, dispose/blend method to vary.
|
||||
static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||
const AnimatedImage* const img2,
|
||||
int premultiply,
|
||||
double min_psnr) {
|
||||
int premultiply, double min_psnr) {
|
||||
int ok = 1;
|
||||
const int is_multi_frame_image = (img1->num_frames > 1);
|
||||
uint32_t i;
|
||||
|
||||
ok = CompareValues(img1->canvas_width, img2->canvas_width,
|
||||
"Canvas width mismatch") && ok;
|
||||
ok = CompareValues(img1->canvas_height, img2->canvas_height,
|
||||
"Canvas height mismatch") && ok;
|
||||
ok = CompareValues(img1->num_frames, img2->num_frames,
|
||||
"Frame count mismatch") && ok;
|
||||
ok &= CompareValues(img1->canvas_width, img2->canvas_width,
|
||||
"Canvas width mismatch");
|
||||
ok &= CompareValues(img1->canvas_height, img2->canvas_height,
|
||||
"Canvas height mismatch");
|
||||
ok &=
|
||||
CompareValues(img1->num_frames, img2->num_frames, "Frame count mismatch");
|
||||
if (!ok) return 0; // These are fatal failures, can't proceed.
|
||||
|
||||
if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
|
||||
@@ -154,11 +153,10 @@ static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||
img2->format == ANIM_GIF && img2->loop_count == 65536)) {
|
||||
max_loop_count_workaround = 1;
|
||||
}
|
||||
ok = (max_loop_count_workaround ||
|
||||
CompareValues(img1->loop_count, img2->loop_count,
|
||||
"Loop count mismatch")) && ok;
|
||||
ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
|
||||
premultiply) && ok;
|
||||
ok &= (max_loop_count_workaround ||
|
||||
CompareValues(img1->loop_count, img2->loop_count,
|
||||
"Loop count mismatch"));
|
||||
ok &= CompareBackgroundColor(img1->bgcolor, img2->bgcolor, premultiply);
|
||||
}
|
||||
|
||||
for (i = 0; i < img1->num_frames; ++i) {
|
||||
@@ -178,8 +176,8 @@ static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||
premultiply, &max_diff, &psnr);
|
||||
if (min_psnr > 0.) {
|
||||
if (psnr < min_psnr) {
|
||||
fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i,
|
||||
psnr, min_psnr);
|
||||
fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i, psnr,
|
||||
min_psnr);
|
||||
ok = 0;
|
||||
}
|
||||
} else {
|
||||
@@ -199,9 +197,10 @@ static void Help(void) {
|
||||
printf(" -min_psnr <float> ... minimum per-frame PSNR\n");
|
||||
printf(" -raw_comparison ..... if this flag is not used, RGB is\n");
|
||||
printf(" premultiplied before comparison\n");
|
||||
printf(" -max_diff <int> ..... maximum allowed difference per channel\n"
|
||||
" between corresponding pixels in subsequent\n"
|
||||
" frames\n");
|
||||
printf(
|
||||
" -max_diff <int> ..... maximum allowed difference per channel\n"
|
||||
" between corresponding pixels in subsequent\n"
|
||||
" frames\n");
|
||||
printf(" -h .................. this help\n");
|
||||
printf(" -version ............ print version number and exit\n");
|
||||
}
|
||||
@@ -217,7 +216,7 @@ int main(int argc, const char* argv[]) {
|
||||
int premultiply = 1;
|
||||
int max_diff = 0;
|
||||
int i, c;
|
||||
const char* files[2] = { NULL, NULL };
|
||||
const char* files[2] = {NULL, NULL};
|
||||
AnimatedImage images[2];
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
@@ -253,9 +252,8 @@ int main(int argc, const char* argv[]) {
|
||||
GetAnimatedImageVersions(&dec_version, &demux_version);
|
||||
printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
|
||||
(dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
|
||||
(dec_version >> 0) & 0xff,
|
||||
(demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
|
||||
(demux_version >> 0) & 0xff);
|
||||
(dec_version >> 0) & 0xff, (demux_version >> 16) & 0xff,
|
||||
(demux_version >> 8) & 0xff, (demux_version >> 0) & 0xff);
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
if (!got_input1) {
|
||||
@@ -278,7 +276,6 @@ int main(int argc, const char* argv[]) {
|
||||
FREE_WARGV_AND_RETURN(return_code);
|
||||
}
|
||||
|
||||
|
||||
if (!got_input2) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(return_code);
|
||||
@@ -301,8 +298,8 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!CompareAnimatedImagePair(&images[0], &images[1],
|
||||
premultiply, min_psnr)) {
|
||||
if (!CompareAnimatedImagePair(&images[0], &images[1], premultiply,
|
||||
min_psnr)) {
|
||||
WFPRINTF(stderr, "\nFiles %s and %s differ.\n", (const W_CHAR*)files[0],
|
||||
(const W_CHAR*)files[1]);
|
||||
return_code = 1;
|
||||
@@ -311,7 +308,7 @@ int main(int argc, const char* argv[]) {
|
||||
(const W_CHAR*)files[1]);
|
||||
return_code = 0;
|
||||
}
|
||||
End:
|
||||
End:
|
||||
ClearAnimatedImage(&images[0]);
|
||||
ClearAnimatedImage(&images[1]);
|
||||
FREE_WARGV_AND_RETURN(return_code);
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // for 'strcmp'.
|
||||
|
||||
#include "./anim_util.h"
|
||||
#include "webp/decode.h"
|
||||
#include "../imageio/image_enc.h"
|
||||
#include "./anim_util.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@@ -28,8 +29,9 @@ static void Help(void) {
|
||||
printf("Usage: anim_dump [options] files...\n");
|
||||
printf("\nOptions:\n");
|
||||
printf(" -folder <string> .... dump folder (default: '.')\n");
|
||||
printf(" -prefix <string> .... prefix for dumped frames "
|
||||
"(default: 'dump_')\n");
|
||||
printf(
|
||||
" -prefix <string> .... prefix for dumped frames "
|
||||
"(default: 'dump_')\n");
|
||||
printf(" -tiff ............... save frames as TIFF\n");
|
||||
printf(" -pam ................ save frames as PAM\n");
|
||||
printf(" -h .................. this help\n");
|
||||
@@ -81,17 +83,16 @@ int main(int argc, const char* argv[]) {
|
||||
GetAnimatedImageVersions(&dec_version, &demux_version);
|
||||
printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
|
||||
(dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
|
||||
(dec_version >> 0) & 0xff,
|
||||
(demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
|
||||
(demux_version >> 0) & 0xff);
|
||||
(dec_version >> 0) & 0xff, (demux_version >> 16) & 0xff,
|
||||
(demux_version >> 8) & 0xff, (demux_version >> 0) & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
} else {
|
||||
uint32_t i;
|
||||
AnimatedImage image;
|
||||
const W_CHAR* const file = GET_WARGV(argv, c);
|
||||
memset(&image, 0, sizeof(image));
|
||||
WPRINTF("Decoding file: %s as %s/%sxxxx.%s\n",
|
||||
file, dump_folder, prefix, suffix);
|
||||
WPRINTF("Decoding file: %s as %s/%sxxxx.%s\n", file, dump_folder, prefix,
|
||||
suffix);
|
||||
if (!ReadAnimatedImage((const char*)file, &image, 0, NULL)) {
|
||||
WFPRINTF(stderr, "Error decoding file: %s\n Aborting.\n", file);
|
||||
error = 1;
|
||||
@@ -112,8 +113,8 @@ int main(int argc, const char* argv[]) {
|
||||
buffer.u.RGBA.rgba = image.frames[i].rgba;
|
||||
buffer.u.RGBA.stride = buffer.width * sizeof(uint32_t);
|
||||
buffer.u.RGBA.size = buffer.u.RGBA.stride * buffer.height;
|
||||
WSNPRINTF(out_file, sizeof(out_file), "%s/%s%.4d.%s",
|
||||
dump_folder, prefix, i, suffix);
|
||||
WSNPRINTF(out_file, sizeof(out_file), "%s/%s%.4d.%s", dump_folder,
|
||||
prefix, i, suffix);
|
||||
if (!WebPSaveImage(&buffer, format, (const char*)out_file)) {
|
||||
WFPRINTF(stderr, "Error while saving image '%s'\n", out_file);
|
||||
error = 1;
|
||||
|
||||
@@ -19,13 +19,16 @@
|
||||
#if defined(WEBP_HAVE_GIF)
|
||||
#include <gif_lib.h>
|
||||
#endif
|
||||
#include "webp/format_constants.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./gifdec.h"
|
||||
#include "./unicode.h"
|
||||
#include "./unicode_gif.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
#include "webp/format_constants.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@@ -38,11 +41,11 @@ static const int kNumChannels = 4;
|
||||
|
||||
#if defined(WEBP_HAVE_GIF)
|
||||
// Returns true if the frame covers the full canvas.
|
||||
static int IsFullFrame(int width, int height,
|
||||
int canvas_width, int canvas_height) {
|
||||
static int IsFullFrame(int width, int height, int canvas_width,
|
||||
int canvas_height) {
|
||||
return (width == canvas_width && height == canvas_height);
|
||||
}
|
||||
#endif // WEBP_HAVE_GIF
|
||||
#endif // WEBP_HAVE_GIF
|
||||
|
||||
static int CheckSizeForOverflow(uint64_t size) {
|
||||
return (size == (size_t)size);
|
||||
@@ -92,8 +95,8 @@ void ClearAnimatedImage(AnimatedImage* const image) {
|
||||
|
||||
#if defined(WEBP_HAVE_GIF)
|
||||
// Clear the canvas to transparent.
|
||||
static void ZeroFillCanvas(uint8_t* rgba,
|
||||
uint32_t canvas_width, uint32_t canvas_height) {
|
||||
static void ZeroFillCanvas(uint8_t* rgba, uint32_t canvas_width,
|
||||
uint32_t canvas_height) {
|
||||
memset(rgba, 0, canvas_width * kNumChannels * canvas_height);
|
||||
}
|
||||
|
||||
@@ -110,16 +113,16 @@ static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset,
|
||||
}
|
||||
|
||||
// Copy width * height pixels from 'src' to 'dst'.
|
||||
static void CopyCanvas(const uint8_t* src, uint8_t* dst,
|
||||
uint32_t width, uint32_t height) {
|
||||
static void CopyCanvas(const uint8_t* src, uint8_t* dst, uint32_t width,
|
||||
uint32_t height) {
|
||||
assert(src != NULL && dst != NULL);
|
||||
memcpy(dst, src, width * kNumChannels * height);
|
||||
}
|
||||
|
||||
// Copy pixels in the given rectangle from 'src' to 'dst' honoring the 'stride'.
|
||||
static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
|
||||
int x_offset, int y_offset,
|
||||
int width, int height) {
|
||||
int x_offset, int y_offset, int width,
|
||||
int height) {
|
||||
int j;
|
||||
const int width_in_bytes = width * kNumChannels;
|
||||
const size_t offset = y_offset * stride + x_offset * kNumChannels;
|
||||
@@ -132,11 +135,11 @@ static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
|
||||
dst += stride;
|
||||
}
|
||||
}
|
||||
#endif // WEBP_HAVE_GIF
|
||||
#endif // WEBP_HAVE_GIF
|
||||
|
||||
// Canonicalize all transparent pixels to transparent black to aid comparison.
|
||||
static void CleanupTransparentPixels(uint32_t* rgba,
|
||||
uint32_t width, uint32_t height) {
|
||||
static void CleanupTransparentPixels(uint32_t* rgba, uint32_t width,
|
||||
uint32_t height) {
|
||||
const uint32_t* const rgba_end = rgba + width * height;
|
||||
while (rgba < rgba_end) {
|
||||
const uint8_t alpha = (*rgba >> 24) & 0xff;
|
||||
@@ -149,8 +152,8 @@ static void CleanupTransparentPixels(uint32_t* 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) {
|
||||
uint32_t frame_num, const uint8_t rgba[], int canvas_width,
|
||||
int canvas_height) {
|
||||
int ok = 0;
|
||||
size_t max_len;
|
||||
int y;
|
||||
@@ -163,8 +166,8 @@ static int DumpFrame(const char filename[], const char dump_folder[],
|
||||
|
||||
base_name = WSTRRCHR(filename, '/');
|
||||
base_name = (base_name == NULL) ? (const W_CHAR*)filename : base_name + 1;
|
||||
max_len = WSTRLEN(dump_folder) + 1 + WSTRLEN(base_name)
|
||||
+ strlen("_frame_") + strlen(".pam") + 8;
|
||||
max_len = WSTRLEN(dump_folder) + 1 + WSTRLEN(base_name) + strlen("_frame_") +
|
||||
strlen(".pam") + 8;
|
||||
file_name = (W_CHAR*)WebPMalloc(max_len * sizeof(*file_name));
|
||||
if (file_name == NULL) goto End;
|
||||
|
||||
@@ -180,7 +183,8 @@ static int DumpFrame(const char filename[], const char dump_folder[],
|
||||
ok = 0;
|
||||
goto End;
|
||||
}
|
||||
if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n"
|
||||
if (fprintf(f,
|
||||
"P7\nWIDTH %d\nHEIGHT %d\n"
|
||||
"DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
|
||||
canvas_width, canvas_height) < 0) {
|
||||
WFPRINTF(stderr, "Write error for file %s\n", file_name);
|
||||
@@ -195,7 +199,7 @@ static int DumpFrame(const char filename[], const char dump_folder[],
|
||||
row += canvas_width * kNumChannels;
|
||||
}
|
||||
ok = 1;
|
||||
End:
|
||||
End:
|
||||
if (f != NULL) fclose(f);
|
||||
WebPFree(file_name);
|
||||
return ok;
|
||||
@@ -263,8 +267,8 @@ static int ReadAnimatedWebP(const char filename[],
|
||||
image->canvas_width * kNumChannels * image->canvas_height);
|
||||
|
||||
// Needed only because we may want to compare with GIF later.
|
||||
CleanupTransparentPixels((uint32_t*)curr_rgba,
|
||||
image->canvas_width, image->canvas_height);
|
||||
CleanupTransparentPixels((uint32_t*)curr_rgba, image->canvas_width,
|
||||
image->canvas_height);
|
||||
|
||||
if (dump_frames && dump_ok) {
|
||||
dump_ok = DumpFrame(filename, dump_folder, frame_index, curr_rgba,
|
||||
@@ -280,7 +284,7 @@ static int ReadAnimatedWebP(const char filename[],
|
||||
ok = dump_ok;
|
||||
if (ok) image->format = ANIM_WEBP;
|
||||
|
||||
End:
|
||||
End:
|
||||
WebPAnimDecoderDelete(dec);
|
||||
return ok;
|
||||
}
|
||||
@@ -300,12 +304,11 @@ static int IsGIF(const WebPData* const data) {
|
||||
|
||||
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
|
||||
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
|
||||
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||
# define LOCAL_GIF_PREREQ(maj, min) \
|
||||
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||
#define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||
#define LOCAL_GIF_PREREQ(maj, min) (LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||
#else
|
||||
# define LOCAL_GIF_VERSION 0
|
||||
# define LOCAL_GIF_PREREQ(maj, min) 0
|
||||
#define LOCAL_GIF_VERSION 0
|
||||
#define LOCAL_GIF_PREREQ(maj, min) 0
|
||||
#endif
|
||||
|
||||
#if !LOCAL_GIF_PREREQ(5, 0)
|
||||
@@ -313,14 +316,14 @@ static int IsGIF(const WebPData* const data) {
|
||||
// Added in v5.0
|
||||
typedef struct {
|
||||
int DisposalMode;
|
||||
#define DISPOSAL_UNSPECIFIED 0 // No disposal specified
|
||||
#define DISPOSE_DO_NOT 1 // Leave image in place
|
||||
#define DISPOSE_BACKGROUND 2 // Set area to background color
|
||||
#define DISPOSE_PREVIOUS 3 // Restore to previous content
|
||||
int UserInputFlag; // User confirmation required before disposal
|
||||
int DelayTime; // Pre-display delay in 0.01sec units
|
||||
int TransparentColor; // Palette index for transparency, -1 if none
|
||||
#define NO_TRANSPARENT_COLOR -1
|
||||
#define DISPOSAL_UNSPECIFIED 0 // No disposal specified
|
||||
#define DISPOSE_DO_NOT 1 // Leave image in place
|
||||
#define DISPOSE_BACKGROUND 2 // Set area to background color
|
||||
#define DISPOSE_PREVIOUS 3 // Restore to previous content
|
||||
int UserInputFlag; // User confirmation required before disposal
|
||||
int DelayTime; // Pre-display delay in 0.01sec units
|
||||
int TransparentColor; // Palette index for transparency, -1 if none
|
||||
#define NO_TRANSPARENT_COLOR -1
|
||||
} GraphicsControlBlock;
|
||||
|
||||
static int DGifExtensionToGCB(const size_t GifExtensionLength,
|
||||
@@ -354,8 +357,8 @@ static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex,
|
||||
for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
|
||||
ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
|
||||
if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
|
||||
return DGifExtensionToGCB(
|
||||
ep->ByteCount, (const GifByteType*)ep->Bytes, gcb);
|
||||
return DGifExtensionToGCB(ep->ByteCount, (const GifByteType*)ep->Bytes,
|
||||
gcb);
|
||||
}
|
||||
}
|
||||
return GIF_ERROR;
|
||||
@@ -374,12 +377,12 @@ static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex,
|
||||
#endif
|
||||
|
||||
static int IsKeyFrameGIF(const GifImageDesc* prev_desc, int prev_dispose,
|
||||
const DecodedFrame* const prev_frame,
|
||||
int canvas_width, int canvas_height) {
|
||||
const DecodedFrame* const prev_frame, int canvas_width,
|
||||
int canvas_height) {
|
||||
if (prev_frame == NULL) return 1;
|
||||
if (prev_dispose == DISPOSE_BACKGROUND) {
|
||||
if (IsFullFrame(prev_desc->Width, prev_desc->Height,
|
||||
canvas_width, canvas_height)) {
|
||||
if (IsFullFrame(prev_desc->Width, prev_desc->Height, canvas_width,
|
||||
canvas_height)) {
|
||||
return 1;
|
||||
}
|
||||
if (prev_frame->is_key_frame) return 1;
|
||||
@@ -400,14 +403,12 @@ static uint32_t GetBackgroundColorGIF(GifFileType* gif) {
|
||||
if (transparent_index != NO_TRANSPARENT_COLOR &&
|
||||
gif->SBackGroundColor == transparent_index) {
|
||||
return 0x00000000; // Special case: transparent black.
|
||||
} else if (color_map == NULL || color_map->Colors == NULL
|
||||
|| gif->SBackGroundColor >= color_map->ColorCount) {
|
||||
} else if (color_map == NULL || color_map->Colors == NULL ||
|
||||
gif->SBackGroundColor >= color_map->ColorCount) {
|
||||
return 0xffffffff; // Invalid: assume white.
|
||||
} else {
|
||||
const GifColorType color = color_map->Colors[gif->SBackGroundColor];
|
||||
return (0xffu << 24) |
|
||||
(color.Red << 16) |
|
||||
(color.Green << 8) |
|
||||
return (0xffu << 24) | (color.Red << 16) | (color.Green << 8) |
|
||||
(color.Blue << 0);
|
||||
}
|
||||
}
|
||||
@@ -432,11 +433,10 @@ static uint32_t GetLoopCountGIF(const GifFileType* const gif) {
|
||||
(eb1->ByteCount == 11) &&
|
||||
(!memcmp(signature, "NETSCAPE2.0", 11) ||
|
||||
!memcmp(signature, "ANIMEXTS1.0", 11));
|
||||
if (signature_is_ok &&
|
||||
eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 &&
|
||||
eb2->Bytes[0] == 1) {
|
||||
const uint32_t extra_loop = ((uint32_t)(eb2->Bytes[2]) << 8) +
|
||||
((uint32_t)(eb2->Bytes[1]) << 0);
|
||||
if (signature_is_ok && eb2->Function == CONTINUE_EXT_FUNC_CODE &&
|
||||
eb2->ByteCount >= 3 && eb2->Bytes[0] == 1) {
|
||||
const uint32_t extra_loop =
|
||||
((uint32_t)(eb2->Bytes[2]) << 8) + ((uint32_t)(eb2->Bytes[1]) << 0);
|
||||
return (extra_loop > 0) ? extra_loop + 1 : 0;
|
||||
}
|
||||
}
|
||||
@@ -532,8 +532,8 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
image->canvas_height = (uint32_t)gif->SHeight;
|
||||
if (image->canvas_width > MAX_CANVAS_SIZE ||
|
||||
image->canvas_height > MAX_CANVAS_SIZE) {
|
||||
fprintf(stderr, "Invalid canvas dimension: %d x %d\n",
|
||||
image->canvas_width, image->canvas_height);
|
||||
fprintf(stderr, "Invalid canvas dimension: %d x %d\n", image->canvas_width,
|
||||
image->canvas_height);
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -608,11 +608,9 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height);
|
||||
|
||||
// Dispose previous frame rectangle.
|
||||
prev_frame_disposed =
|
||||
(prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
|
||||
prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
|
||||
curr_frame_opaque =
|
||||
(curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
|
||||
prev_frame_disposed = (prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
|
||||
prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
|
||||
curr_frame_opaque = (curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
|
||||
prev_frame_completely_covered =
|
||||
curr_frame_opaque &&
|
||||
CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc);
|
||||
@@ -640,9 +638,9 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
uint8_t* const src_frame_rgba =
|
||||
image->frames[src_frame_num].rgba;
|
||||
CopyFrameRectangle(src_frame_rgba, curr_rgba,
|
||||
canvas_width_in_bytes,
|
||||
prev_desc->Left, prev_desc->Top,
|
||||
prev_desc->Width, prev_desc->Height);
|
||||
canvas_width_in_bytes, prev_desc->Left,
|
||||
prev_desc->Top, prev_desc->Width,
|
||||
prev_desc->Height);
|
||||
} else {
|
||||
// Source canvas doesn't exist. So clear previous frame
|
||||
// rectangle to background.
|
||||
@@ -667,8 +665,8 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
}
|
||||
|
||||
if (dump_frames) {
|
||||
if (!DumpFrame(filename, dump_folder, i, curr_rgba,
|
||||
canvas_width, canvas_height)) {
|
||||
if (!DumpFrame(filename, dump_folder, i, curr_rgba, canvas_width,
|
||||
canvas_height)) {
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -692,7 +690,8 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
(void)image;
|
||||
(void)dump_frames;
|
||||
(void)dump_folder;
|
||||
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
|
||||
fprintf(stderr,
|
||||
"GIF support not compiled. Please install the libgif-dev "
|
||||
"package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -715,8 +714,8 @@ int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
|
||||
}
|
||||
|
||||
if (IsWebP(&webp_data)) {
|
||||
ok = ReadAnimatedWebP(filename, &webp_data, image, dump_frames,
|
||||
dump_folder);
|
||||
ok =
|
||||
ReadAnimatedWebP(filename, &webp_data, image, dump_frames, dump_folder);
|
||||
} else if (IsGIF(&webp_data)) {
|
||||
ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder);
|
||||
} else {
|
||||
@@ -760,8 +759,7 @@ void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
|
||||
// premultiply R/G/B channels with alpha value
|
||||
for (k = 0; k < kAlphaChannel; ++k) {
|
||||
Accumulate(rgba1[offset + k] * alpha1 / 255.,
|
||||
rgba2[offset + k] * alpha2 / 255.,
|
||||
&f_max_diff, &sse);
|
||||
rgba2[offset + k] * alpha2 / 255., &f_max_diff, &sse);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -771,6 +769,7 @@ void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
|
||||
*psnr = 99.; // PSNR when images are identical.
|
||||
} else {
|
||||
sse /= stride * height;
|
||||
assert(sse != 0.0);
|
||||
*psnr = 4.3429448 * log(255. * 255. / sse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,15 +22,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ANIM_GIF,
|
||||
ANIM_WEBP
|
||||
} AnimatedFileFormat;
|
||||
typedef enum { ANIM_GIF, ANIM_WEBP } AnimatedFileFormat;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* rgba; // Decoded and reconstructed full frame.
|
||||
int duration; // Frame duration in milliseconds.
|
||||
int is_key_frame; // True if this frame is a key-frame.
|
||||
uint8_t* rgba; // Decoded and reconstructed full frame.
|
||||
int duration; // Frame duration in milliseconds.
|
||||
int is_key_frame; // True if this frame is a key-frame.
|
||||
} DecodedFrame;
|
||||
|
||||
typedef struct {
|
||||
@@ -67,7 +64,7 @@ void GetAnimatedImageVersions(int* const decoder_version,
|
||||
int* const demux_version);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_EXAMPLES_ANIM_UTIL_H_
|
||||
|
||||
381
examples/cwebp.c
381
examples/cwebp.c
@@ -27,18 +27,20 @@
|
||||
#include "../imageio/webpdec.h"
|
||||
#include "./stopwatch.h"
|
||||
#include "./unicode.h"
|
||||
#include "imageio/metadata.h"
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifndef WEBP_DLL
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif // WEBP_DLL
|
||||
|
||||
@@ -64,12 +66,12 @@ static int ReadYUV(const uint8_t* const data, size_t data_size,
|
||||
|
||||
pic->use_argb = 0;
|
||||
if (!WebPPictureAlloc(pic)) return 0;
|
||||
ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride,
|
||||
pic->width, pic->height);
|
||||
ImgIoUtilCopyPlane(data + y_plane_size, uv_width,
|
||||
pic->u, pic->uv_stride, uv_width, uv_height);
|
||||
ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width,
|
||||
pic->v, pic->uv_stride, uv_width, uv_height);
|
||||
ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride, pic->width,
|
||||
pic->height);
|
||||
ImgIoUtilCopyPlane(data + y_plane_size, uv_width, pic->u, pic->uv_stride,
|
||||
uv_width, uv_height);
|
||||
ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width, pic->v,
|
||||
pic->uv_stride, uv_width, uv_height);
|
||||
return use_argb ? WebPPictureYUVAToARGB(pic) : 1;
|
||||
}
|
||||
|
||||
@@ -117,7 +119,7 @@ static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
// If image size is specified, infer it as YUV format.
|
||||
ok = ReadYUV(data, data_size, pic);
|
||||
}
|
||||
End:
|
||||
End:
|
||||
if (!ok) {
|
||||
WFPRINTF(stderr, "Error! Could not process file %s\n",
|
||||
(const W_CHAR*)filename);
|
||||
@@ -166,8 +168,8 @@ static void PrintValues(const int values[4]) {
|
||||
|
||||
static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
|
||||
const char* const description) {
|
||||
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
|
||||
description, stats->lossless_size);
|
||||
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n", description,
|
||||
stats->lossless_size);
|
||||
fprintf(stderr, " * Header size: %d bytes, image data size: %d\n",
|
||||
stats->lossless_hdr_size, stats->lossless_data_size);
|
||||
if (stats->lossless_features) {
|
||||
@@ -207,8 +209,7 @@ static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
||||
}
|
||||
|
||||
static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
||||
int full_details,
|
||||
const char* const file_name) {
|
||||
int full_details, const char* const file_name) {
|
||||
const WebPAuxStats* const stats = pic->stats;
|
||||
if (short_output) {
|
||||
fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
|
||||
@@ -218,25 +219,25 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
||||
const int num_skip = stats->block_count[2];
|
||||
const int total = num_i4 + num_i16;
|
||||
WFPRINTF(stderr, "File: %s\n", (const W_CHAR*)file_name);
|
||||
fprintf(stderr, "Dimension: %d x %d%s\n",
|
||||
pic->width, pic->height,
|
||||
fprintf(stderr, "Dimension: %d x %d%s\n", pic->width, pic->height,
|
||||
stats->alpha_data_size ? " (with alpha)" : "");
|
||||
fprintf(stderr, "Output: "
|
||||
fprintf(stderr,
|
||||
"Output: "
|
||||
"%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f %2.2f dB\n"
|
||||
" (%.2f bpp)\n",
|
||||
stats->coded_size,
|
||||
stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3],
|
||||
8.f * stats->coded_size / pic->width / pic->height);
|
||||
stats->coded_size, stats->PSNR[0], stats->PSNR[1], stats->PSNR[2],
|
||||
stats->PSNR[3], 8.f * stats->coded_size / pic->width / pic->height);
|
||||
if (total > 0) {
|
||||
int totals[4] = { 0, 0, 0, 0 };
|
||||
fprintf(stderr, "block count: intra4: %6d (%.2f%%)\n"
|
||||
" intra16: %6d (%.2f%%)\n"
|
||||
" skipped: %6d (%.2f%%)\n",
|
||||
num_i4, 100.f * num_i4 / total,
|
||||
num_i16, 100.f * num_i16 / total,
|
||||
int totals[4] = {0, 0, 0, 0};
|
||||
fprintf(stderr,
|
||||
"block count: intra4: %6d (%.2f%%)\n"
|
||||
" intra16: %6d (%.2f%%)\n"
|
||||
" skipped: %6d (%.2f%%)\n",
|
||||
num_i4, 100.f * num_i4 / total, num_i16, 100.f * num_i16 / total,
|
||||
num_skip, 100.f * num_skip / total);
|
||||
fprintf(stderr, "bytes used: header: %6d (%.1f%%)\n"
|
||||
" mode-partition: %6d (%.1f%%)\n",
|
||||
fprintf(stderr,
|
||||
"bytes used: header: %6d (%.1f%%)\n"
|
||||
" mode-partition: %6d (%.1f%%)\n",
|
||||
stats->header_bytes[0],
|
||||
100.f * stats->header_bytes[0] / stats->coded_size,
|
||||
stats->header_bytes[1],
|
||||
@@ -245,9 +246,10 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
||||
fprintf(stderr, " transparency: %6d (%.1f dB)\n",
|
||||
stats->alpha_data_size, stats->PSNR[4]);
|
||||
}
|
||||
fprintf(stderr, " Residuals bytes "
|
||||
"|segment 1|segment 2|segment 3"
|
||||
"|segment 4| total\n");
|
||||
fprintf(stderr,
|
||||
" Residuals bytes "
|
||||
"|segment 1|segment 2|segment 3"
|
||||
"|segment 4| total\n");
|
||||
if (full_details) {
|
||||
fprintf(stderr, " intra4-coeffs: ");
|
||||
PrintByteCount(stats->residual_bytes[0], stats->coded_size, totals);
|
||||
@@ -284,11 +286,11 @@ static void PrintMapInfo(const WebPPicture* const pic) {
|
||||
for (y = 0; y < mb_h; ++y) {
|
||||
for (x = 0; x < mb_w; ++x) {
|
||||
const int c = pic->extra_info[x + y * mb_w];
|
||||
if (type == 1) { // intra4/intra16
|
||||
if (type == 1) { // intra4/intra16
|
||||
fprintf(stderr, "%c", "+."[c]);
|
||||
} else if (type == 2) { // segments
|
||||
} else if (type == 2) { // segments
|
||||
fprintf(stderr, "%c", ".-*X"[c]);
|
||||
} else if (type == 3) { // quantizers
|
||||
} else if (type == 3) { // quantizers
|
||||
fprintf(stderr, "%.2d ", c);
|
||||
} else if (type == 6 || type == 7) {
|
||||
fprintf(stderr, "%3d ", c);
|
||||
@@ -344,7 +346,7 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
fclose(f);
|
||||
return ok;
|
||||
}
|
||||
@@ -354,9 +356,9 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
|
||||
|
||||
enum {
|
||||
METADATA_EXIF = (1 << 0),
|
||||
METADATA_ICC = (1 << 1),
|
||||
METADATA_XMP = (1 << 2),
|
||||
METADATA_ALL = METADATA_EXIF | METADATA_ICC | METADATA_XMP
|
||||
METADATA_ICC = (1 << 1),
|
||||
METADATA_XMP = (1 << 2),
|
||||
METADATA_ALL = METADATA_EXIF | METADATA_ICC | METADATA_XMP
|
||||
};
|
||||
|
||||
static const int kChunkHeaderSize = 8;
|
||||
@@ -409,9 +411,9 @@ static int WriteMetadataChunk(FILE* const out, const char fourcc[4],
|
||||
|
||||
// Sets 'flag' in 'vp8x_flags' and updates 'metadata_size' with the size of the
|
||||
// chunk if there is metadata and 'keep' is true.
|
||||
static int UpdateFlagsAndSize(const MetadataPayload* const payload,
|
||||
int keep, int flag,
|
||||
uint32_t* vp8x_flags, uint64_t* metadata_size) {
|
||||
static int UpdateFlagsAndSize(const MetadataPayload* const payload, int keep,
|
||||
int flag, uint32_t* vp8x_flags,
|
||||
uint64_t* metadata_size) {
|
||||
if (keep && payload->bytes != NULL && payload->size > 0) {
|
||||
*vp8x_flags |= flag;
|
||||
*metadata_size += kChunkHeaderSize + payload->size + (payload->size & 1);
|
||||
@@ -432,23 +434,23 @@ static int WriteWebPWithMetadata(FILE* const out,
|
||||
int* const metadata_written) {
|
||||
const char kVP8XHeader[] = "VP8X\x0a\x00\x00\x00";
|
||||
const int kAlphaFlag = 0x10;
|
||||
const int kEXIFFlag = 0x08;
|
||||
const int kICCPFlag = 0x20;
|
||||
const int kXMPFlag = 0x04;
|
||||
const int kEXIFFlag = 0x08;
|
||||
const int kICCPFlag = 0x20;
|
||||
const int kXMPFlag = 0x04;
|
||||
const size_t kRiffHeaderSize = 12;
|
||||
const size_t kMaxChunkPayload = ~0 - kChunkHeaderSize - 1;
|
||||
const size_t kMinSize = kRiffHeaderSize + kChunkHeaderSize;
|
||||
uint32_t flags = 0;
|
||||
uint64_t metadata_size = 0;
|
||||
const int write_exif = UpdateFlagsAndSize(&metadata->exif,
|
||||
!!(keep_metadata & METADATA_EXIF),
|
||||
kEXIFFlag, &flags, &metadata_size);
|
||||
const int write_iccp = UpdateFlagsAndSize(&metadata->iccp,
|
||||
!!(keep_metadata & METADATA_ICC),
|
||||
kICCPFlag, &flags, &metadata_size);
|
||||
const int write_xmp = UpdateFlagsAndSize(&metadata->xmp,
|
||||
!!(keep_metadata & METADATA_XMP),
|
||||
kXMPFlag, &flags, &metadata_size);
|
||||
const int write_exif =
|
||||
UpdateFlagsAndSize(&metadata->exif, !!(keep_metadata & METADATA_EXIF),
|
||||
kEXIFFlag, &flags, &metadata_size);
|
||||
const int write_iccp =
|
||||
UpdateFlagsAndSize(&metadata->iccp, !!(keep_metadata & METADATA_ICC),
|
||||
kICCPFlag, &flags, &metadata_size);
|
||||
const int write_xmp =
|
||||
UpdateFlagsAndSize(&metadata->xmp, !!(keep_metadata & METADATA_XMP),
|
||||
kXMPFlag, &flags, &metadata_size);
|
||||
uint8_t* webp = memory_writer->mem;
|
||||
size_t webp_size = memory_writer->size;
|
||||
|
||||
@@ -456,17 +458,18 @@ static int WriteWebPWithMetadata(FILE* const out,
|
||||
|
||||
if (webp_size < kMinSize) return 0;
|
||||
if (webp_size - kChunkHeaderSize + metadata_size > kMaxChunkPayload) {
|
||||
fprintf(stderr, "Error! Addition of metadata would exceed "
|
||||
"container size limit.\n");
|
||||
fprintf(stderr,
|
||||
"Error! Addition of metadata would exceed "
|
||||
"container size limit.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (metadata_size > 0) {
|
||||
const int kVP8XChunkSize = 18;
|
||||
const int has_vp8x = !memcmp(webp + kRiffHeaderSize, "VP8X", kTagSize);
|
||||
const uint32_t riff_size = (uint32_t)(webp_size - kChunkHeaderSize +
|
||||
(has_vp8x ? 0 : kVP8XChunkSize) +
|
||||
metadata_size);
|
||||
const uint32_t riff_size =
|
||||
(uint32_t)(webp_size - kChunkHeaderSize +
|
||||
(has_vp8x ? 0 : kVP8XChunkSize) + metadata_size);
|
||||
// RIFF
|
||||
int ok = (fwrite(webp, kTagSize, 1, out) == 1);
|
||||
// RIFF size (file header size is not recorded)
|
||||
@@ -515,11 +518,39 @@ static int WriteWebPWithMetadata(FILE* const out,
|
||||
return (fwrite(webp, webp_size, 1, out) == 1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Resize
|
||||
|
||||
enum {
|
||||
RESIZE_MODE_DOWN_ONLY,
|
||||
RESIZE_MODE_UP_ONLY,
|
||||
RESIZE_MODE_ALWAYS,
|
||||
RESIZE_MODE_DEFAULT = RESIZE_MODE_ALWAYS
|
||||
};
|
||||
|
||||
static void ApplyResizeMode(const int resize_mode, const WebPPicture* const pic,
|
||||
int* const resize_w, int* const resize_h) {
|
||||
const int src_w = pic->width;
|
||||
const int src_h = pic->height;
|
||||
const int dst_w = *resize_w;
|
||||
const int dst_h = *resize_h;
|
||||
|
||||
if (resize_mode == RESIZE_MODE_DOWN_ONLY) {
|
||||
if ((dst_w == 0 && src_h <= dst_h) || (dst_h == 0 && src_w <= dst_w) ||
|
||||
(src_w <= dst_w && src_h <= dst_h)) {
|
||||
*resize_w = *resize_h = 0;
|
||||
}
|
||||
} else if (resize_mode == RESIZE_MODE_UP_ONLY) {
|
||||
if (src_w >= dst_w && src_h >= dst_h) {
|
||||
*resize_w = *resize_h = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int ProgressReport(int percent, const WebPPicture* const picture) {
|
||||
fprintf(stderr, "[%s]: %3d %% \r",
|
||||
(char*)picture->user_data, percent);
|
||||
fprintf(stderr, "[%s]: %3d %% \r", (char*)picture->user_data, percent);
|
||||
return 1; // all ok
|
||||
}
|
||||
|
||||
@@ -536,8 +567,9 @@ static void HelpShort(void) {
|
||||
static void HelpLong(void) {
|
||||
printf("Usage:\n");
|
||||
printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
|
||||
printf("If input size (-s) for an image is not specified, it is\n"
|
||||
"assumed to be a PNG, JPEG, TIFF or WebP file.\n");
|
||||
printf(
|
||||
"If input size (-s) for an image is not specified, it is\n"
|
||||
"assumed to be a PNG, JPEG, TIFF or WebP file.\n");
|
||||
printf("Note: Animated PNG and WebP files are not supported.\n");
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
printf("Windows builds can take as input any of the files handled by WIC.\n");
|
||||
@@ -545,44 +577,59 @@ static void HelpLong(void) {
|
||||
printf("\nOptions:\n");
|
||||
printf(" -h / -help ............. short help\n");
|
||||
printf(" -H / -longhelp ......... long help\n");
|
||||
printf(" -q <float> ............. quality factor (0:small..100:big), "
|
||||
"default=75\n");
|
||||
printf(" -alpha_q <int> ......... transparency-compression quality (0..100),"
|
||||
"\n default=100\n");
|
||||
printf(
|
||||
" -q <float> ............. quality factor (0:small..100:big), "
|
||||
"default=75\n");
|
||||
printf(
|
||||
" -alpha_q <int> ......... transparency-compression quality (0..100),"
|
||||
"\n default=100\n");
|
||||
printf(" -preset <string> ....... preset setting, one of:\n");
|
||||
printf(" default, photo, picture,\n");
|
||||
printf(" drawing, icon, text\n");
|
||||
printf(" -preset must come first, as it overwrites other parameters\n");
|
||||
printf(" -z <int> ............... activates lossless preset with given\n"
|
||||
" level in [0:fast, ..., 9:slowest]\n");
|
||||
printf(
|
||||
" -z <int> ............... activates lossless preset with given\n"
|
||||
" level in [0:fast, ..., 9:slowest]\n");
|
||||
printf("\n");
|
||||
printf(" -m <int> ............... compression method (0=fast, 6=slowest), "
|
||||
"default=4\n");
|
||||
printf(" -segments <int> ........ number of segments to use (1..4), "
|
||||
"default=4\n");
|
||||
printf(
|
||||
" -m <int> ............... compression method (0=fast, 6=slowest), "
|
||||
"default=4\n");
|
||||
printf(
|
||||
" -segments <int> ........ number of segments to use (1..4), "
|
||||
"default=4\n");
|
||||
printf(" -size <int> ............ target size (in bytes)\n");
|
||||
printf(" -psnr <float> .......... target PSNR (in dB. typically: 42)\n");
|
||||
printf("\n");
|
||||
printf(" -s <int> <int> ......... input size (width x height) for YUV\n");
|
||||
printf(" -sns <int> ............. spatial noise shaping (0:off, 100:max), "
|
||||
"default=50\n");
|
||||
printf(" -f <int> ............... filter strength (0=off..100), "
|
||||
"default=60\n");
|
||||
printf(" -sharpness <int> ....... "
|
||||
"filter sharpness (0:most .. 7:least sharp), default=0\n");
|
||||
printf(" -strong ................ use strong filter instead "
|
||||
"of simple (default)\n");
|
||||
printf(
|
||||
" -sns <int> ............. spatial noise shaping (0:off, 100:max), "
|
||||
"default=50\n");
|
||||
printf(
|
||||
" -f <int> ............... filter strength (0=off..100), "
|
||||
"default=60\n");
|
||||
printf(
|
||||
" -sharpness <int> ....... "
|
||||
"filter sharpness (0:most .. 7:least sharp), default=0\n");
|
||||
printf(
|
||||
" -strong ................ use strong filter instead "
|
||||
"of simple (default)\n");
|
||||
printf(" -nostrong .............. use simple filter instead of strong\n");
|
||||
printf(" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
|
||||
"conversion\n");
|
||||
printf(
|
||||
" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
|
||||
"conversion\n");
|
||||
printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n");
|
||||
printf(" "
|
||||
"the first partition (0=no degradation ... 100=full)\n");
|
||||
printf(
|
||||
" "
|
||||
"the first partition (0=no degradation ... 100=full)\n");
|
||||
printf(" -pass <int> ............ analysis pass number (1..10)\n");
|
||||
printf(" -qrange <min> <max> .... specifies the permissible quality range\n"
|
||||
" (default: 0 100)\n");
|
||||
printf(
|
||||
" -qrange <min> <max> .... specifies the permissible quality range\n"
|
||||
" (default: 0 100)\n");
|
||||
printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
|
||||
printf(" -resize <w> <h> ........ resize picture (*after* any cropping)\n");
|
||||
printf(
|
||||
" -resize_mode <string> .. one of: up_only, down_only,"
|
||||
" always (default)\n");
|
||||
printf(" -mt .................... use multi-threading if available\n");
|
||||
printf(" -low_memory ............ reduce memory usage (slower encoding)\n");
|
||||
printf(" -map <int> ............. print map of extra info\n");
|
||||
@@ -590,20 +637,24 @@ static void HelpLong(void) {
|
||||
printf(" -print_ssim ............ prints averaged SSIM distortion\n");
|
||||
printf(" -print_lsim ............ prints local-similarity distortion\n");
|
||||
printf(" -d <file.pgm> .......... dump the compressed output (PGM file)\n");
|
||||
printf(" -alpha_method <int> .... transparency-compression method (0..1), "
|
||||
"default=1\n");
|
||||
printf(
|
||||
" -alpha_method <int> .... transparency-compression method (0..1), "
|
||||
"default=1\n");
|
||||
printf(" -alpha_filter <string> . predictive filtering for alpha plane,\n");
|
||||
printf(" one of: none, fast (default) or best\n");
|
||||
printf(" -exact ................. preserve RGB values in transparent area, "
|
||||
"default=off\n");
|
||||
printf(" -blend_alpha <hex> ..... blend colors against background color\n"
|
||||
" expressed as RGB values written in\n"
|
||||
" hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
|
||||
" green=0xe0 and blue=0xd0\n");
|
||||
printf(
|
||||
" -exact ................. preserve RGB values in transparent area, "
|
||||
"default=off\n");
|
||||
printf(
|
||||
" -blend_alpha <hex> ..... blend colors against background color\n"
|
||||
" expressed as RGB values written in\n"
|
||||
" hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
|
||||
" green=0xe0 and blue=0xd0\n");
|
||||
printf(" -noalpha ............... discard any transparency information\n");
|
||||
printf(" -lossless .............. encode image losslessly, default=off\n");
|
||||
printf(" -near_lossless <int> ... use near-lossless image preprocessing\n"
|
||||
" (0..100=off), default=100\n");
|
||||
printf(
|
||||
" -near_lossless <int> ... use near-lossless image preprocessing\n"
|
||||
" (0..100=off), default=100\n");
|
||||
printf(" -hint <string> ......... specify image characteristics hint,\n");
|
||||
printf(" one of: photo, picture or graph\n");
|
||||
|
||||
@@ -611,8 +662,9 @@ static void HelpLong(void) {
|
||||
printf(" -metadata <string> ..... comma separated list of metadata to\n");
|
||||
printf(" ");
|
||||
printf("copy from the input to the output if present.\n");
|
||||
printf(" "
|
||||
"Valid values: all, none (default), exif, icc, xmp\n");
|
||||
printf(
|
||||
" "
|
||||
"Valid values: all, none (default), exif, icc, xmp\n");
|
||||
|
||||
printf("\n");
|
||||
printf(" -short ................. condense printed message\n");
|
||||
@@ -621,8 +673,9 @@ static void HelpLong(void) {
|
||||
#ifndef WEBP_DLL
|
||||
printf(" -noasm ................. disable all assembly optimizations\n");
|
||||
#endif
|
||||
printf(" -v ..................... verbose, e.g. print encoding/decoding "
|
||||
"times\n");
|
||||
printf(
|
||||
" -v ..................... verbose, e.g. print encoding/decoding "
|
||||
"times\n");
|
||||
printf(" -progress .............. report encoding progress\n");
|
||||
printf("\n");
|
||||
printf("Experimental Options:\n");
|
||||
@@ -637,30 +690,29 @@ static void HelpLong(void) {
|
||||
// Error messages
|
||||
|
||||
static const char* const kErrorMessages[VP8_ENC_ERROR_LAST] = {
|
||||
"OK",
|
||||
"OUT_OF_MEMORY: Out of memory allocating objects",
|
||||
"BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
|
||||
"NULL_PARAMETER: NULL parameter passed to function",
|
||||
"INVALID_CONFIGURATION: configuration is invalid",
|
||||
"BAD_DIMENSION: Bad picture dimension. Maximum width and height "
|
||||
"allowed is 16383 pixels.",
|
||||
"PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
|
||||
"To reduce the size of this partition, try using less segments "
|
||||
"with the -segments option, and eventually reduce the number of "
|
||||
"header bits using -partition_limit. More details are available "
|
||||
"in the manual (`man cwebp`)",
|
||||
"PARTITION_OVERFLOW: Partition is too big to fit 16M",
|
||||
"BAD_WRITE: Picture writer returned an I/O error",
|
||||
"FILE_TOO_BIG: File would be too big to fit in 4G",
|
||||
"USER_ABORT: encoding abort requested by user"
|
||||
};
|
||||
"OK",
|
||||
"OUT_OF_MEMORY: Out of memory allocating objects",
|
||||
"BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
|
||||
"NULL_PARAMETER: NULL parameter passed to function",
|
||||
"INVALID_CONFIGURATION: configuration is invalid",
|
||||
"BAD_DIMENSION: Bad picture dimension. Maximum width and height "
|
||||
"allowed is 16383 pixels.",
|
||||
"PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
|
||||
"To reduce the size of this partition, try using less segments "
|
||||
"with the -segments option, and eventually reduce the number of "
|
||||
"header bits using -partition_limit. More details are available "
|
||||
"in the manual (`man cwebp`)",
|
||||
"PARTITION_OVERFLOW: Partition is too big to fit 16M",
|
||||
"BAD_WRITE: Picture writer returned an I/O error",
|
||||
"FILE_TOO_BIG: File would be too big to fit in 4G",
|
||||
"USER_ABORT: encoding abort requested by user"};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
|
||||
int main(int argc, const char* argv[]) {
|
||||
int return_value = EXIT_FAILURE;
|
||||
const char* in_file = NULL, *out_file = NULL, *dump_file = NULL;
|
||||
const char *in_file = NULL, *out_file = NULL, *dump_file = NULL;
|
||||
FILE* out = NULL;
|
||||
int c;
|
||||
int short_output = 0;
|
||||
@@ -670,14 +722,15 @@ int main(int argc, const char* argv[]) {
|
||||
uint32_t background_color = 0xffffffu;
|
||||
int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
|
||||
int resize_w = 0, resize_h = 0;
|
||||
int resize_mode = RESIZE_MODE_DEFAULT;
|
||||
int lossless_preset = 6;
|
||||
int use_lossless_preset = -1; // -1=unset, 0=don't use, 1=use it
|
||||
int show_progress = 0;
|
||||
int keep_metadata = 0;
|
||||
int metadata_written = 0;
|
||||
WebPPicture picture;
|
||||
int print_distortion = -1; // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
|
||||
WebPPicture original_picture; // when PSNR or SSIM is requested
|
||||
int print_distortion = -1; // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
|
||||
WebPPicture original_picture; // when PSNR or SSIM is requested
|
||||
WebPConfig config;
|
||||
WebPAuxStats stats;
|
||||
WebPMemoryWriter memory_writer;
|
||||
@@ -689,8 +742,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
MetadataInit(&metadata);
|
||||
WebPMemoryWriterInit(&memory_writer);
|
||||
if (!WebPPictureInit(&picture) ||
|
||||
!WebPPictureInit(&original_picture) ||
|
||||
if (!WebPPictureInit(&picture) || !WebPPictureInit(&original_picture) ||
|
||||
!WebPConfigInit(&config)) {
|
||||
fprintf(stderr, "Error! Version mismatch!\n");
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
@@ -698,7 +750,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (argc == 1) {
|
||||
HelpShort();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
@@ -729,18 +781,17 @@ int main(int argc, const char* argv[]) {
|
||||
picture.width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
picture.height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 ||
|
||||
picture.height > WEBP_MAX_DIMENSION || picture.height < 0) {
|
||||
fprintf(stderr,
|
||||
"Specified dimension (%d x %d) is out of range.\n",
|
||||
picture.height > WEBP_MAX_DIMENSION || picture.height < 0) {
|
||||
fprintf(stderr, "Specified dimension (%d x %d) is out of range.\n",
|
||||
picture.width, picture.height);
|
||||
goto Error;
|
||||
}
|
||||
} else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
|
||||
config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
} else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
|
||||
config.quality = ExUtilGetFloat(argv[++c], &parse_error);
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
} else if (!strcmp(argv[c], "-z") && c + 1 < argc) {
|
||||
lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
if (use_lossless_preset != 0) use_lossless_preset = 1;
|
||||
@@ -837,6 +888,18 @@ int main(int argc, const char* argv[]) {
|
||||
} else if (!strcmp(argv[c], "-resize") && c + 2 < argc) {
|
||||
resize_w = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
resize_h = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-resize_mode") && c + 1 < argc) {
|
||||
++c;
|
||||
if (!strcmp(argv[c], "down_only")) {
|
||||
resize_mode = RESIZE_MODE_DOWN_ONLY;
|
||||
} else if (!strcmp(argv[c], "up_only")) {
|
||||
resize_mode = RESIZE_MODE_UP_ONLY;
|
||||
} else if (!strcmp(argv[c], "always")) {
|
||||
resize_mode = RESIZE_MODE_ALWAYS;
|
||||
} else {
|
||||
fprintf(stderr, "Error! Unrecognized resize mode: %s\n", argv[c]);
|
||||
goto Error;
|
||||
}
|
||||
#ifndef WEBP_DLL
|
||||
} else if (!strcmp(argv[c], "-noasm")) {
|
||||
VP8GetCPUInfo = NULL;
|
||||
@@ -844,11 +907,10 @@ int main(int argc, const char* argv[]) {
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
const int version = WebPGetEncoderVersion();
|
||||
const int sharpyuv_version = SharpYuvGetVersion();
|
||||
printf("%d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
printf("libsharpyuv: %d.%d.%d\n",
|
||||
(sharpyuv_version >> 24) & 0xff, (sharpyuv_version >> 16) & 0xffff,
|
||||
sharpyuv_version & 0xff);
|
||||
printf("%d.%d.%d\n", (version >> 16) & 0xff, (version >> 8) & 0xff,
|
||||
version & 0xff);
|
||||
printf("libsharpyuv: %d.%d.%d\n", (sharpyuv_version >> 24) & 0xff,
|
||||
(sharpyuv_version >> 16) & 0xffff, sharpyuv_version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
} else if (!strcmp(argv[c], "-progress")) {
|
||||
show_progress = 1;
|
||||
@@ -882,11 +944,11 @@ int main(int argc, const char* argv[]) {
|
||||
const char* option;
|
||||
int flag;
|
||||
} kTokens[] = {
|
||||
{ "all", METADATA_ALL },
|
||||
{ "none", 0 },
|
||||
{ "exif", METADATA_EXIF },
|
||||
{ "icc", METADATA_ICC },
|
||||
{ "xmp", METADATA_XMP },
|
||||
{"all", METADATA_ALL}, //
|
||||
{"none", 0}, //
|
||||
{"exif", METADATA_EXIF}, //
|
||||
{"icc", METADATA_ICC}, //
|
||||
{"xmp", METADATA_XMP}, //
|
||||
};
|
||||
const size_t kNumTokens = sizeof(kTokens) / sizeof(kTokens[0]);
|
||||
const char* start = argv[++c];
|
||||
@@ -918,8 +980,9 @@ int main(int argc, const char* argv[]) {
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
if (keep_metadata != 0 && keep_metadata != METADATA_ICC) {
|
||||
// TODO(jzern): remove when -metadata is supported on all platforms.
|
||||
fprintf(stderr, "Warning: only ICC profile extraction is currently"
|
||||
" supported on this platform!\n");
|
||||
fprintf(stderr,
|
||||
"Warning: only ICC profile extraction is currently"
|
||||
" supported on this platform!\n");
|
||||
}
|
||||
#endif
|
||||
} else if (!strcmp(argv[c], "-v")) {
|
||||
@@ -957,12 +1020,14 @@ int main(int argc, const char* argv[]) {
|
||||
// warning for such options.
|
||||
if (!quiet && config.lossless == 1) {
|
||||
if (config.target_size > 0 || config.target_PSNR > 0) {
|
||||
fprintf(stderr, "Encoding for specified size or PSNR is not supported"
|
||||
" for lossless encoding. Ignoring such option(s)!\n");
|
||||
fprintf(stderr,
|
||||
"Encoding for specified size or PSNR is not supported"
|
||||
" for lossless encoding. Ignoring such option(s)!\n");
|
||||
}
|
||||
if (config.partition_limit > 0) {
|
||||
fprintf(stderr, "Partition limit option is not required for lossless"
|
||||
" encoding. Ignoring this option!\n");
|
||||
fprintf(stderr,
|
||||
"Partition limit option is not required for lossless"
|
||||
" encoding. Ignoring this option!\n");
|
||||
}
|
||||
}
|
||||
// If a target size or PSNR was given, but somehow the -pass option was
|
||||
@@ -979,9 +1044,9 @@ int main(int argc, const char* argv[]) {
|
||||
// Read the input. We need to decide if we prefer ARGB or YUVA
|
||||
// samples, depending on the expected compression mode (this saves
|
||||
// some conversion steps).
|
||||
picture.use_argb = (config.lossless || config.use_sharp_yuv ||
|
||||
config.preprocessing > 0 ||
|
||||
crop || (resize_w | resize_h) > 0);
|
||||
picture.use_argb =
|
||||
(config.lossless || config.use_sharp_yuv || config.preprocessing > 0 ||
|
||||
crop || (resize_w | resize_h) > 0);
|
||||
if (verbose) {
|
||||
StopwatchReset(&stop_watch);
|
||||
}
|
||||
@@ -1057,6 +1122,7 @@ int main(int argc, const char* argv[]) {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
ApplyResizeMode(resize_mode, &picture, &resize_w, &resize_h);
|
||||
if ((resize_w | resize_h) > 0) {
|
||||
WebPPicture picture_no_alpha;
|
||||
if (config.exact) {
|
||||
@@ -1128,8 +1194,8 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
if (!WebPEncode(&config, &picture)) {
|
||||
fprintf(stderr, "Error! Cannot encode picture as WebP\n");
|
||||
fprintf(stderr, "Error code: %d (%s)\n",
|
||||
picture.error_code, kErrorMessages[picture.error_code]);
|
||||
fprintf(stderr, "Error code: %d (%s)\n", picture.error_code,
|
||||
kErrorMessages[picture.error_code]);
|
||||
goto Error;
|
||||
}
|
||||
if (verbose) {
|
||||
@@ -1172,8 +1238,9 @@ int main(int argc, const char* argv[]) {
|
||||
// Write the YUV planes to a PGM file. Only available for lossy.
|
||||
if (dump_file) {
|
||||
if (picture.use_argb) {
|
||||
fprintf(stderr, "Warning: can't dump file (-d option) "
|
||||
"in lossless mode.\n");
|
||||
fprintf(stderr,
|
||||
"Warning: can't dump file (-d option) "
|
||||
"in lossless mode.\n");
|
||||
} else if (!DumpPicture(&picture, dump_file)) {
|
||||
WFPRINTF(stderr, "Warning, couldn't dump picture %s\n",
|
||||
(const W_CHAR*)dump_file);
|
||||
@@ -1218,18 +1285,18 @@ int main(int argc, const char* argv[]) {
|
||||
if (!short_output && picture.extra_info_type > 0) {
|
||||
PrintMapInfo(&picture);
|
||||
}
|
||||
if (print_distortion >= 0) { // print distortion
|
||||
static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
|
||||
if (print_distortion >= 0) { // print distortion
|
||||
static const char* distortion_names[] = {"PSNR", "SSIM", "LSIM"};
|
||||
float values[5];
|
||||
if (!WebPPictureDistortion(&picture, &original_picture,
|
||||
print_distortion, values)) {
|
||||
if (!WebPPictureDistortion(&picture, &original_picture, print_distortion,
|
||||
values)) {
|
||||
fprintf(stderr, "Error while computing the distortion.\n");
|
||||
goto Error;
|
||||
}
|
||||
if (!short_output) {
|
||||
fprintf(stderr, "%s: ", distortion_names[print_distortion]);
|
||||
fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f Total:%.2f\n",
|
||||
values[0], values[1], values[2], values[3], values[4]);
|
||||
fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f Total:%.2f\n", values[0],
|
||||
values[1], values[2], values[3], values[4]);
|
||||
} else {
|
||||
fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]);
|
||||
}
|
||||
@@ -1240,7 +1307,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
return_value = EXIT_SUCCESS;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
WebPMemoryWriterClear(&memory_writer);
|
||||
WebPFree(picture.extra_info);
|
||||
MetadataFree(&metadata);
|
||||
|
||||
207
examples/dwebp.c
207
examples/dwebp.c
@@ -25,6 +25,8 @@
|
||||
#include "../imageio/webpdec.h"
|
||||
#include "./stopwatch.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
static int verbose = 0;
|
||||
static int quiet = 0;
|
||||
@@ -33,14 +35,13 @@ static int quiet = 0;
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif // WEBP_DLL
|
||||
|
||||
|
||||
static int SaveOutput(const WebPDecBuffer* const buffer,
|
||||
WebPOutputFileFormat format, const char* const out_file) {
|
||||
const int use_stdout = (out_file != NULL) && !WSTRCMP(out_file, "-");
|
||||
@@ -75,43 +76,42 @@ static int SaveOutput(const WebPDecBuffer* const buffer,
|
||||
}
|
||||
|
||||
static void Help(void) {
|
||||
printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
|
||||
"Decodes the WebP image file to PNG format [Default].\n"
|
||||
"Note: Animated WebP files are not supported.\n\n"
|
||||
"Use following options to convert into alternate image formats:\n"
|
||||
" -pam ......... save the raw RGBA samples as a color PAM\n"
|
||||
" -ppm ......... save the raw RGB samples as a color PPM\n"
|
||||
" -bmp ......... save as uncompressed BMP format\n"
|
||||
" -tiff ........ save as uncompressed TIFF format\n"
|
||||
" -pgm ......... save the raw YUV samples as a grayscale PGM\n"
|
||||
" file with IMC4 layout\n"
|
||||
" -yuv ......... save the raw YUV samples in flat layout\n"
|
||||
"\n"
|
||||
" Other options are:\n"
|
||||
" -version ..... print version number and exit\n"
|
||||
" -nofancy ..... don't use the fancy YUV420 upscaler\n"
|
||||
" -nofilter .... disable in-loop filtering\n"
|
||||
" -nodither .... disable dithering\n"
|
||||
" -dither <d> .. dithering strength (in 0..100)\n"
|
||||
" -alpha_dither use alpha-plane dithering if needed\n"
|
||||
" -mt .......... use multi-threading\n"
|
||||
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
|
||||
" -resize <w> <h> ......... resize output (*after* any cropping)\n"
|
||||
" -flip ........ flip the output vertically\n"
|
||||
" -alpha ....... only save the alpha plane\n"
|
||||
" -incremental . use incremental decoding (useful for tests)\n"
|
||||
" -h ........... this help message\n"
|
||||
" -v ........... verbose (e.g. print encoding/decoding times)\n"
|
||||
" -quiet ....... quiet mode, don't print anything\n"
|
||||
printf(
|
||||
"Usage: dwebp in_file [options] [-o out_file]\n\n"
|
||||
"Decodes the WebP image file to PNG format [Default].\n"
|
||||
"Note: Animated WebP files are not supported.\n\n"
|
||||
"Use following options to convert into alternate image formats:\n"
|
||||
" -pam ......... save the raw RGBA samples as a color PAM\n"
|
||||
" -ppm ......... save the raw RGB samples as a color PPM\n"
|
||||
" -bmp ......... save as uncompressed BMP format\n"
|
||||
" -tiff ........ save as uncompressed TIFF format\n"
|
||||
" -pgm ......... save the raw YUV samples as a grayscale PGM\n"
|
||||
" file with IMC4 layout\n"
|
||||
" -yuv ......... save the raw YUV samples in flat layout\n"
|
||||
"\n"
|
||||
" Other options are:\n"
|
||||
" -version ..... print version number and exit\n"
|
||||
" -nofancy ..... don't use the fancy YUV420 upscaler\n"
|
||||
" -nofilter .... disable in-loop filtering\n"
|
||||
" -nodither .... disable dithering\n"
|
||||
" -dither <d> .. dithering strength (in 0..100)\n"
|
||||
" -alpha_dither use alpha-plane dithering if needed\n"
|
||||
" -mt .......... use multi-threading\n"
|
||||
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
|
||||
" -resize <w> <h> ......... resize output (*after* any cropping)\n"
|
||||
" -flip ........ flip the output vertically\n"
|
||||
" -alpha ....... only save the alpha plane\n"
|
||||
" -incremental . use incremental decoding (useful for tests)\n"
|
||||
" -h ........... this help message\n"
|
||||
" -v ........... verbose (e.g. print encoding/decoding times)\n"
|
||||
" -quiet ....... quiet mode, don't print anything\n"
|
||||
#ifndef WEBP_DLL
|
||||
" -noasm ....... disable all assembly optimizations\n"
|
||||
" -noasm ....... disable all assembly optimizations\n"
|
||||
#endif
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
static const char* const kFormatType[] = {
|
||||
"unspecified", "lossy", "lossless"
|
||||
};
|
||||
static const char* const kFormatType[] = {"unspecified", "lossy", "lossless"};
|
||||
|
||||
static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
|
||||
WebPOutputFileFormat format,
|
||||
@@ -128,23 +128,23 @@ static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
|
||||
h = config->options.crop_height;
|
||||
}
|
||||
if (format >= RGB && format <= rgbA_4444) {
|
||||
const int bpp = (format == RGB || format == BGR) ? 3
|
||||
: (format == RGBA_4444 || format == rgbA_4444 ||
|
||||
format == RGB_565) ? 2
|
||||
: 4;
|
||||
uint32_t stride = bpp * w + 7; // <- just for exercising
|
||||
const int bpp =
|
||||
(format == RGB || format == BGR) ? 3
|
||||
: (format == RGBA_4444 || format == rgbA_4444 || format == RGB_565) ? 2
|
||||
: 4;
|
||||
uint32_t stride = bpp * w + 7; // <- just for exercising
|
||||
external_buffer = (uint8_t*)WebPMalloc(stride * h);
|
||||
if (external_buffer == NULL) return NULL;
|
||||
output_buffer->u.RGBA.stride = stride;
|
||||
output_buffer->u.RGBA.size = stride * h;
|
||||
output_buffer->u.RGBA.rgba = external_buffer;
|
||||
} else { // YUV and YUVA
|
||||
} else { // YUV and YUVA
|
||||
const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace);
|
||||
uint8_t* tmp;
|
||||
uint32_t stride = w + 3;
|
||||
uint32_t uv_stride = (w + 1) / 2 + 13;
|
||||
uint32_t total_size = stride * h * (has_alpha ? 2 : 1)
|
||||
+ 2 * uv_stride * (h + 1) / 2;
|
||||
uint32_t total_size =
|
||||
stride * h * (has_alpha ? 2 : 1) + 2 * uv_stride * (h + 1) / 2;
|
||||
assert(format >= YUV && format <= YUVA);
|
||||
external_buffer = (uint8_t*)WebPMalloc(total_size);
|
||||
if (external_buffer == NULL) return NULL;
|
||||
@@ -226,8 +226,8 @@ int main(int argc, const char* argv[]) {
|
||||
quiet = 1;
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
const int version = WebPGetDecoderVersion();
|
||||
printf("%d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
printf("%d.%d.%d\n", (version >> 16) & 0xff, (version >> 8) & 0xff,
|
||||
version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
} else if (!strcmp(argv[c], "-pgm")) {
|
||||
format = PGM;
|
||||
@@ -235,20 +235,33 @@ int main(int argc, const char* argv[]) {
|
||||
format = RAW_YUV;
|
||||
} else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) {
|
||||
const char* const fmt = argv[++c];
|
||||
if (!strcmp(fmt, "RGB")) format = RGB;
|
||||
else if (!strcmp(fmt, "RGBA")) format = RGBA;
|
||||
else if (!strcmp(fmt, "BGR")) format = BGR;
|
||||
else if (!strcmp(fmt, "BGRA")) format = BGRA;
|
||||
else if (!strcmp(fmt, "ARGB")) format = ARGB;
|
||||
else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444;
|
||||
else if (!strcmp(fmt, "RGB_565")) format = RGB_565;
|
||||
else if (!strcmp(fmt, "rgbA")) format = rgbA;
|
||||
else if (!strcmp(fmt, "bgrA")) format = bgrA;
|
||||
else if (!strcmp(fmt, "Argb")) format = Argb;
|
||||
else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444;
|
||||
else if (!strcmp(fmt, "YUV")) format = YUV;
|
||||
else if (!strcmp(fmt, "YUVA")) format = YUVA;
|
||||
else {
|
||||
if (!strcmp(fmt, "RGB")) {
|
||||
format = RGB;
|
||||
} else if (!strcmp(fmt, "RGBA")) {
|
||||
format = RGBA;
|
||||
} else if (!strcmp(fmt, "BGR")) {
|
||||
format = BGR;
|
||||
} else if (!strcmp(fmt, "BGRA")) {
|
||||
format = BGRA;
|
||||
} else if (!strcmp(fmt, "ARGB")) {
|
||||
format = ARGB;
|
||||
} else if (!strcmp(fmt, "RGBA_4444")) {
|
||||
format = RGBA_4444;
|
||||
} else if (!strcmp(fmt, "RGB_565")) {
|
||||
format = RGB_565;
|
||||
} else if (!strcmp(fmt, "rgbA")) {
|
||||
format = rgbA;
|
||||
} else if (!strcmp(fmt, "bgrA")) {
|
||||
format = bgrA;
|
||||
} else if (!strcmp(fmt, "Argb")) {
|
||||
format = Argb;
|
||||
} else if (!strcmp(fmt, "rgbA_4444")) {
|
||||
format = rgbA_4444;
|
||||
} else if (!strcmp(fmt, "YUV")) {
|
||||
format = YUV;
|
||||
} else if (!strcmp(fmt, "YUVA")) {
|
||||
format = YUVA;
|
||||
} else {
|
||||
fprintf(stderr, "Can't parse pixel_format %s\n", fmt);
|
||||
parse_error = 1;
|
||||
}
|
||||
@@ -269,14 +282,14 @@ int main(int argc, const char* argv[]) {
|
||||
ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
||||
config.options.use_cropping = 1;
|
||||
config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.crop_height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if ((!strcmp(argv[c], "-scale") || !strcmp(argv[c], "-resize")) &&
|
||||
c < argc - 2) { // '-scale' is left for compatibility
|
||||
config.options.use_scaling = 1;
|
||||
config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.options.scaled_height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-flip")) {
|
||||
config.options.flip = 1;
|
||||
@@ -348,25 +361,52 @@ int main(int argc, const char* argv[]) {
|
||||
output_buffer->colorspace = MODE_YUVA;
|
||||
break;
|
||||
// forced modes:
|
||||
case RGB: output_buffer->colorspace = MODE_RGB; break;
|
||||
case RGBA: output_buffer->colorspace = MODE_RGBA; break;
|
||||
case BGR: output_buffer->colorspace = MODE_BGR; break;
|
||||
case BGRA: output_buffer->colorspace = MODE_BGRA; break;
|
||||
case ARGB: output_buffer->colorspace = MODE_ARGB; break;
|
||||
case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break;
|
||||
case RGB_565: output_buffer->colorspace = MODE_RGB_565; break;
|
||||
case rgbA: output_buffer->colorspace = MODE_rgbA; break;
|
||||
case bgrA: output_buffer->colorspace = MODE_bgrA; break;
|
||||
case Argb: output_buffer->colorspace = MODE_Argb; break;
|
||||
case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break;
|
||||
case YUV: output_buffer->colorspace = MODE_YUV; break;
|
||||
case YUVA: output_buffer->colorspace = MODE_YUVA; break;
|
||||
default: goto Exit;
|
||||
case RGB:
|
||||
output_buffer->colorspace = MODE_RGB;
|
||||
break;
|
||||
case RGBA:
|
||||
output_buffer->colorspace = MODE_RGBA;
|
||||
break;
|
||||
case BGR:
|
||||
output_buffer->colorspace = MODE_BGR;
|
||||
break;
|
||||
case BGRA:
|
||||
output_buffer->colorspace = MODE_BGRA;
|
||||
break;
|
||||
case ARGB:
|
||||
output_buffer->colorspace = MODE_ARGB;
|
||||
break;
|
||||
case RGBA_4444:
|
||||
output_buffer->colorspace = MODE_RGBA_4444;
|
||||
break;
|
||||
case RGB_565:
|
||||
output_buffer->colorspace = MODE_RGB_565;
|
||||
break;
|
||||
case rgbA:
|
||||
output_buffer->colorspace = MODE_rgbA;
|
||||
break;
|
||||
case bgrA:
|
||||
output_buffer->colorspace = MODE_bgrA;
|
||||
break;
|
||||
case Argb:
|
||||
output_buffer->colorspace = MODE_Argb;
|
||||
break;
|
||||
case rgbA_4444:
|
||||
output_buffer->colorspace = MODE_rgbA_4444;
|
||||
break;
|
||||
case YUV:
|
||||
output_buffer->colorspace = MODE_YUV;
|
||||
break;
|
||||
case YUVA:
|
||||
output_buffer->colorspace = MODE_YUVA;
|
||||
break;
|
||||
default:
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (use_external_memory > 0 && format >= RGB) {
|
||||
external_buffer = AllocateExternalBuffer(&config, format,
|
||||
use_external_memory);
|
||||
external_buffer =
|
||||
AllocateExternalBuffer(&config, format, use_external_memory);
|
||||
if (external_buffer == NULL) goto Exit;
|
||||
}
|
||||
|
||||
@@ -408,11 +448,12 @@ int main(int argc, const char* argv[]) {
|
||||
output_buffer->width, output_buffer->height,
|
||||
bitstream->has_alpha ? " (with alpha)" : "",
|
||||
kFormatType[bitstream->format]);
|
||||
fprintf(stderr, "Nothing written; "
|
||||
"use -o flag to save the result as e.g. PNG.\n");
|
||||
fprintf(stderr,
|
||||
"Nothing written; "
|
||||
"use -o flag to save the result as e.g. PNG.\n");
|
||||
}
|
||||
}
|
||||
Exit:
|
||||
Exit:
|
||||
WebPFreeDecBuffer(output_buffer);
|
||||
WebPFree((void*)external_buffer);
|
||||
WebPFree((void*)data);
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/mux_types.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// String parsing
|
||||
@@ -45,7 +46,7 @@ int ExUtilGetInts(const char* v, int base, int max_output, int output[]) {
|
||||
if (error) return -1;
|
||||
output[n] = value;
|
||||
v = strchr(v, ',');
|
||||
if (v != NULL) ++v; // skip over the trailing ','
|
||||
if (v != NULL) ++v; // skip over the trailing ','
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -66,17 +67,17 @@ float ExUtilGetFloat(const char* const v, int* const error) {
|
||||
static void ResetCommandLineArguments(int argc, const char* argv[],
|
||||
CommandLineArguments* const args) {
|
||||
assert(args != NULL);
|
||||
args->argc_ = argc;
|
||||
args->argv_ = argv;
|
||||
args->own_argv_ = 0;
|
||||
WebPDataInit(&args->argv_data_);
|
||||
args->argc = argc;
|
||||
args->argv = argv;
|
||||
args->own_argv = 0;
|
||||
WebPDataInit(&args->argv_data);
|
||||
}
|
||||
|
||||
void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args) {
|
||||
if (args != NULL) {
|
||||
if (args->own_argv_) {
|
||||
WebPFree((void*)args->argv_);
|
||||
WebPDataClear(&args->argv_data_);
|
||||
if (args->own_argv) {
|
||||
WebPFree((void*)args->argv);
|
||||
WebPDataClear(&args->argv_data);
|
||||
}
|
||||
ResetCommandLineArguments(0, NULL, args);
|
||||
}
|
||||
@@ -98,19 +99,18 @@ int ExUtilInitCommandLineArguments(int argc, const char* argv[],
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data_)) {
|
||||
if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data)) {
|
||||
return 0;
|
||||
}
|
||||
args->own_argv_ = 1;
|
||||
args->argv_ = (const char**)WebPMalloc(MAX_ARGC * sizeof(*args->argv_));
|
||||
if (args->argv_ == NULL) {
|
||||
args->own_argv = 1;
|
||||
args->argv = (const char**)WebPMalloc(MAX_ARGC * sizeof(*args->argv));
|
||||
if (args->argv == NULL) {
|
||||
ExUtilDeleteCommandLineArguments(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
argc = 0;
|
||||
for (cur = strtok((char*)args->argv_data_.bytes, sep);
|
||||
cur != NULL;
|
||||
for (cur = strtok((char*)args->argv_data.bytes, sep); cur != NULL;
|
||||
cur = strtok(NULL, sep)) {
|
||||
if (argc == MAX_ARGC) {
|
||||
fprintf(stderr, "ERROR: Arguments limit %d reached\n", MAX_ARGC);
|
||||
@@ -118,9 +118,9 @@ int ExUtilInitCommandLineArguments(int argc, const char* argv[],
|
||||
return 0;
|
||||
}
|
||||
assert(strlen(cur) != 0);
|
||||
args->argv_[argc++] = cur;
|
||||
args->argv[argc++] = cur;
|
||||
}
|
||||
args->argc_ = argc;
|
||||
args->argc = argc;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#ifndef WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
||||
#define WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
||||
|
||||
#include "webp/types.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -45,10 +45,10 @@ int ExUtilReadFileToWebPData(const char* const filename,
|
||||
// Command-line arguments
|
||||
|
||||
typedef struct {
|
||||
int argc_;
|
||||
const char** argv_;
|
||||
WebPData argv_data_;
|
||||
int own_argv_;
|
||||
int argc;
|
||||
const char** argv;
|
||||
WebPData argv_data;
|
||||
int own_argv;
|
||||
} CommandLineArguments;
|
||||
|
||||
// Initializes the structure from the command-line parameters. If there is
|
||||
@@ -64,7 +64,7 @@ int ExUtilInitCommandLineArguments(int argc, const char* argv[],
|
||||
void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
||||
|
||||
@@ -28,13 +28,15 @@
|
||||
#endif
|
||||
|
||||
#include <gif_lib.h>
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux.h"
|
||||
|
||||
#include "../examples/example_util.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./gifdec.h"
|
||||
#include "./unicode.h"
|
||||
#include "./unicode_gif.h"
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux.h"
|
||||
|
||||
#if !defined(STDIN_FILENO)
|
||||
#define STDIN_FILENO 0
|
||||
@@ -45,9 +47,8 @@
|
||||
static int transparent_index = GIF_INDEX_INVALID; // Opaque by default.
|
||||
|
||||
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
|
||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
|
||||
};
|
||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"};
|
||||
|
||||
static const char* ErrorString(WebPMuxError err) {
|
||||
assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
|
||||
@@ -55,9 +56,9 @@ static const char* ErrorString(WebPMuxError err) {
|
||||
}
|
||||
|
||||
enum {
|
||||
METADATA_ICC = (1 << 0),
|
||||
METADATA_XMP = (1 << 1),
|
||||
METADATA_ALL = METADATA_ICC | METADATA_XMP
|
||||
METADATA_ICC = (1 << 0),
|
||||
METADATA_XMP = (1 << 1),
|
||||
METADATA_ALL = METADATA_ICC | METADATA_XMP
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -68,14 +69,25 @@ static void Help(void) {
|
||||
printf("Options:\n");
|
||||
printf(" -h / -help ............. this help\n");
|
||||
printf(" -lossy ................. encode image using lossy compression\n");
|
||||
printf(" -mixed ................. for each frame in the image, pick lossy\n"
|
||||
" or lossless compression heuristically\n");
|
||||
printf(
|
||||
" -mixed ................. for each frame in the image, pick lossy\n"
|
||||
" or lossless compression heuristically\n");
|
||||
printf(
|
||||
" -near_lossless <int> ... use near-lossless image preprocessing\n"
|
||||
" (0..100=off), default=100\n");
|
||||
printf(
|
||||
" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
|
||||
"conversion\n"
|
||||
" (lossy only)\n");
|
||||
printf(" -q <float> ............. quality factor (0:small..100:big)\n");
|
||||
printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
|
||||
printf(" -min_size .............. minimize output size (default:off)\n"
|
||||
" lossless compression by default; can be\n"
|
||||
" combined with -q, -m, -lossy or -mixed\n"
|
||||
" options\n");
|
||||
printf(
|
||||
" -m <int> ............... compression method (0=fast, 6=slowest), "
|
||||
"default=4\n");
|
||||
printf(
|
||||
" -min_size .............. minimize output size (default:off)\n"
|
||||
" lossless compression by default; can be\n"
|
||||
" combined with -q, -m, -lossy or -mixed\n"
|
||||
" options\n");
|
||||
printf(" -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");
|
||||
@@ -102,21 +114,21 @@ int main(int argc, const char* argv[]) {
|
||||
int gif_error = GIF_ERROR;
|
||||
WebPMuxError err = WEBP_MUX_OK;
|
||||
int ok = 0;
|
||||
const W_CHAR* in_file = NULL, *out_file = NULL;
|
||||
const W_CHAR *in_file = NULL, *out_file = NULL;
|
||||
GifFileType* gif = NULL;
|
||||
int frame_duration = 0;
|
||||
int frame_timestamp = 0;
|
||||
GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE;
|
||||
|
||||
WebPPicture frame; // Frame rectangle only (not disposed).
|
||||
WebPPicture curr_canvas; // Not disposed.
|
||||
WebPPicture prev_canvas; // Disposed.
|
||||
WebPPicture frame; // Frame rectangle only (not disposed).
|
||||
WebPPicture curr_canvas; // Not disposed.
|
||||
WebPPicture prev_canvas; // Disposed.
|
||||
|
||||
WebPAnimEncoder* enc = NULL;
|
||||
WebPAnimEncoderOptions enc_options;
|
||||
WebPConfig config;
|
||||
|
||||
int frame_number = 0; // Whether we are processing the first frame.
|
||||
int frame_number = 0; // Whether we are processing the first frame.
|
||||
int done;
|
||||
int c;
|
||||
int quiet = 0;
|
||||
@@ -124,7 +136,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
int keep_metadata = METADATA_XMP; // ICC not output by default.
|
||||
WebPData icc_data;
|
||||
int stored_icc = 0; // Whether we have already stored an ICC profile.
|
||||
int stored_icc = 0; // Whether we have already stored an ICC profile.
|
||||
WebPData xmp_data;
|
||||
int stored_xmp = 0; // Whether we have already stored an XMP profile.
|
||||
int loop_count = 0; // default: infinite
|
||||
@@ -151,7 +163,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (argc == 1) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
@@ -166,6 +178,10 @@ int main(int argc, const char* argv[]) {
|
||||
} else if (!strcmp(argv[c], "-mixed")) {
|
||||
enc_options.allow_mixed = 1;
|
||||
config.lossless = 0;
|
||||
} else if (!strcmp(argv[c], "-near_lossless") && c < argc - 1) {
|
||||
config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-sharp_yuv")) {
|
||||
config.use_sharp_yuv = 1;
|
||||
} else if (!strcmp(argv[c], "-loop_compatibility")) {
|
||||
loop_compatibility = 1;
|
||||
} else if (!strcmp(argv[c], "-q") && c < argc - 1) {
|
||||
@@ -187,10 +203,10 @@ int main(int argc, const char* argv[]) {
|
||||
const char* option;
|
||||
int flag;
|
||||
} kTokens[] = {
|
||||
{ "all", METADATA_ALL },
|
||||
{ "none", 0 },
|
||||
{ "icc", METADATA_ICC },
|
||||
{ "xmp", METADATA_XMP },
|
||||
{"all", METADATA_ALL},
|
||||
{"none", 0},
|
||||
{"icc", METADATA_ICC},
|
||||
{"xmp", METADATA_XMP},
|
||||
};
|
||||
const size_t kNumTokens = sizeof(kTokens) / sizeof(*kTokens);
|
||||
const char* start = argv[++c];
|
||||
@@ -226,10 +242,13 @@ int main(int argc, const char* argv[]) {
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
const int enc_version = WebPGetEncoderVersion();
|
||||
const int mux_version = WebPGetMuxVersion();
|
||||
const int sharpyuv_version = SharpYuvGetVersion();
|
||||
printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
|
||||
(enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
|
||||
enc_version & 0xff, (mux_version >> 16) & 0xff,
|
||||
(mux_version >> 8) & 0xff, mux_version & 0xff);
|
||||
printf("libsharpyuv: %d.%d.%d\n", (sharpyuv_version >> 24) & 0xff,
|
||||
(sharpyuv_version >> 16) & 0xffff, sharpyuv_version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
} else if (!strcmp(argv[c], "-quiet")) {
|
||||
quiet = 1;
|
||||
@@ -305,8 +324,8 @@ int main(int argc, const char* argv[]) {
|
||||
goto End;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("Fixed canvas screen dimension to: %d x %d\n",
|
||||
gif->SWidth, gif->SHeight);
|
||||
printf("Fixed canvas screen dimension to: %d x %d\n", gif->SWidth,
|
||||
gif->SHeight);
|
||||
}
|
||||
}
|
||||
// Allocate current buffer.
|
||||
@@ -404,7 +423,7 @@ int main(int argc, const char* argv[]) {
|
||||
break;
|
||||
}
|
||||
case APPLICATION_EXT_FUNC_CODE: {
|
||||
if (data[0] != 11) break; // Chunk is too short
|
||||
if (data[0] != 11) break; // Chunk is too short
|
||||
if (!memcmp(data + 1, "NETSCAPE2.0", 11) ||
|
||||
!memcmp(data + 1, "ANIMEXTS1.0", 11)) {
|
||||
if (!GIFReadLoopCount(gif, &data, &loop_count)) {
|
||||
@@ -515,7 +534,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (stored_icc) { // Add ICCP chunk.
|
||||
if (stored_icc) { // Add ICCP chunk.
|
||||
err = WebPMuxSetChunk(mux, "ICCP", &icc_data, 1);
|
||||
if (verbose) {
|
||||
fprintf(stderr, "ICC size: %d\n", (int)icc_data.size);
|
||||
@@ -527,7 +546,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (stored_xmp) { // Add XMP chunk.
|
||||
if (stored_xmp) { // Add XMP chunk.
|
||||
err = WebPMuxSetChunk(mux, "XMP ", &xmp_data, 1);
|
||||
if (verbose) {
|
||||
fprintf(stderr, "XMP size: %d\n", (int)xmp_data.size);
|
||||
@@ -541,8 +560,10 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
err = WebPMuxAssemble(mux, &webp_data);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
fprintf(stderr, "ERROR (%s): Could not assemble when re-muxing to add "
|
||||
"loop count/metadata.\n", ErrorString(err));
|
||||
fprintf(stderr,
|
||||
"ERROR (%s): Could not assemble when re-muxing to add "
|
||||
"loop count/metadata.\n",
|
||||
ErrorString(err));
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
@@ -555,8 +576,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
if (!quiet) {
|
||||
if (!WSTRCMP(out_file, "-")) {
|
||||
fprintf(stderr, "Saved %d bytes to STDIO\n",
|
||||
(int)webp_data.size);
|
||||
fprintf(stderr, "Saved %d bytes to STDIO\n", (int)webp_data.size);
|
||||
} else {
|
||||
WFPRINTF(stderr, "Saved output file (%d bytes): %s\n",
|
||||
(int)webp_data.size, out_file);
|
||||
@@ -564,8 +584,10 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
} else {
|
||||
if (!quiet) {
|
||||
fprintf(stderr, "Nothing written; use -o flag to save the result "
|
||||
"(%d bytes).\n", (int)webp_data.size);
|
||||
fprintf(stderr,
|
||||
"Nothing written; use -o flag to save the result "
|
||||
"(%d bytes).\n",
|
||||
(int)webp_data.size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,7 +595,7 @@ int main(int argc, const char* argv[]) {
|
||||
ok = 1;
|
||||
gif_error = GIF_OK;
|
||||
|
||||
End:
|
||||
End:
|
||||
WebPDataClear(&icc_data);
|
||||
WebPDataClear(&xmp_data);
|
||||
WebPMuxDelete(mux);
|
||||
@@ -587,7 +609,7 @@ int main(int argc, const char* argv[]) {
|
||||
GIFDisplayError(gif, gif_error);
|
||||
}
|
||||
if (gif != NULL) {
|
||||
#if LOCAL_GIF_PREREQ(5,1)
|
||||
#if LOCAL_GIF_PREREQ(5, 1)
|
||||
DGifCloseFile(gif, &gif_error);
|
||||
#else
|
||||
DGifCloseFile(gif);
|
||||
|
||||
@@ -20,20 +20,20 @@
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#define GIF_TRANSPARENT_COLOR 0x00000000u
|
||||
#define GIF_WHITE_COLOR 0xffffffffu
|
||||
#define GIF_TRANSPARENT_MASK 0x01
|
||||
#define GIF_DISPOSE_MASK 0x07
|
||||
#define GIF_DISPOSE_SHIFT 2
|
||||
#define GIF_WHITE_COLOR 0xffffffffu
|
||||
#define GIF_TRANSPARENT_MASK 0x01
|
||||
#define GIF_DISPOSE_MASK 0x07
|
||||
#define GIF_DISPOSE_SHIFT 2
|
||||
|
||||
// from utils/utils.h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern void WebPCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride,
|
||||
int width, int height);
|
||||
extern void WebPCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
|
||||
int dst_stride, int width, int height);
|
||||
extern void WebPCopyPixels(const WebPPicture* const src,
|
||||
WebPPicture* const dst);
|
||||
#ifdef __cplusplus
|
||||
@@ -46,18 +46,16 @@ void GIFGetBackgroundColor(const ColorMapObject* const color_map,
|
||||
if (transparent_index != GIF_INDEX_INVALID &&
|
||||
bgcolor_index == transparent_index) {
|
||||
*bgcolor = GIF_TRANSPARENT_COLOR; // Special case.
|
||||
} else if (color_map == NULL || color_map->Colors == NULL
|
||||
|| bgcolor_index >= color_map->ColorCount) {
|
||||
} else if (color_map == NULL || color_map->Colors == NULL ||
|
||||
bgcolor_index >= color_map->ColorCount) {
|
||||
*bgcolor = GIF_WHITE_COLOR;
|
||||
fprintf(stderr,
|
||||
"GIF decode warning: invalid background color index. Assuming "
|
||||
"white background.\n");
|
||||
} else {
|
||||
const GifColorType color = color_map->Colors[bgcolor_index];
|
||||
*bgcolor = (0xffu << 24)
|
||||
| (color.Red << 16)
|
||||
| (color.Green << 8)
|
||||
| (color.Blue << 0);
|
||||
*bgcolor = (0xffu << 24) | (color.Red << 16) | (color.Green << 8) |
|
||||
(color.Blue << 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +114,8 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
const GifImageDesc* const image_desc = &gif->Image;
|
||||
uint32_t* dst = NULL;
|
||||
uint8_t* tmp = NULL;
|
||||
const GIFFrameRect rect = {
|
||||
image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
|
||||
};
|
||||
const GIFFrameRect rect = {image_desc->Left, image_desc->Top,
|
||||
image_desc->Width, image_desc->Height};
|
||||
const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height;
|
||||
int ok = 0;
|
||||
*gif_rect = rect;
|
||||
@@ -129,8 +126,8 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
}
|
||||
|
||||
// Use a view for the sub-picture:
|
||||
if (!WebPPictureView(picture, rect.x_offset, rect.y_offset,
|
||||
rect.width, rect.height, &sub_image)) {
|
||||
if (!WebPPictureView(picture, rect.x_offset, rect.y_offset, rect.width,
|
||||
rect.height, &sub_image)) {
|
||||
fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
|
||||
rect.width, rect.height, rect.x_offset, rect.y_offset);
|
||||
return 0;
|
||||
@@ -142,8 +139,8 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
|
||||
if (image_desc->Interlace) { // Interlaced image.
|
||||
// We need 4 passes, with the following offsets and jumps.
|
||||
const int interlace_offsets[] = { 0, 4, 2, 1 };
|
||||
const int interlace_jumps[] = { 8, 8, 4, 2 };
|
||||
const int interlace_offsets[] = {0, 4, 2, 1};
|
||||
const int interlace_jumps[] = {8, 8, 4, 2};
|
||||
int pass;
|
||||
for (pass = 0; pass < 4; ++pass) {
|
||||
const size_t stride = (size_t)sub_image.argb_stride;
|
||||
@@ -165,7 +162,7 @@ int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
End:
|
||||
End:
|
||||
if (!ok) picture->error_code = sub_image.error_code;
|
||||
WebPPictureFree(&sub_image);
|
||||
WebPFree(tmp);
|
||||
@@ -183,7 +180,7 @@ int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf,
|
||||
return 0; // Loop count sub-block missing.
|
||||
}
|
||||
if ((*buf)[0] < 3 || (*buf)[1] != 1) {
|
||||
return 0; // wrong size/marker
|
||||
return 0; // wrong size/marker
|
||||
}
|
||||
*loop_count = (*buf)[2] | ((*buf)[3] << 8);
|
||||
return 1;
|
||||
@@ -219,8 +216,7 @@ int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
|
||||
if (tmp == NULL) {
|
||||
return 0;
|
||||
}
|
||||
memcpy((void*)(tmp + metadata->size),
|
||||
subblock.bytes, subblock.size);
|
||||
memcpy((void*)(tmp + metadata->size), subblock.bytes, subblock.size);
|
||||
metadata->bytes = tmp;
|
||||
metadata->size += subblock.size;
|
||||
}
|
||||
@@ -234,8 +230,8 @@ int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ClearRectangle(WebPPicture* const picture,
|
||||
int left, int top, int width, int height) {
|
||||
static void ClearRectangle(WebPPicture* const picture, int left, int top,
|
||||
int width, int height) {
|
||||
int i, j;
|
||||
const size_t stride = picture->argb_stride;
|
||||
uint32_t* dst = picture->argb + top * stride + left;
|
||||
@@ -246,8 +242,8 @@ static void ClearRectangle(WebPPicture* const picture,
|
||||
|
||||
void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) {
|
||||
if (rect != NULL) {
|
||||
ClearRectangle(pic, rect->x_offset, rect->y_offset,
|
||||
rect->width, rect->height);
|
||||
ClearRectangle(pic, rect->x_offset, rect->y_offset, rect->width,
|
||||
rect->height);
|
||||
} else {
|
||||
ClearRectangle(pic, 0, 0, pic->width, pic->height);
|
||||
}
|
||||
@@ -265,15 +261,14 @@ void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
|
||||
GIFClearPic(curr_canvas, rect);
|
||||
} else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
|
||||
const size_t src_stride = prev_canvas->argb_stride;
|
||||
const uint32_t* const src = prev_canvas->argb + rect->x_offset
|
||||
+ rect->y_offset * src_stride;
|
||||
const uint32_t* const src =
|
||||
prev_canvas->argb + rect->x_offset + rect->y_offset * src_stride;
|
||||
const size_t dst_stride = curr_canvas->argb_stride;
|
||||
uint32_t* const dst = curr_canvas->argb + rect->x_offset
|
||||
+ rect->y_offset * dst_stride;
|
||||
uint32_t* const dst =
|
||||
curr_canvas->argb + rect->x_offset + rect->y_offset * dst_stride;
|
||||
assert(prev_canvas != NULL);
|
||||
WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride),
|
||||
(uint8_t*)dst, (int)(4 * dst_stride),
|
||||
4 * rect->width, rect->height);
|
||||
WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride), (uint8_t*)dst,
|
||||
(int)(4 * dst_stride), 4 * rect->width, rect->height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,11 +291,11 @@ void GIFBlendFrames(const WebPPicture* const src,
|
||||
|
||||
void GIFDisplayError(const GifFileType* const gif, int gif_error) {
|
||||
// libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
|
||||
#if LOCAL_GIF_PREREQ(4,2)
|
||||
#if LOCAL_GIF_PREREQ(5,0)
|
||||
#if LOCAL_GIF_PREREQ(4, 2)
|
||||
#if LOCAL_GIF_PREREQ(5, 0)
|
||||
// Static string actually, hence the const char* cast.
|
||||
const char* error_str = (const char*)GifErrorString(
|
||||
(gif == NULL) ? gif_error : gif->Error);
|
||||
const char* error_str =
|
||||
(const char*)GifErrorString((gif == NULL) ? gif_error : gif->Error);
|
||||
#else
|
||||
const char* error_str = (const char*)GifErrorString();
|
||||
(void)gif;
|
||||
@@ -318,7 +313,8 @@ void GIFDisplayError(const GifFileType* const gif, int gif_error) {
|
||||
#else // !WEBP_HAVE_GIF
|
||||
|
||||
static void ErrorGIFNotAvailable(void) {
|
||||
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
|
||||
fprintf(stderr,
|
||||
"GIF support not compiled. Please install the libgif-dev "
|
||||
"package before building.\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define WEBP_EXAMPLES_GIFDEC_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@@ -29,12 +30,11 @@ extern "C" {
|
||||
|
||||
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
|
||||
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
|
||||
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||
# define LOCAL_GIF_PREREQ(maj, min) \
|
||||
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||
#define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||
#define LOCAL_GIF_PREREQ(maj, min) (LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||
#else
|
||||
# define LOCAL_GIF_VERSION 0
|
||||
# define LOCAL_GIF_PREREQ(maj, min) 0
|
||||
#define LOCAL_GIF_VERSION 0
|
||||
#define LOCAL_GIF_PREREQ(maj, min) 0
|
||||
#endif
|
||||
|
||||
#define GIF_INDEX_INVALID (-1)
|
||||
@@ -110,7 +110,7 @@ void GIFCopyPixels(const struct WebPPicture* const src,
|
||||
struct WebPPicture* const dst);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_EXAMPLES_GIFDEC_H_
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -41,16 +43,20 @@ static void Help(void) {
|
||||
|
||||
printf("File-level options (only used at the start of compression):\n");
|
||||
printf(" -min_size ............ minimize size\n");
|
||||
printf(" -kmax <int> .......... maximum number of frame between key-frames\n"
|
||||
" (0=only keyframes)\n");
|
||||
printf(" -kmin <int> .......... minimum number of frame between key-frames\n"
|
||||
" (0=disable key-frames altogether)\n");
|
||||
printf(
|
||||
" -kmax <int> .......... maximum number of frame between key-frames\n"
|
||||
" (0=only keyframes)\n");
|
||||
printf(
|
||||
" -kmin <int> .......... minimum number of frame between key-frames\n"
|
||||
" (0=disable key-frames altogether)\n");
|
||||
printf(" -mixed ............... use mixed lossy/lossless automatic mode\n");
|
||||
printf(" -near_lossless <int> . use near-lossless image preprocessing\n"
|
||||
" (0..100=off), default=100\n");
|
||||
printf(" -sharp_yuv ........... use sharper (and slower) RGB->YUV "
|
||||
"conversion\n "
|
||||
"(lossy only)\n");
|
||||
printf(
|
||||
" -near_lossless <int> . use near-lossless image preprocessing\n"
|
||||
" (0..100=off), default=100\n");
|
||||
printf(
|
||||
" -sharp_yuv ........... use sharper (and slower) RGB->YUV "
|
||||
"conversion\n "
|
||||
"(lossy only)\n");
|
||||
printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
|
||||
printf(" -v ................... verbose mode\n");
|
||||
printf(" -h ................... this help\n");
|
||||
@@ -59,18 +65,28 @@ static void Help(void) {
|
||||
|
||||
printf("Per-frame options (only used for subsequent images input):\n");
|
||||
printf(" -d <int> ............. frame duration in ms (default: 100)\n");
|
||||
printf(" -lossless ........... use lossless mode (default)\n");
|
||||
printf(" -lossy ... ........... use lossy mode\n");
|
||||
printf(" -lossless ............ use lossless mode (default)\n");
|
||||
printf(" -lossy ............... use lossy mode\n");
|
||||
printf(" -q <float> ........... quality\n");
|
||||
printf(" -m <int> ............. method to use\n");
|
||||
printf(
|
||||
" -m <int> ............. compression method (0=fast, 6=slowest), "
|
||||
"default=4\n");
|
||||
printf(
|
||||
" -exact, -noexact ..... preserve or alter RGB values in transparent "
|
||||
"area\n"
|
||||
" (default: -noexact, may cause artifacts\n"
|
||||
" with lossy animations)\n");
|
||||
|
||||
printf("\n");
|
||||
printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
|
||||
" -d 80 in2.tiff -o out.webp\n");
|
||||
printf("\nNote: if a single file name is passed as the argument, the "
|
||||
"arguments will be\n");
|
||||
printf("tokenized from this file. The file name must not start with "
|
||||
"the character '-'.\n");
|
||||
printf(
|
||||
"example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
|
||||
" -d 80 in2.tiff -o out.webp\n");
|
||||
printf(
|
||||
"\nNote: if a single file name is passed as the argument, the "
|
||||
"arguments will be\n");
|
||||
printf(
|
||||
"tokenized from this file. The file name must not start with "
|
||||
"the character '-'.\n");
|
||||
printf("\nSupported input formats:\n %s\n",
|
||||
WebPGetEnabledInputFileFormats());
|
||||
}
|
||||
@@ -120,7 +136,7 @@ static int SetLoopCount(int loop_count, WebPData* const webp_data) {
|
||||
ok = (err == WEBP_MUX_OK);
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
WebPMuxDelete(mux);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Error during loop-count setting\n");
|
||||
@@ -146,6 +162,7 @@ int main(int argc, const char* argv[]) {
|
||||
WebPData webp_data;
|
||||
int c;
|
||||
int have_input = 0;
|
||||
int last_input_index = 0;
|
||||
CommandLineArguments cmd_args;
|
||||
int ok;
|
||||
|
||||
@@ -154,12 +171,11 @@ int main(int argc, const char* argv[]) {
|
||||
ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args);
|
||||
if (!ok) FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
|
||||
argc = cmd_args.argc_;
|
||||
argv = cmd_args.argv_;
|
||||
argc = cmd_args.argc;
|
||||
argv = cmd_args.argv;
|
||||
|
||||
WebPDataInit(&webp_data);
|
||||
if (!WebPAnimEncoderOptionsInit(&anim_config) ||
|
||||
!WebPConfigInit(&config) ||
|
||||
if (!WebPAnimEncoderOptionsInit(&anim_config) || !WebPConfigInit(&config) ||
|
||||
!WebPPictureInit(&pic)) {
|
||||
fprintf(stderr, "Library version mismatch!\n");
|
||||
ok = 0;
|
||||
@@ -217,13 +233,15 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
ok = !parse_error;
|
||||
if (!ok) goto End;
|
||||
argv[c] = NULL; // mark option as 'parsed' during 1st pass
|
||||
argv[c] = NULL; // mark option as 'parsed' during 1st pass
|
||||
} else {
|
||||
have_input |= 1;
|
||||
}
|
||||
}
|
||||
if (!have_input) {
|
||||
fprintf(stderr, "No input file(s) for generating animation!\n");
|
||||
ok = 0;
|
||||
Help();
|
||||
goto End;
|
||||
}
|
||||
|
||||
@@ -232,7 +250,7 @@ int main(int argc, const char* argv[]) {
|
||||
config.lossless = 1;
|
||||
for (c = 0; ok && c < argc; ++c) {
|
||||
if (argv[c] == NULL) continue;
|
||||
if (argv[c][0] == '-') { // parse local options
|
||||
if (argv[c][0] == '-') { // parse local options
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-lossy")) {
|
||||
if (!anim_config.allow_mixed) config.lossless = 0;
|
||||
@@ -248,8 +266,12 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "Invalid negative duration (%d)\n", duration);
|
||||
parse_error = 1;
|
||||
}
|
||||
} else if (!strcmp(argv[c], "-exact")) {
|
||||
config.exact = 1;
|
||||
} else if (!strcmp(argv[c], "-noexact")) {
|
||||
config.exact = 0;
|
||||
} else {
|
||||
parse_error = 1; // shouldn't be here.
|
||||
parse_error = 1; // shouldn't be here.
|
||||
fprintf(stderr, "Unknown option [%s]\n", argv[c]);
|
||||
}
|
||||
ok = !parse_error;
|
||||
@@ -268,10 +290,11 @@ int main(int argc, const char* argv[]) {
|
||||
// read next input image
|
||||
pic.use_argb = 1;
|
||||
ok = ReadImage((const char*)GET_WARGV_SHIFTED(argv, c), &pic);
|
||||
last_input_index = c;
|
||||
if (!ok) goto End;
|
||||
|
||||
if (enc == NULL) {
|
||||
width = pic.width;
|
||||
width = pic.width;
|
||||
height = pic.height;
|
||||
enc = WebPAnimEncoderNew(width, height, &anim_config);
|
||||
ok = (enc != NULL);
|
||||
@@ -283,8 +306,9 @@ int main(int argc, const char* argv[]) {
|
||||
if (ok) {
|
||||
ok = (width == pic.width && height == pic.height);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Frame #%d dimension mismatched! "
|
||||
"Got %d x %d. Was expecting %d x %d.\n",
|
||||
fprintf(stderr,
|
||||
"Frame #%d dimension mismatched! "
|
||||
"Got %d x %d. Was expecting %d x %d.\n",
|
||||
pic_num, pic.width, pic.height, width, height);
|
||||
}
|
||||
}
|
||||
@@ -299,13 +323,22 @@ int main(int argc, const char* argv[]) {
|
||||
if (!ok) goto End;
|
||||
|
||||
if (verbose) {
|
||||
WFPRINTF(stderr, "Added frame #%3d at time %4d (file: %s)\n",
|
||||
pic_num, timestamp_ms, GET_WARGV_SHIFTED(argv, c));
|
||||
WFPRINTF(stderr, "Added frame #%3d at time %4d (file: %s)\n", pic_num,
|
||||
timestamp_ms, GET_WARGV_SHIFTED(argv, c));
|
||||
}
|
||||
timestamp_ms += duration;
|
||||
++pic_num;
|
||||
}
|
||||
|
||||
for (c = last_input_index + 1; c < argc; ++c) {
|
||||
if (argv[c] != NULL) {
|
||||
fprintf(stderr,
|
||||
"Warning: unused option [%s]!"
|
||||
" Frame options go before the input frame.\n",
|
||||
argv[c]);
|
||||
}
|
||||
}
|
||||
|
||||
// add a last fake frame to signal the last duration
|
||||
ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
|
||||
ok = ok && WebPAnimEncoderAssemble(enc, &webp_data);
|
||||
@@ -313,7 +346,7 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "Error during final animation assembly.\n");
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
// free resources
|
||||
WebPAnimEncoderDelete(enc);
|
||||
|
||||
@@ -331,8 +364,8 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
fprintf(stderr, "[%d frames, %u bytes].\n",
|
||||
pic_num, (unsigned int)webp_data.size);
|
||||
fprintf(stderr, "[%d frames, %u bytes].\n", pic_num,
|
||||
(unsigned int)webp_data.size);
|
||||
}
|
||||
WebPDataClear(&webp_data);
|
||||
ExUtilDeleteCommandLineArguments(&cmd_args);
|
||||
|
||||
@@ -28,17 +28,13 @@ static WEBP_INLINE void StopwatchReset(Stopwatch* watch) {
|
||||
static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
|
||||
const LARGE_INTEGER old_value = *watch;
|
||||
LARGE_INTEGER freq;
|
||||
if (!QueryPerformanceCounter(watch))
|
||||
return 0.0;
|
||||
if (!QueryPerformanceFrequency(&freq))
|
||||
return 0.0;
|
||||
if (freq.QuadPart == 0)
|
||||
return 0.0;
|
||||
if (!QueryPerformanceCounter(watch)) return 0.0;
|
||||
if (!QueryPerformanceFrequency(&freq)) return 0.0;
|
||||
if (freq.QuadPart == 0) return 0.0;
|
||||
return (watch->QuadPart - old_value.QuadPart) / (double)freq.QuadPart;
|
||||
}
|
||||
|
||||
|
||||
#else /* !_WIN32 */
|
||||
#else /* !_WIN32 */
|
||||
#include <string.h> // memcpy
|
||||
#include <sys/time.h>
|
||||
|
||||
@@ -58,6 +54,6 @@ static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
|
||||
return delta_sec + delta_usec / 1000000.0;
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif // WEBP_EXAMPLES_STOPWATCH_H_
|
||||
|
||||
@@ -27,18 +27,20 @@
|
||||
#include <io.h>
|
||||
#include <wchar.h>
|
||||
#include <windows.h>
|
||||
|
||||
// shellapi.h must be included after windows.h.
|
||||
#include <shellapi.h>
|
||||
|
||||
// Create a wchar_t array containing Unicode parameters.
|
||||
#define INIT_WARGV(ARGC, ARGV) \
|
||||
int wargc; \
|
||||
const W_CHAR** const wargv = \
|
||||
(const W_CHAR**)CommandLineToArgvW(GetCommandLineW(), &wargc); \
|
||||
do { \
|
||||
if (wargv == NULL || wargc != (ARGC)) { \
|
||||
fprintf(stderr, "Error: Unable to get Unicode arguments.\n"); \
|
||||
FREE_WARGV_AND_RETURN(-1); \
|
||||
} \
|
||||
#define INIT_WARGV(ARGC, ARGV) \
|
||||
int wargc; \
|
||||
const W_CHAR** const wargv = \
|
||||
(const W_CHAR**)CommandLineToArgvW(GetCommandLineW(), &wargc); \
|
||||
do { \
|
||||
if (wargv == NULL || wargc != (ARGC)) { \
|
||||
fprintf(stderr, "Error: Unable to get Unicode arguments.\n"); \
|
||||
FREE_WARGV_AND_RETURN(-1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Use this to get a Unicode argument (e.g. file path).
|
||||
@@ -48,7 +50,7 @@
|
||||
#define GET_WARGV_OR_NULL() wargv
|
||||
|
||||
// Release resources. LocalFree() is needed after CommandLineToArgvW().
|
||||
#define FREE_WARGV() LOCAL_FREE((W_CHAR** const)wargv)
|
||||
#define FREE_WARGV() LOCAL_FREE((W_CHAR**)wargv)
|
||||
#define LOCAL_FREE(WARGV) \
|
||||
do { \
|
||||
if ((WARGV) != NULL) LocalFree(WARGV); \
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <gif_lib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./gifdec.h"
|
||||
|
||||
#if !defined(STDIN_FILENO)
|
||||
|
||||
@@ -38,12 +38,11 @@
|
||||
#include <qcms.h>
|
||||
#endif
|
||||
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
|
||||
#include "../examples/example_util.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@@ -135,9 +134,8 @@ static int ApplyColorProfile(const WebPData* const profile,
|
||||
}
|
||||
|
||||
qcms_profile_precache_output_transform(output_profile);
|
||||
transform = qcms_transform_create(input_profile, input_type,
|
||||
output_profile, output_type,
|
||||
intent);
|
||||
transform = qcms_transform_create(input_profile, input_type, output_profile,
|
||||
output_type, intent);
|
||||
if (transform == NULL) {
|
||||
fprintf(stderr, "Error creating color transform!\n");
|
||||
goto Error;
|
||||
@@ -149,7 +147,7 @@ static int ApplyColorProfile(const WebPData* const profile,
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
if (input_profile != NULL) qcms_profile_release(input_profile);
|
||||
if (output_profile != NULL) qcms_profile_release(output_profile);
|
||||
if (transform != NULL) qcms_transform_release(transform);
|
||||
@@ -164,7 +162,7 @@ static int ApplyColorProfile(const WebPData* const profile,
|
||||
//------------------------------------------------------------------------------
|
||||
// File decoding
|
||||
|
||||
static int Decode(void) { // Fills kParams.curr_frame
|
||||
static int Decode(void) { // Fills kParams.curr_frame
|
||||
const WebPIterator* const curr = &kParams.curr_frame;
|
||||
WebPDecoderConfig* const config = &kParams.config;
|
||||
WebPDecBuffer* const output_buffer = &config->output;
|
||||
@@ -172,8 +170,8 @@ static int Decode(void) { // Fills kParams.curr_frame
|
||||
|
||||
ClearPreviousPic();
|
||||
output_buffer->colorspace = MODE_RGBA;
|
||||
ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size,
|
||||
config) == VP8_STATUS_OK);
|
||||
ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size, config) ==
|
||||
VP8_STATUS_OK);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Decoding of frame #%d failed!\n", curr->frame_num);
|
||||
} else {
|
||||
@@ -341,8 +339,7 @@ static void DrawBackground(void) {
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glColor4f(GetColorf(kParams.bg_color, 16), // BGRA from spec
|
||||
GetColorf(kParams.bg_color, 8),
|
||||
GetColorf(kParams.bg_color, 0),
|
||||
GetColorf(kParams.bg_color, 8), GetColorf(kParams.bg_color, 0),
|
||||
GetColorf(kParams.bg_color, 24));
|
||||
glRecti(-1, -1, +1, +1);
|
||||
glPopMatrix();
|
||||
@@ -402,8 +399,7 @@ static void HandleDisplay(void) {
|
||||
|
||||
*prev = *curr;
|
||||
|
||||
glDrawPixels(pic->width, pic->height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
glDrawPixels(pic->width, pic->height, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
(GLvoid*)pic->u.RGBA.rgba);
|
||||
if (kParams.print_info) {
|
||||
char tmp[32];
|
||||
@@ -417,8 +413,8 @@ static void HandleDisplay(void) {
|
||||
glRasterPos2f(-0.95f, 0.80f);
|
||||
PrintString(tmp);
|
||||
if (curr->x_offset != 0 || curr->y_offset != 0) {
|
||||
snprintf(tmp, sizeof(tmp), " (offset:%d,%d)",
|
||||
curr->x_offset, curr->y_offset);
|
||||
snprintf(tmp, sizeof(tmp), " (offset:%d,%d)", curr->x_offset,
|
||||
curr->y_offset);
|
||||
glRasterPos2f(-0.95f, 0.70f);
|
||||
PrintString(tmp);
|
||||
}
|
||||
@@ -568,11 +564,11 @@ int main(int argc, char* argv[]) {
|
||||
if (kParams.file_name == NULL) {
|
||||
printf("missing input file!!\n");
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!ImgIoUtilReadFile(kParams.file_name,
|
||||
&kParams.data.bytes, &kParams.data.size)) {
|
||||
if (!ImgIoUtilReadFile(kParams.file_name, &kParams.data.bytes,
|
||||
&kParams.data.size)) {
|
||||
goto Error;
|
||||
}
|
||||
|
||||
@@ -603,7 +599,8 @@ int main(int argc, char* argv[]) {
|
||||
if (!WebPDemuxGetChunk(kParams.dmux, "ICCP", 1, &kParams.iccp)) goto Error;
|
||||
printf("VP8X: Found color profile\n");
|
||||
#else
|
||||
fprintf(stderr, "Warning: color profile present, but qcms is unavailable!\n"
|
||||
fprintf(stderr,
|
||||
"Warning: color profile present, but qcms is unavailable!\n"
|
||||
"Build libqcms from Mozilla or Chromium and define WEBP_HAVE_QCMS "
|
||||
"before building.\n");
|
||||
#endif
|
||||
@@ -614,8 +611,8 @@ int main(int argc, char* argv[]) {
|
||||
kParams.has_animation = (curr->num_frames > 1);
|
||||
kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
|
||||
kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR);
|
||||
printf("VP8X: Found %d images in file (loop count = %d)\n",
|
||||
curr->num_frames, kParams.loop_count);
|
||||
printf("VP8X: Found %d images in file (loop count = %d)\n", curr->num_frames,
|
||||
kParams.loop_count);
|
||||
|
||||
// Decode first frame
|
||||
if (!Decode()) goto Error;
|
||||
@@ -645,12 +642,12 @@ int main(int argc, char* argv[]) {
|
||||
ClearParams();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
|
||||
Error:
|
||||
Error:
|
||||
ClearParams();
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#else // !WEBP_HAVE_GL
|
||||
#else // !WEBP_HAVE_GL
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
@@ -25,6 +26,7 @@
|
||||
#include "webp/decode.h"
|
||||
#include "webp/format_constants.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@@ -32,38 +34,26 @@
|
||||
|
||||
#define LOG_ERROR(MESSAGE) \
|
||||
do { \
|
||||
if (webp_info->show_diagnosis_) { \
|
||||
if (webp_info->show_diagnosis) { \
|
||||
fprintf(stderr, "Error: %s\n", MESSAGE); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LOG_WARN(MESSAGE) \
|
||||
do { \
|
||||
if (webp_info->show_diagnosis_) { \
|
||||
if (webp_info->show_diagnosis) { \
|
||||
fprintf(stderr, "Warning: %s\n", MESSAGE); \
|
||||
} \
|
||||
++webp_info->num_warnings_; \
|
||||
++webp_info->num_warnings; \
|
||||
} while (0)
|
||||
|
||||
static const char* const kFormats[3] = {
|
||||
"Unknown",
|
||||
"Lossy",
|
||||
"Lossless"
|
||||
};
|
||||
static const char* const kFormats[3] = {"Unknown", "Lossy", "Lossless"};
|
||||
|
||||
static const char* const kLosslessTransforms[4] = {
|
||||
"Predictor",
|
||||
"Cross Color",
|
||||
"Subtract Green",
|
||||
"Color Indexing"
|
||||
};
|
||||
"Predictor", "Cross Color", "Subtract Green", "Color Indexing"};
|
||||
|
||||
static const char* const kAlphaFilterMethods[4] = {
|
||||
"None",
|
||||
"Horizontal",
|
||||
"Vertical",
|
||||
"Gradient"
|
||||
};
|
||||
static const char* const kAlphaFilterMethods[4] = {"None", "Horizontal",
|
||||
"Vertical", "Gradient"};
|
||||
|
||||
typedef enum {
|
||||
WEBP_INFO_OK = 0,
|
||||
@@ -90,36 +80,36 @@ typedef enum ChunkID {
|
||||
} ChunkID;
|
||||
|
||||
typedef struct {
|
||||
size_t start_;
|
||||
size_t end_;
|
||||
const uint8_t* buf_;
|
||||
size_t start;
|
||||
size_t end;
|
||||
const uint8_t* buf;
|
||||
} MemBuffer;
|
||||
|
||||
typedef struct {
|
||||
size_t offset_;
|
||||
size_t size_;
|
||||
const uint8_t* payload_;
|
||||
ChunkID id_;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
const uint8_t* payload;
|
||||
ChunkID id;
|
||||
} ChunkData;
|
||||
|
||||
typedef struct WebPInfo {
|
||||
int canvas_width_;
|
||||
int canvas_height_;
|
||||
int loop_count_;
|
||||
int num_frames_;
|
||||
int chunk_counts_[CHUNK_TYPES];
|
||||
int anmf_subchunk_counts_[3]; // 0 VP8; 1 VP8L; 2 ALPH.
|
||||
uint32_t bgcolor_;
|
||||
int feature_flags_;
|
||||
int has_alpha_;
|
||||
int canvas_width;
|
||||
int canvas_height;
|
||||
int loop_count;
|
||||
int num_frames;
|
||||
int chunk_counts[CHUNK_TYPES];
|
||||
int anmf_subchunk_counts[3]; // 0 VP8; 1 VP8L; 2 ALPH.
|
||||
uint32_t bgcolor;
|
||||
int feature_flags;
|
||||
int has_alpha;
|
||||
// Used for parsing ANMF chunks.
|
||||
int frame_width_, frame_height_;
|
||||
size_t anim_frame_data_size_;
|
||||
int is_processing_anim_frame_, seen_alpha_subchunk_, seen_image_subchunk_;
|
||||
int frame_width, frame_height;
|
||||
size_t anim_frame_data_size;
|
||||
int is_processing_anim_frame, seen_alpha_subchunk, seen_image_subchunk;
|
||||
// Print output control.
|
||||
int quiet_, show_diagnosis_, show_summary_;
|
||||
int num_warnings_;
|
||||
int parse_bitstream_;
|
||||
int quiet, show_diagnosis, show_summary;
|
||||
int num_warnings;
|
||||
int parse_bitstream;
|
||||
} WebPInfo;
|
||||
|
||||
static void WebPInfoInit(WebPInfo* const webp_info) {
|
||||
@@ -127,15 +117,15 @@ static void WebPInfoInit(WebPInfo* const webp_info) {
|
||||
}
|
||||
|
||||
static const uint32_t kWebPChunkTags[CHUNK_TYPES] = {
|
||||
MKFOURCC('V', 'P', '8', ' '),
|
||||
MKFOURCC('V', 'P', '8', 'L'),
|
||||
MKFOURCC('V', 'P', '8', 'X'),
|
||||
MKFOURCC('A', 'L', 'P', 'H'),
|
||||
MKFOURCC('A', 'N', 'I', 'M'),
|
||||
MKFOURCC('A', 'N', 'M', 'F'),
|
||||
MKFOURCC('I', 'C', 'C', 'P'),
|
||||
MKFOURCC('E', 'X', 'I', 'F'),
|
||||
MKFOURCC('X', 'M', 'P', ' '),
|
||||
MKFOURCC('V', 'P', '8', ' '), //
|
||||
MKFOURCC('V', 'P', '8', 'L'), //
|
||||
MKFOURCC('V', 'P', '8', 'X'), //
|
||||
MKFOURCC('A', 'L', 'P', 'H'), //
|
||||
MKFOURCC('A', 'N', 'I', 'M'), //
|
||||
MKFOURCC('A', 'N', 'M', 'F'), //
|
||||
MKFOURCC('I', 'C', 'C', 'P'), //
|
||||
MKFOURCC('E', 'X', 'I', 'F'), //
|
||||
MKFOURCC('X', 'M', 'P', ' '), //
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -185,25 +175,23 @@ static int ReadFileToWebPData(const char* const filename,
|
||||
// MemBuffer object.
|
||||
|
||||
static void InitMemBuffer(MemBuffer* const mem, const WebPData* webp_data) {
|
||||
mem->buf_ = webp_data->bytes;
|
||||
mem->start_ = 0;
|
||||
mem->end_ = webp_data->size;
|
||||
mem->buf = webp_data->bytes;
|
||||
mem->start = 0;
|
||||
mem->end = webp_data->size;
|
||||
}
|
||||
|
||||
static size_t MemDataSize(const MemBuffer* const mem) {
|
||||
return (mem->end_ - mem->start_);
|
||||
return (mem->end - mem->start);
|
||||
}
|
||||
|
||||
static const uint8_t* GetBuffer(MemBuffer* const mem) {
|
||||
return mem->buf_ + mem->start_;
|
||||
return mem->buf + mem->start;
|
||||
}
|
||||
|
||||
static void Skip(MemBuffer* const mem, size_t size) {
|
||||
mem->start_ += size;
|
||||
}
|
||||
static void Skip(MemBuffer* const mem, size_t size) { mem->start += size; }
|
||||
|
||||
static uint32_t ReadMemBufLE32(MemBuffer* const mem) {
|
||||
const uint8_t* const data = mem->buf_ + mem->start_;
|
||||
const uint8_t* const data = mem->buf + mem->start;
|
||||
const uint32_t val = GetLE32(data);
|
||||
assert(MemDataSize(mem) >= 4);
|
||||
Skip(mem, 4);
|
||||
@@ -264,9 +252,10 @@ static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info,
|
||||
int update_map, update_data;
|
||||
GET_BITS(update_map, 1);
|
||||
GET_BITS(update_data, 1);
|
||||
printf(" Update map: %d\n"
|
||||
" Update data: %d\n",
|
||||
update_map, update_data);
|
||||
printf(
|
||||
" Update map: %d\n"
|
||||
" Update data: %d\n",
|
||||
update_map, update_data);
|
||||
if (update_data) {
|
||||
int i, a_delta;
|
||||
int quantizer[4] = {0, 0, 0, 0};
|
||||
@@ -296,8 +285,8 @@ static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info,
|
||||
GET_BITS(bit, 1);
|
||||
if (bit) GET_BITS(prob_segment[i], 8);
|
||||
}
|
||||
printf(" Prob segment: %d %d %d\n",
|
||||
prob_segment[0], prob_segment[1], prob_segment[2]);
|
||||
printf(" Prob segment: %d %d %d\n", prob_segment[0], prob_segment[1],
|
||||
prob_segment[2]);
|
||||
}
|
||||
}
|
||||
return WEBP_INFO_OK;
|
||||
@@ -334,8 +323,8 @@ static WebPInfoStatus ParseLossyFilterHeader(const WebPInfo* const webp_info,
|
||||
|
||||
static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
|
||||
const WebPInfo* const webp_info) {
|
||||
const uint8_t* data = chunk_data->payload_;
|
||||
size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
|
||||
const uint8_t* data = chunk_data->payload;
|
||||
size_t data_size = chunk_data->size - CHUNK_HEADER_SIZE;
|
||||
const uint32_t bits = (uint32_t)data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
const int key_frame = !(bits & 1);
|
||||
const int profile = (bits >> 1) & 7;
|
||||
@@ -347,7 +336,7 @@ static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
|
||||
int colorspace, clamp_type;
|
||||
printf(" Parsing lossy bitstream...\n");
|
||||
// Calling WebPGetFeatures() in ProcessImageChunk() should ensure this.
|
||||
assert(chunk_data->size_ >= CHUNK_HEADER_SIZE + 10);
|
||||
assert(chunk_data->size >= CHUNK_HEADER_SIZE + 10);
|
||||
if (profile > 3) {
|
||||
LOG_ERROR("Unknown profile.");
|
||||
return WEBP_INFO_BITSTREAM_ERROR;
|
||||
@@ -369,12 +358,13 @@ static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
|
||||
LOG_ERROR("Invalid lossy bitstream signature.");
|
||||
return WEBP_INFO_BITSTREAM_ERROR;
|
||||
}
|
||||
printf(" Width: %d\n"
|
||||
" X scale: %d\n"
|
||||
" Height: %d\n"
|
||||
" Y scale: %d\n",
|
||||
((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6,
|
||||
((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6);
|
||||
printf(
|
||||
" Width: %d\n"
|
||||
" X scale: %d\n"
|
||||
" Height: %d\n"
|
||||
" Y scale: %d\n",
|
||||
((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6,
|
||||
((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6);
|
||||
data += 7;
|
||||
data_size -= 7;
|
||||
} else {
|
||||
@@ -477,7 +467,7 @@ static int LLGetBits(const uint8_t* const data, size_t data_size, size_t nb,
|
||||
static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
|
||||
const uint8_t* const data,
|
||||
size_t data_size,
|
||||
uint64_t* const bit_pos) {
|
||||
uint64_t* const bit_pos) {
|
||||
int use_transform, block_size, n_colors;
|
||||
LL_GET_BITS(use_transform, 1);
|
||||
printf(" Use transform: %s\n", use_transform ? "Yes" : "No");
|
||||
@@ -497,7 +487,8 @@ static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
|
||||
n_colors += 1;
|
||||
printf(" No. of colors: %d\n", n_colors);
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return WEBP_INFO_OK;
|
||||
@@ -505,8 +496,8 @@ static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
|
||||
|
||||
static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
const uint8_t* data = chunk_data->payload_;
|
||||
size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
|
||||
const uint8_t* data = chunk_data->payload;
|
||||
size_t data_size = chunk_data->size - CHUNK_HEADER_SIZE;
|
||||
uint64_t bit_position = 0;
|
||||
uint64_t* const bit_pos = &bit_position;
|
||||
WebPInfoStatus status;
|
||||
@@ -541,8 +532,8 @@ static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data,
|
||||
|
||||
static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
const uint8_t* data = chunk_data->payload_;
|
||||
size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
|
||||
const uint8_t* data = chunk_data->payload;
|
||||
size_t data_size = chunk_data->size - CHUNK_HEADER_SIZE;
|
||||
if (data_size <= ALPHA_HEADER_LEN) {
|
||||
LOG_ERROR("Truncated ALPH chunk.");
|
||||
return WEBP_INFO_TRUNCATED_DATA;
|
||||
@@ -554,8 +545,8 @@ static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data,
|
||||
const int pre_processing = (data[0] >> 4) & 0x03;
|
||||
const int reserved_bits = (data[0] >> 6) & 0x03;
|
||||
printf(" Compression: %d\n", compression_method);
|
||||
printf(" Filter: %s (%d)\n",
|
||||
kAlphaFilterMethods[filter], filter);
|
||||
printf(" Filter: %s (%d)\n", kAlphaFilterMethods[filter],
|
||||
filter);
|
||||
printf(" Pre-processing: %d\n", pre_processing);
|
||||
if (compression_method > ALPHA_LOSSLESS_COMPRESSION) {
|
||||
LOG_ERROR("Invalid Alpha compression method.");
|
||||
@@ -607,14 +598,14 @@ static WebPInfoStatus ParseRIFFHeader(WebPInfo* const webp_info,
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
riff_size += CHUNK_HEADER_SIZE;
|
||||
if (!webp_info->quiet_) {
|
||||
if (!webp_info->quiet) {
|
||||
printf("RIFF HEADER:\n");
|
||||
printf(" File size: %6d\n", (int)riff_size);
|
||||
}
|
||||
if (riff_size < mem->end_) {
|
||||
if (riff_size < mem->end) {
|
||||
LOG_WARN("RIFF size is smaller than the file size.");
|
||||
mem->end_ = riff_size;
|
||||
} else if (riff_size > mem->end_) {
|
||||
mem->end = riff_size;
|
||||
} else if (riff_size > mem->end) {
|
||||
LOG_ERROR("Truncated data detected when parsing RIFF payload.");
|
||||
return WEBP_INFO_TRUNCATED_DATA;
|
||||
}
|
||||
@@ -630,7 +621,7 @@ static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
|
||||
LOG_ERROR("Truncated data detected when parsing chunk header.");
|
||||
return WEBP_INFO_TRUNCATED_DATA;
|
||||
} else {
|
||||
const size_t chunk_start_offset = mem->start_;
|
||||
const size_t chunk_start_offset = mem->start;
|
||||
const uint32_t fourcc = ReadMemBufLE32(mem);
|
||||
const uint32_t payload_size = ReadMemBufLE32(mem);
|
||||
const uint32_t payload_size_padded = payload_size + (payload_size & 1);
|
||||
@@ -640,18 +631,18 @@ static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
|
||||
LOG_ERROR("Size of chunk payload is over limit.");
|
||||
return WEBP_INFO_INVALID_PARAM;
|
||||
}
|
||||
if (payload_size_padded > MemDataSize(mem)){
|
||||
if (payload_size_padded > MemDataSize(mem)) {
|
||||
LOG_ERROR("Truncated data detected when parsing chunk payload.");
|
||||
return WEBP_INFO_TRUNCATED_DATA;
|
||||
}
|
||||
for (i = 0; i < CHUNK_TYPES; ++i) {
|
||||
if (kWebPChunkTags[i] == fourcc) break;
|
||||
}
|
||||
chunk_data->offset_ = chunk_start_offset;
|
||||
chunk_data->size_ = chunk_size;
|
||||
chunk_data->id_ = (ChunkID)i;
|
||||
chunk_data->payload_ = GetBuffer(mem);
|
||||
if (chunk_data->id_ == CHUNK_ANMF) {
|
||||
chunk_data->offset = chunk_start_offset;
|
||||
chunk_data->size = chunk_size;
|
||||
chunk_data->id = (ChunkID)i;
|
||||
chunk_data->payload = GetBuffer(mem);
|
||||
if (chunk_data->id == CHUNK_ANMF) {
|
||||
if (payload_size != payload_size_padded) {
|
||||
LOG_ERROR("ANMF chunk size should always be even.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
@@ -670,39 +661,39 @@ static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
|
||||
|
||||
static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
const uint8_t* data = chunk_data->payload_;
|
||||
if (webp_info->chunk_counts_[CHUNK_VP8] ||
|
||||
webp_info->chunk_counts_[CHUNK_VP8L] ||
|
||||
webp_info->chunk_counts_[CHUNK_VP8X]) {
|
||||
const uint8_t* data = chunk_data->payload;
|
||||
if (webp_info->chunk_counts[CHUNK_VP8] ||
|
||||
webp_info->chunk_counts[CHUNK_VP8L] ||
|
||||
webp_info->chunk_counts[CHUNK_VP8X]) {
|
||||
LOG_ERROR("Already seen a VP8/VP8L/VP8X chunk when parsing VP8X chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (chunk_data->size_ != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
|
||||
if (chunk_data->size != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
|
||||
LOG_ERROR("Corrupted VP8X chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
++webp_info->chunk_counts_[CHUNK_VP8X];
|
||||
webp_info->feature_flags_ = *data;
|
||||
++webp_info->chunk_counts[CHUNK_VP8X];
|
||||
webp_info->feature_flags = *data;
|
||||
data += 4;
|
||||
webp_info->canvas_width_ = 1 + ReadLE24(&data);
|
||||
webp_info->canvas_height_ = 1 + ReadLE24(&data);
|
||||
if (!webp_info->quiet_) {
|
||||
webp_info->canvas_width = 1 + ReadLE24(&data);
|
||||
webp_info->canvas_height = 1 + ReadLE24(&data);
|
||||
if (!webp_info->quiet) {
|
||||
printf(" ICCP: %d\n Alpha: %d\n EXIF: %d\n XMP: %d\n Animation: %d\n",
|
||||
(webp_info->feature_flags_ & ICCP_FLAG) != 0,
|
||||
(webp_info->feature_flags_ & ALPHA_FLAG) != 0,
|
||||
(webp_info->feature_flags_ & EXIF_FLAG) != 0,
|
||||
(webp_info->feature_flags_ & XMP_FLAG) != 0,
|
||||
(webp_info->feature_flags_ & ANIMATION_FLAG) != 0);
|
||||
printf(" Canvas size %d x %d\n",
|
||||
webp_info->canvas_width_, webp_info->canvas_height_);
|
||||
(webp_info->feature_flags & ICCP_FLAG) != 0,
|
||||
(webp_info->feature_flags & ALPHA_FLAG) != 0,
|
||||
(webp_info->feature_flags & EXIF_FLAG) != 0,
|
||||
(webp_info->feature_flags & XMP_FLAG) != 0,
|
||||
(webp_info->feature_flags & ANIMATION_FLAG) != 0);
|
||||
printf(" Canvas size %d x %d\n", webp_info->canvas_width,
|
||||
webp_info->canvas_height);
|
||||
}
|
||||
if (webp_info->canvas_width_ > MAX_CANVAS_SIZE) {
|
||||
if (webp_info->canvas_width > MAX_CANVAS_SIZE) {
|
||||
LOG_WARN("Canvas width is out of range in VP8X chunk.");
|
||||
}
|
||||
if (webp_info->canvas_height_ > MAX_CANVAS_SIZE) {
|
||||
if (webp_info->canvas_height > MAX_CANVAS_SIZE) {
|
||||
LOG_WARN("Canvas height is out of range in VP8X chunk.");
|
||||
}
|
||||
if ((uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
|
||||
if ((uint64_t)webp_info->canvas_width * webp_info->canvas_height >
|
||||
MAX_IMAGE_AREA) {
|
||||
LOG_WARN("Canvas area is out of range in VP8X chunk.");
|
||||
}
|
||||
@@ -711,27 +702,25 @@ static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data,
|
||||
|
||||
static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
const uint8_t* data = chunk_data->payload_;
|
||||
if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
|
||||
const uint8_t* data = chunk_data->payload;
|
||||
if (!webp_info->chunk_counts[CHUNK_VP8X]) {
|
||||
LOG_ERROR("ANIM chunk detected before VP8X chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (chunk_data->size_ != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
|
||||
if (chunk_data->size != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
|
||||
LOG_ERROR("Corrupted ANIM chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
webp_info->bgcolor_ = ReadLE32(&data);
|
||||
webp_info->loop_count_ = ReadLE16(&data);
|
||||
++webp_info->chunk_counts_[CHUNK_ANIM];
|
||||
if (!webp_info->quiet_) {
|
||||
webp_info->bgcolor = ReadLE32(&data);
|
||||
webp_info->loop_count = ReadLE16(&data);
|
||||
++webp_info->chunk_counts[CHUNK_ANIM];
|
||||
if (!webp_info->quiet) {
|
||||
printf(" Background color:(ARGB) %02x %02x %02x %02x\n",
|
||||
(webp_info->bgcolor_ >> 24) & 0xff,
|
||||
(webp_info->bgcolor_ >> 16) & 0xff,
|
||||
(webp_info->bgcolor_ >> 8) & 0xff,
|
||||
webp_info->bgcolor_ & 0xff);
|
||||
printf(" Loop count : %d\n", webp_info->loop_count_);
|
||||
(webp_info->bgcolor >> 24) & 0xff, (webp_info->bgcolor >> 16) & 0xff,
|
||||
(webp_info->bgcolor >> 8) & 0xff, webp_info->bgcolor & 0xff);
|
||||
printf(" Loop count : %d\n", webp_info->loop_count);
|
||||
}
|
||||
if (webp_info->loop_count_ > MAX_LOOP_COUNT) {
|
||||
if (webp_info->loop_count > MAX_LOOP_COUNT) {
|
||||
LOG_WARN("Loop count is out of range in ANIM chunk.");
|
||||
}
|
||||
return WEBP_INFO_OK;
|
||||
@@ -739,17 +728,17 @@ static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data,
|
||||
|
||||
static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
const uint8_t* data = chunk_data->payload_;
|
||||
const uint8_t* data = chunk_data->payload;
|
||||
int offset_x, offset_y, width, height, duration, blend, dispose, temp;
|
||||
if (webp_info->is_processing_anim_frame_) {
|
||||
if (webp_info->is_processing_anim_frame) {
|
||||
LOG_ERROR("ANMF chunk detected within another ANMF chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (!webp_info->chunk_counts_[CHUNK_ANIM]) {
|
||||
if (!webp_info->chunk_counts[CHUNK_ANIM]) {
|
||||
LOG_ERROR("ANMF chunk detected before ANIM chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (chunk_data->size_ <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) {
|
||||
if (chunk_data->size <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) {
|
||||
LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
|
||||
return WEBP_INFO_TRUNCATED_DATA;
|
||||
}
|
||||
@@ -761,11 +750,12 @@ static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
|
||||
temp = *data;
|
||||
dispose = temp & 1;
|
||||
blend = (temp >> 1) & 1;
|
||||
++webp_info->chunk_counts_[CHUNK_ANMF];
|
||||
if (!webp_info->quiet_) {
|
||||
printf(" Offset_X: %d\n Offset_Y: %d\n Width: %d\n Height: %d\n"
|
||||
" Duration: %d\n Dispose: %d\n Blend: %d\n",
|
||||
offset_x, offset_y, width, height, duration, dispose, blend);
|
||||
++webp_info->chunk_counts[CHUNK_ANMF];
|
||||
if (!webp_info->quiet) {
|
||||
printf(
|
||||
" Offset_X: %d\n Offset_Y: %d\n Width: %d\n Height: %d\n"
|
||||
" Duration: %d\n Dispose: %d\n Blend: %d\n",
|
||||
offset_x, offset_y, width, height, duration, dispose, blend);
|
||||
}
|
||||
if (duration > MAX_DURATION) {
|
||||
LOG_ERROR("Invalid duration parameter in ANMF chunk.");
|
||||
@@ -775,92 +765,92 @@ static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
|
||||
LOG_ERROR("Invalid offset parameters in ANMF chunk.");
|
||||
return WEBP_INFO_INVALID_PARAM;
|
||||
}
|
||||
if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width_ ||
|
||||
(uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height_) {
|
||||
if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width ||
|
||||
(uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height) {
|
||||
LOG_ERROR("Frame exceeds canvas in ANMF chunk.");
|
||||
return WEBP_INFO_INVALID_PARAM;
|
||||
}
|
||||
webp_info->is_processing_anim_frame_ = 1;
|
||||
webp_info->seen_alpha_subchunk_ = 0;
|
||||
webp_info->seen_image_subchunk_ = 0;
|
||||
webp_info->frame_width_ = width;
|
||||
webp_info->frame_height_ = height;
|
||||
webp_info->anim_frame_data_size_ =
|
||||
chunk_data->size_ - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE;
|
||||
webp_info->is_processing_anim_frame = 1;
|
||||
webp_info->seen_alpha_subchunk = 0;
|
||||
webp_info->seen_image_subchunk = 0;
|
||||
webp_info->frame_width = width;
|
||||
webp_info->frame_height = height;
|
||||
webp_info->anim_frame_data_size =
|
||||
chunk_data->size - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE;
|
||||
return WEBP_INFO_OK;
|
||||
}
|
||||
|
||||
static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
const uint8_t* data = chunk_data->payload_ - CHUNK_HEADER_SIZE;
|
||||
const uint8_t* data = chunk_data->payload - CHUNK_HEADER_SIZE;
|
||||
WebPBitstreamFeatures features;
|
||||
const VP8StatusCode vp8_status =
|
||||
WebPGetFeatures(data, chunk_data->size_, &features);
|
||||
WebPGetFeatures(data, chunk_data->size, &features);
|
||||
if (vp8_status != VP8_STATUS_OK) {
|
||||
LOG_ERROR("VP8/VP8L bitstream error.");
|
||||
return WEBP_INFO_BITSTREAM_ERROR;
|
||||
}
|
||||
if (!webp_info->quiet_) {
|
||||
if (!webp_info->quiet) {
|
||||
assert(features.format >= 0 && features.format <= 2);
|
||||
printf(" Width: %d\n Height: %d\n Alpha: %d\n Animation: %d\n"
|
||||
" Format: %s (%d)\n",
|
||||
features.width, features.height, features.has_alpha,
|
||||
features.has_animation, kFormats[features.format], features.format);
|
||||
printf(
|
||||
" Width: %d\n Height: %d\n Alpha: %d\n Animation: %d\n"
|
||||
" Format: %s (%d)\n",
|
||||
features.width, features.height, features.has_alpha,
|
||||
features.has_animation, kFormats[features.format], features.format);
|
||||
}
|
||||
if (webp_info->is_processing_anim_frame_) {
|
||||
++webp_info->anmf_subchunk_counts_[chunk_data->id_ == CHUNK_VP8 ? 0 : 1];
|
||||
if (chunk_data->id_ == CHUNK_VP8L && webp_info->seen_alpha_subchunk_) {
|
||||
if (webp_info->is_processing_anim_frame) {
|
||||
++webp_info->anmf_subchunk_counts[chunk_data->id == CHUNK_VP8 ? 0 : 1];
|
||||
if (chunk_data->id == CHUNK_VP8L && webp_info->seen_alpha_subchunk) {
|
||||
LOG_ERROR("Both VP8L and ALPH sub-chunks are present in an ANMF chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (webp_info->frame_width_ != features.width ||
|
||||
webp_info->frame_height_ != features.height) {
|
||||
if (webp_info->frame_width != features.width ||
|
||||
webp_info->frame_height != features.height) {
|
||||
LOG_ERROR("Frame size in VP8/VP8L sub-chunk differs from ANMF header.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (webp_info->seen_image_subchunk_) {
|
||||
if (webp_info->seen_image_subchunk) {
|
||||
LOG_ERROR("Consecutive VP8/VP8L sub-chunks in an ANMF chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
webp_info->seen_image_subchunk_ = 1;
|
||||
webp_info->seen_image_subchunk = 1;
|
||||
} else {
|
||||
if (webp_info->chunk_counts_[CHUNK_VP8] ||
|
||||
webp_info->chunk_counts_[CHUNK_VP8L]) {
|
||||
if (webp_info->chunk_counts[CHUNK_VP8] ||
|
||||
webp_info->chunk_counts[CHUNK_VP8L]) {
|
||||
LOG_ERROR("Multiple VP8/VP8L chunks detected.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (chunk_data->id_ == CHUNK_VP8L &&
|
||||
webp_info->chunk_counts_[CHUNK_ALPHA]) {
|
||||
if (chunk_data->id == CHUNK_VP8L && webp_info->chunk_counts[CHUNK_ALPHA]) {
|
||||
LOG_WARN("Both VP8L and ALPH chunks are detected.");
|
||||
}
|
||||
if (webp_info->chunk_counts_[CHUNK_ANIM] ||
|
||||
webp_info->chunk_counts_[CHUNK_ANMF]) {
|
||||
if (webp_info->chunk_counts[CHUNK_ANIM] ||
|
||||
webp_info->chunk_counts[CHUNK_ANMF]) {
|
||||
LOG_ERROR("VP8/VP8L chunk and ANIM/ANMF chunk are both detected.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (webp_info->chunk_counts_[CHUNK_VP8X]) {
|
||||
if (webp_info->canvas_width_ != features.width ||
|
||||
webp_info->canvas_height_ != features.height) {
|
||||
if (webp_info->chunk_counts[CHUNK_VP8X]) {
|
||||
if (webp_info->canvas_width != features.width ||
|
||||
webp_info->canvas_height != features.height) {
|
||||
LOG_ERROR("Image size in VP8/VP8L chunk differs from VP8X chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
} else {
|
||||
webp_info->canvas_width_ = features.width;
|
||||
webp_info->canvas_height_ = features.height;
|
||||
if (webp_info->canvas_width_ < 1 || webp_info->canvas_height_ < 1 ||
|
||||
webp_info->canvas_width_ > MAX_CANVAS_SIZE ||
|
||||
webp_info->canvas_height_ > MAX_CANVAS_SIZE ||
|
||||
(uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
|
||||
webp_info->canvas_width = features.width;
|
||||
webp_info->canvas_height = features.height;
|
||||
if (webp_info->canvas_width < 1 || webp_info->canvas_height < 1 ||
|
||||
webp_info->canvas_width > MAX_CANVAS_SIZE ||
|
||||
webp_info->canvas_height > MAX_CANVAS_SIZE ||
|
||||
(uint64_t)webp_info->canvas_width * webp_info->canvas_height >
|
||||
MAX_IMAGE_AREA) {
|
||||
LOG_WARN("Invalid parameters in VP8/VP8L chunk.");
|
||||
}
|
||||
}
|
||||
++webp_info->chunk_counts_[chunk_data->id_];
|
||||
++webp_info->chunk_counts[chunk_data->id];
|
||||
}
|
||||
++webp_info->num_frames_;
|
||||
webp_info->has_alpha_ |= features.has_alpha;
|
||||
if (webp_info->parse_bitstream_) {
|
||||
const int is_lossy = (chunk_data->id_ == CHUNK_VP8);
|
||||
++webp_info->num_frames;
|
||||
webp_info->has_alpha |= features.has_alpha;
|
||||
if (webp_info->parse_bitstream) {
|
||||
const int is_lossy = (chunk_data->id == CHUNK_VP8);
|
||||
const WebPInfoStatus status =
|
||||
is_lossy ? ParseLossyHeader(chunk_data, webp_info)
|
||||
: ParseLosslessHeader(chunk_data, webp_info);
|
||||
@@ -871,41 +861,42 @@ static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data,
|
||||
|
||||
static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
if (webp_info->is_processing_anim_frame_) {
|
||||
++webp_info->anmf_subchunk_counts_[2];
|
||||
if (webp_info->seen_alpha_subchunk_) {
|
||||
if (webp_info->is_processing_anim_frame) {
|
||||
++webp_info->anmf_subchunk_counts[2];
|
||||
if (webp_info->seen_alpha_subchunk) {
|
||||
LOG_ERROR("Consecutive ALPH sub-chunks in an ANMF chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
webp_info->seen_alpha_subchunk_ = 1;
|
||||
webp_info->seen_alpha_subchunk = 1;
|
||||
|
||||
if (webp_info->seen_image_subchunk_) {
|
||||
LOG_ERROR("ALPHA sub-chunk detected after VP8 sub-chunk "
|
||||
"in an ANMF chunk.");
|
||||
if (webp_info->seen_image_subchunk) {
|
||||
LOG_ERROR(
|
||||
"ALPHA sub-chunk detected after VP8 sub-chunk "
|
||||
"in an ANMF chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (webp_info->chunk_counts_[CHUNK_ANIM] ||
|
||||
webp_info->chunk_counts_[CHUNK_ANMF]) {
|
||||
if (webp_info->chunk_counts[CHUNK_ANIM] ||
|
||||
webp_info->chunk_counts[CHUNK_ANMF]) {
|
||||
LOG_ERROR("ALPHA chunk and ANIM/ANMF chunk are both detected.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
|
||||
if (!webp_info->chunk_counts[CHUNK_VP8X]) {
|
||||
LOG_ERROR("ALPHA chunk detected before VP8X chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (webp_info->chunk_counts_[CHUNK_VP8]) {
|
||||
if (webp_info->chunk_counts[CHUNK_VP8]) {
|
||||
LOG_ERROR("ALPHA chunk detected after VP8 chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (webp_info->chunk_counts_[CHUNK_ALPHA]) {
|
||||
if (webp_info->chunk_counts[CHUNK_ALPHA]) {
|
||||
LOG_ERROR("Multiple ALPHA chunks detected.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
++webp_info->chunk_counts_[CHUNK_ALPHA];
|
||||
++webp_info->chunk_counts[CHUNK_ALPHA];
|
||||
}
|
||||
webp_info->has_alpha_ = 1;
|
||||
if (webp_info->parse_bitstream_) {
|
||||
webp_info->has_alpha = 1;
|
||||
if (webp_info->parse_bitstream) {
|
||||
const WebPInfoStatus status = ParseAlphaHeader(chunk_data, webp_info);
|
||||
if (status != WEBP_INFO_OK) return status;
|
||||
}
|
||||
@@ -915,41 +906,40 @@ static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data,
|
||||
static WebPInfoStatus ProcessICCPChunk(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
(void)chunk_data;
|
||||
if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
|
||||
if (!webp_info->chunk_counts[CHUNK_VP8X]) {
|
||||
LOG_ERROR("ICCP chunk detected before VP8X chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (webp_info->chunk_counts_[CHUNK_VP8] ||
|
||||
webp_info->chunk_counts_[CHUNK_VP8L] ||
|
||||
webp_info->chunk_counts_[CHUNK_ANIM]) {
|
||||
if (webp_info->chunk_counts[CHUNK_VP8] ||
|
||||
webp_info->chunk_counts[CHUNK_VP8L] ||
|
||||
webp_info->chunk_counts[CHUNK_ANIM]) {
|
||||
LOG_ERROR("ICCP chunk detected after image data.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
++webp_info->chunk_counts_[CHUNK_ICCP];
|
||||
++webp_info->chunk_counts[CHUNK_ICCP];
|
||||
return WEBP_INFO_OK;
|
||||
}
|
||||
|
||||
static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
|
||||
WebPInfo* const webp_info) {
|
||||
WebPInfoStatus status = WEBP_INFO_OK;
|
||||
ChunkID id = chunk_data->id_;
|
||||
if (chunk_data->id_ == CHUNK_UNKNOWN) {
|
||||
ChunkID id = chunk_data->id;
|
||||
if (chunk_data->id == CHUNK_UNKNOWN) {
|
||||
char error_message[50];
|
||||
snprintf(error_message, 50, "Unknown chunk at offset %6d, length %6d",
|
||||
(int)chunk_data->offset_, (int)chunk_data->size_);
|
||||
(int)chunk_data->offset, (int)chunk_data->size);
|
||||
LOG_WARN(error_message);
|
||||
} else {
|
||||
if (!webp_info->quiet_) {
|
||||
if (!webp_info->quiet) {
|
||||
char tag[4];
|
||||
uint32_t fourcc = kWebPChunkTags[chunk_data->id_];
|
||||
uint32_t fourcc = kWebPChunkTags[chunk_data->id];
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
fourcc = (fourcc >> 24) | ((fourcc >> 8) & 0xff00) |
|
||||
((fourcc << 8) & 0xff0000) | (fourcc << 24);
|
||||
#endif
|
||||
memcpy(tag, &fourcc, sizeof(tag));
|
||||
printf("Chunk %c%c%c%c at offset %6d, length %6d\n",
|
||||
tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset_,
|
||||
(int)chunk_data->size_);
|
||||
printf("Chunk %c%c%c%c at offset %6d, length %6d\n", tag[0], tag[1],
|
||||
tag[2], tag[3], (int)chunk_data->offset, (int)chunk_data->size);
|
||||
}
|
||||
}
|
||||
switch (id) {
|
||||
@@ -974,21 +964,21 @@ static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
|
||||
break;
|
||||
case CHUNK_EXIF:
|
||||
case CHUNK_XMP:
|
||||
++webp_info->chunk_counts_[id];
|
||||
++webp_info->chunk_counts[id];
|
||||
break;
|
||||
case CHUNK_UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (webp_info->is_processing_anim_frame_ && id != CHUNK_ANMF) {
|
||||
if (webp_info->anim_frame_data_size_ == chunk_data->size_) {
|
||||
if (!webp_info->seen_image_subchunk_) {
|
||||
if (webp_info->is_processing_anim_frame && id != CHUNK_ANMF) {
|
||||
if (webp_info->anim_frame_data_size == chunk_data->size) {
|
||||
if (!webp_info->seen_image_subchunk) {
|
||||
LOG_ERROR("No VP8/VP8L chunk detected in an ANMF chunk.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
webp_info->is_processing_anim_frame_ = 0;
|
||||
} else if (webp_info->anim_frame_data_size_ > chunk_data->size_) {
|
||||
webp_info->anim_frame_data_size_ -= chunk_data->size_;
|
||||
webp_info->is_processing_anim_frame = 0;
|
||||
} else if (webp_info->anim_frame_data_size > chunk_data->size) {
|
||||
webp_info->anim_frame_data_size -= chunk_data->size;
|
||||
} else {
|
||||
LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
|
||||
return WEBP_INFO_TRUNCATED_DATA;
|
||||
@@ -998,55 +988,55 @@ static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
|
||||
}
|
||||
|
||||
static WebPInfoStatus Validate(WebPInfo* const webp_info) {
|
||||
if (webp_info->num_frames_ < 1) {
|
||||
if (webp_info->num_frames < 1) {
|
||||
LOG_ERROR("No image/frame detected.");
|
||||
return WEBP_INFO_MISSING_DATA;
|
||||
}
|
||||
if (webp_info->chunk_counts_[CHUNK_VP8X]) {
|
||||
const int iccp = !!(webp_info->feature_flags_ & ICCP_FLAG);
|
||||
const int exif = !!(webp_info->feature_flags_ & EXIF_FLAG);
|
||||
const int xmp = !!(webp_info->feature_flags_ & XMP_FLAG);
|
||||
const int animation = !!(webp_info->feature_flags_ & ANIMATION_FLAG);
|
||||
const int alpha = !!(webp_info->feature_flags_ & ALPHA_FLAG);
|
||||
if (!alpha && webp_info->has_alpha_) {
|
||||
if (webp_info->chunk_counts[CHUNK_VP8X]) {
|
||||
const int iccp = !!(webp_info->feature_flags & ICCP_FLAG);
|
||||
const int exif = !!(webp_info->feature_flags & EXIF_FLAG);
|
||||
const int xmp = !!(webp_info->feature_flags & XMP_FLAG);
|
||||
const int animation = !!(webp_info->feature_flags & ANIMATION_FLAG);
|
||||
const int alpha = !!(webp_info->feature_flags & ALPHA_FLAG);
|
||||
if (!alpha && webp_info->has_alpha) {
|
||||
LOG_ERROR("Unexpected alpha data detected.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (alpha && !webp_info->has_alpha_) {
|
||||
if (alpha && !webp_info->has_alpha) {
|
||||
LOG_WARN("Alpha flag is set with no alpha data present.");
|
||||
}
|
||||
if (iccp && !webp_info->chunk_counts_[CHUNK_ICCP]) {
|
||||
if (iccp && !webp_info->chunk_counts[CHUNK_ICCP]) {
|
||||
LOG_ERROR("Missing ICCP chunk.");
|
||||
return WEBP_INFO_MISSING_DATA;
|
||||
}
|
||||
if (exif && !webp_info->chunk_counts_[CHUNK_EXIF]) {
|
||||
if (exif && !webp_info->chunk_counts[CHUNK_EXIF]) {
|
||||
LOG_ERROR("Missing EXIF chunk.");
|
||||
return WEBP_INFO_MISSING_DATA;
|
||||
}
|
||||
if (xmp && !webp_info->chunk_counts_[CHUNK_XMP]) {
|
||||
if (xmp && !webp_info->chunk_counts[CHUNK_XMP]) {
|
||||
LOG_ERROR("Missing XMP chunk.");
|
||||
return WEBP_INFO_MISSING_DATA;
|
||||
}
|
||||
if (!iccp && webp_info->chunk_counts_[CHUNK_ICCP]) {
|
||||
if (!iccp && webp_info->chunk_counts[CHUNK_ICCP]) {
|
||||
LOG_ERROR("Unexpected ICCP chunk detected.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (!exif && webp_info->chunk_counts_[CHUNK_EXIF]) {
|
||||
if (!exif && webp_info->chunk_counts[CHUNK_EXIF]) {
|
||||
LOG_ERROR("Unexpected EXIF chunk detected.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (!xmp && webp_info->chunk_counts_[CHUNK_XMP]) {
|
||||
if (!xmp && webp_info->chunk_counts[CHUNK_XMP]) {
|
||||
LOG_ERROR("Unexpected XMP chunk detected.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
// Incomplete animation frame.
|
||||
if (webp_info->is_processing_anim_frame_) return WEBP_INFO_MISSING_DATA;
|
||||
if (!animation && webp_info->num_frames_ > 1) {
|
||||
if (webp_info->is_processing_anim_frame) return WEBP_INFO_MISSING_DATA;
|
||||
if (!animation && webp_info->num_frames > 1) {
|
||||
LOG_ERROR("More than 1 frame detected in non-animation file.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
if (animation && (!webp_info->chunk_counts_[CHUNK_ANIM] ||
|
||||
!webp_info->chunk_counts_[CHUNK_ANMF])) {
|
||||
if (animation && (!webp_info->chunk_counts[CHUNK_ANIM] ||
|
||||
!webp_info->chunk_counts[CHUNK_ANMF])) {
|
||||
LOG_ERROR("No ANIM/ANMF chunk detected in animation file.");
|
||||
return WEBP_INFO_PARSE_ERROR;
|
||||
}
|
||||
@@ -1057,17 +1047,17 @@ static WebPInfoStatus Validate(WebPInfo* const webp_info) {
|
||||
static void ShowSummary(const WebPInfo* const webp_info) {
|
||||
int i;
|
||||
printf("Summary:\n");
|
||||
printf("Number of frames: %d\n", webp_info->num_frames_);
|
||||
printf("Chunk type : VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP "
|
||||
printf("Number of frames: %d\n", webp_info->num_frames);
|
||||
printf(
|
||||
"Chunk type : VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP "
|
||||
"EXIF XMP\n");
|
||||
printf("Chunk counts: ");
|
||||
for (i = 0; i < CHUNK_TYPES; ++i) {
|
||||
printf("%4d ", webp_info->chunk_counts_[i]);
|
||||
printf("%4d ", webp_info->chunk_counts[i]);
|
||||
if (i == CHUNK_ANMF) {
|
||||
printf("%4d %4d %4d ",
|
||||
webp_info->anmf_subchunk_counts_[0],
|
||||
webp_info->anmf_subchunk_counts_[1],
|
||||
webp_info->anmf_subchunk_counts_[2]);
|
||||
printf("%4d %4d %4d ", webp_info->anmf_subchunk_counts[0],
|
||||
webp_info->anmf_subchunk_counts[1],
|
||||
webp_info->anmf_subchunk_counts[2]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
@@ -1090,35 +1080,36 @@ static WebPInfoStatus AnalyzeWebP(WebPInfo* const webp_info,
|
||||
webp_info_status = ProcessChunk(&chunk_data, webp_info);
|
||||
}
|
||||
if (webp_info_status != WEBP_INFO_OK) goto Error;
|
||||
if (webp_info->show_summary_) ShowSummary(webp_info);
|
||||
if (webp_info->show_summary) ShowSummary(webp_info);
|
||||
|
||||
// Final check.
|
||||
webp_info_status = Validate(webp_info);
|
||||
|
||||
Error:
|
||||
if (!webp_info->quiet_) {
|
||||
Error:
|
||||
if (!webp_info->quiet) {
|
||||
if (webp_info_status == WEBP_INFO_OK) {
|
||||
printf("No error detected.\n");
|
||||
} else {
|
||||
printf("Errors detected.\n");
|
||||
}
|
||||
if (webp_info->num_warnings_ > 0) {
|
||||
printf("There were %d warning(s).\n", webp_info->num_warnings_);
|
||||
if (webp_info->num_warnings > 0) {
|
||||
printf("There were %d warning(s).\n", webp_info->num_warnings);
|
||||
}
|
||||
}
|
||||
return webp_info_status;
|
||||
}
|
||||
|
||||
static void Help(void) {
|
||||
printf("Usage: webpinfo [options] in_files\n"
|
||||
"Note: there could be multiple input files;\n"
|
||||
" options must come before input files.\n"
|
||||
"Options:\n"
|
||||
" -version ........... Print version number and exit.\n"
|
||||
" -quiet ............. Do not show chunk parsing information.\n"
|
||||
" -diag .............. Show parsing error diagnosis.\n"
|
||||
" -summary ........... Show chunk stats summary.\n"
|
||||
" -bitstream_info .... Parse bitstream header.\n");
|
||||
printf(
|
||||
"Usage: webpinfo [options] in_files\n"
|
||||
"Note: there could be multiple input files;\n"
|
||||
" options must come before input files.\n"
|
||||
"Options:\n"
|
||||
" -version ........... Print version number and exit.\n"
|
||||
" -quiet ............. Do not show chunk parsing information.\n"
|
||||
" -diag .............. Show parsing error diagnosis.\n"
|
||||
" -summary ........... Show chunk stats summary.\n"
|
||||
" -bitstream_info .... Parse bitstream header.\n");
|
||||
}
|
||||
|
||||
// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
|
||||
@@ -1132,7 +1123,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (argc == 1) {
|
||||
Help();
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
FREE_WARGV_AND_RETURN(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Parse command-line input.
|
||||
@@ -1151,8 +1142,8 @@ int main(int argc, const char* argv[]) {
|
||||
parse_bitstream = 1;
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
const int version = WebPGetDecoderVersion();
|
||||
printf("WebP Decoder version: %d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
printf("WebP Decoder version: %d.%d.%d\n", (version >> 16) & 0xff,
|
||||
(version >> 8) & 0xff, version & 0xff);
|
||||
FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
|
||||
} else { // Assume the remaining are all input files.
|
||||
break;
|
||||
@@ -1169,10 +1160,10 @@ int main(int argc, const char* argv[]) {
|
||||
WebPData webp_data;
|
||||
const W_CHAR* in_file = NULL;
|
||||
WebPInfoInit(&webp_info);
|
||||
webp_info.quiet_ = quiet;
|
||||
webp_info.show_diagnosis_ = show_diag;
|
||||
webp_info.show_summary_ = show_summary;
|
||||
webp_info.parse_bitstream_ = parse_bitstream;
|
||||
webp_info.quiet = quiet;
|
||||
webp_info.show_diagnosis = show_diag;
|
||||
webp_info.show_summary = show_summary;
|
||||
webp_info.parse_bitstream = parse_bitstream;
|
||||
in_file = GET_WARGV(argv, c);
|
||||
if (in_file == NULL ||
|
||||
!ReadFileToWebPData((const char*)in_file, &webp_data)) {
|
||||
@@ -1180,7 +1171,7 @@ int main(int argc, const char* argv[]) {
|
||||
WFPRINTF(stderr, "Failed to open input file %s.\n", in_file);
|
||||
continue;
|
||||
}
|
||||
if (!webp_info.quiet_) WPRINTF("File: %s\n", in_file);
|
||||
if (!webp_info.quiet) WPRINTF("File: %s\n", in_file);
|
||||
webp_info_status = AnalyzeWebP(&webp_info, &webp_data);
|
||||
WebPDataClear(&webp_data);
|
||||
}
|
||||
|
||||
@@ -60,11 +60,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/decode.h"
|
||||
#include "webp/mux.h"
|
||||
#include "../examples/example_util.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/mux.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Config object to parse command-line arguments.
|
||||
@@ -87,9 +89,9 @@ typedef enum {
|
||||
} FeatureSubType;
|
||||
|
||||
typedef struct {
|
||||
FeatureSubType subtype_;
|
||||
const char* filename_;
|
||||
const char* params_;
|
||||
FeatureSubType subtype;
|
||||
const char* filename;
|
||||
const char* params;
|
||||
} FeatureArg;
|
||||
|
||||
typedef enum {
|
||||
@@ -104,24 +106,21 @@ typedef enum {
|
||||
LAST_FEATURE
|
||||
} FeatureType;
|
||||
|
||||
static const char* const kFourccList[LAST_FEATURE] = {
|
||||
NULL, "EXIF", "XMP ", "ICCP", "ANMF"
|
||||
};
|
||||
static const char* const kFourccList[LAST_FEATURE] = {NULL, "EXIF", "XMP ",
|
||||
"ICCP", "ANMF"};
|
||||
|
||||
static const char* const kDescriptions[LAST_FEATURE] = {
|
||||
NULL, "EXIF metadata", "XMP metadata", "ICC profile",
|
||||
"Animation frame"
|
||||
};
|
||||
NULL, "EXIF metadata", "XMP metadata", "ICC profile", "Animation frame"};
|
||||
|
||||
typedef struct {
|
||||
CommandLineArguments cmd_args_;
|
||||
CommandLineArguments cmd_args;
|
||||
|
||||
ActionType action_type_;
|
||||
const char* input_;
|
||||
const char* output_;
|
||||
FeatureType type_;
|
||||
FeatureArg* args_;
|
||||
int arg_count_;
|
||||
ActionType action_type;
|
||||
const char* input;
|
||||
const char* output;
|
||||
FeatureType type;
|
||||
FeatureArg* args;
|
||||
int arg_count;
|
||||
} Config;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -132,8 +131,8 @@ static int CountOccurrences(const CommandLineArguments* const args,
|
||||
int i;
|
||||
int num_occurences = 0;
|
||||
|
||||
for (i = 0; i < args->argc_; ++i) {
|
||||
if (!strcmp(args->argv_[i], arg)) {
|
||||
for (i = 0; i < args->argc; ++i) {
|
||||
if (!strcmp(args->argv[i], arg)) {
|
||||
++num_occurences;
|
||||
}
|
||||
}
|
||||
@@ -141,50 +140,49 @@ static int CountOccurrences(const CommandLineArguments* const args,
|
||||
}
|
||||
|
||||
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
|
||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
|
||||
};
|
||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||
"WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"};
|
||||
|
||||
static const char* ErrorString(WebPMuxError err) {
|
||||
assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
|
||||
return kErrorMessages[-err];
|
||||
}
|
||||
|
||||
#define RETURN_IF_ERROR(ERR_MSG) \
|
||||
do { \
|
||||
if (err != WEBP_MUX_OK) { \
|
||||
fprintf(stderr, ERR_MSG); \
|
||||
return err; \
|
||||
} \
|
||||
#define RETURN_IF_ERROR(ERR_MSG) \
|
||||
do { \
|
||||
if (err != WEBP_MUX_OK) { \
|
||||
fprintf(stderr, ERR_MSG); \
|
||||
return err; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
|
||||
do { \
|
||||
if (err != WEBP_MUX_OK) { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||
return err; \
|
||||
} \
|
||||
#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
|
||||
do { \
|
||||
if (err != WEBP_MUX_OK) { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||
return err; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ERROR_GOTO1(ERR_MSG, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
#define ERROR_GOTO1(ERR_MSG, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
} while (0)
|
||||
|
||||
#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
} while (0)
|
||||
|
||||
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
|
||||
do { \
|
||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||
ok = 0; \
|
||||
goto LABEL; \
|
||||
} while (0)
|
||||
|
||||
static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||
@@ -206,10 +204,10 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||
// Print the features present.
|
||||
printf("Features present:");
|
||||
if (flag & ANIMATION_FLAG) printf(" animation");
|
||||
if (flag & ICCP_FLAG) printf(" ICC profile");
|
||||
if (flag & EXIF_FLAG) printf(" EXIF metadata");
|
||||
if (flag & XMP_FLAG) printf(" XMP metadata");
|
||||
if (flag & ALPHA_FLAG) printf(" transparency");
|
||||
if (flag & ICCP_FLAG) printf(" ICC profile");
|
||||
if (flag & EXIF_FLAG) printf(" EXIF metadata");
|
||||
if (flag & XMP_FLAG) printf(" XMP metadata");
|
||||
if (flag & ALPHA_FLAG) printf(" transparency");
|
||||
printf("\n");
|
||||
|
||||
if (flag & ANIMATION_FLAG) {
|
||||
@@ -220,8 +218,8 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||
WebPMuxAnimParams params;
|
||||
err = WebPMuxGetAnimationParams(mux, ¶ms);
|
||||
assert(err == WEBP_MUX_OK);
|
||||
printf("Background color : 0x%.8X Loop Count : %d\n",
|
||||
params.bgcolor, params.loop_count);
|
||||
printf("Background color : 0x%.8X Loop Count : %d\n", params.bgcolor,
|
||||
params.loop_count);
|
||||
|
||||
err = WebPMuxNumChunks(mux, id, &nFrames);
|
||||
assert(err == WEBP_MUX_OK);
|
||||
@@ -253,9 +251,9 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||
printf("%8d %10s %5s ", frame.duration, dispose, blend);
|
||||
}
|
||||
printf("%10d %11s\n", (int)frame.bitstream.size,
|
||||
(features.format == 1) ? "lossy" :
|
||||
(features.format == 2) ? "lossless" :
|
||||
"undefined");
|
||||
(features.format == 1) ? "lossy"
|
||||
: (features.format == 2) ? "lossless"
|
||||
: "undefined");
|
||||
}
|
||||
WebPDataClear(&frame.bitstream);
|
||||
RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
|
||||
@@ -303,8 +301,9 @@ static void PrintHelp(void) {
|
||||
printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n");
|
||||
printf(" INPUT -o OUTPUT\n");
|
||||
printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
|
||||
printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
|
||||
"\n");
|
||||
printf(
|
||||
" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
|
||||
"\n");
|
||||
printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
|
||||
printf(" webpmux -info INPUT\n");
|
||||
printf(" webpmux [-h|-help]\n");
|
||||
@@ -358,8 +357,9 @@ static void PrintHelp(void) {
|
||||
printf(" 'di' is the pause duration before next frame,\n");
|
||||
printf(" 'xi','yi' specify the image offset for this frame,\n");
|
||||
printf(" 'mi' is the dispose method for this frame (0 or 1),\n");
|
||||
printf(" 'bi' is the blending method for this frame (+b or -b)"
|
||||
"\n");
|
||||
printf(
|
||||
" 'bi' is the blending method for this frame (+b or -b)"
|
||||
"\n");
|
||||
|
||||
printf("\n");
|
||||
printf("LOOP_COUNT:\n");
|
||||
@@ -370,27 +370,33 @@ static void PrintHelp(void) {
|
||||
printf("BACKGROUND_COLOR:\n");
|
||||
printf(" Background color of the canvas.\n");
|
||||
printf(" A,R,G,B\n");
|
||||
printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
|
||||
"specifying\n");
|
||||
printf(" the Alpha, Red, Green and Blue component values "
|
||||
"respectively\n");
|
||||
printf(
|
||||
" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
|
||||
"specifying\n");
|
||||
printf(
|
||||
" the Alpha, Red, Green and Blue component values "
|
||||
"respectively\n");
|
||||
printf(" [Default: 255,255,255,255]\n");
|
||||
|
||||
printf("\nINPUT & OUTPUT are in WebP format.\n");
|
||||
|
||||
printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
|
||||
printf(" and is assumed to be\nvalid.\n");
|
||||
printf("\nNote: if a single file name is passed as the argument, the "
|
||||
"arguments will be\n");
|
||||
printf("tokenized from this file. The file name must not start with "
|
||||
"the character '-'.\n");
|
||||
printf(
|
||||
"\nNote: if a single file name is passed as the argument, the "
|
||||
"arguments will be\n");
|
||||
printf(
|
||||
"tokenized from this file. The file name must not start with "
|
||||
"the character '-'.\n");
|
||||
}
|
||||
|
||||
static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
|
||||
if ((info->x_offset | info->y_offset) & 1) {
|
||||
fprintf(stderr, "Warning: odd offsets will be snapped to even values"
|
||||
" (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
|
||||
info->x_offset & ~1, info->y_offset & ~1);
|
||||
fprintf(stderr,
|
||||
"Warning: odd offsets will be snapped to even values"
|
||||
" (%d, %d) -> (%d, %d)\n",
|
||||
info->x_offset, info->y_offset, info->x_offset & ~1,
|
||||
info->y_offset & ~1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,8 +424,8 @@ static int WriteData(const char* filename, const WebPData* const webpdata) {
|
||||
if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
|
||||
WFPRINTF(stderr, "Error writing file %s!\n", (const W_CHAR*)filename);
|
||||
} else {
|
||||
WFPRINTF(stderr, "Saved file %s (%d bytes)\n",
|
||||
(const W_CHAR*)filename, (int)webpdata->size);
|
||||
WFPRINTF(stderr, "Saved file %s (%d bytes)\n", (const W_CHAR*)filename,
|
||||
(int)webpdata->size);
|
||||
ok = 1;
|
||||
}
|
||||
if (fout != stdout) fclose(fout);
|
||||
@@ -452,8 +458,8 @@ static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
|
||||
if (err == WEBP_MUX_OK) {
|
||||
err = WebPMuxSetAnimationParams(new_mux, &p);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO2("Error (%s) handling animation params.\n",
|
||||
ErrorString(err), End);
|
||||
ERROR_GOTO2("Error (%s) handling animation params.\n", ErrorString(err),
|
||||
End);
|
||||
}
|
||||
} else {
|
||||
/* it might not be an animation. Just keep moving. */
|
||||
@@ -471,7 +477,7 @@ static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
|
||||
}
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
if (!ok) {
|
||||
WebPMuxDelete(new_mux);
|
||||
new_mux = NULL;
|
||||
@@ -509,8 +515,7 @@ static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
|
||||
|
||||
if (blend_method != 'b') return 0;
|
||||
if (plus_minus != '-' && plus_minus != '+') return 0;
|
||||
info->blend_method =
|
||||
(plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
|
||||
info->blend_method = (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -527,8 +532,8 @@ static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
|
||||
|
||||
static void DeleteConfig(Config* const config) {
|
||||
if (config != NULL) {
|
||||
free(config->args_);
|
||||
ExUtilDeleteCommandLineArguments(&config->cmd_args_);
|
||||
free(config->args);
|
||||
ExUtilDeleteCommandLineArguments(&config->cmd_args);
|
||||
memset(config, 0, sizeof(*config));
|
||||
}
|
||||
}
|
||||
@@ -582,8 +587,10 @@ static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
|
||||
}
|
||||
|
||||
if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
|
||||
ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
|
||||
"case of animation.\n", ErrValidate);
|
||||
ERROR_GOTO1(
|
||||
"ERROR: Loop count and background color are relevant only in "
|
||||
"case of animation.\n",
|
||||
ErrValidate);
|
||||
}
|
||||
if (num_durations_args > 0 && num_frame_args != 0) {
|
||||
ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
|
||||
@@ -601,20 +608,20 @@ static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
|
||||
*num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
|
||||
}
|
||||
|
||||
ErrValidate:
|
||||
ErrValidate:
|
||||
return ok;
|
||||
}
|
||||
|
||||
#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
|
||||
#define ACTION_IS_NIL (config->action_type == NIL_ACTION)
|
||||
|
||||
#define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE)
|
||||
#define FEATURETYPE_IS_NIL (config->type == NIL_FEATURE)
|
||||
|
||||
#define CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL) \
|
||||
do { \
|
||||
if (argc < i + (NUM)) { \
|
||||
fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
|
||||
goto LABEL; \
|
||||
} \
|
||||
#define CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL) \
|
||||
do { \
|
||||
if (argc < i + (NUM)) { \
|
||||
fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
|
||||
goto LABEL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_NUM_ARGS_AT_MOST(NUM, LABEL) \
|
||||
@@ -625,10 +632,10 @@ static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \
|
||||
do { \
|
||||
CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \
|
||||
CHECK_NUM_ARGS_AT_MOST(NUM, LABEL); \
|
||||
#define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \
|
||||
do { \
|
||||
CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \
|
||||
CHECK_NUM_ARGS_AT_MOST(NUM, LABEL); \
|
||||
} while (0)
|
||||
|
||||
// Parses command-line arguments to fill up config object. Also performs some
|
||||
@@ -637,117 +644,116 @@ static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
|
||||
int i = 0;
|
||||
int feature_arg_index = 0;
|
||||
int ok = 1;
|
||||
int argc = config->cmd_args_.argc_;
|
||||
const char* const* argv = config->cmd_args_.argv_;
|
||||
int argc = config->cmd_args.argc;
|
||||
const char* const* argv = config->cmd_args.argv;
|
||||
// Unicode file paths will be used if available.
|
||||
const char* const* wargv =
|
||||
(unicode_argv != NULL) ? (const char**)(unicode_argv + 1) : argv;
|
||||
|
||||
while (i < argc) {
|
||||
FeatureArg* const arg = &config->args_[feature_arg_index];
|
||||
FeatureArg* const arg = &config->args[feature_arg_index];
|
||||
if (argv[i][0] == '-') { // One of the action types or output.
|
||||
if (!strcmp(argv[i], "-set")) {
|
||||
if (ACTION_IS_NIL) {
|
||||
config->action_type_ = ACTION_SET;
|
||||
config->action_type = ACTION_SET;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||
}
|
||||
++i;
|
||||
} else if (!strcmp(argv[i], "-duration")) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
|
||||
config->action_type_ = ACTION_DURATION;
|
||||
if (ACTION_IS_NIL || config->action_type == ACTION_DURATION) {
|
||||
config->action_type = ACTION_DURATION;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||
}
|
||||
if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) {
|
||||
config->type_ = FEATURE_DURATION;
|
||||
if (FEATURETYPE_IS_NIL || config->type == FEATURE_DURATION) {
|
||||
config->type = FEATURE_DURATION;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||
}
|
||||
arg->params_ = argv[i + 1];
|
||||
arg->params = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "-get")) {
|
||||
if (ACTION_IS_NIL) {
|
||||
config->action_type_ = ACTION_GET;
|
||||
config->action_type = ACTION_GET;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||
}
|
||||
++i;
|
||||
} else if (!strcmp(argv[i], "-strip")) {
|
||||
if (ACTION_IS_NIL) {
|
||||
config->action_type_ = ACTION_STRIP;
|
||||
config->arg_count_ = 0;
|
||||
config->action_type = ACTION_STRIP;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||
}
|
||||
++i;
|
||||
} else if (!strcmp(argv[i], "-frame")) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(3, ErrParse);
|
||||
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
|
||||
config->action_type_ = ACTION_SET;
|
||||
if (ACTION_IS_NIL || config->action_type == ACTION_SET) {
|
||||
config->action_type = ACTION_SET;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||
}
|
||||
if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
|
||||
config->type_ = FEATURE_ANMF;
|
||||
if (FEATURETYPE_IS_NIL || config->type == FEATURE_ANMF) {
|
||||
config->type = FEATURE_ANMF;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||
}
|
||||
arg->subtype_ = SUBTYPE_ANMF;
|
||||
arg->filename_ = wargv[i + 1];
|
||||
arg->params_ = argv[i + 2];
|
||||
arg->subtype = SUBTYPE_ANMF;
|
||||
arg->filename = wargv[i + 1];
|
||||
arg->params = argv[i + 2];
|
||||
++feature_arg_index;
|
||||
i += 3;
|
||||
} else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
|
||||
config->action_type_ = ACTION_SET;
|
||||
if (ACTION_IS_NIL || config->action_type == ACTION_SET) {
|
||||
config->action_type = ACTION_SET;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||
}
|
||||
if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
|
||||
config->type_ = FEATURE_ANMF;
|
||||
if (FEATURETYPE_IS_NIL || config->type == FEATURE_ANMF) {
|
||||
config->type = FEATURE_ANMF;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||
}
|
||||
arg->subtype_ =
|
||||
arg->subtype =
|
||||
!strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
|
||||
arg->params_ = argv[i + 1];
|
||||
arg->params = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "-o")) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->output_ = wargv[i + 1];
|
||||
config->output = wargv[i + 1];
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "-info")) {
|
||||
CHECK_NUM_ARGS_EXACTLY(2, ErrParse);
|
||||
if (config->action_type_ != NIL_ACTION) {
|
||||
if (config->action_type != NIL_ACTION) {
|
||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||
} else {
|
||||
config->action_type_ = ACTION_INFO;
|
||||
config->arg_count_ = 0;
|
||||
config->input_ = wargv[i + 1];
|
||||
config->action_type = ACTION_INFO;
|
||||
config->arg_count = 0;
|
||||
config->input = wargv[i + 1];
|
||||
}
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
|
||||
PrintHelp();
|
||||
DeleteConfig(config);
|
||||
LOCAL_FREE((W_CHAR** const)unicode_argv);
|
||||
LOCAL_FREE((W_CHAR**)unicode_argv);
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[i], "-version")) {
|
||||
const int version = WebPGetMuxVersion();
|
||||
printf("%d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
printf("%d.%d.%d\n", (version >> 16) & 0xff, (version >> 8) & 0xff,
|
||||
version & 0xff);
|
||||
DeleteConfig(config);
|
||||
LOCAL_FREE((W_CHAR** const)unicode_argv);
|
||||
LOCAL_FREE((W_CHAR**)unicode_argv);
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
if (i < argc - 1) {
|
||||
++i;
|
||||
if (config->input_ == NULL) {
|
||||
config->input_ = wargv[i];
|
||||
if (config->input == NULL) {
|
||||
config->input = wargv[i];
|
||||
} else {
|
||||
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
|
||||
argv[i], ErrParse);
|
||||
@@ -758,50 +764,66 @@ static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
|
||||
ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
|
||||
}
|
||||
} else { // One of the feature types or input.
|
||||
// After consuming the arguments to -get/-set/-strip, treat any remaining
|
||||
// arguments as input. This allows files that are named the same as the
|
||||
// keywords used with these options.
|
||||
int is_input = feature_arg_index == config->arg_count;
|
||||
if (ACTION_IS_NIL) {
|
||||
ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
|
||||
ErrParse);
|
||||
}
|
||||
if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
|
||||
!strcmp(argv[i], "xmp")) {
|
||||
if (FEATURETYPE_IS_NIL) {
|
||||
config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
|
||||
(!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||
}
|
||||
if (config->action_type_ == ACTION_SET) {
|
||||
if (!is_input) {
|
||||
if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
|
||||
!strcmp(argv[i], "xmp")) {
|
||||
if (FEATURETYPE_IS_NIL) {
|
||||
config->type = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP
|
||||
: (!strcmp(argv[i], "exif")) ? FEATURE_EXIF
|
||||
: FEATURE_XMP;
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||
}
|
||||
if (config->action_type == ACTION_SET) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
arg->filename = wargv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else {
|
||||
// Note: 'arg->params' is not used in this case. 'arg_count' is
|
||||
// used as a flag to indicate the -get/-strip feature has already
|
||||
// been consumed, allowing input types to be named the same as the
|
||||
// feature type.
|
||||
config->arg_count = 0;
|
||||
++i;
|
||||
}
|
||||
} else if (!strcmp(argv[i], "frame") &&
|
||||
(config->action_type == ACTION_GET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
arg->filename_ = wargv[i + 1];
|
||||
config->type = FEATURE_ANMF;
|
||||
arg->params = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "loop") &&
|
||||
(config->action_type == ACTION_SET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->type = FEATURE_LOOP;
|
||||
arg->params = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "bgcolor") &&
|
||||
(config->action_type == ACTION_SET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->type = FEATURE_BGCOLOR;
|
||||
arg->params = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else {
|
||||
++i;
|
||||
is_input = 1;
|
||||
}
|
||||
} else if (!strcmp(argv[i], "frame") &&
|
||||
(config->action_type_ == ACTION_GET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->type_ = FEATURE_ANMF;
|
||||
arg->params_ = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "loop") &&
|
||||
(config->action_type_ == ACTION_SET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->type_ = FEATURE_LOOP;
|
||||
arg->params_ = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "bgcolor") &&
|
||||
(config->action_type_ == ACTION_SET)) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->type_ = FEATURE_BGCOLOR;
|
||||
arg->params_ = argv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else { // Assume input file.
|
||||
if (config->input_ == NULL) {
|
||||
config->input_ = wargv[i];
|
||||
}
|
||||
|
||||
if (is_input) {
|
||||
if (config->input == NULL) {
|
||||
config->input = wargv[i];
|
||||
} else {
|
||||
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
|
||||
argv[i], ErrParse);
|
||||
@@ -810,7 +832,7 @@ static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
|
||||
}
|
||||
}
|
||||
}
|
||||
ErrParse:
|
||||
ErrParse:
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -824,25 +846,25 @@ static int ValidateConfig(Config* const config) {
|
||||
}
|
||||
|
||||
// Feature type.
|
||||
if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
|
||||
if (FEATURETYPE_IS_NIL && config->action_type != ACTION_INFO) {
|
||||
ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
|
||||
}
|
||||
|
||||
// Input file.
|
||||
if (config->input_ == NULL) {
|
||||
if (config->action_type_ != ACTION_SET) {
|
||||
if (config->input == NULL) {
|
||||
if (config->action_type != ACTION_SET) {
|
||||
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
||||
} else if (config->type_ != FEATURE_ANMF) {
|
||||
} else if (config->type != FEATURE_ANMF) {
|
||||
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
||||
}
|
||||
}
|
||||
|
||||
// Output file.
|
||||
if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
|
||||
if (config->output == NULL && config->action_type != ACTION_INFO) {
|
||||
ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
|
||||
}
|
||||
|
||||
ErrValidate2:
|
||||
ErrValidate2:
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -854,17 +876,17 @@ static int InitializeConfig(int argc, const char* argv[], Config* const config,
|
||||
|
||||
memset(config, 0, sizeof(*config));
|
||||
|
||||
ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_);
|
||||
ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args);
|
||||
if (!ok) return 0;
|
||||
|
||||
// Validate command-line arguments.
|
||||
if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) {
|
||||
if (!ValidateCommandLine(&config->cmd_args, &num_feature_args)) {
|
||||
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
||||
}
|
||||
|
||||
config->arg_count_ = num_feature_args;
|
||||
config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_));
|
||||
if (config->args_ == NULL) {
|
||||
config->arg_count = num_feature_args;
|
||||
config->args = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args));
|
||||
if (config->args == NULL) {
|
||||
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
|
||||
}
|
||||
|
||||
@@ -873,7 +895,7 @@ static int InitializeConfig(int argc, const char* argv[], Config* const config,
|
||||
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
||||
}
|
||||
|
||||
Err1:
|
||||
Err1:
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -896,7 +918,7 @@ static int GetFrame(const WebPMux* mux, const Config* config) {
|
||||
WebPMuxFrameInfo info;
|
||||
WebPDataInit(&info.bitstream);
|
||||
|
||||
num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
|
||||
num = ExUtilGetInt(config->args[0].params, 10, &parse_error);
|
||||
if (num < 0) {
|
||||
ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
|
||||
}
|
||||
@@ -905,8 +927,8 @@ static int GetFrame(const WebPMux* mux, const Config* config) {
|
||||
err = WebPMuxGetFrame(mux, num, &info);
|
||||
if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n",
|
||||
ErrorString(err), num, ErrGet);
|
||||
ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n", ErrorString(err), num,
|
||||
ErrGet);
|
||||
}
|
||||
|
||||
mux_single = WebPMuxNew();
|
||||
@@ -921,9 +943,9 @@ static int GetFrame(const WebPMux* mux, const Config* config) {
|
||||
ErrorString(err), ErrGet);
|
||||
}
|
||||
|
||||
ok = WriteWebP(mux_single, config->output_);
|
||||
ok = WriteWebP(mux_single, config->output);
|
||||
|
||||
ErrGet:
|
||||
ErrGet:
|
||||
WebPDataClear(&info.bitstream);
|
||||
WebPMuxDelete(mux_single);
|
||||
return ok && !parse_error;
|
||||
@@ -936,11 +958,11 @@ static int Process(const Config* config) {
|
||||
WebPMuxError err = WEBP_MUX_OK;
|
||||
int ok = 1;
|
||||
|
||||
switch (config->action_type_) {
|
||||
switch (config->action_type) {
|
||||
case ACTION_GET: {
|
||||
ok = CreateMux(config->input_, &mux);
|
||||
ok = CreateMux(config->input, &mux);
|
||||
if (!ok) goto Err2;
|
||||
switch (config->type_) {
|
||||
switch (config->type) {
|
||||
case FEATURE_ANMF:
|
||||
ok = GetFrame(mux, config);
|
||||
break;
|
||||
@@ -948,12 +970,12 @@ static int Process(const Config* config) {
|
||||
case FEATURE_ICCP:
|
||||
case FEATURE_EXIF:
|
||||
case FEATURE_XMP:
|
||||
err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk);
|
||||
err = WebPMuxGetChunk(mux, kFourccList[config->type], &chunk);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
|
||||
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||
ERROR_GOTO3("ERROR (%s): Could not get the %s.\n", ErrorString(err),
|
||||
kDescriptions[config->type], Err2);
|
||||
}
|
||||
ok = WriteData(config->output_, &chunk);
|
||||
ok = WriteData(config->output, &chunk);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -963,20 +985,20 @@ static int Process(const Config* config) {
|
||||
break;
|
||||
}
|
||||
case ACTION_SET: {
|
||||
switch (config->type_) {
|
||||
switch (config->type) {
|
||||
case FEATURE_ANMF: {
|
||||
int i;
|
||||
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
|
||||
WebPMuxAnimParams params = {0xFFFFFFFF, 0};
|
||||
mux = WebPMuxNew();
|
||||
if (mux == NULL) {
|
||||
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
|
||||
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
|
||||
}
|
||||
for (i = 0; i < config->arg_count_; ++i) {
|
||||
switch (config->args_[i].subtype_) {
|
||||
for (i = 0; i < config->arg_count; ++i) {
|
||||
switch (config->args[i].subtype) {
|
||||
case SUBTYPE_BGCOLOR: {
|
||||
uint32_t bgcolor;
|
||||
ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor);
|
||||
ok = ParseBgcolorArgs(config->args[i].params, &bgcolor);
|
||||
if (!ok) {
|
||||
ERROR_GOTO1("ERROR: Could not parse the background color \n",
|
||||
Err2);
|
||||
@@ -987,13 +1009,15 @@ static int Process(const Config* config) {
|
||||
case SUBTYPE_LOOP: {
|
||||
int parse_error = 0;
|
||||
const int loop_count =
|
||||
ExUtilGetInt(config->args_[i].params_, 10, &parse_error);
|
||||
ExUtilGetInt(config->args[i].params, 10, &parse_error);
|
||||
if (loop_count < 0 || loop_count > 65535) {
|
||||
// Note: This is only a 'necessary' condition for loop_count
|
||||
// to be valid. The 'sufficient' conditioned in checked in
|
||||
// WebPMuxSetAnimationParams() method called later.
|
||||
ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
|
||||
"65535.\n", Err2);
|
||||
ERROR_GOTO1(
|
||||
"ERROR: Loop count must be in the range 0 to "
|
||||
"65535.\n",
|
||||
Err2);
|
||||
}
|
||||
ok = !parse_error;
|
||||
if (!ok) goto Err2;
|
||||
@@ -1003,10 +1027,10 @@ static int Process(const Config* config) {
|
||||
case SUBTYPE_ANMF: {
|
||||
WebPMuxFrameInfo frame;
|
||||
frame.id = WEBP_CHUNK_ANMF;
|
||||
ok = ExUtilReadFileToWebPData(config->args_[i].filename_,
|
||||
ok = ExUtilReadFileToWebPData(config->args[i].filename,
|
||||
&frame.bitstream);
|
||||
if (!ok) goto Err2;
|
||||
ok = ParseFrameArgs(config->args_[i].params_, &frame);
|
||||
ok = ParseFrameArgs(config->args[i].params, &frame);
|
||||
if (!ok) {
|
||||
WebPDataClear(&frame.bitstream);
|
||||
ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
|
||||
@@ -1015,8 +1039,10 @@ static int Process(const Config* config) {
|
||||
err = WebPMuxPushFrame(mux, &frame, 1);
|
||||
WebPDataClear(&frame.bitstream);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
|
||||
"\n", ErrorString(err), i, Err2);
|
||||
ERROR_GOTO3(
|
||||
"ERROR (%s): Could not add a frame at index %d."
|
||||
"\n",
|
||||
ErrorString(err), i, Err2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1037,28 +1063,28 @@ static int Process(const Config* config) {
|
||||
case FEATURE_ICCP:
|
||||
case FEATURE_EXIF:
|
||||
case FEATURE_XMP: {
|
||||
ok = CreateMux(config->input_, &mux);
|
||||
ok = CreateMux(config->input, &mux);
|
||||
if (!ok) goto Err2;
|
||||
ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk);
|
||||
ok = ExUtilReadFileToWebPData(config->args[0].filename, &chunk);
|
||||
if (!ok) goto Err2;
|
||||
err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1);
|
||||
err = WebPMuxSetChunk(mux, kFourccList[config->type], &chunk, 1);
|
||||
WebPDataClear(&chunk);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
|
||||
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||
ERROR_GOTO3("ERROR (%s): Could not set the %s.\n", ErrorString(err),
|
||||
kDescriptions[config->type], Err2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FEATURE_LOOP: {
|
||||
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
|
||||
WebPMuxAnimParams params = {0xFFFFFFFF, 0};
|
||||
int parse_error = 0;
|
||||
const int loop_count =
|
||||
ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
|
||||
ExUtilGetInt(config->args[0].params, 10, &parse_error);
|
||||
if (loop_count < 0 || loop_count > 65535 || parse_error) {
|
||||
ERROR_GOTO1("ERROR: Loop count must be in the range 0 to 65535.\n",
|
||||
Err2);
|
||||
}
|
||||
ok = CreateMux(config->input_, &mux);
|
||||
ok = CreateMux(config->input, &mux);
|
||||
if (!ok) goto Err2;
|
||||
ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK);
|
||||
if (!ok) {
|
||||
@@ -1075,14 +1101,13 @@ static int Process(const Config* config) {
|
||||
break;
|
||||
}
|
||||
case FEATURE_BGCOLOR: {
|
||||
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
|
||||
WebPMuxAnimParams params = {0xFFFFFFFF, 0};
|
||||
uint32_t bgcolor;
|
||||
ok = ParseBgcolorArgs(config->args_[0].params_, &bgcolor);
|
||||
ok = ParseBgcolorArgs(config->args[0].params, &bgcolor);
|
||||
if (!ok) {
|
||||
ERROR_GOTO1("ERROR: Could not parse the background color.\n",
|
||||
Err2);
|
||||
ERROR_GOTO1("ERROR: Could not parse the background color.\n", Err2);
|
||||
}
|
||||
ok = CreateMux(config->input_, &mux);
|
||||
ok = CreateMux(config->input, &mux);
|
||||
if (!ok) goto Err2;
|
||||
ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK);
|
||||
if (!ok) {
|
||||
@@ -1103,12 +1128,12 @@ static int Process(const Config* config) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok = WriteWebP(mux, config->output_);
|
||||
ok = WriteWebP(mux, config->output);
|
||||
break;
|
||||
}
|
||||
case ACTION_DURATION: {
|
||||
int num_frames;
|
||||
ok = CreateMux(config->input_, &mux);
|
||||
ok = CreateMux(config->input, &mux);
|
||||
if (!ok) goto Err2;
|
||||
err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
|
||||
ok = (err == WEBP_MUX_OK);
|
||||
@@ -1116,9 +1141,10 @@ static int Process(const Config* config) {
|
||||
ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
|
||||
}
|
||||
if (num_frames == 0) {
|
||||
fprintf(stderr, "Doesn't look like the source is animated. "
|
||||
"Skipping duration setting.\n");
|
||||
ok = WriteWebP(mux, config->output_);
|
||||
fprintf(stderr,
|
||||
"Doesn't look like the source is animated. "
|
||||
"Skipping duration setting.\n");
|
||||
ok = WriteWebP(mux, config->output);
|
||||
if (!ok) goto Err2;
|
||||
} else {
|
||||
int i;
|
||||
@@ -1130,12 +1156,12 @@ static int Process(const Config* config) {
|
||||
for (i = 0; i < num_frames; ++i) durations[i] = -1;
|
||||
|
||||
// Parse intervals to process.
|
||||
for (i = 0; i < config->arg_count_; ++i) {
|
||||
for (i = 0; i < config->arg_count; ++i) {
|
||||
int k;
|
||||
int args[3];
|
||||
int duration, start, end;
|
||||
const int nb_args = ExUtilGetInts(config->args_[i].params_,
|
||||
10, 3, args);
|
||||
const int nb_args =
|
||||
ExUtilGetInts(config->args[i].params, 10, 3, args);
|
||||
ok = (nb_args >= 1);
|
||||
if (!ok) goto Err3;
|
||||
duration = args[0];
|
||||
@@ -1143,7 +1169,7 @@ static int Process(const Config* config) {
|
||||
ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
|
||||
}
|
||||
|
||||
if (nb_args == 1) { // only duration is present -> use full interval
|
||||
if (nb_args == 1) { // only duration is present -> use full interval
|
||||
start = 1;
|
||||
end = num_frames;
|
||||
} else {
|
||||
@@ -1178,11 +1204,11 @@ static int Process(const Config* config) {
|
||||
WebPDataClear(&frame.bitstream);
|
||||
}
|
||||
WebPMuxDelete(mux);
|
||||
ok = WriteWebP(new_mux, config->output_);
|
||||
ok = WriteWebP(new_mux, config->output);
|
||||
mux = new_mux; // transfer for the WebPMuxDelete() call
|
||||
new_mux = NULL;
|
||||
|
||||
Err3:
|
||||
Err3:
|
||||
WebPFree(durations);
|
||||
WebPMuxDelete(new_mux);
|
||||
if (!ok) goto Err2;
|
||||
@@ -1190,24 +1216,24 @@ static int Process(const Config* config) {
|
||||
break;
|
||||
}
|
||||
case ACTION_STRIP: {
|
||||
ok = CreateMux(config->input_, &mux);
|
||||
ok = CreateMux(config->input, &mux);
|
||||
if (!ok) goto Err2;
|
||||
if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF ||
|
||||
config->type_ == FEATURE_XMP) {
|
||||
err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]);
|
||||
if (config->type == FEATURE_ICCP || config->type == FEATURE_EXIF ||
|
||||
config->type == FEATURE_XMP) {
|
||||
err = WebPMuxDeleteChunk(mux, kFourccList[config->type]);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
|
||||
ErrorString(err), kDescriptions[config->type_], Err2);
|
||||
ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n", ErrorString(err),
|
||||
kDescriptions[config->type], Err2);
|
||||
}
|
||||
} else {
|
||||
ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
|
||||
break;
|
||||
}
|
||||
ok = WriteWebP(mux, config->output_);
|
||||
ok = WriteWebP(mux, config->output);
|
||||
break;
|
||||
}
|
||||
case ACTION_INFO: {
|
||||
ok = CreateMux(config->input_, &mux);
|
||||
ok = CreateMux(config->input, &mux);
|
||||
if (!ok) goto Err2;
|
||||
ok = (DisplayInfo(mux) == WEBP_MUX_OK);
|
||||
break;
|
||||
@@ -1218,7 +1244,7 @@ static int Process(const Config* config) {
|
||||
}
|
||||
}
|
||||
|
||||
Err2:
|
||||
Err2:
|
||||
WebPMuxDelete(mux);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/encode.h"
|
||||
#include "webp/format_constants.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#define XTRA_MAJ_VERSION 1
|
||||
#define XTRA_MIN_VERSION 4
|
||||
#define XTRA_MIN_VERSION 6
|
||||
#define XTRA_REV_VERSION 0
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -44,7 +45,7 @@ int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) {
|
||||
uv_width = (width + 1) >> 1;
|
||||
for (y = 0; y < pic->height; ++y) {
|
||||
memcpy(pic->y + y * pic->y_stride, gray_data, width);
|
||||
gray_data += width; // <- we could use some 'data_stride' here if needed
|
||||
gray_data += width; // <- we could use some 'data_stride' here if needed
|
||||
if ((y & 1) == 0) {
|
||||
memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width);
|
||||
memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#ifndef WEBP_EXTRAS_EXTRAS_H_
|
||||
#define WEBP_EXTRAS_EXTRAS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -20,7 +22,7 @@ extern "C" {
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "webp/encode.h"
|
||||
|
||||
#define WEBP_EXTRAS_ABI_VERSION 0x0003 // MAJOR(8b) + MINOR(8b)
|
||||
#define WEBP_EXTRAS_ABI_VERSION 0x0003 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -47,10 +49,10 @@ WEBP_EXTERN int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic);
|
||||
// MAX_PALETTE_SIZE. 'pic' must have been initialized. Its content, if any,
|
||||
// will be discarded. Returns 'false' in case of error, or if indexed[] contains
|
||||
// invalid indices.
|
||||
WEBP_EXTERN int
|
||||
WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
|
||||
const uint32_t palette[], int palette_size,
|
||||
WebPPicture* pic);
|
||||
WEBP_EXTERN int WebPImportColorMappedARGB(const uint8_t* indexed,
|
||||
int indexed_stride,
|
||||
const uint32_t palette[],
|
||||
int palette_size, WebPPicture* pic);
|
||||
|
||||
// Convert the ARGB content of 'pic' from associated to unassociated.
|
||||
// 'pic' can be for instance the result of calling of some WebPPictureImportXXX
|
||||
@@ -96,15 +98,17 @@ WEBP_EXTERN int VP8EstimateQuality(const uint8_t* const data, size_t size);
|
||||
// currently supported.
|
||||
// width, height: width and height of the image in pixels
|
||||
// Returns 0 on failure.
|
||||
WEBP_EXTERN int SharpYuvEstimate420Risk(
|
||||
const void* r_ptr, const void* g_ptr, const void* b_ptr, int rgb_step,
|
||||
int rgb_stride, int rgb_bit_depth, int width, int height,
|
||||
const SharpYuvOptions* options, float* score);
|
||||
WEBP_EXTERN int SharpYuvEstimate420Risk(const void* r_ptr, const void* g_ptr,
|
||||
const void* b_ptr, int rgb_step,
|
||||
int rgb_stride, int rgb_bit_depth,
|
||||
int width, int height,
|
||||
const SharpYuvOptions* options,
|
||||
float* score);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_EXTRAS_EXTRAS_H_
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "imageio/image_dec.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "src/webp/types.h"
|
||||
#include "webp/encode.h"
|
||||
|
||||
static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
int keep_alpha) {
|
||||
@@ -47,7 +48,7 @@ static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
reader = WebPGuessImageReader(data, data_size);
|
||||
ok = reader(data, data_size, pic, keep_alpha, NULL);
|
||||
|
||||
End:
|
||||
End:
|
||||
if (!ok) {
|
||||
WFPRINTF(stderr, "Error! Could not process file %s\n",
|
||||
(const W_CHAR*)filename);
|
||||
@@ -56,8 +57,8 @@ static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
return ok ? data_size : 0;
|
||||
}
|
||||
|
||||
static void RescalePlane(uint8_t* plane, int width, int height,
|
||||
int x_stride, int y_stride, int max) {
|
||||
static void RescalePlane(uint8_t* plane, int width, int height, int x_stride,
|
||||
int y_stride, int max) {
|
||||
const uint32_t factor = (max > 0) ? (255u << 16) / max : 0;
|
||||
int x, y;
|
||||
for (y = 0; y < height; ++y) {
|
||||
@@ -70,9 +71,9 @@ static void RescalePlane(uint8_t* plane, int width, int height,
|
||||
}
|
||||
|
||||
// Return the max absolute difference.
|
||||
static int DiffScaleChannel(uint8_t* src1, int stride1,
|
||||
const uint8_t* src2, int stride2,
|
||||
int x_stride, int w, int h, int do_scaling) {
|
||||
static int DiffScaleChannel(uint8_t* src1, int stride1, const uint8_t* src2,
|
||||
int stride2, int x_stride, int w, int h,
|
||||
int do_scaling) {
|
||||
int x, y;
|
||||
int max = 0;
|
||||
for (y = 0; y < h; ++y) {
|
||||
@@ -94,7 +95,7 @@ static int DiffScaleChannel(uint8_t* src1, int stride1,
|
||||
// breaking the library's hidden visibility. This code duplication avoids the
|
||||
// bigger annoyance of having to open up internal details of libdsp...
|
||||
|
||||
#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1
|
||||
#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1
|
||||
|
||||
// struct for accumulating statistical moments
|
||||
typedef struct {
|
||||
@@ -104,19 +105,19 @@ typedef struct {
|
||||
} DistoStats;
|
||||
|
||||
// hat-shaped filter. Sum of coefficients is equal to 16.
|
||||
static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = { 1, 2, 3, 4, 3, 2, 1 };
|
||||
static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = {1, 2, 3, 4, 3, 2, 1};
|
||||
|
||||
static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) {
|
||||
const uint32_t N = stats->w;
|
||||
const uint32_t w2 = N * N;
|
||||
const uint32_t w2 = N * N;
|
||||
const uint32_t C1 = 20 * w2;
|
||||
const uint32_t C2 = 60 * w2;
|
||||
const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
|
||||
const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
|
||||
const uint64_t xmxm = (uint64_t)stats->xm * stats->xm;
|
||||
const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
|
||||
if (xmxm + ymym >= C3) {
|
||||
const int64_t xmym = (int64_t)stats->xm * stats->ym;
|
||||
const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
|
||||
const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
|
||||
const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
|
||||
const uint64_t syy = (uint64_t)stats->yym * N - ymym;
|
||||
// we descale by 8 to prevent overflow during the fnum/fden multiply.
|
||||
@@ -128,13 +129,13 @@ static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) {
|
||||
assert(r >= 0. && r <= 1.0);
|
||||
return r;
|
||||
}
|
||||
return 1.; // area is too dark to contribute meaningfully
|
||||
return 1.; // area is too dark to contribute meaningfully
|
||||
}
|
||||
|
||||
static double SSIMGetClipped(const uint8_t* src1, int stride1,
|
||||
const uint8_t* src2, int stride2,
|
||||
int xo, int yo, int W, int H) {
|
||||
DistoStats stats = { 0, 0, 0, 0, 0, 0 };
|
||||
const uint8_t* src2, int stride2, int xo, int yo,
|
||||
int W, int H) {
|
||||
DistoStats stats = {0, 0, 0, 0, 0, 0};
|
||||
const int ymin = (yo - SSIM_KERNEL < 0) ? 0 : yo - SSIM_KERNEL;
|
||||
const int ymax = (yo + SSIM_KERNEL > H - 1) ? H - 1 : yo + SSIM_KERNEL;
|
||||
const int xmin = (xo - SSIM_KERNEL < 0) ? 0 : xo - SSIM_KERNEL;
|
||||
@@ -144,13 +145,13 @@ static double SSIMGetClipped(const uint8_t* src1, int stride1,
|
||||
src2 += ymin * stride2;
|
||||
for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
|
||||
for (x = xmin; x <= xmax; ++x) {
|
||||
const uint32_t w = kWeight[SSIM_KERNEL + x - xo]
|
||||
* kWeight[SSIM_KERNEL + y - yo];
|
||||
const uint32_t w =
|
||||
kWeight[SSIM_KERNEL + x - xo] * kWeight[SSIM_KERNEL + y - yo];
|
||||
const uint32_t s1 = src1[x];
|
||||
const uint32_t s2 = src2[x];
|
||||
stats.w += w;
|
||||
stats.xm += w * s1;
|
||||
stats.ym += w * s2;
|
||||
stats.w += w;
|
||||
stats.xm += w * s1;
|
||||
stats.ym += w * s2;
|
||||
stats.xxm += w * s1 * s1;
|
||||
stats.xym += w * s1 * s2;
|
||||
stats.yym += w * s2 * s2;
|
||||
@@ -160,9 +161,9 @@ static double SSIMGetClipped(const uint8_t* src1, int stride1,
|
||||
}
|
||||
|
||||
// Compute SSIM-score map. Return -1 in case of error, max diff otherwise.
|
||||
static int SSIMScaleChannel(uint8_t* src1, int stride1,
|
||||
const uint8_t* src2, int stride2,
|
||||
int x_stride, int w, int h, int do_scaling) {
|
||||
static int SSIMScaleChannel(uint8_t* src1, int stride1, const uint8_t* src2,
|
||||
int stride2, int x_stride, int w, int h,
|
||||
int do_scaling) {
|
||||
int x, y;
|
||||
int max = 0;
|
||||
uint8_t* const plane1 = (uint8_t*)malloc(2 * w * h * sizeof(*plane1));
|
||||
@@ -204,8 +205,8 @@ static void ConvertToGray(WebPPicture* const pic) {
|
||||
for (x = 0; x < pic->width; ++x) {
|
||||
const uint32_t argb = row[x];
|
||||
const uint32_t r = (argb >> 16) & 0xff;
|
||||
const uint32_t g = (argb >> 8) & 0xff;
|
||||
const uint32_t b = (argb >> 0) & 0xff;
|
||||
const uint32_t g = (argb >> 8) & 0xff;
|
||||
const uint32_t b = (argb >> 0) & 0xff;
|
||||
// We use BT.709 for converting to luminance.
|
||||
const uint32_t Y = (uint32_t)(0.2126 * r + 0.7152 * g + 0.0722 * b + .5);
|
||||
row[x] = (argb & 0xff000000u) | (Y * 0x010101u);
|
||||
@@ -296,8 +297,7 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "Error while computing the distortion.\n");
|
||||
goto End;
|
||||
}
|
||||
printf("%u %.2f %.2f %.2f %.2f %.2f [ %.2f bpp ]\n",
|
||||
(unsigned int)size1,
|
||||
printf("%u %.2f %.2f %.2f %.2f %.2f [ %.2f bpp ]\n", (unsigned int)size1,
|
||||
disto[4], disto[0], disto[1], disto[2], disto[3],
|
||||
8.f * size1 / pic1.width / pic1.height);
|
||||
|
||||
@@ -305,21 +305,25 @@ int main(int argc, const char* argv[]) {
|
||||
uint8_t* data = NULL;
|
||||
size_t data_size = 0;
|
||||
if (pic1.use_argb != pic2.use_argb) {
|
||||
fprintf(stderr, "Pictures are not in the same argb format. "
|
||||
"Can't save the difference map.\n");
|
||||
fprintf(stderr,
|
||||
"Pictures are not in the same argb format. "
|
||||
"Can't save the difference map.\n");
|
||||
goto End;
|
||||
}
|
||||
if (pic1.use_argb) {
|
||||
int n;
|
||||
fprintf(stderr, "max differences per channel: ");
|
||||
for (n = 0; n < 3; ++n) { // skip the alpha channel
|
||||
const int range = (type == 1) ?
|
||||
SSIMScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
|
||||
4, pic1.width, pic1.height, scale) :
|
||||
DiffScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
|
||||
4, pic1.width, pic1.height, scale);
|
||||
for (n = 0; n < 3; ++n) { // skip the alpha channel
|
||||
const int range =
|
||||
(type == 1)
|
||||
? SSIMScaleChannel(
|
||||
(uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4, 4,
|
||||
pic1.width, pic1.height, scale)
|
||||
: DiffScaleChannel(
|
||||
(uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4, 4,
|
||||
pic1.width, pic1.height, scale);
|
||||
if (range < 0) fprintf(stderr, "\nError computing diff map\n");
|
||||
fprintf(stderr, "[%d]", range);
|
||||
}
|
||||
@@ -330,10 +334,9 @@ int main(int argc, const char* argv[]) {
|
||||
goto End;
|
||||
}
|
||||
#if !defined(WEBP_REDUCE_CSP)
|
||||
data_size = WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb,
|
||||
pic1.width, pic1.height,
|
||||
pic1.argb_stride * 4,
|
||||
&data);
|
||||
data_size =
|
||||
WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb, pic1.width,
|
||||
pic1.height, pic1.argb_stride * 4, &data);
|
||||
if (data_size == 0) {
|
||||
fprintf(stderr, "Error during lossless encoding.\n");
|
||||
goto End;
|
||||
@@ -345,14 +348,15 @@ int main(int argc, const char* argv[]) {
|
||||
#else
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
fprintf(stderr, "Cannot save the difference map. Please recompile "
|
||||
"without the WEBP_REDUCE_CSP flag.\n");
|
||||
fprintf(stderr,
|
||||
"Cannot save the difference map. Please recompile "
|
||||
"without the WEBP_REDUCE_CSP flag.\n");
|
||||
goto End;
|
||||
#endif // WEBP_REDUCE_CSP
|
||||
}
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
End:
|
||||
End:
|
||||
WebPPictureFree(&pic1);
|
||||
WebPPictureFree(&pic2);
|
||||
FREE_WARGV_AND_RETURN(ret);
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "extras/extras.h"
|
||||
#include "webp/decode.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "extras/extras.h"
|
||||
#include "src/webp/types.h"
|
||||
#include "webp/decode.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -52,10 +54,10 @@ int VP8EstimateQuality(const uint8_t* const data, size_t size) {
|
||||
if (data == NULL) return -1;
|
||||
|
||||
if (WebPGetFeatures(data, size, &features) != VP8_STATUS_OK) {
|
||||
return -1; // invalid file
|
||||
return -1; // invalid file
|
||||
}
|
||||
if (features.format == 2) return 101; // lossless
|
||||
if (features.format == 0 || features.has_animation) return -1; // mixed
|
||||
if (features.format == 2) return 101; // lossless
|
||||
if (features.format == 0 || features.has_animation) return -1; // mixed
|
||||
|
||||
while (pos < size) {
|
||||
sig = (sig >> 8) | ((uint64_t)data[pos++] << 40);
|
||||
@@ -76,29 +78,29 @@ int VP8EstimateQuality(const uint8_t* const data, size_t size) {
|
||||
GET_BIT(2); // colorspace + clamp type
|
||||
|
||||
// Segment header
|
||||
if (GET_BIT(1)) { // use_segment_
|
||||
if (GET_BIT(1)) { // use_segment
|
||||
int s;
|
||||
const int update_map = GET_BIT(1);
|
||||
if (GET_BIT(1)) { // update data
|
||||
if (GET_BIT(1)) { // update data
|
||||
const int absolute_delta = GET_BIT(1);
|
||||
int q[4] = { 0, 0, 0, 0 };
|
||||
int q[4] = {0, 0, 0, 0};
|
||||
for (s = 0; s < 4; ++s) {
|
||||
if (GET_BIT(1)) {
|
||||
q[s] = GET_BIT(7);
|
||||
if (GET_BIT(1)) q[s] = -q[s]; // sign
|
||||
if (GET_BIT(1)) q[s] = -q[s]; // sign
|
||||
}
|
||||
}
|
||||
if (absolute_delta) Q = q[0]; // just use the first segment's quantizer
|
||||
for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength
|
||||
for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength
|
||||
}
|
||||
if (update_map) {
|
||||
for (s = 0; s < 3; ++s) CONDITIONAL_SKIP(8);
|
||||
}
|
||||
}
|
||||
// Filter header
|
||||
GET_BIT(1 + 6 + 3); // simple + level + sharpness
|
||||
if (GET_BIT(1)) { // use_lf_delta
|
||||
if (GET_BIT(1)) { // update lf_delta?
|
||||
GET_BIT(1 + 6 + 3); // simple + level + sharpness
|
||||
if (GET_BIT(1)) { // use_lf_delta
|
||||
if (GET_BIT(1)) { // update lf_delta?
|
||||
int n;
|
||||
for (n = 0; n < 4 + 4; ++n) CONDITIONAL_SKIP(6);
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
|
||||
#if defined(WEBP_HAVE_SDL)
|
||||
|
||||
#include "webp_to_sdl.h"
|
||||
#include "webp/decode.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp_to_sdl.h"
|
||||
|
||||
#if defined(WEBP_HAVE_JUST_SDL_H)
|
||||
#include <SDL.h>
|
||||
@@ -41,11 +41,15 @@ static void ProcessEvents(void) {
|
||||
switch (event.type) {
|
||||
case SDL_KEYUP:
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_q: done = 1; break;
|
||||
default: break;
|
||||
case SDLK_q:
|
||||
done = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +61,12 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
if (argc == 1) {
|
||||
fprintf(stderr, "Usage: %s [-h] image.webp [more_files.webp...]\n",
|
||||
argv[0]);
|
||||
goto Error;
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
const char* file = NULL;
|
||||
const uint8_t* webp = NULL;
|
||||
@@ -87,7 +97,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
SDL_Quit();
|
||||
FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "extras/extras.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
// Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
|
||||
int main(int argc, const char* argv[]) {
|
||||
@@ -45,7 +46,7 @@ int main(int argc, const char* argv[]) {
|
||||
if (!quiet) {
|
||||
printf("Estimated quality factor: %d\n", q);
|
||||
} else {
|
||||
printf("%d\n", q); // just print the number
|
||||
printf("%d\n", q); // just print the number
|
||||
}
|
||||
}
|
||||
free((void*)data);
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
|
||||
#if defined(WEBP_HAVE_SDL)
|
||||
|
||||
#include "webp_to_sdl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "src/webp/decode.h"
|
||||
#include "webp_to_sdl.h"
|
||||
|
||||
#if defined(WEBP_HAVE_JUST_SDL_H)
|
||||
#include <SDL.h>
|
||||
@@ -67,11 +66,11 @@ int WebPToSDL(const char* data, unsigned int data_size) {
|
||||
}
|
||||
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
output = WebPDecodeBGRA((const uint8_t*)data, (size_t)data_size, &width,
|
||||
&height);
|
||||
output =
|
||||
WebPDecodeBGRA((const uint8_t*)data, (size_t)data_size, &width, &height);
|
||||
#else
|
||||
output = WebPDecodeRGBA((const uint8_t*)data, (size_t)data_size, &width,
|
||||
&height);
|
||||
output =
|
||||
WebPDecodeRGBA((const uint8_t*)data, (size_t)data_size, &width, &height);
|
||||
#endif
|
||||
if (output == NULL) {
|
||||
fprintf(stderr, "Error decoding image (%d)\n", status);
|
||||
@@ -84,7 +83,7 @@ int WebPToSDL(const char* data, unsigned int data_size) {
|
||||
SDL_RenderPresent(renderer);
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
// We should call SDL_DestroyWindow(window) but that makes .js fail.
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyTexture(texture);
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
#include "./image_dec.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
const char* WebPGetEnabledInputFileFormats(void) {
|
||||
return "WebP"
|
||||
#ifdef WEBP_HAVE_JPEG
|
||||
@@ -57,8 +63,8 @@ WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
|
||||
}
|
||||
|
||||
static int FailReader(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)pic;
|
||||
@@ -69,12 +75,18 @@ static int FailReader(const uint8_t* const data, size_t data_size,
|
||||
|
||||
WebPImageReader WebPGetImageReader(WebPInputFileFormat format) {
|
||||
switch (format) {
|
||||
case WEBP_PNG_FORMAT: return ReadPNG;
|
||||
case WEBP_JPEG_FORMAT: return ReadJPEG;
|
||||
case WEBP_TIFF_FORMAT: return ReadTIFF;
|
||||
case WEBP_WEBP_FORMAT: return ReadWebP;
|
||||
case WEBP_PNM_FORMAT: return ReadPNM;
|
||||
default: return FailReader;
|
||||
case WEBP_PNG_FORMAT:
|
||||
return ReadPNG;
|
||||
case WEBP_JPEG_FORMAT:
|
||||
return ReadJPEG;
|
||||
case WEBP_TIFF_FORMAT:
|
||||
return ReadTIFF;
|
||||
case WEBP_WEBP_FORMAT:
|
||||
return ReadWebP;
|
||||
case WEBP_PNM_FORMAT:
|
||||
return ReadPNM;
|
||||
default:
|
||||
return FailReader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||
#define WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "./metadata.h"
|
||||
#include "./jpegdec.h"
|
||||
#include "./metadata.h"
|
||||
#include "./pngdec.h"
|
||||
#include "./pnmdec.h"
|
||||
#include "./tiffdec.h"
|
||||
@@ -51,8 +53,8 @@ WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
|
||||
|
||||
// Signature for common image-reading functions (ReadPNG, ReadJPEG, ...)
|
||||
typedef int (*WebPImageReader)(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
// Return the reader associated to a given file format.
|
||||
WebPImageReader WebPGetImageReader(WebPInputFileFormat format);
|
||||
@@ -64,7 +66,7 @@ WebPImageReader WebPGuessImageReader(const uint8_t* const data,
|
||||
size_t data_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
#include "./image_enc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WEBP_HAVE_PNG
|
||||
#include <png.h>
|
||||
#include <setjmp.h> // note: this must be included *after* png.h
|
||||
#include <setjmp.h> // note: this must be included *after* png.h
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
@@ -25,29 +26,32 @@
|
||||
#endif
|
||||
#define CINTERFACE
|
||||
#define COBJMACROS
|
||||
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#define _WIN32_IE \
|
||||
0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||
#include <shlwapi.h>
|
||||
#include <tchar.h>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "./imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PNG
|
||||
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -56,8 +60,8 @@
|
||||
#define MAKE_REFGUID(x) &(x)
|
||||
#endif
|
||||
|
||||
static HRESULT CreateOutputStream(const char* out_file_name,
|
||||
int write_to_mem, IStream** stream) {
|
||||
static HRESULT CreateOutputStream(const char* out_file_name, int write_to_mem,
|
||||
IStream** stream) {
|
||||
HRESULT hr = S_OK;
|
||||
if (write_to_mem) {
|
||||
// Output to a memory buffer. This is freed when 'stream' is released.
|
||||
@@ -74,24 +78,22 @@ static HRESULT CreateOutputStream(const char* out_file_name,
|
||||
}
|
||||
|
||||
static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
|
||||
REFGUID container_guid,
|
||||
uint8_t* rgb, int stride,
|
||||
REFGUID container_guid, uint8_t* rgb, int stride,
|
||||
uint32_t width, uint32_t height, int has_alpha) {
|
||||
HRESULT hr = S_OK;
|
||||
IWICImagingFactory* factory = NULL;
|
||||
IWICBitmapFrameEncode* frame = NULL;
|
||||
IWICBitmapEncoder* encoder = NULL;
|
||||
IStream* stream = NULL;
|
||||
WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
|
||||
: GUID_WICPixelFormat24bppBGR;
|
||||
WICPixelFormatGUID pixel_format =
|
||||
has_alpha ? GUID_WICPixelFormat32bppBGRA : GUID_WICPixelFormat24bppBGR;
|
||||
|
||||
if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG;
|
||||
|
||||
IFS(CoInitialize(NULL));
|
||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory),
|
||||
(LPVOID*)&factory));
|
||||
IFS(CoCreateInstance(
|
||||
MAKE_REFGUID(CLSID_WICImagingFactory), NULL, CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory), (LPVOID*)&factory));
|
||||
if (hr == REGDB_E_CLASSNOTREG) {
|
||||
fprintf(stderr,
|
||||
"Couldn't access Windows Imaging Component (are you running "
|
||||
@@ -101,14 +103,13 @@ static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
|
||||
IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
|
||||
IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
|
||||
&encoder));
|
||||
IFS(IWICBitmapEncoder_Initialize(encoder, stream,
|
||||
WICBitmapEncoderNoCache));
|
||||
IFS(IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache));
|
||||
IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
|
||||
IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
|
||||
IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
|
||||
IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
|
||||
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
|
||||
height * stride, rgb));
|
||||
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride, height * stride,
|
||||
rgb));
|
||||
IFS(IWICBitmapFrameEncode_Commit(frame));
|
||||
IFS(IWICBitmapEncoder_Commit(encoder));
|
||||
|
||||
@@ -150,11 +151,11 @@ int WebPWritePNG(const char* out_file_name, int use_stdout,
|
||||
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||
|
||||
return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
|
||||
MAKE_REFGUID(GUID_ContainerFormatPng),
|
||||
rgb, stride, width, height, has_alpha));
|
||||
MAKE_REFGUID(GUID_ContainerFormatPng), rgb,
|
||||
stride, width, height, has_alpha));
|
||||
}
|
||||
|
||||
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
|
||||
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
|
||||
static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp unused) {
|
||||
(void)unused; // remove variable-unused warning
|
||||
longjmp(png_jmpbuf(png), 1);
|
||||
@@ -166,8 +167,8 @@ int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
||||
|
||||
if (out_file == NULL || buffer == NULL) return 0;
|
||||
|
||||
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, PNGErrorFunction, NULL);
|
||||
png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, PNGErrorFunction,
|
||||
NULL);
|
||||
if (png == NULL) {
|
||||
return 0;
|
||||
}
|
||||
@@ -203,11 +204,12 @@ int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
||||
png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
|
||||
return 1;
|
||||
}
|
||||
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
|
||||
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
|
||||
int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
if (fout == NULL || buffer == NULL) return 0;
|
||||
|
||||
fprintf(stderr, "PNG support not compiled. Please install the libpng "
|
||||
fprintf(stderr,
|
||||
"PNG support not compiled. Please install the libpng "
|
||||
"development package before building.\n");
|
||||
fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
|
||||
return 0;
|
||||
@@ -232,8 +234,10 @@ static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer,
|
||||
if (row == NULL) return 0;
|
||||
|
||||
if (alpha) {
|
||||
fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
|
||||
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
|
||||
fprintf(fout,
|
||||
"P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
|
||||
"TUPLTYPE RGB_ALPHA\nENDHDR\n",
|
||||
width, height);
|
||||
} else {
|
||||
fprintf(fout, "P6\n%u %u\n255\n", width, height);
|
||||
}
|
||||
@@ -294,7 +298,7 @@ static void PutLE16(uint8_t* const dst, uint32_t value) {
|
||||
}
|
||||
|
||||
static void PutLE32(uint8_t* const dst, uint32_t value) {
|
||||
PutLE16(dst + 0, (value >> 0) & 0xffff);
|
||||
PutLE16(dst + 0, (value >> 0) & 0xffff);
|
||||
PutLE16(dst + 2, (value >> 16) & 0xffff);
|
||||
}
|
||||
|
||||
@@ -307,7 +311,7 @@ int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
int stride;
|
||||
uint32_t y;
|
||||
uint32_t bytes_per_px, line_size, image_size, bmp_stride, total_size;
|
||||
uint8_t bmp_header[BMP_HEADER_SIZE + BMP_HEADER_ALPHA_EXTRA_SIZE] = { 0 };
|
||||
uint8_t bmp_header[BMP_HEADER_SIZE + BMP_HEADER_ALPHA_EXTRA_SIZE] = {0};
|
||||
|
||||
if (fout == NULL || buffer == NULL) return 0;
|
||||
|
||||
@@ -326,27 +330,27 @@ int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
if (rgba == NULL) return 0;
|
||||
|
||||
// bitmap file header
|
||||
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
|
||||
PutLE32(bmp_header + 2, total_size); // size including header
|
||||
PutLE32(bmp_header + 6, 0); // reserved
|
||||
PutLE32(bmp_header + 10, header_size); // offset to pixel array
|
||||
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
|
||||
PutLE32(bmp_header + 2, total_size); // size including header
|
||||
PutLE32(bmp_header + 6, 0); // reserved
|
||||
PutLE32(bmp_header + 10, header_size); // offset to pixel array
|
||||
// bitmap info header
|
||||
PutLE32(bmp_header + 14, header_size - 14); // DIB header size
|
||||
PutLE32(bmp_header + 18, width); // dimensions
|
||||
PutLE32(bmp_header + 22, height); // no vertical flip
|
||||
PutLE16(bmp_header + 26, 1); // number of planes
|
||||
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
|
||||
PutLE32(bmp_header + 30, has_alpha ? 3 : 0); // BI_BITFIELDS or BI_RGB
|
||||
PutLE32(bmp_header + 14, header_size - 14); // DIB header size
|
||||
PutLE32(bmp_header + 18, width); // dimensions
|
||||
PutLE32(bmp_header + 22, height); // no vertical flip
|
||||
PutLE16(bmp_header + 26, 1); // number of planes
|
||||
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
|
||||
PutLE32(bmp_header + 30, has_alpha ? 3 : 0); // BI_BITFIELDS or BI_RGB
|
||||
PutLE32(bmp_header + 34, image_size);
|
||||
PutLE32(bmp_header + 38, 2400); // x pixels/meter
|
||||
PutLE32(bmp_header + 42, 2400); // y pixels/meter
|
||||
PutLE32(bmp_header + 46, 0); // number of palette colors
|
||||
PutLE32(bmp_header + 50, 0); // important color count
|
||||
if (has_alpha) { // BITMAPV3INFOHEADER complement
|
||||
PutLE32(bmp_header + 54, 0x00ff0000); // red mask
|
||||
PutLE32(bmp_header + 58, 0x0000ff00); // green mask
|
||||
PutLE32(bmp_header + 62, 0x000000ff); // blue mask
|
||||
PutLE32(bmp_header + 66, 0xff000000); // alpha mask
|
||||
PutLE32(bmp_header + 38, 2400); // x pixels/meter
|
||||
PutLE32(bmp_header + 42, 2400); // y pixels/meter
|
||||
PutLE32(bmp_header + 46, 0); // number of palette colors
|
||||
PutLE32(bmp_header + 50, 0); // important color count
|
||||
if (has_alpha) { // BITMAPV3INFOHEADER complement
|
||||
PutLE32(bmp_header + 54, 0x00ff0000); // red mask
|
||||
PutLE32(bmp_header + 58, 0x0000ff00); // green mask
|
||||
PutLE32(bmp_header + 62, 0x000000ff); // blue mask
|
||||
PutLE32(bmp_header + 66, 0xff000000); // alpha mask
|
||||
}
|
||||
|
||||
// TODO(skal): color profile
|
||||
@@ -364,7 +368,7 @@ int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
}
|
||||
// write padding zeroes
|
||||
if (bmp_stride != line_size) {
|
||||
const uint8_t zeroes[3] = { 0 };
|
||||
const uint8_t zeroes[3] = {0};
|
||||
if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
|
||||
return 0;
|
||||
}
|
||||
@@ -394,35 +398,35 @@ int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
|
||||
const uint8_t num_ifd_entries = 0;
|
||||
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
|
||||
0x49, 0x49, 0x2a, 0x00, // little endian signature
|
||||
8, 0, 0, 0, // offset to the unique IFD that follows
|
||||
// IFD (offset = 8). Entries must be written in increasing tag order.
|
||||
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
|
||||
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
|
||||
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
|
||||
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
|
||||
EXTRA_DATA_OFFSET + 0, 0, 0, 0,
|
||||
0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
|
||||
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
|
||||
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
|
||||
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
|
||||
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
|
||||
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
|
||||
bytes_per_px, 0, 0, 0,
|
||||
0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
|
||||
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
|
||||
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||
0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
|
||||
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
||||
0x52, 0x01, 3, 0, 1, 0, 0, 0,
|
||||
assoc_alpha, 0, 0, 0, // 178: ExtraSamples: rgbA/RGBA
|
||||
0, 0, 0, 0, // 190: IFD terminator
|
||||
// EXTRA_DATA_OFFSET:
|
||||
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
||||
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
|
||||
0x49, 0x49, 0x2a, 0x00, // little endian signature
|
||||
8, 0, 0, 0, // offset to the unique IFD that follows
|
||||
// IFD (offset = 8). Entries must be written in increasing tag order.
|
||||
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
|
||||
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
|
||||
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
|
||||
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
|
||||
EXTRA_DATA_OFFSET + 0, 0, 0, 0, 0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0,
|
||||
0, // 46: Compression: none
|
||||
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
|
||||
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
|
||||
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
|
||||
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
|
||||
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
|
||||
bytes_per_px, 0, 0, 0, 0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, // 106: Rows per strip (TBD)
|
||||
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
|
||||
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0, 0x1b, 0x01, 5, 0, 1, 0, 0,
|
||||
0, // 142: Y-resolution
|
||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0, 0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0,
|
||||
0, // 154: PlanarConfiguration
|
||||
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
||||
0x52, 0x01, 3, 0, 1, 0, 0, 0, assoc_alpha, 0, 0,
|
||||
0, // 178: ExtraSamples: rgbA/RGBA
|
||||
0, 0, 0, 0, // 190: IFD terminator
|
||||
// EXTRA_DATA_OFFSET:
|
||||
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
||||
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
|
||||
};
|
||||
uint32_t y;
|
||||
|
||||
@@ -514,11 +518,11 @@ int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
|
||||
if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
|
||||
|
||||
fprintf(fout, "P5\n%d %d\n255\n",
|
||||
(width + 1) & ~1, height + uv_height + a_height);
|
||||
fprintf(fout, "P5\n%d %d\n255\n", (width + 1) & ~1,
|
||||
height + uv_height + a_height);
|
||||
for (y = 0; ok && y < height; ++y) {
|
||||
ok &= (fwrite(src_y, width, 1, fout) == 1);
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
src_y += yuv->y_stride;
|
||||
}
|
||||
for (y = 0; ok && y < uv_height; ++y) {
|
||||
@@ -529,7 +533,7 @@ int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||
}
|
||||
for (y = 0; ok && y < a_height; ++y) {
|
||||
ok &= (fwrite(src_a, width, 1, fout) == 1);
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
if (width & 1) fputc(0, fout); // padding byte
|
||||
src_a += yuv->a_stride;
|
||||
}
|
||||
return ok;
|
||||
@@ -606,8 +610,7 @@ int WebPSaveImage(const WebPDecBuffer* const buffer,
|
||||
}
|
||||
}
|
||||
|
||||
if (format == PNG ||
|
||||
format == RGBA || format == BGRA || format == ARGB ||
|
||||
if (format == PNG || format == RGBA || format == BGRA || format == ARGB ||
|
||||
format == rgbA || format == bgrA || format == Argb) {
|
||||
#ifdef HAVE_WINCODEC_H
|
||||
ok &= WebPWritePNG(out_file_name, use_stdout, buffer);
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "webp/types.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -38,10 +38,19 @@ typedef enum {
|
||||
RAW_YUV,
|
||||
ALPHA_PLANE_ONLY, // this is for experimenting only
|
||||
// forced colorspace output (for testing, mostly)
|
||||
RGB, RGBA, BGR, BGRA, ARGB,
|
||||
RGBA_4444, RGB_565,
|
||||
rgbA, bgrA, Argb, rgbA_4444,
|
||||
YUV, YUVA
|
||||
RGB,
|
||||
RGBA,
|
||||
BGR,
|
||||
BGRA,
|
||||
ARGB,
|
||||
RGBA_4444,
|
||||
RGB_565,
|
||||
rgbA,
|
||||
bgrA,
|
||||
Argb,
|
||||
rgbA_4444,
|
||||
YUV,
|
||||
YUVA
|
||||
} WebPOutputFileFormat;
|
||||
|
||||
// General all-purpose call.
|
||||
@@ -90,7 +99,7 @@ int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGE_ENC_H_
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
#include "./imageio_util.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <fcntl.h> // for _O_BINARY
|
||||
#include <io.h> // for _setmode()
|
||||
#include <fcntl.h> // for _O_BINARY
|
||||
#include <io.h> // for _setmode()
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// File I/O
|
||||
@@ -62,14 +65,14 @@ int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
|
||||
*data_size = size;
|
||||
return 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
free(input);
|
||||
fprintf(stderr, "Could not read from stdin\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ImgIoUtilReadFile(const char* const file_name,
|
||||
const uint8_t** data, size_t* data_size) {
|
||||
int ImgIoUtilReadFile(const char* const file_name, const uint8_t** data,
|
||||
size_t* data_size) {
|
||||
int ok;
|
||||
uint8_t* file_data;
|
||||
size_t file_size;
|
||||
@@ -120,8 +123,8 @@ int ImgIoUtilReadFile(const char* const file_name,
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int ImgIoUtilWriteFile(const char* const file_name,
|
||||
const uint8_t* data, size_t data_size) {
|
||||
int ImgIoUtilWriteFile(const char* const file_name, const uint8_t* data,
|
||||
size_t data_size) {
|
||||
int ok;
|
||||
FILE* out;
|
||||
const int to_stdout = (file_name == NULL) || !WSTRCMP(file_name, "-");
|
||||
@@ -142,8 +145,8 @@ int ImgIoUtilWriteFile(const char* const file_name,
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height) {
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
|
||||
int dst_stride, int width, int height) {
|
||||
while (height-- > 0) {
|
||||
memcpy(dst, src, width * sizeof(*dst));
|
||||
src += src_stride;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -35,22 +36,22 @@ FILE* ImgIoUtilSetBinaryMode(FILE* file);
|
||||
// to be used as a C-string.
|
||||
// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
|
||||
// the function ImgIoUtilReadFromStdin().
|
||||
int ImgIoUtilReadFile(const char* const file_name,
|
||||
const uint8_t** data, size_t* data_size);
|
||||
int ImgIoUtilReadFile(const char* const file_name, const uint8_t** data,
|
||||
size_t* data_size);
|
||||
|
||||
// Same as ImgIoUtilReadFile(), but reads until EOF from stdin instead.
|
||||
int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size);
|
||||
|
||||
// Write a data segment into a file named 'file_name'. Returns true if ok.
|
||||
// If 'file_name' is NULL or equal to "-", output is written to stdout.
|
||||
int ImgIoUtilWriteFile(const char* const file_name,
|
||||
const uint8_t* data, size_t data_size);
|
||||
int ImgIoUtilWriteFile(const char* const file_name, const uint8_t* data,
|
||||
size_t data_size);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Copy width x height pixels from 'src' to 'dst' honoring the strides.
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height);
|
||||
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride, uint8_t* dst,
|
||||
int dst_stride, int width, int height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -58,7 +59,7 @@ void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t stride, size_t height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||
|
||||
@@ -18,24 +18,25 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WEBP_HAVE_JPEG
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
#include <jpeglib.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Metadata processing
|
||||
|
||||
#ifndef JPEG_APP1
|
||||
# define JPEG_APP1 (JPEG_APP0 + 1)
|
||||
#define JPEG_APP1 (JPEG_APP0 + 1)
|
||||
#endif
|
||||
#ifndef JPEG_APP2
|
||||
# define JPEG_APP2 (JPEG_APP0 + 2)
|
||||
#define JPEG_APP2 (JPEG_APP0 + 2)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
@@ -63,7 +64,7 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
// ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
|
||||
static const char kICCPSignature[] = "ICC_PROFILE";
|
||||
static const size_t kICCPSignatureLength = 12; // signature includes '\0'
|
||||
static const size_t kICCPSkipLength = 14; // signature + seq & count
|
||||
static const size_t kICCPSkipLength = 14; // signature + seq & count
|
||||
int expected_count = 0;
|
||||
int actual_count = 0;
|
||||
int seq_max = 0;
|
||||
@@ -73,8 +74,7 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
|
||||
memset(iccp_segments, 0, sizeof(iccp_segments));
|
||||
for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
|
||||
if (marker->marker == JPEG_APP2 &&
|
||||
marker->data_length > kICCPSkipLength &&
|
||||
if (marker->marker == JPEG_APP2 && marker->data_length > kICCPSkipLength &&
|
||||
!memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
|
||||
// ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
|
||||
const int seq = marker->data[kICCPSignatureLength];
|
||||
@@ -83,8 +83,9 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
ICCPSegment* segment;
|
||||
|
||||
if (segment_size == 0 || count == 0 || seq == 0) {
|
||||
fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
|
||||
" cannot be 0!\n",
|
||||
fprintf(stderr,
|
||||
"[ICCP] size (%d) / count (%d) / sequence number (%d)"
|
||||
" cannot be 0!\n",
|
||||
(int)segment_size, seq, count);
|
||||
return 0;
|
||||
}
|
||||
@@ -99,7 +100,7 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
|
||||
segment = iccp_segments + seq - 1;
|
||||
if (segment->data_length != 0) {
|
||||
fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
|
||||
fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n", seq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -137,8 +138,8 @@ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
|
||||
int i;
|
||||
size_t offset = 0;
|
||||
for (i = 0; i < seq_max; ++i) {
|
||||
memcpy(iccp->bytes + offset,
|
||||
iccp_segments[i].data, iccp_segments[i].data_length);
|
||||
memcpy(iccp->bytes + offset, iccp_segments[i].data,
|
||||
iccp_segments[i].data_length);
|
||||
offset += iccp_segments[i].data_length;
|
||||
}
|
||||
}
|
||||
@@ -155,12 +156,12 @@ static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
|
||||
size_t signature_length;
|
||||
size_t storage_offset;
|
||||
} kJPEGMetadataMap[] = {
|
||||
// Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
|
||||
{ JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif) },
|
||||
// XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
|
||||
// TODO(jzern) Add support for 'ExtendedXMP'
|
||||
{ JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
|
||||
{ 0, NULL, 0, 0 },
|
||||
// Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
|
||||
{JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif)},
|
||||
// XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
|
||||
// TODO(jzern) Add support for 'ExtendedXMP'
|
||||
{JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp)},
|
||||
{0, NULL, 0, 0},
|
||||
};
|
||||
jpeg_saved_marker_ptr marker;
|
||||
// Treat ICC profiles separately as they may be segmented and out of order.
|
||||
@@ -178,8 +179,8 @@ static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
|
||||
kJPEGMetadataMap[i].storage_offset);
|
||||
|
||||
if (payload->bytes == NULL) {
|
||||
const char* marker_data = (const char*)marker->data +
|
||||
kJPEGMetadataMap[i].signature_length;
|
||||
const char* marker_data =
|
||||
(const char*)marker->data + kJPEGMetadataMap[i].signature_length;
|
||||
const size_t marker_data_length =
|
||||
marker->data_length - kJPEGMetadataMap[i].signature_length;
|
||||
if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
|
||||
@@ -249,9 +250,7 @@ static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
|
||||
ctx->pub.next_input_byte += jump;
|
||||
}
|
||||
|
||||
static void ContextTerm(j_decompress_ptr cinfo) {
|
||||
(void)cinfo;
|
||||
}
|
||||
static void ContextTerm(j_decompress_ptr cinfo) { (void)cinfo; }
|
||||
|
||||
static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
|
||||
JPEGReadContext* const ctx) {
|
||||
@@ -266,8 +265,7 @@ static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
|
||||
}
|
||||
|
||||
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
WebPPicture* const pic, int keep_alpha, Metadata* const metadata) {
|
||||
volatile int ok = 0;
|
||||
int width, height;
|
||||
int64_t stride;
|
||||
@@ -284,12 +282,12 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
ctx.data = data;
|
||||
ctx.data_size = data_size;
|
||||
|
||||
memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp safety
|
||||
memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp safety
|
||||
dinfo.err = jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = my_error_exit;
|
||||
|
||||
if (setjmp(jerr.setjmp_buffer)) {
|
||||
Error:
|
||||
Error:
|
||||
MetadataFree(metadata);
|
||||
jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
|
||||
goto End;
|
||||
@@ -352,11 +350,11 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
MetadataFree(metadata); // In case the caller forgets to free it on error.
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
free(rgb);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_JPEG
|
||||
#else // !WEBP_HAVE_JPEG
|
||||
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
@@ -365,7 +363,8 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
|
||||
fprintf(stderr,
|
||||
"JPEG support not compiled. Please install the libjpeg "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#ifndef WEBP_IMAGEIO_JPEGDEC_H_
|
||||
#define WEBP_IMAGEIO_JPEGDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -31,7 +33,7 @@ int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_JPEGDEC_H_
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#ifndef WEBP_IMAGEIO_METADATA_H_
|
||||
#define WEBP_IMAGEIO_METADATA_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -41,7 +43,7 @@ int MetadataCopy(const char* metadata, size_t metadata_len,
|
||||
MetadataPayload* const payload);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_METADATA_H_
|
||||
|
||||
@@ -22,24 +22,24 @@
|
||||
#define PNG_USER_MEM_SUPPORTED // for png_create_read_struct_2
|
||||
#endif
|
||||
#include <png.h>
|
||||
#include <setjmp.h> // note: this must be included *after* png.h
|
||||
#include <setjmp.h> // note: this must be included *after* png.h
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#define LOCAL_PNG_VERSION ((PNG_LIBPNG_VER_MAJOR << 8) | PNG_LIBPNG_VER_MINOR)
|
||||
#define LOCAL_PNG_PREREQ(maj, min) \
|
||||
(LOCAL_PNG_VERSION >= (((maj) << 8) | (min)))
|
||||
#define LOCAL_PNG_PREREQ(maj, min) (LOCAL_PNG_VERSION >= (((maj) << 8) | (min)))
|
||||
|
||||
static void PNGAPI error_function(png_structp png, png_const_charp error) {
|
||||
if (error != NULL) fprintf(stderr, "libpng error: %s\n", error);
|
||||
longjmp(png_jmpbuf(png), 1);
|
||||
}
|
||||
|
||||
#if LOCAL_PNG_PREREQ(1,4)
|
||||
#if LOCAL_PNG_PREREQ(1, 4)
|
||||
typedef png_alloc_size_t LocalPngAllocSize;
|
||||
#else
|
||||
typedef png_size_t LocalPngAllocSize;
|
||||
@@ -111,7 +111,8 @@ static int ProcessRawProfile(const char* profile, size_t profile_len,
|
||||
}
|
||||
++src;
|
||||
// skip the profile name and extract the length.
|
||||
while (*src != '\0' && *src++ != '\n') {}
|
||||
while (*src != '\0' && *src++ != '\n') {
|
||||
}
|
||||
expected_length = (int)strtol(src, &end, 10);
|
||||
if (*end != '\n') {
|
||||
fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n",
|
||||
@@ -133,32 +134,47 @@ static const struct {
|
||||
MetadataPayload* const payload);
|
||||
size_t storage_offset;
|
||||
} kPNGMetadataMap[] = {
|
||||
// https://exiftool.org/TagNames/PNG.html#TextualData
|
||||
// See also: ExifTool on CPAN.
|
||||
{ "Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif) },
|
||||
{ "Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp) },
|
||||
// Exiftool puts exif data in APP1 chunk, too.
|
||||
{ "Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif) },
|
||||
// XMP Specification Part 3, Section 3 #PNG
|
||||
{ "XML:com.adobe.xmp", MetadataCopy, METADATA_OFFSET(xmp) },
|
||||
{ NULL, NULL, 0 },
|
||||
// https://exiftool.org/TagNames/PNG.html#TextualData
|
||||
// See also: ExifTool on CPAN.
|
||||
{"Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif)},
|
||||
{"Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp)},
|
||||
// Exiftool puts exif data in APP1 chunk, too.
|
||||
{"Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif)},
|
||||
// ImageMagick uses lowercase app1.
|
||||
{"Raw profile type app1", ProcessRawProfile, METADATA_OFFSET(exif)},
|
||||
// XMP Specification Part 3, Section 3 #PNG
|
||||
{"XML:com.adobe.xmp", MetadataCopy, METADATA_OFFSET(xmp)},
|
||||
{NULL, NULL, 0},
|
||||
};
|
||||
|
||||
// Looks for metadata at both the beginning and end of the PNG file, giving
|
||||
// preference to the head.
|
||||
// Returns true on success. The caller must use MetadataFree() on 'metadata' in
|
||||
// all cases.
|
||||
static int ExtractMetadataFromPNG(png_structp png,
|
||||
png_infop const head_info,
|
||||
static int ExtractMetadataFromPNG(png_structp png, png_infop const head_info,
|
||||
png_infop const end_info,
|
||||
Metadata* const metadata) {
|
||||
int p;
|
||||
|
||||
for (p = 0; p < 2; ++p) {
|
||||
for (p = 0; p < 2; ++p) {
|
||||
png_infop const info = (p == 0) ? head_info : end_info;
|
||||
png_textp text = NULL;
|
||||
const png_uint_32 num = png_get_text(png, info, &text, NULL);
|
||||
png_uint_32 i;
|
||||
|
||||
#ifdef PNG_eXIf_SUPPORTED
|
||||
// Look for an 'eXIf' tag. Preference is given to this tag as it's newer
|
||||
// than the TextualData tags.
|
||||
{
|
||||
png_bytep exif;
|
||||
png_uint_32 len;
|
||||
|
||||
if (png_get_eXIf_1(png, info, &len, &exif) == PNG_INFO_eXIf) {
|
||||
if (!MetadataCopy((const char*)exif, len, &metadata->exif)) return 0;
|
||||
}
|
||||
}
|
||||
#endif // PNG_eXIf_SUPPORTED
|
||||
|
||||
// Look for EXIF / XMP metadata.
|
||||
for (i = 0; i < num; ++i, ++text) {
|
||||
int j;
|
||||
@@ -192,22 +208,24 @@ static int ExtractMetadataFromPNG(png_structp png,
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef PNG_iCCP_SUPPORTED
|
||||
// Look for an ICC profile.
|
||||
{
|
||||
png_charp name;
|
||||
int comp_type;
|
||||
#if LOCAL_PNG_PREREQ(1,5)
|
||||
#if LOCAL_PNG_PREREQ(1, 5)
|
||||
png_bytep profile;
|
||||
#else
|
||||
png_charp profile;
|
||||
#endif
|
||||
png_uint_32 len;
|
||||
|
||||
if (png_get_iCCP(png, info,
|
||||
&name, &comp_type, &profile, &len) == PNG_INFO_iCCP) {
|
||||
if (png_get_iCCP(png, info, &name, &comp_type, &profile, &len) ==
|
||||
PNG_INFO_iCCP) {
|
||||
if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0;
|
||||
}
|
||||
}
|
||||
#endif // PNG_iCCP_SUPPORTED
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -228,12 +246,12 @@ static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
}
|
||||
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
volatile png_structp png = NULL;
|
||||
volatile png_infop info = NULL;
|
||||
volatile png_infop end_info = NULL;
|
||||
PNGReadContext context = { NULL, 0, 0 };
|
||||
PNGReadContext context = {NULL, 0, 0};
|
||||
int color_type, bit_depth, interlaced;
|
||||
int num_channels;
|
||||
int num_passes;
|
||||
@@ -248,19 +266,19 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
context.data = data;
|
||||
context.data_size = data_size;
|
||||
|
||||
png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
|
||||
NULL, MallocFunc, FreeFunc);
|
||||
png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL,
|
||||
MallocFunc, FreeFunc);
|
||||
if (png == NULL) goto End;
|
||||
|
||||
png_set_error_fn(png, 0, error_function, NULL);
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
Error:
|
||||
Error:
|
||||
MetadataFree(metadata);
|
||||
goto End;
|
||||
}
|
||||
|
||||
#if LOCAL_PNG_PREREQ(1,5) || \
|
||||
(LOCAL_PNG_PREREQ(1,4) && PNG_LIBPNG_VER_RELEASE >= 1)
|
||||
#if LOCAL_PNG_PREREQ(1, 5) || \
|
||||
(LOCAL_PNG_PREREQ(1, 4) && PNG_LIBPNG_VER_RELEASE >= 1)
|
||||
// If it looks like the bitstream is going to need more memory than libpng's
|
||||
// internal limit (default: 8M), try to (reasonably) raise it.
|
||||
if (data_size > png_get_chunk_malloc_max(png) && data_size < (1u << 24)) {
|
||||
@@ -275,9 +293,9 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
|
||||
png_set_read_fn(png, &context, ReadFunc);
|
||||
png_read_info(png, info);
|
||||
if (!png_get_IHDR(png, info,
|
||||
&width, &height, &bit_depth, &color_type, &interlaced,
|
||||
NULL, NULL)) goto Error;
|
||||
if (!png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type,
|
||||
&interlaced, NULL, NULL))
|
||||
goto Error;
|
||||
|
||||
png_set_strip_16(png);
|
||||
png_set_packing(png);
|
||||
@@ -348,24 +366,25 @@ int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
goto Error;
|
||||
}
|
||||
|
||||
End:
|
||||
End:
|
||||
if (png != NULL) {
|
||||
png_destroy_read_struct((png_structpp)&png,
|
||||
(png_infopp)&info, (png_infopp)&end_info);
|
||||
png_destroy_read_struct((png_structpp)&png, (png_infopp)&info,
|
||||
(png_infopp)&end_info);
|
||||
}
|
||||
free(rgb);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_PNG
|
||||
#else // !WEBP_HAVE_PNG
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr, "PNG support not compiled. Please install the libpng "
|
||||
fprintf(stderr,
|
||||
"PNG support not compiled. Please install the libpng "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#ifndef WEBP_IMAGEIO_PNGDEC_H_
|
||||
#define WEBP_IMAGEIO_PNGDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -27,11 +29,11 @@ struct WebPPicture;
|
||||
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||
// Returns true on success.
|
||||
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_PNGDEC_H_
|
||||
|
||||
@@ -17,19 +17,20 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
WIDTH_FLAG = 1 << 0,
|
||||
HEIGHT_FLAG = 1 << 1,
|
||||
DEPTH_FLAG = 1 << 2,
|
||||
MAXVAL_FLAG = 1 << 3,
|
||||
TUPLE_FLAG = 1 << 4,
|
||||
WIDTH_FLAG = 1 << 0,
|
||||
HEIGHT_FLAG = 1 << 1,
|
||||
DEPTH_FLAG = 1 << 2,
|
||||
MAXVAL_FLAG = 1 << 3,
|
||||
TUPLE_FLAG = 1 << 4,
|
||||
ALL_NEEDED_FLAGS = WIDTH_FLAG | HEIGHT_FLAG | DEPTH_FLAG | MAXVAL_FLAG
|
||||
} PNMFlags;
|
||||
|
||||
@@ -38,9 +39,9 @@ typedef struct {
|
||||
size_t data_size;
|
||||
int width, height;
|
||||
int bytes_per_px;
|
||||
int depth; // 1 (grayscale), 2 (grayscale + alpha), 3 (rgb), 4 (rgba)
|
||||
int depth; // 1 (grayscale), 2 (grayscale + alpha), 3 (rgb), 4 (rgba)
|
||||
int max_value;
|
||||
int type; // 5, 6 or 7
|
||||
int type; // 5, 6 or 7
|
||||
int seen_flags;
|
||||
} PNMInfo;
|
||||
|
||||
@@ -54,7 +55,7 @@ static size_t ReadLine(const uint8_t* const data, size_t off, size_t data_size,
|
||||
char out[MAX_LINE_SIZE + 1], size_t* const out_size) {
|
||||
size_t i = 0;
|
||||
*out_size = 0;
|
||||
redo:
|
||||
redo:
|
||||
for (i = 0; i < MAX_LINE_SIZE && off < data_size; ++i) {
|
||||
out[i] = data[off++];
|
||||
if (out[i] == '\n') break;
|
||||
@@ -63,7 +64,7 @@ static size_t ReadLine(const uint8_t* const data, size_t off, size_t data_size,
|
||||
if (i == 0) goto redo; // empty line
|
||||
if (out[0] == '#') goto redo; // skip comment
|
||||
}
|
||||
out[i] = 0; // safety sentinel
|
||||
out[i] = 0; // safety sentinel
|
||||
*out_size = i;
|
||||
return off;
|
||||
}
|
||||
@@ -172,9 +173,8 @@ static size_t ReadHeader(PNMInfo* const info) {
|
||||
info->depth = (info->type == 5) ? 1 : 3;
|
||||
}
|
||||
// perform some basic numerical validation
|
||||
if (info->width <= 0 || info->height <= 0 ||
|
||||
info->type <= 0 || info->type >= 9 ||
|
||||
info->depth <= 0 || info->depth > 4 ||
|
||||
if (info->width <= 0 || info->height <= 0 || info->type <= 0 ||
|
||||
info->type >= 9 || info->depth <= 0 || info->depth > 4 ||
|
||||
info->max_value <= 0 || info->max_value >= 65536) {
|
||||
return 0;
|
||||
}
|
||||
@@ -182,13 +182,12 @@ static size_t ReadHeader(PNMInfo* const info) {
|
||||
return off;
|
||||
}
|
||||
|
||||
int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
int ReadPNM(const uint8_t* const data, size_t data_size, WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata) {
|
||||
int ok = 0;
|
||||
int i, j;
|
||||
uint64_t stride, pixel_bytes, sample_size, depth;
|
||||
uint8_t* rgb = NULL, *tmp_rgb;
|
||||
uint8_t *rgb = NULL, *tmp_rgb;
|
||||
size_t offset;
|
||||
PNMInfo info;
|
||||
|
||||
@@ -208,8 +207,8 @@ int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
// Some basic validations.
|
||||
if (pic == NULL) goto End;
|
||||
if (info.width > WEBP_MAX_DIMENSION || info.height > WEBP_MAX_DIMENSION) {
|
||||
fprintf(stderr, "Invalid %dx%d dimension for PNM\n",
|
||||
info.width, info.height);
|
||||
fprintf(stderr, "Invalid %dx%d dimension for PNM\n", info.width,
|
||||
info.height);
|
||||
goto End;
|
||||
}
|
||||
|
||||
@@ -257,8 +256,8 @@ int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
const uint32_t round = info.max_value / 2;
|
||||
int k = 0;
|
||||
for (i = 0; i < info.width * info.depth; ++i) {
|
||||
uint32_t v = (sample_size == 2) ? 256u * in[2 * i + 0] + in[2 * i + 1]
|
||||
: in[i];
|
||||
uint32_t v =
|
||||
(sample_size == 2) ? 256u * in[2 * i + 0] + in[2 * i + 1] : in[i];
|
||||
if (info.max_value != 255) v = (v * 255u + round) / info.max_value;
|
||||
if (v > 255u) v = 255u;
|
||||
if (info.depth > 2) {
|
||||
@@ -290,7 +289,7 @@ int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
if (!ok) goto End;
|
||||
|
||||
ok = 1;
|
||||
End:
|
||||
End:
|
||||
free((void*)rgb);
|
||||
|
||||
(void)metadata;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#ifndef WEBP_IMAGEIO_PNMDEC_H_
|
||||
#define WEBP_IMAGEIO_PNMDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -31,7 +33,7 @@ int ReadPNM(const uint8_t* const data, size_t data_size,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_PNMDEC_H_
|
||||
|
||||
@@ -22,17 +22,18 @@
|
||||
#ifdef WEBP_HAVE_TIFF
|
||||
#include <tiffio.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
static const struct {
|
||||
ttag_t tag;
|
||||
size_t storage_offset;
|
||||
} kTIFFMetadataMap[] = {
|
||||
{ TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp) },
|
||||
{ TIFFTAG_XMLPACKET, METADATA_OFFSET(xmp) },
|
||||
{ 0, 0 },
|
||||
{TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp)},
|
||||
{TIFFTAG_XMLPACKET, METADATA_OFFSET(xmp)},
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
// Returns true on success. The caller must use MetadataFree() on 'metadata' in
|
||||
@@ -85,9 +86,9 @@ static toff_t MySize(thandle_t opaque) {
|
||||
|
||||
static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) {
|
||||
MyData* const my_data = (MyData*)opaque;
|
||||
offset += (whence == SEEK_CUR) ? my_data->pos
|
||||
: (whence == SEEK_SET) ? 0
|
||||
: my_data->size;
|
||||
offset += (whence == SEEK_CUR) ? my_data->pos
|
||||
: (whence == SEEK_SET) ? 0
|
||||
: my_data->size;
|
||||
if (offset > my_data->size) return (toff_t)-1;
|
||||
my_data->pos = offset;
|
||||
return offset;
|
||||
@@ -119,7 +120,7 @@ static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
|
||||
|
||||
// Unmultiply Argb data. Taken from dsp/alpha_processing
|
||||
// (we don't want to force a dependency to a libdspdec library).
|
||||
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||
#define HALF ((1u << MFIX) >> 1)
|
||||
|
||||
static uint32_t Unmult(uint8_t x, uint32_t mult) {
|
||||
@@ -127,9 +128,7 @@ static uint32_t Unmult(uint8_t x, uint32_t mult) {
|
||||
return (v > 255u) ? 255u : v;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t GetScale(uint32_t a) {
|
||||
return (255u << MFIX) / a;
|
||||
}
|
||||
static WEBP_INLINE uint32_t GetScale(uint32_t a) { return (255u << MFIX) / a; }
|
||||
|
||||
#undef MFIX
|
||||
#undef HALF
|
||||
@@ -139,7 +138,7 @@ static void MultARGBRow(uint8_t* ptr, int width) {
|
||||
for (x = 0; x < width; ++x, ptr += 4) {
|
||||
const uint32_t alpha = ptr[3];
|
||||
if (alpha < 255) {
|
||||
if (alpha == 0) { // alpha == 0
|
||||
if (alpha == 0) { // alpha == 0
|
||||
ptr[0] = ptr[1] = ptr[2] = 0;
|
||||
} else {
|
||||
const uint32_t scale = GetScale(alpha);
|
||||
@@ -152,9 +151,8 @@ static void MultARGBRow(uint8_t* ptr, int width) {
|
||||
}
|
||||
|
||||
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
MyData my_data = { data, (toff_t)data_size, 0 };
|
||||
WebPPicture* const pic, int keep_alpha, Metadata* const metadata) {
|
||||
MyData my_data = {data, (toff_t)data_size, 0};
|
||||
TIFF* tif;
|
||||
uint32_t image_width, image_height, tile_width, tile_height;
|
||||
uint64_t stride;
|
||||
@@ -170,8 +168,7 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
tif = TIFFClientOpen("Memory", "r", &my_data,
|
||||
MyRead, MyRead, MySeek, MyClose,
|
||||
tif = TIFFClientOpen("Memory", "r", &my_data, MyRead, MyRead, MySeek, MyClose,
|
||||
MySize, MyMapFile, MyUnmapFile);
|
||||
if (tif == NULL) {
|
||||
fprintf(stderr, "Error! Cannot parse TIFF file\n");
|
||||
@@ -180,9 +177,10 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
|
||||
dircount = TIFFNumberOfDirectories(tif);
|
||||
if (dircount > 1) {
|
||||
fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
|
||||
"Only the first will be used, %d will be ignored.\n",
|
||||
dircount - 1);
|
||||
fprintf(stderr,
|
||||
"Warning: multi-directory TIFF files are not supported.\n"
|
||||
"Only the first will be used, %d will be ignored.\n",
|
||||
dircount - 1);
|
||||
}
|
||||
if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
|
||||
fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
|
||||
@@ -252,9 +250,10 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
tmp += stride;
|
||||
}
|
||||
}
|
||||
ok = keep_alpha
|
||||
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride)
|
||||
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride);
|
||||
ok =
|
||||
keep_alpha
|
||||
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride)
|
||||
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride);
|
||||
}
|
||||
_TIFFfree(raster);
|
||||
} else {
|
||||
@@ -271,11 +270,11 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
End:
|
||||
End:
|
||||
TIFFClose(tif);
|
||||
return ok;
|
||||
}
|
||||
#else // !WEBP_HAVE_TIFF
|
||||
#else // !WEBP_HAVE_TIFF
|
||||
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
@@ -284,7 +283,8 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
|
||||
fprintf(stderr,
|
||||
"TIFF support not compiled. Please install the libtiff "
|
||||
"development package before building.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#ifndef WEBP_IMAGEIO_TIFFDEC_H_
|
||||
#define WEBP_IMAGEIO_TIFFDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -31,7 +33,7 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_TIFFDEC_H_
|
||||
|
||||
@@ -13,26 +13,32 @@
|
||||
#include "webp/config.h"
|
||||
#endif
|
||||
|
||||
#include "./webpdec.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
#include "webp/encode.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
#include "./webpdec.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux_types.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebP decoding
|
||||
|
||||
static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
|
||||
"OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
|
||||
"UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
|
||||
};
|
||||
"OK",
|
||||
"OUT_OF_MEMORY",
|
||||
"INVALID_PARAM",
|
||||
"BITSTREAM_ERROR",
|
||||
"UNSUPPORTED_FEATURE",
|
||||
"SUSPENDED",
|
||||
"USER_ABORT",
|
||||
"NOT_ENOUGH_DATA"};
|
||||
|
||||
static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
|
||||
if (config->input.has_animation) {
|
||||
@@ -52,8 +58,7 @@ void PrintWebPError(const char* const in_file, int status) {
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
int LoadWebP(const char* const in_file,
|
||||
const uint8_t** data, size_t* data_size,
|
||||
int LoadWebP(const char* const in_file, const uint8_t** data, size_t* data_size,
|
||||
WebPBitstreamFeatures* bitstream) {
|
||||
VP8StatusCode status;
|
||||
WebPBitstreamFeatures local_features;
|
||||
@@ -83,9 +88,8 @@ VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
|
||||
return WebPDecode(data, data_size, config);
|
||||
}
|
||||
|
||||
VP8StatusCode DecodeWebPIncremental(
|
||||
const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config) {
|
||||
VP8StatusCode DecodeWebPIncremental(const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config) {
|
||||
VP8StatusCode status = VP8_STATUS_OK;
|
||||
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
|
||||
|
||||
@@ -110,7 +114,7 @@ VP8StatusCode DecodeWebPIncremental(
|
||||
|
||||
static int ExtractMetadata(const uint8_t* const data, size_t data_size,
|
||||
Metadata* const metadata) {
|
||||
WebPData webp_data = { data, data_size };
|
||||
WebPData webp_data = {data, data_size};
|
||||
WebPDemuxer* const demux = WebPDemux(&webp_data);
|
||||
WebPChunkIterator chunk_iter;
|
||||
uint32_t flags;
|
||||
@@ -142,8 +146,7 @@ static int ExtractMetadata(const uint8_t* const data, size_t data_size,
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
WebPPicture* const pic,
|
||||
int keep_alpha, Metadata* const metadata) {
|
||||
WebPPicture* const pic, int keep_alpha, Metadata* const metadata) {
|
||||
int ok = 0;
|
||||
VP8StatusCode status = VP8_STATUS_OK;
|
||||
WebPDecoderConfig config;
|
||||
@@ -193,7 +196,8 @@ int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
#endif
|
||||
output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb;
|
||||
output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t);
|
||||
output_buffer->u.RGBA.size = output_buffer->u.RGBA.stride * pic->height;
|
||||
output_buffer->u.RGBA.size =
|
||||
(size_t)output_buffer->u.RGBA.stride * pic->height;
|
||||
} else {
|
||||
output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
|
||||
output_buffer->u.YUVA.y = pic->y;
|
||||
@@ -204,10 +208,12 @@ int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
output_buffer->u.YUVA.u_stride = pic->uv_stride;
|
||||
output_buffer->u.YUVA.v_stride = pic->uv_stride;
|
||||
output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0;
|
||||
output_buffer->u.YUVA.y_size = pic->height * pic->y_stride;
|
||||
output_buffer->u.YUVA.u_size = (pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.v_size = (pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.a_size = pic->height * pic->a_stride;
|
||||
output_buffer->u.YUVA.y_size = (size_t)pic->height * pic->y_stride;
|
||||
output_buffer->u.YUVA.u_size =
|
||||
(size_t)(pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.v_size =
|
||||
(size_t)(pic->height + 1) / 2 * pic->uv_stride;
|
||||
output_buffer->u.YUVA.a_size = (size_t)pic->height * pic->a_stride;
|
||||
}
|
||||
output_buffer->is_external_memory = 1;
|
||||
|
||||
@@ -222,7 +228,7 @@ int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
argb += pic->argb_stride;
|
||||
}
|
||||
}
|
||||
} while (0); // <- so we can 'break' out of the loop
|
||||
} while (0); // <- so we can 'break' out of the loop
|
||||
|
||||
if (status != VP8_STATUS_OK) {
|
||||
PrintWebPError("input data", status);
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
#ifndef WEBP_IMAGEIO_WEBPDEC_H_
|
||||
#define WEBP_IMAGEIO_WEBPDEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webp/decode.h"
|
||||
#include "webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -32,8 +35,7 @@ void PrintWebPError(const char* const in_file, int status);
|
||||
// Reads a WebP from 'in_file', returning the contents and size in 'data' and
|
||||
// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures().
|
||||
// Returns true on success.
|
||||
int LoadWebP(const char* const in_file,
|
||||
const uint8_t** data, size_t* data_size,
|
||||
int LoadWebP(const char* const in_file, const uint8_t** data, size_t* data_size,
|
||||
WebPBitstreamFeatures* bitstream);
|
||||
|
||||
// Decodes the WebP contained in 'data'.
|
||||
@@ -45,9 +47,8 @@ VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config);
|
||||
|
||||
// Same as DecodeWebP(), but using the incremental decoder.
|
||||
VP8StatusCode DecodeWebPIncremental(
|
||||
const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config);
|
||||
VP8StatusCode DecodeWebPIncremental(const uint8_t* const data, size_t data_size,
|
||||
WebPDecoderConfig* const config);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -57,11 +58,11 @@ VP8StatusCode DecodeWebPIncremental(
|
||||
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||
// Returns true on success.
|
||||
int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||
struct WebPPicture* const pic,
|
||||
int keep_alpha, struct Metadata* const metadata);
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_WEBPDEC_H_
|
||||
|
||||
132
imageio/wicdec.c
132
imageio/wicdec.c
@@ -25,31 +25,31 @@
|
||||
#endif
|
||||
#define CINTERFACE
|
||||
#define COBJMACROS
|
||||
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#define _WIN32_IE \
|
||||
0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||
// code with COBJMACROS.
|
||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||
#include <shlwapi.h>
|
||||
#include <tchar.h>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
if (SUCCEEDED(hr)) { \
|
||||
hr = (fn); \
|
||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// modified version of DEFINE_GUID from guiddef.h.
|
||||
#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
static const GUID name = \
|
||||
{ l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
static const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define MAKE_REFGUID(x) (x)
|
||||
@@ -66,23 +66,17 @@ typedef struct WICFormatImporter {
|
||||
// From Microsoft SDK 7.0a -- wincodec.h
|
||||
// Create local copies for compatibility when building against earlier
|
||||
// versions of the SDK.
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_, 0x6fddc324, 0x4e03, 0x4bfe, 0xb1,
|
||||
0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_, 0x6fddc324, 0x4e03, 0x4bfe, 0xb1,
|
||||
0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_, 0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
|
||||
0xf5c7ad2d, 0x6a8d, 0x43dd,
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_, 0xf5c7ad2d, 0x6a8d, 0x43dd,
|
||||
0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
|
||||
0x1562ff7c, 0xd352, 0x46f9,
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_, 0x1562ff7c, 0xd352, 0x46f9,
|
||||
0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
|
||||
0x6fddc324, 0x4e03, 0x4bfe,
|
||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_, 0x6fddc324, 0x4e03, 0x4bfe,
|
||||
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
|
||||
|
||||
static HRESULT OpenInputStream(const char* filename, IStream** stream) {
|
||||
@@ -147,8 +141,7 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
UINT num_color_contexts;
|
||||
IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
|
||||
count, color_contexts,
|
||||
IFS(IWICBitmapFrameDecode_GetColorContexts(frame, count, color_contexts,
|
||||
&num_color_contexts));
|
||||
assert(FAILED(hr) || num_color_contexts <= count);
|
||||
for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
|
||||
@@ -156,8 +149,8 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
||||
IFS(IWICColorContext_GetType(color_contexts[i], &type));
|
||||
if (SUCCEEDED(hr) && type == WICColorContextProfile) {
|
||||
UINT size;
|
||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
||||
0, NULL, &size));
|
||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i], 0, NULL,
|
||||
&size));
|
||||
if (SUCCEEDED(hr) && size > 0) {
|
||||
iccp->bytes = (uint8_t*)malloc(size);
|
||||
if (iccp->bytes == NULL) {
|
||||
@@ -165,9 +158,8 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
||||
break;
|
||||
}
|
||||
iccp->size = size;
|
||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
||||
(UINT)iccp->size, iccp->bytes,
|
||||
&size));
|
||||
IFS(IWICColorContext_GetProfileBytes(
|
||||
color_contexts[i], (UINT)iccp->size, iccp->bytes, &size));
|
||||
if (SUCCEEDED(hr) && size != iccp->size) {
|
||||
fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n",
|
||||
size, (uint32_t)iccp->size);
|
||||
@@ -209,8 +201,7 @@ static int HasPalette(GUID pixel_format) {
|
||||
|
||||
static int HasAlpha(IWICImagingFactory* const factory,
|
||||
IWICBitmapDecoder* const decoder,
|
||||
IWICBitmapFrameDecode* const frame,
|
||||
GUID pixel_format) {
|
||||
IWICBitmapFrameDecode* const frame, GUID pixel_format) {
|
||||
int has_alpha;
|
||||
if (HasPalette(pixel_format)) {
|
||||
IWICPalette* frame_palette = NULL;
|
||||
@@ -245,21 +236,20 @@ static int HasAlpha(IWICImagingFactory* const factory,
|
||||
return has_alpha;
|
||||
}
|
||||
|
||||
int ReadPictureWithWIC(const char* const filename,
|
||||
WebPPicture* const pic, int keep_alpha,
|
||||
Metadata* const metadata) {
|
||||
int ReadPictureWithWIC(const char* const filename, WebPPicture* const pic,
|
||||
int keep_alpha, Metadata* const metadata) {
|
||||
// From Microsoft SDK 6.0a -- ks.h
|
||||
// Define a local copy to avoid link errors under mingw.
|
||||
WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
static const WICFormatImporter kAlphaFormatImporters[] = {
|
||||
{ &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
|
||||
{ &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
|
||||
{ NULL, 0, NULL },
|
||||
{&GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA},
|
||||
{&GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA},
|
||||
{NULL, 0, NULL},
|
||||
};
|
||||
static const WICFormatImporter kNonAlphaFormatImporters[] = {
|
||||
{ &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
|
||||
{ &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
|
||||
{ NULL, 0, NULL },
|
||||
{&GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR},
|
||||
{&GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB},
|
||||
{NULL, 0, NULL},
|
||||
};
|
||||
HRESULT hr = S_OK;
|
||||
IWICBitmapFrameDecode* frame = NULL;
|
||||
@@ -274,26 +264,20 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
const WICFormatImporter* importer = NULL;
|
||||
GUID src_container_format = GUID_NULL_;
|
||||
// From Windows Kits\10\Include\10.0.19041.0\um\wincodec.h
|
||||
WEBP_DEFINE_GUID(GUID_ContainerFormatWebp_,
|
||||
0xe094b0e2, 0x67f2, 0x45b3,
|
||||
0xb0, 0xea, 0x11, 0x53, 0x37, 0xca, 0x7c, 0xf3);
|
||||
WEBP_DEFINE_GUID(GUID_ContainerFormatWebp_, 0xe094b0e2, 0x67f2, 0x45b3, 0xb0,
|
||||
0xea, 0x11, 0x53, 0x37, 0xca, 0x7c, 0xf3);
|
||||
static const GUID* kAlphaContainers[] = {
|
||||
&GUID_ContainerFormatBmp,
|
||||
&GUID_ContainerFormatPng,
|
||||
&GUID_ContainerFormatTiff,
|
||||
&GUID_ContainerFormatWebp_,
|
||||
NULL
|
||||
};
|
||||
&GUID_ContainerFormatBmp, &GUID_ContainerFormatPng,
|
||||
&GUID_ContainerFormatTiff, &GUID_ContainerFormatWebp_, NULL};
|
||||
int has_alpha = 0;
|
||||
int64_t stride;
|
||||
|
||||
if (filename == NULL || pic == NULL) return 0;
|
||||
|
||||
IFS(CoInitialize(NULL));
|
||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory),
|
||||
(LPVOID*)&factory));
|
||||
IFS(CoCreateInstance(
|
||||
MAKE_REFGUID(CLSID_WICImagingFactory), NULL, CLSCTX_INPROC_SERVER,
|
||||
MAKE_REFGUID(IID_IWICImagingFactory), (LPVOID*)&factory));
|
||||
if (hr == REGDB_E_CLASSNOTREG) {
|
||||
fprintf(stderr,
|
||||
"Couldn't access Windows Imaging Component (are you running "
|
||||
@@ -303,8 +287,7 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
// Prepare for image decoding.
|
||||
IFS(OpenInputStream(filename, &stream));
|
||||
IFS(IWICImagingFactory_CreateDecoderFromStream(
|
||||
factory, stream, NULL,
|
||||
WICDecodeMetadataCacheOnDemand, &decoder));
|
||||
factory, stream, NULL, WICDecodeMetadataCacheOnDemand, &decoder));
|
||||
IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count));
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (frame_count == 0) {
|
||||
@@ -338,18 +321,15 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
hr == S_OK && importer->import != NULL; ++importer) {
|
||||
BOOL can_convert;
|
||||
const HRESULT cchr = IWICFormatConverter_CanConvert(
|
||||
converter,
|
||||
MAKE_REFGUID(src_pixel_format),
|
||||
MAKE_REFGUID(*importer->pixel_format),
|
||||
&can_convert);
|
||||
converter, MAKE_REFGUID(src_pixel_format),
|
||||
MAKE_REFGUID(*importer->pixel_format), &can_convert);
|
||||
if (SUCCEEDED(cchr) && can_convert) break;
|
||||
}
|
||||
if (importer->import == NULL) hr = E_FAIL;
|
||||
|
||||
IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
|
||||
importer->pixel_format,
|
||||
WICBitmapDitherTypeNone,
|
||||
NULL, 0.0, WICBitmapPaletteTypeCustom));
|
||||
IFS(IWICFormatConverter_Initialize(
|
||||
converter, (IWICBitmapSource*)frame, importer->pixel_format,
|
||||
WICBitmapDitherTypeNone, NULL, 0.0, WICBitmapPaletteTypeCustom));
|
||||
|
||||
// Decode.
|
||||
IFS(IWICFormatConverter_GetSize(converter, &width, &height));
|
||||
@@ -361,18 +341,17 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
rgb = (BYTE*)malloc((size_t)stride * height);
|
||||
if (rgb == NULL)
|
||||
hr = E_OUTOFMEMORY;
|
||||
if (rgb == NULL) hr = E_OUTOFMEMORY;
|
||||
}
|
||||
IFS(IWICFormatConverter_CopyPixels(converter, NULL,
|
||||
(UINT)stride, (UINT)stride * height, rgb));
|
||||
IFS(IWICFormatConverter_CopyPixels(converter, NULL, (UINT)stride,
|
||||
(UINT)stride * height, rgb));
|
||||
|
||||
// WebP conversion.
|
||||
if (SUCCEEDED(hr)) {
|
||||
int ok;
|
||||
pic->width = width;
|
||||
pic->height = height;
|
||||
pic->use_argb = 1; // For WIC, we always force to argb
|
||||
pic->use_argb = 1; // For WIC, we always force to argb
|
||||
ok = importer->import(pic, rgb, (int)stride);
|
||||
if (!ok) hr = E_FAIL;
|
||||
}
|
||||
@@ -394,7 +373,7 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
free(rgb);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
#else // !HAVE_WINCODEC_H
|
||||
#else // !HAVE_WINCODEC_H
|
||||
int ReadPictureWithWIC(const char* const filename,
|
||||
struct WebPPicture* const pic, int keep_alpha,
|
||||
struct Metadata* const metadata) {
|
||||
@@ -402,10 +381,11 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
(void)pic;
|
||||
(void)keep_alpha;
|
||||
(void)metadata;
|
||||
fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. "
|
||||
"Visual Studio and mingw-w64 builds support WIC. Make sure "
|
||||
"wincodec.h detection is working correctly if using autoconf "
|
||||
"and HAVE_WINCODEC_H is defined before building.\n");
|
||||
fprintf(stderr,
|
||||
"Windows Imaging Component (WIC) support not compiled. "
|
||||
"Visual Studio and mingw-w64 builds support WIC. Make sure "
|
||||
"wincodec.h detection is working correctly if using autoconf "
|
||||
"and HAVE_WINCODEC_H is defined before building.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif // HAVE_WINCODEC_H
|
||||
|
||||
@@ -28,7 +28,7 @@ int ReadPictureWithWIC(const char* const filename,
|
||||
struct Metadata* const metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_IMAGEIO_WICDEC_H_
|
||||
|
||||
@@ -31,8 +31,8 @@ readonly OLDPATH=${PATH}
|
||||
|
||||
# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
|
||||
# Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
|
||||
PLATFORMS="iPhoneSimulator iPhoneSimulator64"
|
||||
PLATFORMS+=" iPhoneOS-V7 iPhoneOS-V7s iPhoneOS-V7-arm64"
|
||||
PLATFORMS="iPhoneSimulator64"
|
||||
PLATFORMS+=" iPhoneOS-arm64"
|
||||
readonly PLATFORMS
|
||||
readonly SRCDIR=$(dirname $0)
|
||||
readonly TOPDIR=$(pwd)
|
||||
@@ -53,7 +53,7 @@ DEMUXLIBLIST=''
|
||||
if [[ -z "${SDK}" ]]; then
|
||||
echo "iOS SDK not available"
|
||||
exit 1
|
||||
elif [[ ${SDK%%.*} -gt 8 ]]; then
|
||||
elif [[ ${SDK%%.*} -gt 8 && "${XCODE%%.*}" -lt 16 ]]; then
|
||||
EXTRA_CFLAGS="-fembed-bitcode"
|
||||
elif [[ ${SDK%%.*} -le 6 ]]; then
|
||||
echo "You need iOS SDK version 6.0 or above"
|
||||
@@ -98,7 +98,7 @@ fi
|
||||
|
||||
for PLATFORM in ${PLATFORMS}; do
|
||||
ARCH2=""
|
||||
if [[ "${PLATFORM}" == "iPhoneOS-V7-arm64" ]]; then
|
||||
if [[ "${PLATFORM}" == "iPhoneOS-arm64" ]]; then
|
||||
PLATFORM="iPhoneOS"
|
||||
ARCH="aarch64"
|
||||
ARCH2="arm64"
|
||||
|
||||
14
man/cwebp.1
14
man/cwebp.1
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH CWEBP 1 "July 18, 2024"
|
||||
.TH CWEBP 1 "April 10, 2025"
|
||||
.SH NAME
|
||||
cwebp \- compress an image file to a WebP file
|
||||
.SH SYNOPSIS
|
||||
@@ -102,6 +102,14 @@ If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
|
||||
the value will be calculated preserving the aspect\-ratio. Note: scaling
|
||||
is applied \fIafter\fP cropping.
|
||||
.TP
|
||||
.BI \-resize_mode " string
|
||||
Specify the behavior of the \fB\-resize\fP option. Possible values are:
|
||||
\fBdown_only\fP, \fBup_only\fP, \fBalways\fP (default). \fBdown_only\fP will
|
||||
use the values specified by \fB\-resize\fP if \fIeither\fP the input width or
|
||||
height are larger than the given dimensions. Similarly, \fBup_only\fP will only
|
||||
resize if \fIeither\fP the input width or height are smaller than the given
|
||||
dimensions.
|
||||
.TP
|
||||
.B \-mt
|
||||
Use multi\-threading for encoding, if possible.
|
||||
.TP
|
||||
@@ -180,8 +188,8 @@ Disable strong filtering (if filtering is being used thanks to the
|
||||
\fB\-f\fP option) and use simple filtering instead.
|
||||
.TP
|
||||
.B \-sharp_yuv
|
||||
Use more accurate and sharper RGB->YUV conversion if needed. Note that this
|
||||
process is slower than the default 'fast' RGB->YUV conversion.
|
||||
Use more accurate and sharper RGB->YUV conversion. Note that this process is
|
||||
slower than the default 'fast' RGB->YUV conversion.
|
||||
.TP
|
||||
.BI \-sns " int
|
||||
Specify the amplitude of the spatial noise shaping. Spatial noise shaping
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH GIF2WEBP 1 "July 18, 2024"
|
||||
.TH GIF2WEBP 1 "November 4, 2024"
|
||||
.SH NAME
|
||||
gif2webp \- Convert a GIF image to WebP
|
||||
.SH SYNOPSIS
|
||||
@@ -39,6 +39,18 @@ Encode the image using lossy compression.
|
||||
Mixed compression mode: optimize compression of the image by picking either
|
||||
lossy or lossless compression for each frame heuristically.
|
||||
.TP
|
||||
.BI \-near_lossless " int
|
||||
Specify the level of near\-lossless image preprocessing. This option adjusts
|
||||
pixel values to help compressibility, but has minimal impact on the visual
|
||||
quality. It triggers lossless compression mode automatically. The range is 0
|
||||
(maximum preprocessing) to 100 (no preprocessing, the default). The typical
|
||||
value is around 60. Note that lossy with \fB\-q 100\fP can at times yield
|
||||
better results.
|
||||
.TP
|
||||
.B \-sharp_yuv
|
||||
Use more accurate and sharper RGB->YUV conversion. Note that this process is
|
||||
slower than the default 'fast' RGB->YUV conversion.
|
||||
.TP
|
||||
.BI \-q " float
|
||||
Specify the compression factor for RGB channels between 0 and 100. The default
|
||||
is 75.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH IMG2WEBP 1 "July 18, 2024"
|
||||
.TH IMG2WEBP 1 "November 26, 2024"
|
||||
.SH NAME
|
||||
img2webp \- create animated WebP file from a sequence of input images.
|
||||
.SH SYNOPSIS
|
||||
@@ -53,8 +53,8 @@ value is around 60. Note that lossy with \fB\-q 100\fP can at times yield
|
||||
better results.
|
||||
.TP
|
||||
.B \-sharp_yuv
|
||||
Use more accurate and sharper RGB->YUV conversion if needed. Note that this
|
||||
process is slower than the default 'fast' RGB->YUV conversion.
|
||||
Use more accurate and sharper RGB->YUV conversion. Note that this process is
|
||||
slower than the default 'fast' RGB->YUV conversion.
|
||||
.TP
|
||||
.BI \-loop " int
|
||||
Specifies the number of times the animation should loop. Using '0'
|
||||
@@ -88,6 +88,15 @@ Specify the compression factor between 0 and 100. The default is 75.
|
||||
Specify the compression method to use. This parameter controls the
|
||||
trade off between encoding speed and the compressed file size and quality.
|
||||
Possible values range from 0 to 6. Default value is 4.
|
||||
When higher values are used, the encoder will spend more time inspecting
|
||||
additional encoding possibilities and decide on the quality gain.
|
||||
Lower value can result in faster processing time at the expense of
|
||||
larger file size and lower compression quality.
|
||||
.TP
|
||||
.B \-exact, \-noexact
|
||||
Preserve or alter RGB values in transparent area. The default is
|
||||
\fB-noexact\fP, to help compressibility. Note \fB\-noexact\fP may cause
|
||||
artifacts in frames compressed with \fB\-lossy\fP.
|
||||
|
||||
.SH EXIT STATUS
|
||||
If there were no problems during execution, \fBimg2webp\fP exits with the value
|
||||
|
||||
@@ -33,7 +33,7 @@ libsharpyuv_la_SOURCES += sharpyuv_gamma.c sharpyuv_gamma.h
|
||||
libsharpyuv_la_SOURCES += sharpyuv.c sharpyuv.h
|
||||
|
||||
libsharpyuv_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
libsharpyuv_la_LDFLAGS = -no-undefined -version-info 1:0:1 -lm
|
||||
libsharpyuv_la_LDFLAGS = -no-undefined -version-info 1:2:1 -lm
|
||||
libsharpyuv_la_LIBADD =
|
||||
libsharpyuv_la_LIBADD += libsharpyuv_sse2.la
|
||||
libsharpyuv_la_LIBADD += libsharpyuv_neon.la
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,0,4,0
|
||||
PRODUCTVERSION 0,0,4,0
|
||||
FILEVERSION 0,0,4,2
|
||||
PRODUCTVERSION 0,0,4,2
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -24,12 +24,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libsharpyuv DLL"
|
||||
VALUE "FileVersion", "0.4.0"
|
||||
VALUE "FileVersion", "0.4.2"
|
||||
VALUE "InternalName", "libsharpyuv.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2024"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2025"
|
||||
VALUE "OriginalFilename", "libsharpyuv.dll"
|
||||
VALUE "ProductName", "SharpYuv Library"
|
||||
VALUE "ProductVersion", "0.4.0"
|
||||
VALUE "ProductVersion", "0.4.2"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -19,16 +19,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/webp/types.h"
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
#include "sharpyuv/sharpyuv_gamma.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int SharpYuvGetVersion(void) {
|
||||
return SHARPYUV_VERSION;
|
||||
}
|
||||
int SharpYuvGetVersion(void) { return SHARPYUV_VERSION; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Sharp RGB->YUV conversion
|
||||
@@ -49,8 +47,8 @@ static int GetPrecisionShift(int rgb_bit_depth) {
|
||||
: (kMaxBitDepth - rgb_bit_depth);
|
||||
}
|
||||
|
||||
typedef int16_t fixed_t; // signed type with extra precision for UV
|
||||
typedef uint16_t fixed_y_t; // unsigned type with extra precision for W
|
||||
typedef int16_t fixed_t; // signed type with extra precision for UV
|
||||
typedef uint16_t fixed_y_t; // unsigned type with extra precision for W
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -75,9 +73,8 @@ static int RGBToGray(int64_t r, int64_t g, int64_t b) {
|
||||
}
|
||||
|
||||
static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
|
||||
int rgb_bit_depth,
|
||||
int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
const uint32_t A = SharpYuvGammaToLinear(a, bit_depth, transfer_type);
|
||||
const uint32_t B = SharpYuvGammaToLinear(b, bit_depth, transfer_type);
|
||||
const uint32_t C = SharpYuvGammaToLinear(c, bit_depth, transfer_type);
|
||||
@@ -87,9 +84,8 @@ static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
|
||||
}
|
||||
|
||||
static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
|
||||
int rgb_bit_depth,
|
||||
int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
int i = 0;
|
||||
do {
|
||||
const uint32_t R =
|
||||
@@ -104,24 +100,24 @@ static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
|
||||
}
|
||||
|
||||
static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
|
||||
fixed_t* dst, int uv_w, int rgb_bit_depth,
|
||||
fixed_t* dst, int uv_w, int bit_depth,
|
||||
SharpYuvTransferFunctionType transfer_type) {
|
||||
int i = 0;
|
||||
do {
|
||||
const int r =
|
||||
ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0],
|
||||
src2[0 * uv_w + 1], rgb_bit_depth, transfer_type);
|
||||
src2[0 * uv_w + 1], bit_depth, transfer_type);
|
||||
const int g =
|
||||
ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0],
|
||||
src2[2 * uv_w + 1], rgb_bit_depth, transfer_type);
|
||||
src2[2 * uv_w + 1], bit_depth, transfer_type);
|
||||
const int b =
|
||||
ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0],
|
||||
src2[4 * uv_w + 1], rgb_bit_depth, transfer_type);
|
||||
src2[4 * uv_w + 1], bit_depth, transfer_type);
|
||||
const int W = RGBToGray(r, g, b);
|
||||
dst[0 * uv_w] = (fixed_t)(r - W);
|
||||
dst[1 * uv_w] = (fixed_t)(g - W);
|
||||
dst[2 * uv_w] = (fixed_t)(b - W);
|
||||
dst += 1;
|
||||
dst += 1;
|
||||
src1 += 2;
|
||||
src2 += 2;
|
||||
} while (++i < uv_w);
|
||||
@@ -148,12 +144,9 @@ static WEBP_INLINE int Shift(int v, int shift) {
|
||||
return (shift >= 0) ? (v << shift) : (v >> -shift);
|
||||
}
|
||||
|
||||
static void ImportOneRow(const uint8_t* const r_ptr,
|
||||
const uint8_t* const g_ptr,
|
||||
const uint8_t* const b_ptr,
|
||||
int rgb_step,
|
||||
int rgb_bit_depth,
|
||||
int pic_width,
|
||||
static void ImportOneRow(const uint8_t* const r_ptr, const uint8_t* const g_ptr,
|
||||
const uint8_t* const b_ptr, int rgb_step,
|
||||
int rgb_bit_depth, int pic_width,
|
||||
fixed_y_t* const dst) {
|
||||
// Convert the rgb_step from a number of bytes to a number of uint8_t or
|
||||
// uint16_t values depending the bit depth.
|
||||
@@ -181,18 +174,13 @@ static void ImportOneRow(const uint8_t* const r_ptr,
|
||||
}
|
||||
|
||||
static void InterpolateTwoRows(const fixed_y_t* const best_y,
|
||||
const fixed_t* prev_uv,
|
||||
const fixed_t* cur_uv,
|
||||
const fixed_t* next_uv,
|
||||
int w,
|
||||
fixed_y_t* out1,
|
||||
fixed_y_t* out2,
|
||||
int rgb_bit_depth) {
|
||||
const fixed_t* prev_uv, const fixed_t* cur_uv,
|
||||
const fixed_t* next_uv, int w, fixed_y_t* out1,
|
||||
fixed_y_t* out2, int bit_depth) {
|
||||
const int uv_w = w >> 1;
|
||||
const int len = (w - 1) >> 1; // length to filter
|
||||
const int len = (w - 1) >> 1; // length to filter
|
||||
int k = 3;
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
while (k-- > 0) { // process each R/G/B segments in turn
|
||||
while (k-- > 0) { // process each R/G/B segments in turn
|
||||
// special boundary case for i==0
|
||||
out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0], bit_depth);
|
||||
out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w], bit_depth);
|
||||
@@ -212,7 +200,7 @@ static void InterpolateTwoRows(const fixed_y_t* const best_y,
|
||||
out1 += w;
|
||||
out2 += w;
|
||||
prev_uv += uv_w;
|
||||
cur_uv += uv_w;
|
||||
cur_uv += uv_w;
|
||||
next_uv += uv_w;
|
||||
}
|
||||
}
|
||||
@@ -220,16 +208,16 @@ static void InterpolateTwoRows(const fixed_y_t* const best_y,
|
||||
static WEBP_INLINE int RGBToYUVComponent(int r, int g, int b,
|
||||
const int coeffs[4], int sfix) {
|
||||
const int srounder = 1 << (YUV_FIX + sfix - 1);
|
||||
const int luma = coeffs[0] * r + coeffs[1] * g + coeffs[2] * b +
|
||||
coeffs[3] + srounder;
|
||||
const int luma =
|
||||
coeffs[0] * r + coeffs[1] * g + coeffs[2] * b + coeffs[3] + srounder;
|
||||
return (luma >> (YUV_FIX + sfix));
|
||||
}
|
||||
|
||||
static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
||||
uint8_t* y_ptr, int y_stride, uint8_t* u_ptr,
|
||||
int u_stride, uint8_t* v_ptr, int v_stride,
|
||||
int rgb_bit_depth,
|
||||
int yuv_bit_depth, int width, int height,
|
||||
int rgb_bit_depth, int yuv_bit_depth, int width,
|
||||
int height,
|
||||
const SharpYuvConversionMatrix* yuv_matrix) {
|
||||
int i, j;
|
||||
const fixed_t* const best_uv_base = best_uv;
|
||||
@@ -298,8 +286,6 @@ static void* SafeMalloc(uint64_t nmemb, size_t size) {
|
||||
return malloc((size_t)total_size);
|
||||
}
|
||||
|
||||
#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((uint64_t)(W) * (H), sizeof(T)))
|
||||
|
||||
static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
const uint8_t* b_ptr, int rgb_step, int rgb_stride,
|
||||
int rgb_bit_depth, uint8_t* y_ptr, int y_stride,
|
||||
@@ -317,31 +303,42 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
uint64_t prev_diff_y_sum = ~0;
|
||||
int j, iter;
|
||||
|
||||
// TODO(skal): allocate one big memory chunk. But for now, it's easier
|
||||
// for valgrind debugging to have several chunks.
|
||||
fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch
|
||||
fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);
|
||||
fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);
|
||||
fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
|
||||
fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
|
||||
fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
|
||||
fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
|
||||
fixed_y_t* best_y = best_y_base;
|
||||
fixed_y_t* target_y = target_y_base;
|
||||
fixed_t* best_uv = best_uv_base;
|
||||
fixed_t* target_uv = target_uv_base;
|
||||
const uint64_t tmp_buffer_size = (uint64_t)w * 3 * 2;
|
||||
const uint64_t best_y_base_size = (uint64_t)w * h;
|
||||
const uint64_t target_y_base_size = (uint64_t)w * h;
|
||||
const uint64_t best_rgb_y_size = (uint64_t)w * 2;
|
||||
const uint64_t best_uv_base_size = (uint64_t)uv_w * 3 * uv_h;
|
||||
const uint64_t target_uv_base_size = (uint64_t)uv_w * 3 * uv_h;
|
||||
const uint64_t best_rgb_uv_size = (uint64_t)uv_w * 3;
|
||||
fixed_y_t* const tmp_buffer = (fixed_y_t*)SafeMalloc(
|
||||
(tmp_buffer_size + best_y_base_size + target_y_base_size +
|
||||
best_rgb_y_size) +
|
||||
(best_uv_base_size + target_uv_base_size + best_rgb_uv_size),
|
||||
sizeof(*tmp_buffer));
|
||||
fixed_y_t *best_y_base, *target_y_base, *best_rgb_y;
|
||||
fixed_t *best_uv_base, *target_uv_base, *best_rgb_uv;
|
||||
fixed_y_t *best_y, *target_y;
|
||||
fixed_t *best_uv, *target_uv;
|
||||
const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
|
||||
int ok;
|
||||
assert(w > 0);
|
||||
assert(h > 0);
|
||||
assert(sizeof(fixed_y_t) == sizeof(fixed_t));
|
||||
|
||||
if (best_y_base == NULL || best_uv_base == NULL ||
|
||||
target_y_base == NULL || target_uv_base == NULL ||
|
||||
best_rgb_y == NULL || best_rgb_uv == NULL ||
|
||||
tmp_buffer == NULL) {
|
||||
if (tmp_buffer == NULL) {
|
||||
ok = 0;
|
||||
goto End;
|
||||
}
|
||||
best_y_base = tmp_buffer + tmp_buffer_size;
|
||||
target_y_base = best_y_base + best_y_base_size;
|
||||
best_rgb_y = target_y_base + target_y_base_size;
|
||||
best_uv_base = (fixed_t*)(best_rgb_y + best_rgb_y_size);
|
||||
target_uv_base = best_uv_base + best_uv_base_size;
|
||||
best_rgb_uv = target_uv_base + target_uv_base_size;
|
||||
best_y = best_y_base;
|
||||
target_y = target_y_base;
|
||||
best_uv = best_uv_base;
|
||||
target_uv = target_uv_base;
|
||||
|
||||
// Import RGB samples to W/RGB representation.
|
||||
for (j = 0; j < height; j += 2) {
|
||||
@@ -350,8 +347,7 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
fixed_y_t* const src2 = tmp_buffer + 3 * w;
|
||||
|
||||
// prepare two rows of input
|
||||
ImportOneRow(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, width,
|
||||
src1);
|
||||
ImportOneRow(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, width, src1);
|
||||
if (!is_last_row) {
|
||||
ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
|
||||
rgb_step, rgb_bit_depth, width, src2);
|
||||
@@ -361,9 +357,9 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
StoreGray(src1, best_y + 0, w);
|
||||
StoreGray(src2, best_y + w, w);
|
||||
|
||||
UpdateW(src1, target_y, w, rgb_bit_depth, transfer_type);
|
||||
UpdateW(src2, target_y + w, w, rgb_bit_depth, transfer_type);
|
||||
UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth, transfer_type);
|
||||
UpdateW(src1, target_y, w, y_bit_depth, transfer_type);
|
||||
UpdateW(src2, target_y + w, w, y_bit_depth, transfer_type);
|
||||
UpdateChroma(src1, src2, target_uv, uv_w, y_bit_depth, transfer_type);
|
||||
memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
|
||||
best_y += 2 * w;
|
||||
best_uv += 3 * uv_w;
|
||||
@@ -390,15 +386,15 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
fixed_y_t* const src2 = tmp_buffer + 3 * w;
|
||||
{
|
||||
const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
|
||||
InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w,
|
||||
src1, src2, rgb_bit_depth);
|
||||
InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w, src1, src2,
|
||||
y_bit_depth);
|
||||
prev_uv = cur_uv;
|
||||
cur_uv = next_uv;
|
||||
}
|
||||
|
||||
UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth, transfer_type);
|
||||
UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth, transfer_type);
|
||||
UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth, transfer_type);
|
||||
UpdateW(src1, best_rgb_y + 0 * w, w, y_bit_depth, transfer_type);
|
||||
UpdateW(src2, best_rgb_y + 1 * w, w, y_bit_depth, transfer_type);
|
||||
UpdateChroma(src1, src2, best_rgb_uv, uv_w, y_bit_depth, transfer_type);
|
||||
|
||||
// update two rows of Y and one row of RGB
|
||||
diff_y_sum +=
|
||||
@@ -424,32 +420,26 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
u_stride, v_ptr, v_stride, rgb_bit_depth, yuv_bit_depth,
|
||||
width, height, yuv_matrix);
|
||||
|
||||
End:
|
||||
free(best_y_base);
|
||||
free(best_uv_base);
|
||||
free(target_y_base);
|
||||
free(target_uv_base);
|
||||
free(best_rgb_y);
|
||||
free(best_rgb_uv);
|
||||
End:
|
||||
free(tmp_buffer);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#undef SAFE_ALLOC
|
||||
|
||||
#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
|
||||
#include <pthread.h> // NOLINT
|
||||
|
||||
#define LOCK_ACCESS \
|
||||
static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \
|
||||
if (pthread_mutex_lock(&sharpyuv_lock)) return
|
||||
#define UNLOCK_ACCESS_AND_RETURN \
|
||||
do { \
|
||||
(void)pthread_mutex_unlock(&sharpyuv_lock); \
|
||||
return; \
|
||||
} while (0)
|
||||
#define LOCK_ACCESS \
|
||||
static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \
|
||||
if (pthread_mutex_lock(&sharpyuv_lock)) return
|
||||
#define UNLOCK_ACCESS_AND_RETURN \
|
||||
do { \
|
||||
(void)pthread_mutex_unlock(&sharpyuv_lock); \
|
||||
return; \
|
||||
} while (0)
|
||||
#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
|
||||
#define LOCK_ACCESS do {} while (0)
|
||||
#define LOCK_ACCESS \
|
||||
do { \
|
||||
} while (0)
|
||||
#define UNLOCK_ACCESS_AND_RETURN return
|
||||
#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32)
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ extern "C" {
|
||||
// SharpYUV API version following the convention from semver.org
|
||||
#define SHARPYUV_VERSION_MAJOR 0
|
||||
#define SHARPYUV_VERSION_MINOR 4
|
||||
#define SHARPYUV_VERSION_PATCH 0
|
||||
#define SHARPYUV_VERSION_PATCH 2
|
||||
// Version as a uint32_t. The major number is the high 8 bits.
|
||||
// The minor number is the middle 8 bits. The patch number is the low 16 bits.
|
||||
#define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \
|
||||
@@ -66,10 +66,17 @@ extern "C" {
|
||||
SHARPYUV_EXTERN int SharpYuvGetVersion(void);
|
||||
|
||||
// RGB to YUV conversion matrix, in 16 bit fixed point.
|
||||
// y = rgb_to_y[0] * r + rgb_to_y[1] * g + rgb_to_y[2] * b + rgb_to_y[3]
|
||||
// u = rgb_to_u[0] * r + rgb_to_u[1] * g + rgb_to_u[2] * b + rgb_to_u[3]
|
||||
// v = rgb_to_v[0] * r + rgb_to_v[1] * g + rgb_to_v[2] * b + rgb_to_v[3]
|
||||
// Then y, u and v values are divided by 1<<16 and rounded.
|
||||
// y_ = rgb_to_y[0] * r + rgb_to_y[1] * g + rgb_to_y[2] * b + rgb_to_y[3]
|
||||
// u_ = rgb_to_u[0] * r + rgb_to_u[1] * g + rgb_to_u[2] * b + rgb_to_u[3]
|
||||
// v_ = rgb_to_v[0] * r + rgb_to_v[1] * g + rgb_to_v[2] * b + rgb_to_v[3]
|
||||
// Then the values are divided by 1<<16 and rounded.
|
||||
// y = (y_ + (1 << 15)) >> 16
|
||||
// u = (u_ + (1 << 15)) >> 16
|
||||
// v = (v_ + (1 << 15)) >> 16
|
||||
//
|
||||
// Typically, the offset values rgb_to_y[3], rgb_to_u[3] and rgb_to_v[3] depend
|
||||
// on the input's bit depth, e.g., rgb_to_u[3] = 1 << (rgb_bit_depth - 1 + 16).
|
||||
// See also sharpyuv_csp.h to get a predefined matrix or generate a matrix.
|
||||
typedef struct {
|
||||
int rgb_to_y[4];
|
||||
int rgb_to_u[4];
|
||||
@@ -127,6 +134,8 @@ typedef enum SharpYuvTransferFunctionType {
|
||||
// adjacent pixels on the y, u and v channels. If yuv_bit_depth > 8, they
|
||||
// should be multiples of 2.
|
||||
// width, height: width and height of the image in pixels
|
||||
// yuv_matrix: RGB to YUV conversion matrix. The matrix values typically
|
||||
// depend on the input's rgb_bit_depth.
|
||||
// This function calls SharpYuvConvertWithOptions with a default transfer
|
||||
// function of kSharpYuvTransferFunctionSrgb.
|
||||
SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
|
||||
static int ToFixed16(float f) { return (int)floor(f * (1 << 16) + 0.5f); }
|
||||
|
||||
void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space,
|
||||
@@ -22,16 +24,16 @@ void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space,
|
||||
const float kr = yuv_color_space->kr;
|
||||
const float kb = yuv_color_space->kb;
|
||||
const float kg = 1.0f - kr - kb;
|
||||
const float cr = 0.5f / (1.0f - kb);
|
||||
const float cb = 0.5f / (1.0f - kr);
|
||||
const float cb = 0.5f / (1.0f - kb);
|
||||
const float cr = 0.5f / (1.0f - kr);
|
||||
|
||||
const int shift = yuv_color_space->bit_depth - 8;
|
||||
|
||||
const float denom = (float)((1 << yuv_color_space->bit_depth) - 1);
|
||||
float scale_y = 1.0f;
|
||||
float add_y = 0.0f;
|
||||
float scale_u = cr;
|
||||
float scale_v = cb;
|
||||
float scale_u = cb;
|
||||
float scale_v = cr;
|
||||
float add_uv = (float)(128 << shift);
|
||||
assert(yuv_color_space->bit_depth >= 8);
|
||||
|
||||
@@ -59,35 +61,39 @@ void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space,
|
||||
}
|
||||
|
||||
// Matrices are in YUV_FIX fixed point precision.
|
||||
// WebP's matrix, similar but not identical to kRec601LimitedMatrix.
|
||||
// WebP's matrix, similar but not identical to kRec601LimitedMatrix
|
||||
// Derived using the following formulas:
|
||||
// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
|
||||
// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
|
||||
// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
|
||||
static const SharpYuvConversionMatrix kWebpMatrix = {
|
||||
{16839, 33059, 6420, 16 << 16},
|
||||
{-9719, -19081, 28800, 128 << 16},
|
||||
{28800, -24116, -4684, 128 << 16},
|
||||
{16839, 33059, 6420, 16 << 16},
|
||||
{-9719, -19081, 28800, 128 << 16},
|
||||
{28800, -24116, -4684, 128 << 16},
|
||||
};
|
||||
// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeLimited
|
||||
// Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeLimited
|
||||
static const SharpYuvConversionMatrix kRec601LimitedMatrix = {
|
||||
{16829, 33039, 6416, 16 << 16},
|
||||
{-9714, -19071, 28784, 128 << 16},
|
||||
{28784, -24103, -4681, 128 << 16},
|
||||
{16829, 33039, 6416, 16 << 16},
|
||||
{-9714, -19071, 28784, 128 << 16},
|
||||
{28784, -24103, -4681, 128 << 16},
|
||||
};
|
||||
// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeFull
|
||||
// Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeFull
|
||||
static const SharpYuvConversionMatrix kRec601FullMatrix = {
|
||||
{19595, 38470, 7471, 0},
|
||||
{-11058, -21710, 32768, 128 << 16},
|
||||
{32768, -27439, -5329, 128 << 16},
|
||||
{19595, 38470, 7471, 0},
|
||||
{-11058, -21710, 32768, 128 << 16},
|
||||
{32768, -27439, -5329, 128 << 16},
|
||||
};
|
||||
// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeLimited
|
||||
// Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeLimited
|
||||
static const SharpYuvConversionMatrix kRec709LimitedMatrix = {
|
||||
{11966, 40254, 4064, 16 << 16},
|
||||
{-6596, -22189, 28784, 128 << 16},
|
||||
{28784, -26145, -2639, 128 << 16},
|
||||
{11966, 40254, 4064, 16 << 16},
|
||||
{-6596, -22189, 28784, 128 << 16},
|
||||
{28784, -26145, -2639, 128 << 16},
|
||||
};
|
||||
// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeFull
|
||||
// Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeFull
|
||||
static const SharpYuvConversionMatrix kRec709FullMatrix = {
|
||||
{13933, 46871, 4732, 0},
|
||||
{-7509, -25259, 32768, 128 << 16},
|
||||
{32768, -29763, -3005, 128 << 16},
|
||||
{13933, 46871, 4732, 0},
|
||||
{-7509, -25259, 32768, 128 << 16},
|
||||
{32768, -29763, -3005, 128 << 16},
|
||||
};
|
||||
|
||||
const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix(
|
||||
|
||||
@@ -20,8 +20,8 @@ extern "C" {
|
||||
|
||||
// Range of YUV values.
|
||||
typedef enum {
|
||||
kSharpYuvRangeFull, // YUV values between [0;255] (for 8 bit)
|
||||
kSharpYuvRangeLimited // Y in [16;235], YUV in [16;240] (for 8 bit)
|
||||
kSharpYuvRangeFull, // YUV values between [0;255] (for 8 bit)
|
||||
kSharpYuvRangeLimited // Y in [16;235], YUV in [16;240] (for 8 bit)
|
||||
} SharpYuvRange;
|
||||
|
||||
// Constants that define a YUV color space.
|
||||
@@ -41,10 +41,15 @@ SHARPYUV_EXTERN void SharpYuvComputeConversionMatrix(
|
||||
|
||||
// Enums for precomputed conversion matrices.
|
||||
typedef enum {
|
||||
// WebP's matrix, similar but not identical to kSharpYuvMatrixRec601Limited
|
||||
kSharpYuvMatrixWebp = 0,
|
||||
// Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeLimited
|
||||
kSharpYuvMatrixRec601Limited,
|
||||
// Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeFull
|
||||
kSharpYuvMatrixRec601Full,
|
||||
// Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeLimited
|
||||
kSharpYuvMatrixRec709Limited,
|
||||
// Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeFull
|
||||
kSharpYuvMatrixRec709Full,
|
||||
kSharpYuvMatrixNum
|
||||
} SharpYuvMatrixType;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
// Gamma correction compensates loss of resolution during chroma subsampling.
|
||||
@@ -66,8 +67,7 @@ void SharpYuvInitGammaTables(void) {
|
||||
} else {
|
||||
value = (1. + a) * pow(g, 1. / kGammaF) - a;
|
||||
}
|
||||
kLinearToGammaTabS[v] =
|
||||
(uint32_t)(final_scale * value + 0.5);
|
||||
kLinearToGammaTabS[v] = (uint32_t)(final_scale * value + 0.5);
|
||||
}
|
||||
// to prevent small rounding errors to cause read-overflow:
|
||||
kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =
|
||||
@@ -121,10 +121,11 @@ static uint16_t FromLinearSrgb(uint32_t value, int bit_depth) {
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
static WEBP_INLINE float Roundf(float x) {
|
||||
if (x < 0)
|
||||
if (x < 0) {
|
||||
return (float)ceil((double)(x - 0.5f));
|
||||
else
|
||||
} else {
|
||||
return (float)floor((double)(x + 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE float Powf(float base, float exp) {
|
||||
@@ -197,7 +198,7 @@ static float ToLinearLog100(float gamma) {
|
||||
// The function is non-bijective so choose the middle of [0, 0.01].
|
||||
const float mid_interval = 0.01f / 2.f;
|
||||
return (gamma <= 0.0f) ? mid_interval
|
||||
: Powf(10.0f, 2.f * (MIN(gamma, 1.f) - 1.0f));
|
||||
: Powf(10.0f, 2.f * (MIN(gamma, 1.f) - 1.0f));
|
||||
}
|
||||
|
||||
static float FromLinearLog100(float linear) {
|
||||
@@ -208,12 +209,12 @@ static float ToLinearLog100Sqrt10(float gamma) {
|
||||
// The function is non-bijective so choose the middle of [0, 0.00316227766f[.
|
||||
const float mid_interval = 0.00316227766f / 2.f;
|
||||
return (gamma <= 0.0f) ? mid_interval
|
||||
: Powf(10.0f, 2.5f * (MIN(gamma, 1.f) - 1.0f));
|
||||
: Powf(10.0f, 2.5f * (MIN(gamma, 1.f) - 1.0f));
|
||||
}
|
||||
|
||||
static float FromLinearLog100Sqrt10(float linear) {
|
||||
return (linear < 0.00316227766f) ? 0.0f
|
||||
: 1.0f + Log10f(MIN(linear, 1.f)) / 2.5f;
|
||||
: 1.0f + Log10f(MIN(linear, 1.f)) / 2.5f;
|
||||
}
|
||||
|
||||
static float ToLinearIec61966(float gamma) {
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <arm_neon.h>
|
||||
|
||||
static uint16_t clip_NEON(int v, int max) {
|
||||
return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
|
||||
@@ -35,11 +35,11 @@ static uint64_t SharpYuvUpdateY_NEON(const uint16_t* ref, const uint16_t* src,
|
||||
const int16x8_t A = vreinterpretq_s16_u16(vld1q_u16(ref + i));
|
||||
const int16x8_t B = vreinterpretq_s16_u16(vld1q_u16(src + i));
|
||||
const int16x8_t C = vreinterpretq_s16_u16(vld1q_u16(dst + i));
|
||||
const int16x8_t D = vsubq_s16(A, B); // diff_y
|
||||
const int16x8_t F = vaddq_s16(C, D); // new_y
|
||||
const int16x8_t D = vsubq_s16(A, B); // diff_y
|
||||
const int16x8_t F = vaddq_s16(C, D); // new_y
|
||||
const uint16x8_t H =
|
||||
vreinterpretq_u16_s16(vmaxq_s16(vminq_s16(F, max), zero));
|
||||
const int16x8_t I = vabsq_s16(D); // abs(diff_y)
|
||||
const int16x8_t I = vabsq_s16(D); // abs(diff_y)
|
||||
vst1q_u16(dst + i, H);
|
||||
sum = vpadalq_u32(sum, vpaddlq_u16(vreinterpretq_u16_s16(I)));
|
||||
}
|
||||
@@ -60,8 +60,8 @@ static void SharpYuvUpdateRGB_NEON(const int16_t* ref, const int16_t* src,
|
||||
const int16x8_t A = vld1q_s16(ref + i);
|
||||
const int16x8_t B = vld1q_s16(src + i);
|
||||
const int16x8_t C = vld1q_s16(dst + i);
|
||||
const int16x8_t D = vsubq_s16(A, B); // diff_uv
|
||||
const int16x8_t E = vaddq_s16(C, D); // new_uv
|
||||
const int16x8_t D = vsubq_s16(A, B); // diff_uv
|
||||
const int16x8_t E = vaddq_s16(C, D); // new_uv
|
||||
vst1q_s16(dst + i, E);
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
|
||||
@@ -14,8 +14,11 @@
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
#include <stdlib.h>
|
||||
#include <emmintrin.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
static uint16_t clip_SSE2(int v, int max) {
|
||||
return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
|
||||
@@ -41,7 +44,7 @@ static uint64_t SharpYuvUpdateY_SSE2(const uint16_t* ref, const uint16_t* src,
|
||||
const __m128i F = _mm_add_epi16(C, D); // new_y
|
||||
const __m128i G = _mm_or_si128(E, one); // -1 or 1
|
||||
const __m128i H = _mm_max_epi16(_mm_min_epi16(F, max), zero);
|
||||
const __m128i I = _mm_madd_epi16(D, G); // sum(abs(...))
|
||||
const __m128i I = _mm_madd_epi16(D, G); // sum(abs(...))
|
||||
_mm_storeu_si128((__m128i*)(dst + i), H);
|
||||
sum = _mm_add_epi32(sum, I);
|
||||
}
|
||||
@@ -63,8 +66,8 @@ static void SharpYuvUpdateRGB_SSE2(const int16_t* ref, const int16_t* src,
|
||||
const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
|
||||
const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
|
||||
const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
|
||||
const __m128i D = _mm_sub_epi16(A, B); // diff_uv
|
||||
const __m128i E = _mm_add_epi16(C, D); // new_uv
|
||||
const __m128i D = _mm_sub_epi16(A, B); // diff_uv
|
||||
const __m128i E = _mm_add_epi16(C, D); // new_uv
|
||||
_mm_storeu_si128((__m128i*)(dst + i), E);
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
@@ -90,8 +93,8 @@ static void SharpYuvFilterRow16_SSE2(const int16_t* A, const int16_t* B,
|
||||
const __m128i a1b0 = _mm_add_epi16(a1, b0);
|
||||
const __m128i a0a1b0b1 = _mm_add_epi16(a0b1, a1b0); // A0+A1+B0+B1
|
||||
const __m128i a0a1b0b1_8 = _mm_add_epi16(a0a1b0b1, kCst8);
|
||||
const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1); // 2*(A0+B1)
|
||||
const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0); // 2*(A1+B0)
|
||||
const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1); // 2*(A0+B1)
|
||||
const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0); // 2*(A1+B0)
|
||||
const __m128i c0 = _mm_srai_epi16(_mm_add_epi16(a0b1_2, a0a1b0b1_8), 3);
|
||||
const __m128i c1 = _mm_srai_epi16(_mm_add_epi16(a1b0_2, a0a1b0b1_8), 3);
|
||||
const __m128i d0 = _mm_add_epi16(c1, a0);
|
||||
|
||||
@@ -36,7 +36,7 @@ libwebp_la_LIBADD += utils/libwebputils.la
|
||||
# other than the ones listed on the command line, i.e., after linking, it will
|
||||
# not have unresolved symbols. Some platforms (Windows among them) require all
|
||||
# symbols in shared libraries to be resolved at library creation.
|
||||
libwebp_la_LDFLAGS = -no-undefined -version-info 8:9:1
|
||||
libwebp_la_LDFLAGS = -no-undefined -version-info 9:0:2
|
||||
libwebpincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebp.pc
|
||||
|
||||
@@ -48,7 +48,7 @@ if BUILD_LIBWEBPDECODER
|
||||
libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
|
||||
libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
|
||||
|
||||
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 4:9:1
|
||||
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 5:0:2
|
||||
pkgconfig_DATA += libwebpdecoder.pc
|
||||
endif
|
||||
|
||||
|
||||
@@ -11,17 +11,23 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/dec/alphai_dec.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/quant_levels_dec_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ALPHDecoder object.
|
||||
|
||||
@@ -34,8 +40,8 @@ WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) {
|
||||
// Clears and deallocates an alpha decoder instance.
|
||||
static void ALPHDelete(ALPHDecoder* const dec) {
|
||||
if (dec != NULL) {
|
||||
VP8LDelete(dec->vp8l_dec_);
|
||||
dec->vp8l_dec_ = NULL;
|
||||
VP8LDelete(dec->vp8l_dec);
|
||||
dec->vp8l_dec = NULL;
|
||||
WebPSafeFree(dec);
|
||||
}
|
||||
}
|
||||
@@ -54,29 +60,28 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
|
||||
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
|
||||
int rsrv;
|
||||
VP8Io* const io = &dec->io_;
|
||||
VP8Io* const io = &dec->io;
|
||||
|
||||
assert(data != NULL && output != NULL && src_io != NULL);
|
||||
|
||||
VP8FiltersInit();
|
||||
dec->output_ = output;
|
||||
dec->width_ = src_io->width;
|
||||
dec->height_ = src_io->height;
|
||||
assert(dec->width_ > 0 && dec->height_ > 0);
|
||||
dec->output = output;
|
||||
dec->width = src_io->width;
|
||||
dec->height = src_io->height;
|
||||
assert(dec->width > 0 && dec->height > 0);
|
||||
|
||||
if (data_size <= ALPHA_HEADER_LEN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dec->method_ = (data[0] >> 0) & 0x03;
|
||||
dec->filter_ = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03);
|
||||
dec->pre_processing_ = (data[0] >> 4) & 0x03;
|
||||
dec->method = (data[0] >> 0) & 0x03;
|
||||
dec->filter = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03);
|
||||
dec->pre_processing = (data[0] >> 4) & 0x03;
|
||||
rsrv = (data[0] >> 6) & 0x03;
|
||||
if (dec->method_ < ALPHA_NO_COMPRESSION ||
|
||||
dec->method_ > ALPHA_LOSSLESS_COMPRESSION ||
|
||||
dec->filter_ >= WEBP_FILTER_LAST ||
|
||||
dec->pre_processing_ > ALPHA_PREPROCESSED_LEVELS ||
|
||||
rsrv != 0) {
|
||||
if (dec->method < ALPHA_NO_COMPRESSION ||
|
||||
dec->method > ALPHA_LOSSLESS_COMPRESSION ||
|
||||
dec->filter >= WEBP_FILTER_LAST ||
|
||||
dec->pre_processing > ALPHA_PREPROCESSED_LEVELS || rsrv != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -96,12 +101,17 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
io->crop_bottom = src_io->crop_bottom;
|
||||
// No need to copy the scaling parameters.
|
||||
|
||||
if (dec->method_ == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = dec->width_ * dec->height_;
|
||||
if (dec->method == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = dec->width * dec->height;
|
||||
ok = (alpha_data_size >= alpha_decoded_size);
|
||||
} else {
|
||||
assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION);
|
||||
ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size);
|
||||
assert(dec->method == ALPHA_LOSSLESS_COMPRESSION);
|
||||
{
|
||||
const uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha_data =
|
||||
WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, alpha_data,
|
||||
alpha_data_size);
|
||||
ok = VP8LDecodeAlphaHeader(dec, bounded_alpha_data, alpha_data_size);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
@@ -113,32 +123,32 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
// Returns false in case of bitstream error.
|
||||
WEBP_NODISCARD static int ALPHDecode(VP8Decoder* const dec, int row,
|
||||
int num_rows) {
|
||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||
const int width = alph_dec->width_;
|
||||
const int height = alph_dec->io_.crop_bottom;
|
||||
if (alph_dec->method_ == ALPHA_NO_COMPRESSION) {
|
||||
ALPHDecoder* const alph_dec = dec->alph_dec;
|
||||
const int width = alph_dec->width;
|
||||
const int height = alph_dec->io.crop_bottom;
|
||||
if (alph_dec->method == ALPHA_NO_COMPRESSION) {
|
||||
int y;
|
||||
const uint8_t* prev_line = dec->alpha_prev_line_;
|
||||
const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width;
|
||||
uint8_t* dst = dec->alpha_plane_ + row * width;
|
||||
assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]);
|
||||
assert(WebPUnfilters[alph_dec->filter_] != NULL);
|
||||
const uint8_t* prev_line = dec->alpha_prev_line;
|
||||
const uint8_t* deltas = dec->alpha_data + ALPHA_HEADER_LEN + row * width;
|
||||
uint8_t* dst = dec->alpha_plane + row * width;
|
||||
assert(deltas <= &dec->alpha_data[dec->alpha_data_size]);
|
||||
assert(WebPUnfilters[alph_dec->filter] != NULL);
|
||||
for (y = 0; y < num_rows; ++y) {
|
||||
WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width);
|
||||
WebPUnfilters[alph_dec->filter](prev_line, deltas, dst, width);
|
||||
prev_line = dst;
|
||||
dst += width;
|
||||
deltas += width;
|
||||
}
|
||||
dec->alpha_prev_line_ = prev_line;
|
||||
} else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION
|
||||
assert(alph_dec->vp8l_dec_ != NULL);
|
||||
dec->alpha_prev_line = prev_line;
|
||||
} else { // alph_dec->method == ALPHA_LOSSLESS_COMPRESSION
|
||||
assert(alph_dec->vp8l_dec != NULL);
|
||||
if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (row + num_rows >= height) {
|
||||
dec->is_alpha_decoded_ = 1;
|
||||
dec->is_alpha_decoded = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -148,25 +158,25 @@ WEBP_NODISCARD static int AllocateAlphaPlane(VP8Decoder* const dec,
|
||||
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) {
|
||||
assert(dec->alpha_plane_mem == NULL);
|
||||
dec->alpha_plane_mem =
|
||||
(uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane));
|
||||
if (dec->alpha_plane_mem == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"Alpha decoder initialization failed.");
|
||||
}
|
||||
dec->alpha_plane_ = dec->alpha_plane_mem_;
|
||||
dec->alpha_prev_line_ = NULL;
|
||||
dec->alpha_plane = dec->alpha_plane_mem;
|
||||
dec->alpha_prev_line = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WebPDeallocateAlphaMemory(VP8Decoder* const dec) {
|
||||
assert(dec != NULL);
|
||||
WebPSafeFree(dec->alpha_plane_mem_);
|
||||
dec->alpha_plane_mem_ = NULL;
|
||||
dec->alpha_plane_ = NULL;
|
||||
ALPHDelete(dec->alph_dec_);
|
||||
dec->alph_dec_ = NULL;
|
||||
WebPSafeFree(dec->alpha_plane_mem);
|
||||
dec->alpha_plane_mem = NULL;
|
||||
dec->alpha_plane = NULL;
|
||||
ALPHDelete(dec->alph_dec);
|
||||
dec->alph_dec = NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -184,46 +194,49 @@ WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dec->is_alpha_decoded_) {
|
||||
if (dec->alph_dec_ == NULL) { // Initialize decoder.
|
||||
dec->alph_dec_ = ALPHNew();
|
||||
if (dec->alph_dec_ == NULL) {
|
||||
if (!dec->is_alpha_decoded) {
|
||||
if (dec->alph_dec == NULL) { // Initialize decoder.
|
||||
dec->alph_dec = ALPHNew();
|
||||
if (dec->alph_dec == NULL) {
|
||||
VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"Alpha decoder initialization failed.");
|
||||
return NULL;
|
||||
}
|
||||
if (!AllocateAlphaPlane(dec, io)) goto Error;
|
||||
if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_,
|
||||
io, dec->alpha_plane_)) {
|
||||
VP8LDecoder* const vp8l_dec = dec->alph_dec_->vp8l_dec_;
|
||||
VP8SetError(dec,
|
||||
(vp8l_dec == NULL) ? VP8_STATUS_OUT_OF_MEMORY
|
||||
: vp8l_dec->status_,
|
||||
"Alpha decoder initialization failed.");
|
||||
if (!ALPHInit(dec->alph_dec, dec->alpha_data, dec->alpha_data_size, io,
|
||||
dec->alpha_plane)) {
|
||||
VP8LDecoder* const vp8l_dec = dec->alph_dec->vp8l_dec;
|
||||
VP8SetError(
|
||||
dec,
|
||||
(vp8l_dec == NULL) ? VP8_STATUS_OUT_OF_MEMORY : vp8l_dec->status,
|
||||
"Alpha decoder initialization failed.");
|
||||
goto Error;
|
||||
}
|
||||
// if we allowed use of alpha dithering, check whether it's needed at all
|
||||
if (dec->alph_dec_->pre_processing_ != ALPHA_PREPROCESSED_LEVELS) {
|
||||
dec->alpha_dithering_ = 0; // disable dithering
|
||||
if (dec->alph_dec->pre_processing != ALPHA_PREPROCESSED_LEVELS) {
|
||||
dec->alpha_dithering = 0; // disable dithering
|
||||
} else {
|
||||
num_rows = height - row; // decode everything in one pass
|
||||
num_rows = height - row; // decode everything in one pass
|
||||
}
|
||||
}
|
||||
|
||||
assert(dec->alph_dec_ != NULL);
|
||||
assert(dec->alph_dec != NULL);
|
||||
assert(row + num_rows <= height);
|
||||
if (!ALPHDecode(dec, row, num_rows)) goto Error;
|
||||
|
||||
if (dec->is_alpha_decoded_) { // finished?
|
||||
ALPHDelete(dec->alph_dec_);
|
||||
dec->alph_dec_ = NULL;
|
||||
if (dec->alpha_dithering_ > 0) {
|
||||
uint8_t* const alpha = dec->alpha_plane_ + io->crop_top * width
|
||||
+ io->crop_left;
|
||||
if (!WebPDequantizeLevels(alpha,
|
||||
io->crop_right - io->crop_left,
|
||||
io->crop_bottom - io->crop_top,
|
||||
width, dec->alpha_dithering_)) {
|
||||
if (dec->is_alpha_decoded) { // finished?
|
||||
ALPHDelete(dec->alph_dec);
|
||||
dec->alph_dec = NULL;
|
||||
if (dec->alpha_dithering > 0) {
|
||||
uint8_t* const alpha =
|
||||
dec->alpha_plane + io->crop_top * width + io->crop_left;
|
||||
uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha =
|
||||
WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(
|
||||
uint8_t*, alpha,
|
||||
(size_t)width*(io->crop_bottom - io->crop_top));
|
||||
if (!WebPDequantizeLevels(bounded_alpha, io->crop_right - io->crop_left,
|
||||
io->crop_bottom - io->crop_top, width,
|
||||
dec->alpha_dithering)) {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
@@ -231,9 +244,9 @@ WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
}
|
||||
|
||||
// Return a pointer to the current decoded row.
|
||||
return dec->alpha_plane_ + row * width;
|
||||
return dec->alpha_plane + row * width;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
WebPDeallocateAlphaMemory(dec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,13 @@
|
||||
#ifndef WEBP_DEC_ALPHAI_DEC_H_
|
||||
#define WEBP_DEC_ALPHAI_DEC_H_
|
||||
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/filters_utils.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -25,30 +30,30 @@ struct VP8LDecoder; // Defined in dec/vp8li.h.
|
||||
|
||||
typedef struct ALPHDecoder ALPHDecoder;
|
||||
struct ALPHDecoder {
|
||||
int width_;
|
||||
int height_;
|
||||
int method_;
|
||||
WEBP_FILTER_TYPE filter_;
|
||||
int pre_processing_;
|
||||
struct VP8LDecoder* vp8l_dec_;
|
||||
VP8Io io_;
|
||||
int use_8b_decode_; // Although alpha channel requires only 1 byte per
|
||||
// pixel, sometimes VP8LDecoder may need to allocate
|
||||
// 4 bytes per pixel internally during decode.
|
||||
uint8_t* output_;
|
||||
const uint8_t* prev_line_; // last output row (or NULL)
|
||||
int width;
|
||||
int height;
|
||||
int method;
|
||||
WEBP_FILTER_TYPE filter;
|
||||
int pre_processing;
|
||||
struct VP8LDecoder* vp8l_dec;
|
||||
VP8Io io;
|
||||
int use_8b_decode; // Although alpha channel requires only 1 byte per
|
||||
// pixel, sometimes VP8LDecoder may need to allocate
|
||||
// 4 bytes per pixel internally during decode.
|
||||
uint8_t* output;
|
||||
const uint8_t* prev_line; // last output row (or NULL)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
// Deallocate memory associated to dec->alpha_plane_ decoding
|
||||
// Deallocate memory associated to dec->alpha_plane decoding
|
||||
void WebPDeallocateAlphaMemory(VP8Decoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_DEC_ALPHAI_DEC_H_
|
||||
|
||||
@@ -11,32 +11,37 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer
|
||||
|
||||
// Number of bytes per pixel for the different color-spaces.
|
||||
static const uint8_t kModeBpp[MODE_LAST] = {
|
||||
3, 4, 3, 4, 4, 2, 2,
|
||||
4, 4, 4, 2, // pre-multiplied modes
|
||||
1, 1 };
|
||||
static const uint8_t kModeBpp[MODE_LAST] = {3, 4, 3, 4, 4, 2, 2, //
|
||||
4, 4, 4, 2, // pre-multiplied modes
|
||||
1, 1};
|
||||
|
||||
// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
|
||||
// Convert to an integer to handle both the unsigned/signed enum cases
|
||||
// without the need for casting to remove type limit warnings.
|
||||
static int IsValidColorspace(int webp_csp_mode) {
|
||||
int IsValidColorspace(int webp_csp_mode) {
|
||||
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
|
||||
}
|
||||
|
||||
// strictly speaking, the very last (or first, if flipped) row
|
||||
// doesn't require padding.
|
||||
#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
|
||||
((uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH))
|
||||
#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
|
||||
((uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH))
|
||||
|
||||
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
int ok = 1;
|
||||
@@ -45,9 +50,9 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
const int height = buffer->height;
|
||||
if (!IsValidColorspace(mode)) {
|
||||
ok = 0;
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
const WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_height = (height + 1) / 2;
|
||||
const int y_stride = abs(buf->y_stride);
|
||||
const int u_stride = abs(buf->u_stride);
|
||||
@@ -71,7 +76,7 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
ok &= (a_size <= buf->a_size);
|
||||
ok &= (buf->a != NULL);
|
||||
}
|
||||
} else { // RGB checks
|
||||
} else { // RGB checks
|
||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
const int stride = abs(buf->stride);
|
||||
const uint64_t size =
|
||||
@@ -123,7 +128,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
}
|
||||
buffer->private_memory = output;
|
||||
|
||||
if (!WebPIsRGBMode(mode)) { // YUVA initialization
|
||||
if (!WebPIsRGBMode(mode)) { // YUVA initialization
|
||||
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
buf->y = output;
|
||||
buf->y_stride = stride;
|
||||
@@ -181,14 +186,14 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
||||
if (buffer == NULL || width <= 0 || height <= 0) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
if (options != NULL) { // First, apply options if there is any.
|
||||
if (options != NULL) { // First, apply options if there is any.
|
||||
if (options->use_cropping) {
|
||||
const int cw = options->crop_width;
|
||||
const int ch = options->crop_height;
|
||||
const int x = options->crop_left & ~1;
|
||||
const int y = options->crop_top & ~1;
|
||||
if (!WebPCheckCropDimensions(width, height, x, y, cw, ch)) {
|
||||
return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
|
||||
return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
|
||||
}
|
||||
width = cw;
|
||||
height = ch;
|
||||
@@ -198,14 +203,14 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
int scaled_width = options->scaled_width;
|
||||
int scaled_height = options->scaled_height;
|
||||
if (!WebPRescalerGetScaledDimensions(
|
||||
width, height, &scaled_width, &scaled_height)) {
|
||||
if (!WebPRescalerGetScaledDimensions(width, height, &scaled_width,
|
||||
&scaled_height)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
width = scaled_width;
|
||||
height = scaled_height;
|
||||
#else
|
||||
return VP8_STATUS_INVALID_PARAM; // rescaling not supported
|
||||
return VP8_STATUS_INVALID_PARAM; // rescaling not supported
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -231,7 +236,7 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
|
||||
return 0; // version mismatch
|
||||
}
|
||||
if (buffer == NULL) return 0;
|
||||
memset(buffer, 0, sizeof(*buffer));
|
||||
WEBP_UNSAFE_MEMSET(buffer, 0, sizeof(*buffer));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -249,7 +254,7 @@ void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
||||
if (src != NULL && dst != NULL) {
|
||||
*dst = *src;
|
||||
if (src->private_memory != NULL) {
|
||||
dst->is_external_memory = 1; // dst buffer doesn't own the memory.
|
||||
dst->is_external_memory = 1; // dst buffer doesn't own the memory.
|
||||
dst->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
@@ -260,7 +265,7 @@ void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
|
||||
if (src != NULL && dst != NULL) {
|
||||
*dst = *src;
|
||||
if (src->private_memory != NULL) {
|
||||
src->is_external_memory = 1; // src relinquishes ownership
|
||||
src->is_external_memory = 1; // src relinquishes ownership
|
||||
src->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
@@ -285,8 +290,8 @@ VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf,
|
||||
} else {
|
||||
const WebPYUVABuffer* const src = &src_buf->u.YUVA;
|
||||
const WebPYUVABuffer* const dst = &dst_buf->u.YUVA;
|
||||
WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride,
|
||||
src_buf->width, src_buf->height);
|
||||
WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride, src_buf->width,
|
||||
src_buf->height);
|
||||
WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride,
|
||||
(src_buf->width + 1) / 2, (src_buf->height + 1) / 2);
|
||||
WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride,
|
||||
|
||||
@@ -15,40 +15,48 @@
|
||||
#define WEBP_DEC_COMMON_DEC_H_
|
||||
|
||||
// intra prediction modes
|
||||
enum { B_DC_PRED = 0, // 4x4 modes
|
||||
B_TM_PRED = 1,
|
||||
B_VE_PRED = 2,
|
||||
B_HE_PRED = 3,
|
||||
B_RD_PRED = 4,
|
||||
B_VR_PRED = 5,
|
||||
B_LD_PRED = 6,
|
||||
B_VL_PRED = 7,
|
||||
B_HD_PRED = 8,
|
||||
B_HU_PRED = 9,
|
||||
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
|
||||
enum {
|
||||
B_DC_PRED = 0, // 4x4 modes
|
||||
B_TM_PRED = 1,
|
||||
B_VE_PRED = 2,
|
||||
B_HE_PRED = 3,
|
||||
B_RD_PRED = 4,
|
||||
B_VR_PRED = 5,
|
||||
B_LD_PRED = 6,
|
||||
B_VL_PRED = 7,
|
||||
B_HD_PRED = 8,
|
||||
B_HU_PRED = 9,
|
||||
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
|
||||
|
||||
// Luma16 or UV modes
|
||||
DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
|
||||
H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
|
||||
B_PRED = NUM_BMODES, // refined I4x4 mode
|
||||
NUM_PRED_MODES = 4,
|
||||
// Luma16 or UV modes
|
||||
DC_PRED = B_DC_PRED,
|
||||
V_PRED = B_VE_PRED,
|
||||
H_PRED = B_HE_PRED,
|
||||
TM_PRED = B_TM_PRED,
|
||||
B_PRED = NUM_BMODES, // refined I4x4 mode
|
||||
NUM_PRED_MODES = 4,
|
||||
|
||||
// special modes
|
||||
B_DC_PRED_NOTOP = 4,
|
||||
B_DC_PRED_NOLEFT = 5,
|
||||
B_DC_PRED_NOTOPLEFT = 6,
|
||||
NUM_B_DC_MODES = 7 };
|
||||
// special modes
|
||||
B_DC_PRED_NOTOP = 4,
|
||||
B_DC_PRED_NOLEFT = 5,
|
||||
B_DC_PRED_NOTOPLEFT = 6,
|
||||
NUM_B_DC_MODES = 7
|
||||
};
|
||||
|
||||
enum { MB_FEATURE_TREE_PROBS = 3,
|
||||
NUM_MB_SEGMENTS = 4,
|
||||
NUM_REF_LF_DELTAS = 4,
|
||||
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
|
||||
MAX_NUM_PARTITIONS = 8,
|
||||
// Probabilities
|
||||
NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
|
||||
NUM_BANDS = 8,
|
||||
NUM_CTX = 3,
|
||||
NUM_PROBAS = 11
|
||||
};
|
||||
enum {
|
||||
MB_FEATURE_TREE_PROBS = 3,
|
||||
NUM_MB_SEGMENTS = 4,
|
||||
NUM_REF_LF_DELTAS = 4,
|
||||
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
|
||||
MAX_NUM_PARTITIONS = 8,
|
||||
// Probabilities
|
||||
NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
|
||||
NUM_BANDS = 8,
|
||||
NUM_CTX = 3,
|
||||
NUM_PROBAS = 11
|
||||
};
|
||||
|
||||
// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
|
||||
int IsValidColorspace(int webp_csp_mode);
|
||||
|
||||
#endif // WEBP_DEC_COMMON_DEC_H_
|
||||
|
||||
@@ -11,19 +11,31 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/dec/common_dec.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/random_utils.h"
|
||||
#include "src/utils/thread_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main reconstruction function.
|
||||
|
||||
static const uint16_t kScan[16] = {
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
|
||||
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
|
||||
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
|
||||
};
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
|
||||
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
|
||||
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS};
|
||||
|
||||
static int CheckMode(int mb_x, int mb_y, int mode) {
|
||||
if (mode == B_DC_PRED) {
|
||||
@@ -37,7 +49,7 @@ static int CheckMode(int mb_x, int mb_y, int mode) {
|
||||
}
|
||||
|
||||
static void Copy32b(uint8_t* const dst, const uint8_t* const src) {
|
||||
memcpy(dst, src, 4);
|
||||
WEBP_UNSAFE_MEMCPY(dst, src, 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
|
||||
@@ -59,9 +71,9 @@ static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
|
||||
|
||||
static void DoUVTransform(uint32_t bits, const int16_t* const src,
|
||||
uint8_t* const dst) {
|
||||
if (bits & 0xff) { // any non-zero coeff at all?
|
||||
if (bits & 0xaa) { // any non-zero AC coefficient?
|
||||
VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V
|
||||
if (bits & 0xff) { // any non-zero coeff at all?
|
||||
if (bits & 0xaa) { // any non-zero AC coefficient?
|
||||
VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V
|
||||
} else {
|
||||
VP8TransformDCUV(src, dst);
|
||||
}
|
||||
@@ -72,11 +84,11 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
const VP8ThreadContext* ctx) {
|
||||
int j;
|
||||
int mb_x;
|
||||
const int mb_y = ctx->mb_y_;
|
||||
const int cache_id = ctx->id_;
|
||||
uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
|
||||
uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
|
||||
uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
|
||||
const int mb_y = ctx->mb_y;
|
||||
const int cache_id = ctx->id;
|
||||
uint8_t* const y_dst = dec->yuv_b + Y_OFF;
|
||||
uint8_t* const u_dst = dec->yuv_b + U_OFF;
|
||||
uint8_t* const v_dst = dec->yuv_b + V_OFF;
|
||||
|
||||
// Initialize left-most block.
|
||||
for (j = 0; j < 16; ++j) {
|
||||
@@ -93,14 +105,14 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
} else {
|
||||
// we only need to do this init once at block (0,0).
|
||||
// Afterward, it remains valid for the whole topmost row.
|
||||
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);
|
||||
WEBP_UNSAFE_MEMSET(y_dst - BPS - 1, 127, 16 + 4 + 1);
|
||||
WEBP_UNSAFE_MEMSET(u_dst - BPS - 1, 127, 8 + 1);
|
||||
WEBP_UNSAFE_MEMSET(v_dst - BPS - 1, 127, 8 + 1);
|
||||
}
|
||||
|
||||
// Reconstruct one row.
|
||||
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
|
||||
const VP8MBData* const block = ctx->mb_data_ + mb_x;
|
||||
for (mb_x = 0; mb_x < dec->mb_w; ++mb_x) {
|
||||
const VP8MBData* const block = ctx->mb_data + mb_x;
|
||||
|
||||
// Rotate in the left samples from previously decoded block. We move four
|
||||
// pixels at a time for alignment reason, and because of in-loop filter.
|
||||
@@ -115,26 +127,26 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
}
|
||||
{
|
||||
// bring top samples into the cache
|
||||
VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x;
|
||||
const int16_t* const coeffs = block->coeffs_;
|
||||
uint32_t bits = block->non_zero_y_;
|
||||
VP8TopSamples* const top_yuv = dec->yuv_t + mb_x;
|
||||
const int16_t* const coeffs = block->coeffs;
|
||||
uint32_t bits = block->non_zero_y;
|
||||
int n;
|
||||
|
||||
if (mb_y > 0) {
|
||||
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);
|
||||
WEBP_UNSAFE_MEMCPY(y_dst - BPS, top_yuv[0].y, 16);
|
||||
WEBP_UNSAFE_MEMCPY(u_dst - BPS, top_yuv[0].u, 8);
|
||||
WEBP_UNSAFE_MEMCPY(v_dst - BPS, top_yuv[0].v, 8);
|
||||
}
|
||||
|
||||
// predict and add residuals
|
||||
if (block->is_i4x4_) { // 4x4
|
||||
if (block->is_i4x4) { // 4x4
|
||||
uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
|
||||
|
||||
if (mb_y > 0) {
|
||||
if (mb_x >= dec->mb_w_ - 1) { // on rightmost border
|
||||
memset(top_right, top_yuv[0].y[15], sizeof(*top_right));
|
||||
if (mb_x >= dec->mb_w - 1) { // on rightmost border
|
||||
WEBP_UNSAFE_MEMSET(top_right, top_yuv[0].y[15], sizeof(*top_right));
|
||||
} else {
|
||||
memcpy(top_right, top_yuv[1].y, sizeof(*top_right));
|
||||
WEBP_UNSAFE_MEMCPY(top_right, top_yuv[1].y, sizeof(*top_right));
|
||||
}
|
||||
}
|
||||
// replicate the top-right pixels below
|
||||
@@ -143,11 +155,11 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
// predict and add residuals for all 4x4 blocks in turn.
|
||||
for (n = 0; n < 16; ++n, bits <<= 2) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
VP8PredLuma4[block->imodes_[n]](dst);
|
||||
VP8PredLuma4[block->imodes[n]](dst);
|
||||
DoTransform(bits, coeffs + n * 16, dst);
|
||||
}
|
||||
} else { // 16x16
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]);
|
||||
} else { // 16x16
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->imodes[0]);
|
||||
VP8PredLuma16[pred_func](y_dst);
|
||||
if (bits != 0) {
|
||||
for (n = 0; n < 16; ++n, bits <<= 2) {
|
||||
@@ -157,8 +169,8 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
}
|
||||
{
|
||||
// Chroma
|
||||
const uint32_t bits_uv = block->non_zero_uv_;
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_);
|
||||
const uint32_t bits_uv = block->non_zero_uv;
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->uvmode);
|
||||
VP8PredChroma8[pred_func](u_dst);
|
||||
VP8PredChroma8[pred_func](v_dst);
|
||||
DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst);
|
||||
@@ -166,25 +178,28 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (mb_y < dec->mb_h - 1) {
|
||||
WEBP_UNSAFE_MEMCPY(top_yuv[0].y, y_dst + 15 * BPS, 16);
|
||||
WEBP_UNSAFE_MEMCPY(top_yuv[0].u, u_dst + 7 * BPS, 8);
|
||||
WEBP_UNSAFE_MEMCPY(top_yuv[0].v, v_dst + 7 * BPS, 8);
|
||||
}
|
||||
}
|
||||
// Transfer reconstructed samples from yuv_b_ cache to final destination.
|
||||
// Transfer reconstructed samples from yuv_b cache to final destination.
|
||||
{
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset;
|
||||
uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset;
|
||||
uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset;
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride;
|
||||
uint8_t* const y_out = dec->cache_y + mb_x * 16 + y_offset;
|
||||
uint8_t* const u_out = dec->cache_u + mb_x * 8 + uv_offset;
|
||||
uint8_t* const v_out = dec->cache_v + mb_x * 8 + uv_offset;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16);
|
||||
WEBP_UNSAFE_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);
|
||||
WEBP_UNSAFE_MEMCPY(u_out + j * dec->cache_uv_stride, u_dst + j * BPS,
|
||||
8);
|
||||
WEBP_UNSAFE_MEMCPY(v_out + j * dec->cache_uv_stride, v_dst + j * BPS,
|
||||
8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,43 +213,43 @@ static void ReconstructRow(const VP8Decoder* const dec,
|
||||
// Simple filter: up to 2 luma samples are read and 1 is written.
|
||||
// Complex filter: up to 4 luma samples are read and 3 are written. Same for
|
||||
// U/V, so it's 8 samples total (because of the 2x upsampling).
|
||||
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
|
||||
static const uint8_t kFilterExtraRows[3] = {0, 2, 8};
|
||||
|
||||
static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int cache_id = ctx->id_;
|
||||
const int y_bps = dec->cache_y_stride_;
|
||||
const VP8FInfo* const f_info = ctx->f_info_ + mb_x;
|
||||
uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16;
|
||||
const int ilevel = f_info->f_ilevel_;
|
||||
const int limit = f_info->f_limit_;
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx;
|
||||
const int cache_id = ctx->id;
|
||||
const int y_bps = dec->cache_y_stride;
|
||||
const VP8FInfo* const f_info = ctx->f_info + mb_x;
|
||||
uint8_t* const y_dst = dec->cache_y + cache_id * 16 * y_bps + mb_x * 16;
|
||||
const int ilevel = f_info->f_ilevel;
|
||||
const int limit = f_info->f_limit;
|
||||
if (limit == 0) {
|
||||
return;
|
||||
}
|
||||
assert(limit >= 3);
|
||||
if (dec->filter_type_ == 1) { // simple
|
||||
if (dec->filter_type == 1) { // simple
|
||||
if (mb_x > 0) {
|
||||
VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
if (f_info->f_inner) {
|
||||
VP8SimpleHFilter16i(y_dst, y_bps, limit);
|
||||
}
|
||||
if (mb_y > 0) {
|
||||
VP8SimpleVFilter16(y_dst, y_bps, limit + 4);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
if (f_info->f_inner) {
|
||||
VP8SimpleVFilter16i(y_dst, y_bps, limit);
|
||||
}
|
||||
} else { // complex
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh = f_info->hev_thresh_;
|
||||
} else { // complex
|
||||
const int uv_bps = dec->cache_uv_stride;
|
||||
uint8_t* const u_dst = dec->cache_u + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh = f_info->hev_thresh;
|
||||
if (mb_x > 0) {
|
||||
VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
if (f_info->f_inner) {
|
||||
VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
|
||||
VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
|
||||
}
|
||||
@@ -242,7 +257,7 @@ static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
if (f_info->f_inner) {
|
||||
VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
|
||||
VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
|
||||
}
|
||||
@@ -252,9 +267,9 @@ static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
// Filter the decoded macroblock row (if needed)
|
||||
static void FilterRow(const VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
const int mb_y = dec->thread_ctx_.mb_y_;
|
||||
assert(dec->thread_ctx_.filter_row_);
|
||||
for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
|
||||
const int mb_y = dec->thread_ctx.mb_y;
|
||||
assert(dec->thread_ctx.filter_row);
|
||||
for (mb_x = dec->tl_mb_x; mb_x < dec->br_mb_x; ++mb_x) {
|
||||
DoFilter(dec, mb_x, mb_y);
|
||||
}
|
||||
}
|
||||
@@ -263,51 +278,51 @@ static void FilterRow(const VP8Decoder* const dec) {
|
||||
// Precompute the filtering strength for each segment and each i4x4/i16x16 mode.
|
||||
|
||||
static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
|
||||
if (dec->filter_type_ > 0) {
|
||||
if (dec->filter_type > 0) {
|
||||
int s;
|
||||
const VP8FilterHeader* const hdr = &dec->filter_hdr_;
|
||||
const VP8FilterHeader* const hdr = &dec->filter_hdr;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
int i4x4;
|
||||
// First, compute the initial level
|
||||
int base_level;
|
||||
if (dec->segment_hdr_.use_segment_) {
|
||||
base_level = dec->segment_hdr_.filter_strength_[s];
|
||||
if (!dec->segment_hdr_.absolute_delta_) {
|
||||
base_level += hdr->level_;
|
||||
if (dec->segment_hdr.use_segment) {
|
||||
base_level = dec->segment_hdr.filter_strength[s];
|
||||
if (!dec->segment_hdr.absolute_delta) {
|
||||
base_level += hdr->level;
|
||||
}
|
||||
} else {
|
||||
base_level = hdr->level_;
|
||||
base_level = hdr->level;
|
||||
}
|
||||
for (i4x4 = 0; i4x4 <= 1; ++i4x4) {
|
||||
VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
|
||||
VP8FInfo* const info = &dec->fstrengths[s][i4x4];
|
||||
int level = base_level;
|
||||
if (hdr->use_lf_delta_) {
|
||||
level += hdr->ref_lf_delta_[0];
|
||||
if (hdr->use_lf_delta) {
|
||||
level += hdr->ref_lf_delta[0];
|
||||
if (i4x4) {
|
||||
level += hdr->mode_lf_delta_[0];
|
||||
level += hdr->mode_lf_delta[0];
|
||||
}
|
||||
}
|
||||
level = (level < 0) ? 0 : (level > 63) ? 63 : level;
|
||||
if (level > 0) {
|
||||
int ilevel = level;
|
||||
if (hdr->sharpness_ > 0) {
|
||||
if (hdr->sharpness_ > 4) {
|
||||
if (hdr->sharpness > 0) {
|
||||
if (hdr->sharpness > 4) {
|
||||
ilevel >>= 2;
|
||||
} else {
|
||||
ilevel >>= 1;
|
||||
}
|
||||
if (ilevel > 9 - hdr->sharpness_) {
|
||||
ilevel = 9 - hdr->sharpness_;
|
||||
if (ilevel > 9 - hdr->sharpness) {
|
||||
ilevel = 9 - hdr->sharpness;
|
||||
}
|
||||
}
|
||||
if (ilevel < 1) ilevel = 1;
|
||||
info->f_ilevel_ = ilevel;
|
||||
info->f_limit_ = 2 * level + ilevel;
|
||||
info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
info->f_ilevel = ilevel;
|
||||
info->f_limit = 2 * level + ilevel;
|
||||
info->hev_thresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
} else {
|
||||
info->f_limit_ = 0; // no filtering
|
||||
info->f_limit = 0; // no filtering
|
||||
}
|
||||
info->f_inner_ = i4x4;
|
||||
info->f_inner = i4x4;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,9 +336,8 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
|
||||
|
||||
#define DITHER_AMP_TAB_SIZE 12
|
||||
static const uint8_t kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
|
||||
// roughly, it's dqm->uv_mat_[1]
|
||||
8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1
|
||||
};
|
||||
// roughly, it's dqm->uv_mat[1]
|
||||
8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1};
|
||||
|
||||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec) {
|
||||
@@ -336,24 +350,24 @@ void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
int s;
|
||||
int all_amp = 0;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
VP8QuantMatrix* const dqm = &dec->dqm_[s];
|
||||
if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) {
|
||||
const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_;
|
||||
dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3;
|
||||
VP8QuantMatrix* const dqm = &dec->dqm[s];
|
||||
if (dqm->uv_quant < DITHER_AMP_TAB_SIZE) {
|
||||
const int idx = (dqm->uv_quant < 0) ? 0 : dqm->uv_quant;
|
||||
dqm->dither = (f * kQuantToDitherAmp[idx]) >> 3;
|
||||
}
|
||||
all_amp |= dqm->dither_;
|
||||
all_amp |= dqm->dither;
|
||||
}
|
||||
if (all_amp != 0) {
|
||||
VP8InitRandom(&dec->dithering_rg_, 1.0f);
|
||||
dec->dither_ = 1;
|
||||
VP8InitRandom(&dec->dithering_rg, 1.0f);
|
||||
dec->dither = 1;
|
||||
}
|
||||
}
|
||||
// potentially allow alpha dithering
|
||||
dec->alpha_dithering_ = options->alpha_dithering_strength;
|
||||
if (dec->alpha_dithering_ > 100) {
|
||||
dec->alpha_dithering_ = 100;
|
||||
} else if (dec->alpha_dithering_ < 0) {
|
||||
dec->alpha_dithering_ = 0;
|
||||
dec->alpha_dithering = options->alpha_dithering_strength;
|
||||
if (dec->alpha_dithering > 100) {
|
||||
dec->alpha_dithering = 100;
|
||||
} else if (dec->alpha_dithering < 0) {
|
||||
dec->alpha_dithering = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,17 +384,17 @@ static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
|
||||
|
||||
static void DitherRow(VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
assert(dec->dither_);
|
||||
for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const VP8MBData* const data = ctx->mb_data_ + mb_x;
|
||||
const int cache_id = ctx->id_;
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
if (data->dither_ >= MIN_DITHER_AMP) {
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_);
|
||||
Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_);
|
||||
assert(dec->dither);
|
||||
for (mb_x = dec->tl_mb_x; mb_x < dec->br_mb_x; ++mb_x) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx;
|
||||
const VP8MBData* const data = ctx->mb_data + mb_x;
|
||||
const int cache_id = ctx->id;
|
||||
const int uv_bps = dec->cache_uv_stride;
|
||||
if (data->dither >= MIN_DITHER_AMP) {
|
||||
uint8_t* const u_dst = dec->cache_u + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
Dither8x8(&dec->dithering_rg, u_dst, uv_bps, data->dither);
|
||||
Dither8x8(&dec->dithering_rg, v_dst, uv_bps, data->dither);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,36 +410,36 @@ static void DitherRow(VP8Decoder* const dec) {
|
||||
// * we must clip the remaining pixels against the cropping area. The VP8Io
|
||||
// struct must have the following fields set correctly before calling put():
|
||||
|
||||
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
|
||||
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
|
||||
|
||||
// Finalize and transmit a complete row. Return false in case of user-abort.
|
||||
static int FinishRow(void* arg1, void* arg2) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)arg1;
|
||||
VP8Io* const io = (VP8Io*)arg2;
|
||||
int ok = 1;
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int cache_id = ctx->id_;
|
||||
const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
|
||||
const int ysize = extra_y_rows * dec->cache_y_stride_;
|
||||
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
|
||||
uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
|
||||
const int mb_y = ctx->mb_y_;
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx;
|
||||
const int cache_id = ctx->id;
|
||||
const int extra_y_rows = kFilterExtraRows[dec->filter_type];
|
||||
const int ysize = extra_y_rows * dec->cache_y_stride;
|
||||
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride;
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride;
|
||||
uint8_t* const ydst = dec->cache_y - ysize + y_offset;
|
||||
uint8_t* const udst = dec->cache_u - uvsize + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v - uvsize + uv_offset;
|
||||
const int mb_y = ctx->mb_y;
|
||||
const int is_first_row = (mb_y == 0);
|
||||
const int is_last_row = (mb_y >= dec->br_mb_y_ - 1);
|
||||
const int is_last_row = (mb_y >= dec->br_mb_y - 1);
|
||||
|
||||
if (dec->mt_method_ == 2) {
|
||||
if (dec->mt_method == 2) {
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
|
||||
if (ctx->filter_row_) {
|
||||
if (ctx->filter_row) {
|
||||
FilterRow(dec);
|
||||
}
|
||||
|
||||
if (dec->dither_) {
|
||||
if (dec->dither) {
|
||||
DitherRow(dec);
|
||||
}
|
||||
|
||||
@@ -438,20 +452,20 @@ static int FinishRow(void* arg1, void* arg2) {
|
||||
io->u = udst;
|
||||
io->v = vdst;
|
||||
} else {
|
||||
io->y = dec->cache_y_ + y_offset;
|
||||
io->u = dec->cache_u_ + uv_offset;
|
||||
io->v = dec->cache_v_ + uv_offset;
|
||||
io->y = dec->cache_y + y_offset;
|
||||
io->u = dec->cache_u + uv_offset;
|
||||
io->v = dec->cache_v + uv_offset;
|
||||
}
|
||||
|
||||
if (!is_last_row) {
|
||||
y_end -= extra_y_rows;
|
||||
}
|
||||
if (y_end > io->crop_bottom) {
|
||||
y_end = io->crop_bottom; // make sure we don't overflow on last row.
|
||||
y_end = io->crop_bottom; // make sure we don't overflow on last row.
|
||||
}
|
||||
// If dec->alpha_data_ is not NULL, we have some alpha plane present.
|
||||
// If dec->alpha_data is not NULL, we have some alpha plane present.
|
||||
io->a = NULL;
|
||||
if (dec->alpha_data_ != NULL && y_start < y_end) {
|
||||
if (dec->alpha_data != NULL && y_start < y_end) {
|
||||
io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start);
|
||||
if (io->a == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
@@ -462,9 +476,9 @@ static int FinishRow(void* arg1, void* arg2) {
|
||||
const int delta_y = io->crop_top - y_start;
|
||||
y_start = io->crop_top;
|
||||
assert(!(delta_y & 1));
|
||||
io->y += dec->cache_y_stride_ * delta_y;
|
||||
io->u += dec->cache_uv_stride_ * (delta_y >> 1);
|
||||
io->v += dec->cache_uv_stride_ * (delta_y >> 1);
|
||||
io->y += dec->cache_y_stride * delta_y;
|
||||
io->u += dec->cache_uv_stride * (delta_y >> 1);
|
||||
io->v += dec->cache_uv_stride * (delta_y >> 1);
|
||||
if (io->a != NULL) {
|
||||
io->a += io->width * delta_y;
|
||||
}
|
||||
@@ -483,11 +497,14 @@ static int FinishRow(void* arg1, void* arg2) {
|
||||
}
|
||||
}
|
||||
// rotate top samples if needed
|
||||
if (cache_id + 1 == dec->num_caches_) {
|
||||
if (cache_id + 1 == dec->num_caches) {
|
||||
if (!is_last_row) {
|
||||
memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
|
||||
memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
WEBP_UNSAFE_MEMCPY(dec->cache_y - ysize, ydst + 16 * dec->cache_y_stride,
|
||||
ysize);
|
||||
WEBP_UNSAFE_MEMCPY(dec->cache_u - uvsize, udst + 8 * dec->cache_uv_stride,
|
||||
uvsize);
|
||||
WEBP_UNSAFE_MEMCPY(dec->cache_v - uvsize, vdst + 8 * dec->cache_uv_stride,
|
||||
uvsize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,43 +517,43 @@ static int FinishRow(void* arg1, void* arg2) {
|
||||
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int filter_row =
|
||||
(dec->filter_type_ > 0) &&
|
||||
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
||||
if (dec->mt_method_ == 0) {
|
||||
// ctx->id_ and ctx->f_info_ are already set
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = filter_row;
|
||||
VP8ThreadContext* const ctx = &dec->thread_ctx;
|
||||
const int filter_row = (dec->filter_type > 0) &&
|
||||
(dec->mb_y >= dec->tl_mb_y) &&
|
||||
(dec->mb_y <= dec->br_mb_y);
|
||||
if (dec->mt_method == 0) {
|
||||
// ctx->id and ctx->f_info are already set
|
||||
ctx->mb_y = dec->mb_y;
|
||||
ctx->filter_row = filter_row;
|
||||
ReconstructRow(dec, ctx);
|
||||
ok = FinishRow(dec, io);
|
||||
} else {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
WebPWorker* const worker = &dec->worker;
|
||||
// Finish previous job *before* updating context
|
||||
ok &= WebPGetWorkerInterface()->Sync(worker);
|
||||
assert(worker->status_ == OK);
|
||||
if (ok) { // spawn a new deblocking/output job
|
||||
ctx->io_ = *io;
|
||||
ctx->id_ = dec->cache_id_;
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = filter_row;
|
||||
if (dec->mt_method_ == 2) { // swap macroblock data
|
||||
VP8MBData* const tmp = ctx->mb_data_;
|
||||
ctx->mb_data_ = dec->mb_data_;
|
||||
dec->mb_data_ = tmp;
|
||||
assert(worker->status == OK);
|
||||
if (ok) { // spawn a new deblocking/output job
|
||||
ctx->io = *io;
|
||||
ctx->id = dec->cache_id;
|
||||
ctx->mb_y = dec->mb_y;
|
||||
ctx->filter_row = filter_row;
|
||||
if (dec->mt_method == 2) { // swap macroblock data
|
||||
VP8MBData* const tmp = ctx->mb_data;
|
||||
ctx->mb_data = dec->mb_data;
|
||||
dec->mb_data = tmp;
|
||||
} else {
|
||||
// perform reconstruction directly in main thread
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
if (filter_row) { // swap filter info
|
||||
VP8FInfo* const tmp = ctx->f_info_;
|
||||
ctx->f_info_ = dec->f_info_;
|
||||
dec->f_info_ = tmp;
|
||||
if (filter_row) { // swap filter info
|
||||
VP8FInfo* const tmp = ctx->f_info;
|
||||
ctx->f_info = dec->f_info;
|
||||
dec->f_info = tmp;
|
||||
}
|
||||
// (reconstruct)+filter in parallel
|
||||
WebPGetWorkerInterface()->Launch(worker);
|
||||
if (++dec->cache_id_ == dec->num_caches_) {
|
||||
dec->cache_id_ = 0;
|
||||
if (++dec->cache_id == dec->num_caches) {
|
||||
dec->cache_id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -550,13 +567,13 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// Call setup() first. This may trigger additional decoding features on 'io'.
|
||||
// Note: Afterward, we must call teardown() no matter what.
|
||||
if (io->setup != NULL && !io->setup(io)) {
|
||||
VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
|
||||
return dec->status_;
|
||||
VP8SetError(dec, VP8_STATUS_INVALID_PARAM, "Frame setup failed");
|
||||
return dec->status;
|
||||
}
|
||||
|
||||
// Disable filtering per user request
|
||||
if (io->bypass_filtering) {
|
||||
dec->filter_type_ = 0;
|
||||
dec->filter_type = 0;
|
||||
}
|
||||
|
||||
// Define the area where we can skip in-loop filtering, in case of cropping.
|
||||
@@ -569,29 +586,29 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// top-left corner of the picture (MB #0). We must filter all the previous
|
||||
// macroblocks.
|
||||
{
|
||||
const int extra_pixels = kFilterExtraRows[dec->filter_type_];
|
||||
if (dec->filter_type_ == 2) {
|
||||
const int extra_pixels = kFilterExtraRows[dec->filter_type];
|
||||
if (dec->filter_type == 2) {
|
||||
// For complex filter, we need to preserve the dependency chain.
|
||||
dec->tl_mb_x_ = 0;
|
||||
dec->tl_mb_y_ = 0;
|
||||
dec->tl_mb_x = 0;
|
||||
dec->tl_mb_y = 0;
|
||||
} else {
|
||||
// For simple filter, we can filter only the cropped region.
|
||||
// We include 'extra_pixels' on the other side of the boundary, since
|
||||
// vertical or horizontal filtering of the previous macroblock can
|
||||
// modify some abutting pixels.
|
||||
dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4;
|
||||
dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4;
|
||||
if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0;
|
||||
if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0;
|
||||
dec->tl_mb_x = (io->crop_left - extra_pixels) >> 4;
|
||||
dec->tl_mb_y = (io->crop_top - extra_pixels) >> 4;
|
||||
if (dec->tl_mb_x < 0) dec->tl_mb_x = 0;
|
||||
if (dec->tl_mb_y < 0) dec->tl_mb_y = 0;
|
||||
}
|
||||
// We need some 'extra' pixels on the right/bottom.
|
||||
dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4;
|
||||
dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4;
|
||||
if (dec->br_mb_x_ > dec->mb_w_) {
|
||||
dec->br_mb_x_ = dec->mb_w_;
|
||||
dec->br_mb_y = (io->crop_bottom + 15 + extra_pixels) >> 4;
|
||||
dec->br_mb_x = (io->crop_right + 15 + extra_pixels) >> 4;
|
||||
if (dec->br_mb_x > dec->mb_w) {
|
||||
dec->br_mb_x = dec->mb_w;
|
||||
}
|
||||
if (dec->br_mb_y_ > dec->mb_h_) {
|
||||
dec->br_mb_y_ = dec->mb_h_;
|
||||
if (dec->br_mb_y > dec->mb_h) {
|
||||
dec->br_mb_y = dec->mb_h;
|
||||
}
|
||||
}
|
||||
PrecomputeFilterStrengths(dec);
|
||||
@@ -600,8 +617,8 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
if (dec->mt_method_ > 0) {
|
||||
ok = WebPGetWorkerInterface()->Sync(&dec->worker_);
|
||||
if (dec->mt_method > 0) {
|
||||
ok = WebPGetWorkerInterface()->Sync(&dec->worker);
|
||||
}
|
||||
|
||||
if (io->teardown != NULL) {
|
||||
@@ -635,31 +652,31 @@ int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// io->put: [ 0..15][16..31][ 0..15][...
|
||||
|
||||
#define MT_CACHE_LINES 3
|
||||
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
|
||||
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
|
||||
|
||||
// Initialize multi/single-thread worker
|
||||
static int InitThreadContext(VP8Decoder* const dec) {
|
||||
dec->cache_id_ = 0;
|
||||
if (dec->mt_method_ > 0) {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
dec->cache_id = 0;
|
||||
if (dec->mt_method > 0) {
|
||||
WebPWorker* const worker = &dec->worker;
|
||||
if (!WebPGetWorkerInterface()->Reset(worker)) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"thread initialization failed.");
|
||||
}
|
||||
worker->data1 = dec;
|
||||
worker->data2 = (void*)&dec->thread_ctx_.io_;
|
||||
worker->data2 = (void*)&dec->thread_ctx.io;
|
||||
worker->hook = FinishRow;
|
||||
dec->num_caches_ =
|
||||
(dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
|
||||
dec->num_caches =
|
||||
(dec->filter_type > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
|
||||
} else {
|
||||
dec->num_caches_ = ST_CACHE_LINES;
|
||||
dec->num_caches = ST_CACHE_LINES;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
const WebPHeaderStructure* const headers,
|
||||
int width, int height) {
|
||||
const WebPHeaderStructure* const headers, int width,
|
||||
int height) {
|
||||
if (options == NULL || options->use_threads == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -680,103 +697,104 @@ int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
// Memory setup
|
||||
|
||||
static int AllocateMemory(VP8Decoder* const dec) {
|
||||
const int num_caches = dec->num_caches_;
|
||||
const int mb_w = dec->mb_w_;
|
||||
const int num_caches = dec->num_caches;
|
||||
const int mb_w = dec->mb_w;
|
||||
// Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
|
||||
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
|
||||
const size_t top_size = sizeof(VP8TopSamples) * mb_w;
|
||||
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
|
||||
const size_t f_info_size =
|
||||
(dec->filter_type_ > 0) ?
|
||||
mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo)
|
||||
: 0;
|
||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
|
||||
(dec->filter_type > 0)
|
||||
? mb_w * (dec->mt_method > 0 ? 2 : 1) * sizeof(VP8FInfo)
|
||||
: 0;
|
||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b);
|
||||
const size_t mb_data_size =
|
||||
(dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_);
|
||||
const size_t cache_height = (16 * num_caches
|
||||
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
||||
(dec->mt_method == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data);
|
||||
const size_t cache_height =
|
||||
(16 * num_caches + kFilterExtraRows[dec->filter_type]) * 3 / 2;
|
||||
const size_t cache_size = top_size * cache_height;
|
||||
// alpha_size is the only one that scales as width x height.
|
||||
const uint64_t alpha_size = (dec->alpha_data_ != NULL) ?
|
||||
(uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
|
||||
const uint64_t needed = (uint64_t)intra_pred_mode_size
|
||||
+ top_size + mb_info_size + f_info_size
|
||||
+ yuv_size + mb_data_size
|
||||
+ cache_size + alpha_size + WEBP_ALIGN_CST;
|
||||
const uint64_t alpha_size =
|
||||
(dec->alpha_data != NULL)
|
||||
? (uint64_t)dec->pic_hdr.width * dec->pic_hdr.height
|
||||
: 0ULL;
|
||||
const uint64_t needed = (uint64_t)intra_pred_mode_size + top_size +
|
||||
mb_info_size + f_info_size + yuv_size + mb_data_size +
|
||||
cache_size + alpha_size + WEBP_ALIGN_CST;
|
||||
uint8_t* mem;
|
||||
|
||||
if (!CheckSizeOverflow(needed)) return 0; // check for overflow
|
||||
if (needed > dec->mem_size_) {
|
||||
WebPSafeFree(dec->mem_);
|
||||
dec->mem_size_ = 0;
|
||||
dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
|
||||
if (dec->mem_ == NULL) {
|
||||
if (needed > dec->mem_size) {
|
||||
WebPSafeFree(dec->mem);
|
||||
dec->mem_size = 0;
|
||||
dec->mem = WebPSafeMalloc(needed, sizeof(uint8_t));
|
||||
if (dec->mem == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"no memory during frame initialization.");
|
||||
}
|
||||
// down-cast is ok, thanks to WebPSafeMalloc() above.
|
||||
dec->mem_size_ = (size_t)needed;
|
||||
dec->mem_size = (size_t)needed;
|
||||
}
|
||||
|
||||
mem = (uint8_t*)dec->mem_;
|
||||
dec->intra_t_ = mem;
|
||||
mem = (uint8_t*)dec->mem;
|
||||
dec->intra_t = mem;
|
||||
mem += intra_pred_mode_size;
|
||||
|
||||
dec->yuv_t_ = (VP8TopSamples*)mem;
|
||||
dec->yuv_t = (VP8TopSamples*)mem;
|
||||
mem += top_size;
|
||||
|
||||
dec->mb_info_ = ((VP8MB*)mem) + 1;
|
||||
dec->mb_info = ((VP8MB*)mem) + 1;
|
||||
mem += mb_info_size;
|
||||
|
||||
dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
|
||||
dec->f_info = f_info_size ? (VP8FInfo*)mem : NULL;
|
||||
mem += f_info_size;
|
||||
dec->thread_ctx_.id_ = 0;
|
||||
dec->thread_ctx_.f_info_ = dec->f_info_;
|
||||
if (dec->filter_type_ > 0 && dec->mt_method_ > 0) {
|
||||
dec->thread_ctx.id = 0;
|
||||
dec->thread_ctx.f_info = dec->f_info;
|
||||
if (dec->filter_type > 0 && dec->mt_method > 0) {
|
||||
// secondary cache line. The deblocking process need to make use of the
|
||||
// filtering strength from previous macroblock row, while the new ones
|
||||
// are being decoded in parallel. We'll just swap the pointers.
|
||||
dec->thread_ctx_.f_info_ += mb_w;
|
||||
dec->thread_ctx.f_info += mb_w;
|
||||
}
|
||||
|
||||
mem = (uint8_t*)WEBP_ALIGN(mem);
|
||||
assert((yuv_size & WEBP_ALIGN_CST) == 0);
|
||||
dec->yuv_b_ = mem;
|
||||
dec->yuv_b = mem;
|
||||
mem += yuv_size;
|
||||
|
||||
dec->mb_data_ = (VP8MBData*)mem;
|
||||
dec->thread_ctx_.mb_data_ = (VP8MBData*)mem;
|
||||
if (dec->mt_method_ == 2) {
|
||||
dec->thread_ctx_.mb_data_ += mb_w;
|
||||
dec->mb_data = (VP8MBData*)mem;
|
||||
dec->thread_ctx.mb_data = (VP8MBData*)mem;
|
||||
if (dec->mt_method == 2) {
|
||||
dec->thread_ctx.mb_data += mb_w;
|
||||
}
|
||||
mem += mb_data_size;
|
||||
|
||||
dec->cache_y_stride_ = 16 * mb_w;
|
||||
dec->cache_uv_stride_ = 8 * mb_w;
|
||||
dec->cache_y_stride = 16 * mb_w;
|
||||
dec->cache_uv_stride = 8 * mb_w;
|
||||
{
|
||||
const int extra_rows = kFilterExtraRows[dec->filter_type_];
|
||||
const int extra_y = extra_rows * dec->cache_y_stride_;
|
||||
const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
|
||||
dec->cache_y_ = mem + extra_y;
|
||||
dec->cache_u_ = dec->cache_y_
|
||||
+ 16 * num_caches * dec->cache_y_stride_ + extra_uv;
|
||||
dec->cache_v_ = dec->cache_u_
|
||||
+ 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
|
||||
dec->cache_id_ = 0;
|
||||
const int extra_rows = kFilterExtraRows[dec->filter_type];
|
||||
const int extra_y = extra_rows * dec->cache_y_stride;
|
||||
const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride;
|
||||
dec->cache_y = mem + extra_y;
|
||||
dec->cache_u =
|
||||
dec->cache_y + 16 * num_caches * dec->cache_y_stride + extra_uv;
|
||||
dec->cache_v =
|
||||
dec->cache_u + 8 * num_caches * dec->cache_uv_stride + extra_uv;
|
||||
dec->cache_id = 0;
|
||||
}
|
||||
mem += cache_size;
|
||||
|
||||
// alpha plane
|
||||
dec->alpha_plane_ = alpha_size ? mem : NULL;
|
||||
dec->alpha_plane = alpha_size ? mem : NULL;
|
||||
mem += alpha_size;
|
||||
assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_);
|
||||
assert(mem <= (uint8_t*)dec->mem + dec->mem_size);
|
||||
|
||||
// note: left/top-info is initialized once for all.
|
||||
memset(dec->mb_info_ - 1, 0, mb_info_size);
|
||||
VP8InitScanline(dec); // initialize left too.
|
||||
WEBP_UNSAFE_MEMSET(dec->mb_info - 1, 0, mb_info_size);
|
||||
VP8InitScanline(dec); // initialize left too.
|
||||
|
||||
// initialize top
|
||||
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
|
||||
WEBP_UNSAFE_MEMSET(dec->intra_t, B_DC_PRED, intra_pred_mode_size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -784,16 +802,16 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
static void InitIo(VP8Decoder* const dec, VP8Io* io) {
|
||||
// prepare 'io'
|
||||
io->mb_y = 0;
|
||||
io->y = dec->cache_y_;
|
||||
io->u = dec->cache_u_;
|
||||
io->v = dec->cache_v_;
|
||||
io->y_stride = dec->cache_y_stride_;
|
||||
io->uv_stride = dec->cache_uv_stride_;
|
||||
io->y = dec->cache_y;
|
||||
io->u = dec->cache_u;
|
||||
io->v = dec->cache_v;
|
||||
io->y_stride = dec->cache_y_stride;
|
||||
io->uv_stride = dec->cache_uv_stride;
|
||||
io->a = NULL;
|
||||
}
|
||||
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) {
|
||||
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
|
||||
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches.
|
||||
if (!AllocateMemory(dec)) return 0;
|
||||
InitIo(dec, io);
|
||||
VP8DspInit(); // Init critical function pointers and look-up tables.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
211
src/dec/io_dec.c
211
src/dec/io_dec.c
@@ -12,12 +12,22 @@
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/dsp/yuv.h"
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main YUV<->RGB conversion functions
|
||||
@@ -25,9 +35,9 @@
|
||||
static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* output = p->output;
|
||||
const WebPYUVABuffer* const buf = &output->u.YUVA;
|
||||
uint8_t* const y_dst = buf->y + (size_t)io->mb_y * buf->y_stride;
|
||||
uint8_t* const u_dst = buf->u + (size_t)(io->mb_y >> 1) * buf->u_stride;
|
||||
uint8_t* const v_dst = buf->v + (size_t)(io->mb_y >> 1) * buf->v_stride;
|
||||
uint8_t* const y_dst = buf->y + (ptrdiff_t)io->mb_y * buf->y_stride;
|
||||
uint8_t* const u_dst = buf->u + (ptrdiff_t)(io->mb_y >> 1) * buf->u_stride;
|
||||
uint8_t* const v_dst = buf->v + (ptrdiff_t)(io->mb_y >> 1) * buf->v_stride;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_w = (mb_w + 1) / 2;
|
||||
@@ -42,10 +52,9 @@ static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* const output = p->output;
|
||||
WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* const dst = buf->rgba + (size_t)io->mb_y * buf->stride;
|
||||
WebPSamplerProcessPlane(io->y, io->y_stride,
|
||||
io->u, io->v, io->uv_stride,
|
||||
dst, buf->stride, io->mb_w, io->mb_h,
|
||||
uint8_t* const dst = buf->rgba + (ptrdiff_t)io->mb_y * buf->stride;
|
||||
WebPSamplerProcessPlane(io->y, io->y_stride, io->u, io->v, io->uv_stride, dst,
|
||||
buf->stride, io->mb_w, io->mb_h,
|
||||
WebPSamplers[output->colorspace]);
|
||||
return io->mb_h;
|
||||
}
|
||||
@@ -55,9 +64,9 @@ static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_lines_out = io->mb_h; // a priori guess
|
||||
int num_lines_out = io->mb_h; // a priori guess
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (size_t)io->mb_y * buf->stride;
|
||||
uint8_t* dst = buf->rgba + (ptrdiff_t)io->mb_y * buf->stride;
|
||||
WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
|
||||
const uint8_t* cur_y = io->y;
|
||||
const uint8_t* cur_u = io->u;
|
||||
@@ -74,8 +83,8 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w);
|
||||
} else {
|
||||
// We can finish the left-over line from previous call.
|
||||
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
|
||||
dst - buf->stride, dst, mb_w);
|
||||
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v, dst - buf->stride,
|
||||
dst, mb_w);
|
||||
++num_lines_out;
|
||||
}
|
||||
// Loop over each output pairs of row.
|
||||
@@ -86,38 +95,37 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
cur_v += io->uv_stride;
|
||||
dst += 2 * buf->stride;
|
||||
cur_y += 2 * io->y_stride;
|
||||
upsample(cur_y - io->y_stride, cur_y,
|
||||
top_u, top_v, cur_u, cur_v,
|
||||
upsample(cur_y - io->y_stride, cur_y, top_u, top_v, cur_u, cur_v,
|
||||
dst - buf->stride, dst, mb_w);
|
||||
}
|
||||
// move to last row
|
||||
cur_y += io->y_stride;
|
||||
if (io->crop_top + y_end < io->crop_bottom) {
|
||||
// Save the unfinished samples for next call (as we're not done yet).
|
||||
memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
|
||||
memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
|
||||
memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
|
||||
WEBP_UNSAFE_MEMCPY(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
|
||||
WEBP_UNSAFE_MEMCPY(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
|
||||
WEBP_UNSAFE_MEMCPY(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
|
||||
// The fancy upsampler leaves a row unfinished behind
|
||||
// (except for the very last row)
|
||||
num_lines_out--;
|
||||
} else {
|
||||
// Process the very last row of even-sized picture
|
||||
if (!(y_end & 1)) {
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
|
||||
dst + buf->stride, NULL, mb_w);
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst + buf->stride, NULL,
|
||||
mb_w);
|
||||
}
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
#endif /* FANCY_UPSAMPLING */
|
||||
#endif /* FANCY_UPSAMPLING */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void FillAlphaPlane(uint8_t* dst, int w, int h, int stride) {
|
||||
int j;
|
||||
for (j = 0; j < h; ++j) {
|
||||
memset(dst, 0xff, w * sizeof(*dst));
|
||||
WEBP_UNSAFE_MEMSET(dst, 0xff, w * sizeof(*dst));
|
||||
dst += stride;
|
||||
}
|
||||
}
|
||||
@@ -128,13 +136,13 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
uint8_t* dst = buf->a + (size_t)io->mb_y * buf->a_stride;
|
||||
uint8_t* dst = buf->a + (ptrdiff_t)io->mb_y * buf->a_stride;
|
||||
int j;
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == mb_h);
|
||||
if (alpha != NULL) {
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
||||
WEBP_UNSAFE_MEMCPY(dst, alpha, mb_w * sizeof(*dst));
|
||||
alpha += io->width;
|
||||
dst += buf->a_stride;
|
||||
}
|
||||
@@ -145,8 +153,8 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetAlphaSourceRow(const VP8Io* const io,
|
||||
const uint8_t** alpha, int* const num_rows) {
|
||||
static int GetAlphaSourceRow(const VP8Io* const io, const uint8_t** alpha,
|
||||
int* const num_rows) {
|
||||
int start_y = io->mb_y;
|
||||
*num_rows = io->mb_h;
|
||||
|
||||
@@ -181,17 +189,17 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
int num_rows;
|
||||
const size_t start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + (ptrdiff_t)start_y * buf->stride;
|
||||
uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w,
|
||||
num_rows, dst, buf->stride);
|
||||
const int has_alpha =
|
||||
WebPDispatchAlpha(alpha, io->width, mb_w, num_rows, dst, buf->stride);
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == num_rows);
|
||||
// has_alpha is true if there's non-trivial alpha to premultiply with.
|
||||
if (has_alpha && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
mb_w, num_rows, buf->stride);
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first, mb_w, num_rows,
|
||||
buf->stride);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -205,8 +213,8 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
int num_rows;
|
||||
const size_t start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + (ptrdiff_t)start_y * buf->stride;
|
||||
#if (WEBP_SWAP_16BIT_CSP == 1)
|
||||
uint8_t* alpha_dst = base_rgba;
|
||||
#else
|
||||
@@ -237,14 +245,14 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
|
||||
// YUV rescaling (no final RGB conversion needed)
|
||||
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
static int Rescale(const uint8_t* src, int src_stride,
|
||||
int new_lines, WebPRescaler* const wrk) {
|
||||
static int Rescale(const uint8_t* src, int src_stride, int new_lines,
|
||||
WebPRescaler* const wrk) {
|
||||
int num_lines_out = 0;
|
||||
while (new_lines > 0) { // import new contributions of source rows.
|
||||
while (new_lines > 0) { // import new contributions of source rows.
|
||||
const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride);
|
||||
src += lines_in * src_stride;
|
||||
new_lines -= lines_in;
|
||||
num_lines_out += WebPRescalerExport(wrk); // emit output row(s)
|
||||
num_lines_out += WebPRescalerExport(wrk); // emit output row(s)
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
@@ -257,10 +265,10 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) {
|
||||
// Before rescaling, we premultiply the luma directly into the io->y
|
||||
// internal buffer. This is OK since these samples are not used for
|
||||
// intra-prediction (the top samples are saved in cache_y_/u_/v_).
|
||||
// intra-prediction (the top samples are saved in cache_y/u/v).
|
||||
// But we need to cast the const away, though.
|
||||
WebPMultRows((uint8_t*)io->y, io->y_stride,
|
||||
io->a, io->width, io->mb_w, mb_h, 0);
|
||||
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);
|
||||
@@ -271,12 +279,12 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
uint8_t* const dst_a = buf->a + (size_t)p->last_y * buf->a_stride;
|
||||
uint8_t* const dst_a = buf->a + (ptrdiff_t)p->last_y * buf->a_stride;
|
||||
if (io->a != NULL) {
|
||||
uint8_t* const dst_y = buf->y + (size_t)p->last_y * buf->y_stride;
|
||||
uint8_t* const dst_y = buf->y + (ptrdiff_t)p->last_y * buf->y_stride;
|
||||
const int num_lines_out = Rescale(io->a, io->width, io->mb_h, p->scaler_a);
|
||||
assert(expected_num_lines_out == num_lines_out);
|
||||
if (num_lines_out > 0) { // unmultiply the Y
|
||||
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);
|
||||
}
|
||||
@@ -292,18 +300,18 @@ static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
const int out_width = io->scaled_width;
|
||||
const int out_width = io->scaled_width;
|
||||
const int out_height = io->scaled_height;
|
||||
const int uv_out_width = (out_width + 1) >> 1;
|
||||
const int uv_out_width = (out_width + 1) >> 1;
|
||||
const int uv_out_height = (out_height + 1) >> 1;
|
||||
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;
|
||||
// scratch memory for luma rescaler
|
||||
const size_t work_size = 2 * (size_t)out_width;
|
||||
const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
|
||||
uint64_t total_size;
|
||||
size_t rescaler_size;
|
||||
rescaler_t* work;
|
||||
rescaler_t* WEBP_BIDI_INDEXABLE work;
|
||||
WebPRescaler* scalers;
|
||||
const int num_rescalers = has_alpha ? 4 : 3;
|
||||
|
||||
@@ -317,35 +325,34 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->memory = WebPSafeMalloc(1ULL, (size_t)total_size);
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
work = (rescaler_t*)WebPSafeMalloc(1ULL, (size_t)total_size);
|
||||
if (work == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
work = (rescaler_t*)p->memory;
|
||||
p->memory = work;
|
||||
|
||||
scalers = (WebPRescaler*)WEBP_ALIGN(
|
||||
(const uint8_t*)work + total_size - rescaler_size);
|
||||
scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size -
|
||||
rescaler_size);
|
||||
p->scaler_y = &scalers[0];
|
||||
p->scaler_u = &scalers[1];
|
||||
p->scaler_v = &scalers[2];
|
||||
p->scaler_a = has_alpha ? &scalers[3] : NULL;
|
||||
|
||||
if (!WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
|
||||
buf->y, out_width, out_height, buf->y_stride, 1,
|
||||
work) ||
|
||||
!WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
|
||||
buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
|
||||
if (!WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h, buf->y, out_width,
|
||||
out_height, buf->y_stride, 1, work) ||
|
||||
!WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height, buf->u,
|
||||
uv_out_width, uv_out_height, buf->u_stride, 1,
|
||||
work + work_size) ||
|
||||
!WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
|
||||
buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
|
||||
!WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height, buf->v,
|
||||
uv_out_width, uv_out_height, buf->v_stride, 1,
|
||||
work + work_size + uv_work_size)) {
|
||||
return 0;
|
||||
}
|
||||
p->emit = EmitRescaledYUV;
|
||||
|
||||
if (has_alpha) {
|
||||
if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
|
||||
buf->a, out_width, out_height, buf->a_stride, 1,
|
||||
if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h, buf->a, out_width,
|
||||
out_height, buf->a_stride, 1,
|
||||
work + work_size + 2 * uv_work_size)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -362,7 +369,7 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) {
|
||||
const WebPYUV444Converter convert =
|
||||
WebPYUV444Converters[p->output->colorspace];
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (size_t)y_pos * buf->stride;
|
||||
uint8_t* dst = buf->rgba + (ptrdiff_t)y_pos * buf->stride;
|
||||
int num_lines_out = 0;
|
||||
// For RGB rescaling, because of the YUV420, current scan position
|
||||
// U/V can be +1/-1 line from the Y one. Hence the double test.
|
||||
@@ -373,8 +380,8 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) {
|
||||
WebPRescalerExportRow(p->scaler_y);
|
||||
WebPRescalerExportRow(p->scaler_u);
|
||||
WebPRescalerExportRow(p->scaler_v);
|
||||
convert(p->scaler_y->dst, p->scaler_u->dst, p->scaler_v->dst,
|
||||
dst, p->scaler_y->dst_width);
|
||||
convert(p->scaler_y->dst, p->scaler_u->dst, p->scaler_v->dst, dst,
|
||||
p->scaler_y->dst_width);
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
@@ -389,16 +396,16 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
while (j < mb_h) {
|
||||
const int y_lines_in =
|
||||
WebPRescalerImport(p->scaler_y, mb_h - j,
|
||||
io->y + (size_t)j * io->y_stride, io->y_stride);
|
||||
io->y + (ptrdiff_t)j * io->y_stride, io->y_stride);
|
||||
j += y_lines_in;
|
||||
if (WebPRescaleNeededLines(p->scaler_u, uv_mb_h - uv_j)) {
|
||||
const int u_lines_in = WebPRescalerImport(
|
||||
p->scaler_u, uv_mb_h - uv_j, io->u + (size_t)uv_j * io->uv_stride,
|
||||
p->scaler_u, uv_mb_h - uv_j, io->u + (ptrdiff_t)uv_j * io->uv_stride,
|
||||
io->uv_stride);
|
||||
const int v_lines_in = WebPRescalerImport(
|
||||
p->scaler_v, uv_mb_h - uv_j, io->v + (size_t)uv_j * io->uv_stride,
|
||||
p->scaler_v, uv_mb_h - uv_j, io->v + (ptrdiff_t)uv_j * io->uv_stride,
|
||||
io->uv_stride);
|
||||
(void)v_lines_in; // remove a gcc warning
|
||||
(void)v_lines_in; // remove a gcc warning
|
||||
assert(u_lines_in == v_lines_in);
|
||||
uv_j += u_lines_in;
|
||||
}
|
||||
@@ -409,10 +416,9 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
|
||||
static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (size_t)y_pos * buf->stride;
|
||||
uint8_t* const base_rgba = buf->rgba + (ptrdiff_t)y_pos * buf->stride;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
const int alpha_first = (colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
int num_lines_out = 0;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
@@ -428,8 +434,8 @@ static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha && non_opaque) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
width, num_lines_out, buf->stride);
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first, width, num_lines_out,
|
||||
buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
@@ -437,7 +443,7 @@ static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
|
||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos,
|
||||
int max_lines_out) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (size_t)y_pos * buf->stride;
|
||||
uint8_t* const base_rgba = buf->rgba + (ptrdiff_t)y_pos * buf->stride;
|
||||
#if (WEBP_SWAP_16BIT_CSP == 1)
|
||||
uint8_t* alpha_dst = base_rgba;
|
||||
#else
|
||||
@@ -476,7 +482,7 @@ static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||
int lines_left = expected_num_out_lines;
|
||||
const int y_end = p->last_y + lines_left;
|
||||
while (lines_left > 0) {
|
||||
const int64_t row_offset = (int64_t)scaler->src_y - io->mb_y;
|
||||
const int64_t row_offset = (ptrdiff_t)scaler->src_y - io->mb_y;
|
||||
WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y,
|
||||
io->a + row_offset * io->width, io->width);
|
||||
lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left);
|
||||
@@ -487,14 +493,15 @@ static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||
|
||||
static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
|
||||
const int out_width = io->scaled_width;
|
||||
const int out_width = io->scaled_width;
|
||||
const int out_height = io->scaled_height;
|
||||
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;
|
||||
// scratch memory for one rescaler
|
||||
const size_t work_size = 2 * (size_t)out_width;
|
||||
rescaler_t* work; // rescalers work area
|
||||
uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
|
||||
rescaler_t* WEBP_BIDI_INDEXABLE work; // rescalers work area
|
||||
uint8_t* WEBP_BIDI_INDEXABLE
|
||||
tmp; // tmp storage for scaled YUV444 samples before RGB conversion
|
||||
uint64_t tmp_size1, tmp_size2, total_size;
|
||||
size_t rescaler_size;
|
||||
WebPRescaler* scalers;
|
||||
@@ -509,23 +516,22 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->memory = WebPSafeMalloc(1ULL, (size_t)total_size);
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
work = (rescaler_t*)WebPSafeMalloc(1ULL, (size_t)total_size);
|
||||
if (work == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
work = (rescaler_t*)p->memory;
|
||||
p->memory = work;
|
||||
tmp = (uint8_t*)(work + tmp_size1);
|
||||
|
||||
scalers = (WebPRescaler*)WEBP_ALIGN(
|
||||
(const uint8_t*)work + total_size - rescaler_size);
|
||||
scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size -
|
||||
rescaler_size);
|
||||
p->scaler_y = &scalers[0];
|
||||
p->scaler_u = &scalers[1];
|
||||
p->scaler_v = &scalers[2];
|
||||
p->scaler_a = has_alpha ? &scalers[3] : NULL;
|
||||
|
||||
if (!WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
|
||||
tmp + 0 * out_width, out_width, out_height, 0, 1,
|
||||
work + 0 * work_size) ||
|
||||
if (!WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h, tmp + 0 * out_width,
|
||||
out_width, out_height, 0, 1, work + 0 * work_size) ||
|
||||
!WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
|
||||
tmp + 1 * out_width, out_width, out_height, 0, 1,
|
||||
work + 1 * work_size) ||
|
||||
@@ -538,9 +544,8 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPInitYUV444Converters();
|
||||
|
||||
if (has_alpha) {
|
||||
if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
|
||||
tmp + 3 * out_width, out_width, out_height, 0, 1,
|
||||
work + 3 * work_size)) {
|
||||
if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h, tmp + 3 * out_width,
|
||||
out_width, out_height, 0, 1, work + 3 * work_size)) {
|
||||
return 0;
|
||||
}
|
||||
p->emit_alpha = EmitRescaledAlphaRGB;
|
||||
@@ -570,7 +575,9 @@ static int CustomSetup(VP8Io* io) {
|
||||
p->emit = NULL;
|
||||
p->emit_alpha = NULL;
|
||||
p->emit_alpha_row = NULL;
|
||||
if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
|
||||
// Note: WebPIoInitFromOptions() does not distinguish between MODE_YUV and
|
||||
// MODE_YUVA, only RGB vs YUV.
|
||||
if (!WebPIoInitFromOptions(p->options, io, /*src_colorspace=*/MODE_YUV)) {
|
||||
return 0;
|
||||
}
|
||||
if (is_alpha && WebPIsPremultipliedMode(colorspace)) {
|
||||
@@ -580,21 +587,21 @@ static int CustomSetup(VP8Io* io) {
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
|
||||
if (!ok) {
|
||||
return 0; // memory error
|
||||
return 0; // memory error
|
||||
}
|
||||
#else
|
||||
return 0; // rescaling support not compiled
|
||||
return 0; // rescaling support not compiled
|
||||
#endif
|
||||
} else {
|
||||
if (is_rgb) {
|
||||
WebPInitSamplers();
|
||||
p->emit = EmitSampledRGB; // default
|
||||
p->emit = EmitSampledRGB; // default
|
||||
if (io->fancy_upsampling) {
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
const int uv_width = (io->mb_w + 1) >> 1;
|
||||
p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width));
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error.
|
||||
return 0; // memory error.
|
||||
}
|
||||
p->tmp_y = (uint8_t*)p->memory;
|
||||
p->tmp_u = p->tmp_y + io->mb_w;
|
||||
@@ -608,10 +615,10 @@ static int CustomSetup(VP8Io* io) {
|
||||
}
|
||||
if (is_alpha) { // need transparency output
|
||||
p->emit_alpha =
|
||||
(colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
|
||||
EmitAlphaRGBA4444
|
||||
(colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444)
|
||||
? EmitAlphaRGBA4444
|
||||
: is_rgb ? EmitAlphaRGB
|
||||
: EmitAlphaYUV;
|
||||
: EmitAlphaYUV;
|
||||
if (is_rgb) {
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
@@ -653,10 +660,10 @@ static void CustomTeardown(const VP8Io* io) {
|
||||
// Main entry point
|
||||
|
||||
void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
|
||||
io->put = CustomPut;
|
||||
io->setup = CustomSetup;
|
||||
io->put = CustomPut;
|
||||
io->setup = CustomSetup;
|
||||
io->teardown = CustomTeardown;
|
||||
io->opaque = params;
|
||||
io->opaque = params;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -11,105 +11,97 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "src/dec/common_dec.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/utils/bit_reader_utils.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
static WEBP_INLINE int clip(int v, int M) {
|
||||
return v < 0 ? 0 : v > M ? M : v;
|
||||
}
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
static WEBP_INLINE int clip(int v, int M) { return v < 0 ? 0 : v > M ? M : v; }
|
||||
|
||||
// Paragraph 14.1
|
||||
static const uint8_t kDcTable[128] = {
|
||||
4, 5, 6, 7, 8, 9, 10, 10,
|
||||
11, 12, 13, 14, 15, 16, 17, 17,
|
||||
18, 19, 20, 20, 21, 21, 22, 22,
|
||||
23, 23, 24, 25, 25, 26, 27, 28,
|
||||
29, 30, 31, 32, 33, 34, 35, 36,
|
||||
37, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 46, 47, 48, 49, 50,
|
||||
51, 52, 53, 54, 55, 56, 57, 58,
|
||||
59, 60, 61, 62, 63, 64, 65, 66,
|
||||
67, 68, 69, 70, 71, 72, 73, 74,
|
||||
75, 76, 76, 77, 78, 79, 80, 81,
|
||||
82, 83, 84, 85, 86, 87, 88, 89,
|
||||
91, 93, 95, 96, 98, 100, 101, 102,
|
||||
104, 106, 108, 110, 112, 114, 116, 118,
|
||||
122, 124, 126, 128, 130, 132, 134, 136,
|
||||
138, 140, 143, 145, 148, 151, 154, 157
|
||||
};
|
||||
4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||
17, 18, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
||||
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
|
||||
70, 71, 72, 73, 74, 75, 76, 76, 77, 78, 79, 80, 81, 82, 83,
|
||||
84, 85, 86, 87, 88, 89, 91, 93, 95, 96, 98, 100, 101, 102, 104,
|
||||
106, 108, 110, 112, 114, 116, 118, 122, 124, 126, 128, 130, 132, 134, 136,
|
||||
138, 140, 143, 145, 148, 151, 154, 157};
|
||||
|
||||
static const uint16_t kAcTable[128] = {
|
||||
4, 5, 6, 7, 8, 9, 10, 11,
|
||||
12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27,
|
||||
28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 47, 48, 49, 50, 51,
|
||||
52, 53, 54, 55, 56, 57, 58, 60,
|
||||
62, 64, 66, 68, 70, 72, 74, 76,
|
||||
78, 80, 82, 84, 86, 88, 90, 92,
|
||||
94, 96, 98, 100, 102, 104, 106, 108,
|
||||
110, 112, 114, 116, 119, 122, 125, 128,
|
||||
131, 134, 137, 140, 143, 146, 149, 152,
|
||||
155, 158, 161, 164, 167, 170, 173, 177,
|
||||
181, 185, 189, 193, 197, 201, 205, 209,
|
||||
213, 217, 221, 225, 229, 234, 239, 245,
|
||||
249, 254, 259, 264, 269, 274, 279, 284
|
||||
};
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
|
||||
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 62, 64, 66, 68,
|
||||
70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98,
|
||||
100, 102, 104, 106, 108, 110, 112, 114, 116, 119, 122, 125, 128, 131, 134,
|
||||
137, 140, 143, 146, 149, 152, 155, 158, 161, 164, 167, 170, 173, 177, 181,
|
||||
185, 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 234, 239, 245,
|
||||
249, 254, 259, 264, 269, 274, 279, 284};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Paragraph 9.6
|
||||
|
||||
void VP8ParseQuant(VP8Decoder* const dec) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
VP8BitReader* const br = &dec->br;
|
||||
const int base_q0 = VP8GetValue(br, 7, "global-header");
|
||||
const int dqy1_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dqy2_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dqy2_ac = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dquv_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dquv_ac = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dqy1_dc = VP8Get(br, "global-header")
|
||||
? VP8GetSignedValue(br, 4, "global-header")
|
||||
: 0;
|
||||
const int dqy2_dc = VP8Get(br, "global-header")
|
||||
? VP8GetSignedValue(br, 4, "global-header")
|
||||
: 0;
|
||||
const int dqy2_ac = VP8Get(br, "global-header")
|
||||
? VP8GetSignedValue(br, 4, "global-header")
|
||||
: 0;
|
||||
const int dquv_dc = VP8Get(br, "global-header")
|
||||
? VP8GetSignedValue(br, 4, "global-header")
|
||||
: 0;
|
||||
const int dquv_ac = VP8Get(br, "global-header")
|
||||
? VP8GetSignedValue(br, 4, "global-header")
|
||||
: 0;
|
||||
|
||||
const VP8SegmentHeader* const hdr = &dec->segment_hdr_;
|
||||
const VP8SegmentHeader* const hdr = &dec->segment_hdr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
|
||||
int q;
|
||||
if (hdr->use_segment_) {
|
||||
q = hdr->quantizer_[i];
|
||||
if (!hdr->absolute_delta_) {
|
||||
if (hdr->use_segment) {
|
||||
q = hdr->quantizer[i];
|
||||
if (!hdr->absolute_delta) {
|
||||
q += base_q0;
|
||||
}
|
||||
} else {
|
||||
if (i > 0) {
|
||||
dec->dqm_[i] = dec->dqm_[0];
|
||||
dec->dqm[i] = dec->dqm[0];
|
||||
continue;
|
||||
} else {
|
||||
q = base_q0;
|
||||
}
|
||||
}
|
||||
{
|
||||
VP8QuantMatrix* const m = &dec->dqm_[i];
|
||||
m->y1_mat_[0] = kDcTable[clip(q + dqy1_dc, 127)];
|
||||
m->y1_mat_[1] = kAcTable[clip(q + 0, 127)];
|
||||
VP8QuantMatrix* const m = &dec->dqm[i];
|
||||
m->y1_mat[0] = kDcTable[clip(q + dqy1_dc, 127)];
|
||||
m->y1_mat[1] = kAcTable[clip(q + 0, 127)];
|
||||
|
||||
m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2;
|
||||
m->y2_mat[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2;
|
||||
// For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
|
||||
// The smallest precision for that is '(x*6349) >> 12' but 16 is a good
|
||||
// word size.
|
||||
m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16;
|
||||
if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8;
|
||||
m->y2_mat[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16;
|
||||
if (m->y2_mat[1] < 8) m->y2_mat[1] = 8;
|
||||
|
||||
m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];
|
||||
m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)];
|
||||
m->uv_mat[0] = kDcTable[clip(q + dquv_dc, 117)];
|
||||
m->uv_mat[1] = kAcTable[clip(q + dquv_ac, 127)];
|
||||
|
||||
m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation
|
||||
m->uv_quant = q + dquv_ac; // for dithering strength evaluation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -11,15 +11,22 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "src/dec/common_dec.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/utils/bit_reader_inl_utils.h"
|
||||
#include "src/utils/bit_reader_utils.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
#if !defined(USE_GENERIC_TREE)
|
||||
#if !defined(__arm__) && !defined(_M_ARM) && !WEBP_AARCH64 && \
|
||||
!defined(__wasm__)
|
||||
#if !defined(__arm__) && !defined(_M_ARM) && !WEBP_AARCH64 && !defined(__wasm__)
|
||||
// using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then.
|
||||
#define USE_GENERIC_TREE 1 // ALTERNATE_CODE
|
||||
#define USE_GENERIC_TREE 1 // ALTERNATE_CODE
|
||||
#else
|
||||
#define USE_GENERIC_TREE 0
|
||||
#endif
|
||||
@@ -27,297 +34,256 @@
|
||||
|
||||
#if (USE_GENERIC_TREE == 1)
|
||||
static const int8_t kYModesIntra4[18] = {
|
||||
-B_DC_PRED, 1,
|
||||
-B_TM_PRED, 2,
|
||||
-B_VE_PRED, 3,
|
||||
4, 6,
|
||||
-B_HE_PRED, 5,
|
||||
-B_RD_PRED, -B_VR_PRED,
|
||||
-B_LD_PRED, 7,
|
||||
-B_VL_PRED, 8,
|
||||
-B_HD_PRED, -B_HU_PRED
|
||||
};
|
||||
-B_DC_PRED, 1, -B_TM_PRED, 2, -B_VE_PRED, 3,
|
||||
4, 6, -B_HE_PRED, 5, -B_RD_PRED, -B_VR_PRED,
|
||||
-B_LD_PRED, 7, -B_VL_PRED, 8, -B_HD_PRED, -B_HU_PRED};
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default probabilities
|
||||
|
||||
// Paragraph 13.5
|
||||
static const uint8_t
|
||||
CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
{ { { 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 }
|
||||
},
|
||||
{ { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 },
|
||||
{ 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 },
|
||||
{ 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 },
|
||||
{ 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 },
|
||||
{ 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 },
|
||||
},
|
||||
{ { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 },
|
||||
{ 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 },
|
||||
{ 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 },
|
||||
},
|
||||
{ { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 },
|
||||
{ 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 },
|
||||
{ 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 },
|
||||
{ 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 },
|
||||
{ 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 },
|
||||
{ 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 },
|
||||
{ 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
},
|
||||
{ { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 },
|
||||
{ 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 },
|
||||
{ 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 }
|
||||
},
|
||||
{ { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 },
|
||||
{ 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 },
|
||||
{ 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 },
|
||||
{ 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 },
|
||||
{ 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 },
|
||||
{ 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 },
|
||||
{ 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 },
|
||||
{ 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 },
|
||||
{ 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 },
|
||||
{ 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 },
|
||||
{ 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 },
|
||||
{ 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 },
|
||||
{ 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
|
||||
{ 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
},
|
||||
{ { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 },
|
||||
{ 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 },
|
||||
{ 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 }
|
||||
},
|
||||
{ { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 },
|
||||
{ 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 },
|
||||
{ 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 },
|
||||
{ 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 },
|
||||
{ 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 },
|
||||
{ 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 },
|
||||
{ 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 55, 93, 255, 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 }
|
||||
}
|
||||
},
|
||||
{ { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 },
|
||||
{ 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 },
|
||||
{ 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 }
|
||||
},
|
||||
{ { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 },
|
||||
{ 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 },
|
||||
{ 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 },
|
||||
{ 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 },
|
||||
{ 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 },
|
||||
{ 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 },
|
||||
{ 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 },
|
||||
{ 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 },
|
||||
{ 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 },
|
||||
{ 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 },
|
||||
{ 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 },
|
||||
{ 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 },
|
||||
{ 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
}
|
||||
};
|
||||
static const uint8_t CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
{{{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}},
|
||||
{{253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128},
|
||||
{189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128},
|
||||
{106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128}},
|
||||
{
|
||||
{1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128},
|
||||
{181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128},
|
||||
{78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128},
|
||||
},
|
||||
{
|
||||
{1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128},
|
||||
{184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128},
|
||||
{77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128},
|
||||
},
|
||||
{{1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128},
|
||||
{170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128},
|
||||
{37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128}},
|
||||
{{1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128},
|
||||
{207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128},
|
||||
{102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128}},
|
||||
{{1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128},
|
||||
{177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128},
|
||||
{80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128}},
|
||||
{{1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||
{246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||
{255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}}},
|
||||
{{{198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62},
|
||||
{131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1},
|
||||
{68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128}},
|
||||
{{1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128},
|
||||
{184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128},
|
||||
{81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128}},
|
||||
{{1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128},
|
||||
{99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128},
|
||||
{23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128}},
|
||||
{{1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128},
|
||||
{109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128},
|
||||
{44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128}},
|
||||
{{1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128},
|
||||
{94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128},
|
||||
{22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128}},
|
||||
{{1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128},
|
||||
{124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128},
|
||||
{35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128}},
|
||||
{{1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128},
|
||||
{121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128},
|
||||
{45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128}},
|
||||
{{1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128},
|
||||
{203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||
{137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128}}},
|
||||
{{{253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128},
|
||||
{175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128},
|
||||
{73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128}},
|
||||
{{1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128},
|
||||
{239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128},
|
||||
{155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128}},
|
||||
{{1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128},
|
||||
{201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128},
|
||||
{69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128}},
|
||||
{{1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||
{223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128},
|
||||
{141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128}},
|
||||
{{1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||
{190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128},
|
||||
{149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128}},
|
||||
{{1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||
{247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||
{240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128}},
|
||||
{{1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||
{213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||
{55, 93, 255, 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}}},
|
||||
{{{202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255},
|
||||
{126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128},
|
||||
{61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128}},
|
||||
{{1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128},
|
||||
{166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128},
|
||||
{39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128}},
|
||||
{{1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128},
|
||||
{124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128},
|
||||
{24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128}},
|
||||
{{1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128},
|
||||
{149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128},
|
||||
{28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128}},
|
||||
{{1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128},
|
||||
{123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128},
|
||||
{20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128}},
|
||||
{{1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128},
|
||||
{168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128},
|
||||
{47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128}},
|
||||
{{1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128},
|
||||
{141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128},
|
||||
{42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128}},
|
||||
{{1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||
{244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||
{238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128}}}};
|
||||
|
||||
// Paragraph 11.5
|
||||
static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
|
||||
{ { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
|
||||
{ 152, 179, 64, 126, 170, 118, 46, 70, 95 },
|
||||
{ 175, 69, 143, 80, 85, 82, 72, 155, 103 },
|
||||
{ 56, 58, 10, 171, 218, 189, 17, 13, 152 },
|
||||
{ 114, 26, 17, 163, 44, 195, 21, 10, 173 },
|
||||
{ 121, 24, 80, 195, 26, 62, 44, 64, 85 },
|
||||
{ 144, 71, 10, 38, 171, 213, 144, 34, 26 },
|
||||
{ 170, 46, 55, 19, 136, 160, 33, 206, 71 },
|
||||
{ 63, 20, 8, 114, 114, 208, 12, 9, 226 },
|
||||
{ 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
|
||||
{ { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
|
||||
{ 72, 187, 100, 130, 157, 111, 32, 75, 80 },
|
||||
{ 66, 102, 167, 99, 74, 62, 40, 234, 128 },
|
||||
{ 41, 53, 9, 178, 241, 141, 26, 8, 107 },
|
||||
{ 74, 43, 26, 146, 73, 166, 49, 23, 157 },
|
||||
{ 65, 38, 105, 160, 51, 52, 31, 115, 128 },
|
||||
{ 104, 79, 12, 27, 217, 255, 87, 17, 7 },
|
||||
{ 87, 68, 71, 44, 114, 51, 15, 186, 23 },
|
||||
{ 47, 41, 14, 110, 182, 183, 21, 17, 194 },
|
||||
{ 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
|
||||
{ { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
|
||||
{ 43, 97, 183, 117, 85, 38, 35, 179, 61 },
|
||||
{ 39, 53, 200, 87, 26, 21, 43, 232, 171 },
|
||||
{ 56, 34, 51, 104, 114, 102, 29, 93, 77 },
|
||||
{ 39, 28, 85, 171, 58, 165, 90, 98, 64 },
|
||||
{ 34, 22, 116, 206, 23, 34, 43, 166, 73 },
|
||||
{ 107, 54, 32, 26, 51, 1, 81, 43, 31 },
|
||||
{ 68, 25, 106, 22, 64, 171, 36, 225, 114 },
|
||||
{ 34, 19, 21, 102, 132, 188, 16, 76, 124 },
|
||||
{ 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
|
||||
{ { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
|
||||
{ 60, 148, 31, 172, 219, 228, 21, 18, 111 },
|
||||
{ 112, 113, 77, 85, 179, 255, 38, 120, 114 },
|
||||
{ 40, 42, 1, 196, 245, 209, 10, 25, 109 },
|
||||
{ 88, 43, 29, 140, 166, 213, 37, 43, 154 },
|
||||
{ 61, 63, 30, 155, 67, 45, 68, 1, 209 },
|
||||
{ 100, 80, 8, 43, 154, 1, 51, 26, 71 },
|
||||
{ 142, 78, 78, 16, 255, 128, 34, 197, 171 },
|
||||
{ 41, 40, 5, 102, 211, 183, 4, 1, 221 },
|
||||
{ 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
|
||||
{ { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
|
||||
{ 67, 87, 58, 169, 82, 115, 26, 59, 179 },
|
||||
{ 63, 59, 90, 180, 59, 166, 93, 73, 154 },
|
||||
{ 40, 40, 21, 116, 143, 209, 34, 39, 175 },
|
||||
{ 47, 15, 16, 183, 34, 223, 49, 45, 183 },
|
||||
{ 46, 17, 33, 183, 6, 98, 15, 32, 183 },
|
||||
{ 57, 46, 22, 24, 128, 1, 54, 17, 37 },
|
||||
{ 65, 32, 73, 115, 28, 128, 23, 128, 205 },
|
||||
{ 40, 3, 9, 115, 51, 192, 18, 6, 223 },
|
||||
{ 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
|
||||
{ { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
|
||||
{ 64, 90, 70, 205, 40, 41, 23, 26, 57 },
|
||||
{ 54, 57, 112, 184, 5, 41, 38, 166, 213 },
|
||||
{ 30, 34, 26, 133, 152, 116, 10, 32, 134 },
|
||||
{ 39, 19, 53, 221, 26, 114, 32, 73, 255 },
|
||||
{ 31, 9, 65, 234, 2, 15, 1, 118, 73 },
|
||||
{ 75, 32, 12, 51, 192, 255, 160, 43, 51 },
|
||||
{ 88, 31, 35, 67, 102, 85, 55, 186, 85 },
|
||||
{ 56, 21, 23, 111, 59, 205, 45, 37, 192 },
|
||||
{ 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
|
||||
{ { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
|
||||
{ 95, 84, 53, 89, 128, 100, 113, 101, 45 },
|
||||
{ 75, 79, 123, 47, 51, 128, 81, 171, 1 },
|
||||
{ 57, 17, 5, 71, 102, 57, 53, 41, 49 },
|
||||
{ 38, 33, 13, 121, 57, 73, 26, 1, 85 },
|
||||
{ 41, 10, 67, 138, 77, 110, 90, 47, 114 },
|
||||
{ 115, 21, 2, 10, 102, 255, 166, 23, 6 },
|
||||
{ 101, 29, 16, 10, 85, 128, 101, 196, 26 },
|
||||
{ 57, 18, 10, 102, 102, 213, 34, 20, 43 },
|
||||
{ 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
|
||||
{ { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
|
||||
{ 69, 60, 71, 38, 73, 119, 28, 222, 37 },
|
||||
{ 68, 45, 128, 34, 1, 47, 11, 245, 171 },
|
||||
{ 62, 17, 19, 70, 146, 85, 55, 62, 70 },
|
||||
{ 37, 43, 37, 154, 100, 163, 85, 160, 1 },
|
||||
{ 63, 9, 92, 136, 28, 64, 32, 201, 85 },
|
||||
{ 75, 15, 9, 9, 64, 255, 184, 119, 16 },
|
||||
{ 86, 6, 28, 5, 64, 255, 25, 248, 1 },
|
||||
{ 56, 8, 17, 132, 137, 255, 55, 116, 128 },
|
||||
{ 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
|
||||
{ { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
|
||||
{ 51, 103, 44, 131, 131, 123, 31, 6, 158 },
|
||||
{ 86, 40, 64, 135, 148, 224, 45, 183, 128 },
|
||||
{ 22, 26, 17, 131, 240, 154, 14, 1, 209 },
|
||||
{ 45, 16, 21, 91, 64, 222, 7, 1, 197 },
|
||||
{ 56, 21, 39, 155, 60, 138, 23, 102, 213 },
|
||||
{ 83, 12, 13, 54, 192, 255, 68, 47, 28 },
|
||||
{ 85, 26, 85, 85, 128, 128, 32, 146, 171 },
|
||||
{ 18, 11, 7, 63, 144, 171, 4, 4, 246 },
|
||||
{ 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
|
||||
{ { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
|
||||
{ 85, 126, 47, 87, 176, 51, 41, 20, 32 },
|
||||
{ 101, 75, 128, 139, 118, 146, 116, 128, 85 },
|
||||
{ 56, 41, 15, 176, 236, 85, 37, 9, 62 },
|
||||
{ 71, 30, 17, 119, 118, 255, 17, 18, 138 },
|
||||
{ 101, 38, 60, 138, 55, 70, 43, 26, 142 },
|
||||
{ 146, 36, 19, 30, 171, 255, 97, 27, 20 },
|
||||
{ 138, 45, 61, 62, 219, 1, 81, 188, 64 },
|
||||
{ 32, 41, 20, 117, 151, 142, 20, 21, 163 },
|
||||
{ 112, 19, 12, 61, 195, 128, 48, 4, 24 } }
|
||||
};
|
||||
{{231, 120, 48, 89, 115, 113, 120, 152, 112},
|
||||
{152, 179, 64, 126, 170, 118, 46, 70, 95},
|
||||
{175, 69, 143, 80, 85, 82, 72, 155, 103},
|
||||
{56, 58, 10, 171, 218, 189, 17, 13, 152},
|
||||
{114, 26, 17, 163, 44, 195, 21, 10, 173},
|
||||
{121, 24, 80, 195, 26, 62, 44, 64, 85},
|
||||
{144, 71, 10, 38, 171, 213, 144, 34, 26},
|
||||
{170, 46, 55, 19, 136, 160, 33, 206, 71},
|
||||
{63, 20, 8, 114, 114, 208, 12, 9, 226},
|
||||
{81, 40, 11, 96, 182, 84, 29, 16, 36}},
|
||||
{{134, 183, 89, 137, 98, 101, 106, 165, 148},
|
||||
{72, 187, 100, 130, 157, 111, 32, 75, 80},
|
||||
{66, 102, 167, 99, 74, 62, 40, 234, 128},
|
||||
{41, 53, 9, 178, 241, 141, 26, 8, 107},
|
||||
{74, 43, 26, 146, 73, 166, 49, 23, 157},
|
||||
{65, 38, 105, 160, 51, 52, 31, 115, 128},
|
||||
{104, 79, 12, 27, 217, 255, 87, 17, 7},
|
||||
{87, 68, 71, 44, 114, 51, 15, 186, 23},
|
||||
{47, 41, 14, 110, 182, 183, 21, 17, 194},
|
||||
{66, 45, 25, 102, 197, 189, 23, 18, 22}},
|
||||
{{88, 88, 147, 150, 42, 46, 45, 196, 205},
|
||||
{43, 97, 183, 117, 85, 38, 35, 179, 61},
|
||||
{39, 53, 200, 87, 26, 21, 43, 232, 171},
|
||||
{56, 34, 51, 104, 114, 102, 29, 93, 77},
|
||||
{39, 28, 85, 171, 58, 165, 90, 98, 64},
|
||||
{34, 22, 116, 206, 23, 34, 43, 166, 73},
|
||||
{107, 54, 32, 26, 51, 1, 81, 43, 31},
|
||||
{68, 25, 106, 22, 64, 171, 36, 225, 114},
|
||||
{34, 19, 21, 102, 132, 188, 16, 76, 124},
|
||||
{62, 18, 78, 95, 85, 57, 50, 48, 51}},
|
||||
{{193, 101, 35, 159, 215, 111, 89, 46, 111},
|
||||
{60, 148, 31, 172, 219, 228, 21, 18, 111},
|
||||
{112, 113, 77, 85, 179, 255, 38, 120, 114},
|
||||
{40, 42, 1, 196, 245, 209, 10, 25, 109},
|
||||
{88, 43, 29, 140, 166, 213, 37, 43, 154},
|
||||
{61, 63, 30, 155, 67, 45, 68, 1, 209},
|
||||
{100, 80, 8, 43, 154, 1, 51, 26, 71},
|
||||
{142, 78, 78, 16, 255, 128, 34, 197, 171},
|
||||
{41, 40, 5, 102, 211, 183, 4, 1, 221},
|
||||
{51, 50, 17, 168, 209, 192, 23, 25, 82}},
|
||||
{{138, 31, 36, 171, 27, 166, 38, 44, 229},
|
||||
{67, 87, 58, 169, 82, 115, 26, 59, 179},
|
||||
{63, 59, 90, 180, 59, 166, 93, 73, 154},
|
||||
{40, 40, 21, 116, 143, 209, 34, 39, 175},
|
||||
{47, 15, 16, 183, 34, 223, 49, 45, 183},
|
||||
{46, 17, 33, 183, 6, 98, 15, 32, 183},
|
||||
{57, 46, 22, 24, 128, 1, 54, 17, 37},
|
||||
{65, 32, 73, 115, 28, 128, 23, 128, 205},
|
||||
{40, 3, 9, 115, 51, 192, 18, 6, 223},
|
||||
{87, 37, 9, 115, 59, 77, 64, 21, 47}},
|
||||
{{104, 55, 44, 218, 9, 54, 53, 130, 226},
|
||||
{64, 90, 70, 205, 40, 41, 23, 26, 57},
|
||||
{54, 57, 112, 184, 5, 41, 38, 166, 213},
|
||||
{30, 34, 26, 133, 152, 116, 10, 32, 134},
|
||||
{39, 19, 53, 221, 26, 114, 32, 73, 255},
|
||||
{31, 9, 65, 234, 2, 15, 1, 118, 73},
|
||||
{75, 32, 12, 51, 192, 255, 160, 43, 51},
|
||||
{88, 31, 35, 67, 102, 85, 55, 186, 85},
|
||||
{56, 21, 23, 111, 59, 205, 45, 37, 192},
|
||||
{55, 38, 70, 124, 73, 102, 1, 34, 98}},
|
||||
{{125, 98, 42, 88, 104, 85, 117, 175, 82},
|
||||
{95, 84, 53, 89, 128, 100, 113, 101, 45},
|
||||
{75, 79, 123, 47, 51, 128, 81, 171, 1},
|
||||
{57, 17, 5, 71, 102, 57, 53, 41, 49},
|
||||
{38, 33, 13, 121, 57, 73, 26, 1, 85},
|
||||
{41, 10, 67, 138, 77, 110, 90, 47, 114},
|
||||
{115, 21, 2, 10, 102, 255, 166, 23, 6},
|
||||
{101, 29, 16, 10, 85, 128, 101, 196, 26},
|
||||
{57, 18, 10, 102, 102, 213, 34, 20, 43},
|
||||
{117, 20, 15, 36, 163, 128, 68, 1, 26}},
|
||||
{{102, 61, 71, 37, 34, 53, 31, 243, 192},
|
||||
{69, 60, 71, 38, 73, 119, 28, 222, 37},
|
||||
{68, 45, 128, 34, 1, 47, 11, 245, 171},
|
||||
{62, 17, 19, 70, 146, 85, 55, 62, 70},
|
||||
{37, 43, 37, 154, 100, 163, 85, 160, 1},
|
||||
{63, 9, 92, 136, 28, 64, 32, 201, 85},
|
||||
{75, 15, 9, 9, 64, 255, 184, 119, 16},
|
||||
{86, 6, 28, 5, 64, 255, 25, 248, 1},
|
||||
{56, 8, 17, 132, 137, 255, 55, 116, 128},
|
||||
{58, 15, 20, 82, 135, 57, 26, 121, 40}},
|
||||
{{164, 50, 31, 137, 154, 133, 25, 35, 218},
|
||||
{51, 103, 44, 131, 131, 123, 31, 6, 158},
|
||||
{86, 40, 64, 135, 148, 224, 45, 183, 128},
|
||||
{22, 26, 17, 131, 240, 154, 14, 1, 209},
|
||||
{45, 16, 21, 91, 64, 222, 7, 1, 197},
|
||||
{56, 21, 39, 155, 60, 138, 23, 102, 213},
|
||||
{83, 12, 13, 54, 192, 255, 68, 47, 28},
|
||||
{85, 26, 85, 85, 128, 128, 32, 146, 171},
|
||||
{18, 11, 7, 63, 144, 171, 4, 4, 246},
|
||||
{35, 27, 10, 146, 174, 171, 12, 26, 128}},
|
||||
{{190, 80, 35, 99, 180, 80, 126, 54, 45},
|
||||
{85, 126, 47, 87, 176, 51, 41, 20, 32},
|
||||
{101, 75, 128, 139, 118, 146, 116, 128, 85},
|
||||
{56, 41, 15, 176, 236, 85, 37, 9, 62},
|
||||
{71, 30, 17, 119, 118, 255, 17, 18, 138},
|
||||
{101, 38, 60, 138, 55, 70, 43, 26, 142},
|
||||
{146, 36, 19, 30, 171, 255, 97, 27, 20},
|
||||
{138, 45, 61, 62, 219, 1, 81, 188, 64},
|
||||
{32, 41, 20, 117, 151, 142, 20, 21, 163},
|
||||
{112, 19, 12, 61, 195, 128, 48, 4, 24}}};
|
||||
|
||||
void VP8ResetProba(VP8Proba* const proba) {
|
||||
memset(proba->segments_, 255u, sizeof(proba->segments_));
|
||||
// proba->bands_[][] is initialized later
|
||||
WEBP_UNSAFE_MEMSET(proba->segments, 255u, sizeof(proba->segments));
|
||||
// proba->bands[][] is initialized later
|
||||
}
|
||||
|
||||
static void ParseIntraMode(VP8BitReader* const br,
|
||||
VP8Decoder* const dec, int mb_x) {
|
||||
uint8_t* const top = dec->intra_t_ + 4 * mb_x;
|
||||
uint8_t* const left = dec->intra_l_;
|
||||
VP8MBData* const block = dec->mb_data_ + mb_x;
|
||||
static void ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec,
|
||||
int mb_x) {
|
||||
uint8_t* const top = dec->intra_t + 4 * mb_x;
|
||||
uint8_t* const left = dec->intra_l;
|
||||
VP8MBData* const block = dec->mb_data + mb_x;
|
||||
|
||||
// Note: we don't save segment map (yet), as we don't expect
|
||||
// to decode more than 1 keyframe.
|
||||
if (dec->segment_hdr_.update_map_) {
|
||||
if (dec->segment_hdr.update_map) {
|
||||
// Hardcoded tree parsing
|
||||
block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0], "segments")
|
||||
? VP8GetBit(br, dec->proba_.segments_[1], "segments")
|
||||
: VP8GetBit(br, dec->proba_.segments_[2], "segments") + 2;
|
||||
block->segment =
|
||||
!VP8GetBit(br, dec->proba.segments[0], "segments")
|
||||
? VP8GetBit(br, dec->proba.segments[1], "segments")
|
||||
: VP8GetBit(br, dec->proba.segments[2], "segments") + 2;
|
||||
} else {
|
||||
block->segment_ = 0; // default for intra
|
||||
block->segment = 0; // default for intra
|
||||
}
|
||||
if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_, "skip");
|
||||
if (dec->use_skip_proba) block->skip = VP8GetBit(br, dec->skip_p, "skip");
|
||||
|
||||
block->is_i4x4_ = !VP8GetBit(br, 145, "block-size");
|
||||
if (!block->is_i4x4_) {
|
||||
block->is_i4x4 = !VP8GetBit(br, 145, "block-size");
|
||||
if (!block->is_i4x4) {
|
||||
// Hardcoded 16x16 intra-mode decision tree.
|
||||
const int ymode =
|
||||
VP8GetBit(br, 156, "pred-modes") ?
|
||||
(VP8GetBit(br, 128, "pred-modes") ? TM_PRED : H_PRED) :
|
||||
(VP8GetBit(br, 163, "pred-modes") ? V_PRED : DC_PRED);
|
||||
block->imodes_[0] = ymode;
|
||||
memset(top, ymode, 4 * sizeof(*top));
|
||||
memset(left, ymode, 4 * sizeof(*left));
|
||||
VP8GetBit(br, 156, "pred-modes")
|
||||
? (VP8GetBit(br, 128, "pred-modes") ? TM_PRED : H_PRED)
|
||||
: (VP8GetBit(br, 163, "pred-modes") ? V_PRED : DC_PRED);
|
||||
block->imodes[0] = ymode;
|
||||
WEBP_UNSAFE_MEMSET(top, ymode, 4 * sizeof(*top));
|
||||
WEBP_UNSAFE_MEMSET(left, ymode, 4 * sizeof(*left));
|
||||
} else {
|
||||
uint8_t* modes = block->imodes_;
|
||||
uint8_t* modes = block->imodes;
|
||||
int y;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int ymode = left[y];
|
||||
@@ -333,38 +299,43 @@ static void ParseIntraMode(VP8BitReader* const br,
|
||||
ymode = -i;
|
||||
#else
|
||||
// Hardcoded tree parsing
|
||||
ymode = !VP8GetBit(br, prob[0], "pred-modes") ? B_DC_PRED :
|
||||
!VP8GetBit(br, prob[1], "pred-modes") ? B_TM_PRED :
|
||||
!VP8GetBit(br, prob[2], "pred-modes") ? B_VE_PRED :
|
||||
!VP8GetBit(br, prob[3], "pred-modes") ?
|
||||
(!VP8GetBit(br, prob[4], "pred-modes") ? B_HE_PRED :
|
||||
(!VP8GetBit(br, prob[5], "pred-modes") ? B_RD_PRED
|
||||
: B_VR_PRED)) :
|
||||
(!VP8GetBit(br, prob[6], "pred-modes") ? B_LD_PRED :
|
||||
(!VP8GetBit(br, prob[7], "pred-modes") ? B_VL_PRED :
|
||||
(!VP8GetBit(br, prob[8], "pred-modes") ? B_HD_PRED
|
||||
: B_HU_PRED))
|
||||
);
|
||||
ymode =
|
||||
!VP8GetBit(br, prob[0], "pred-modes") ? B_DC_PRED
|
||||
: !VP8GetBit(br, prob[1], "pred-modes") ? B_TM_PRED
|
||||
: !VP8GetBit(br, prob[2], "pred-modes") ? B_VE_PRED
|
||||
: !VP8GetBit(br, prob[3], "pred-modes")
|
||||
? (!VP8GetBit(br, prob[4], "pred-modes")
|
||||
? B_HE_PRED
|
||||
: (!VP8GetBit(br, prob[5], "pred-modes") ? B_RD_PRED
|
||||
: B_VR_PRED))
|
||||
: (!VP8GetBit(br, prob[6], "pred-modes")
|
||||
? B_LD_PRED
|
||||
: (!VP8GetBit(br, prob[7], "pred-modes")
|
||||
? B_VL_PRED
|
||||
: (!VP8GetBit(br, prob[8], "pred-modes")
|
||||
? B_HD_PRED
|
||||
: B_HU_PRED)));
|
||||
#endif // USE_GENERIC_TREE
|
||||
top[x] = ymode;
|
||||
}
|
||||
memcpy(modes, top, 4 * sizeof(*top));
|
||||
WEBP_UNSAFE_MEMCPY(modes, top, 4 * sizeof(*top));
|
||||
modes += 4;
|
||||
left[y] = ymode;
|
||||
}
|
||||
}
|
||||
// Hardcoded UVMode decision tree
|
||||
block->uvmode_ = !VP8GetBit(br, 142, "pred-modes-uv") ? DC_PRED
|
||||
: !VP8GetBit(br, 114, "pred-modes-uv") ? V_PRED
|
||||
: VP8GetBit(br, 183, "pred-modes-uv") ? TM_PRED : H_PRED;
|
||||
block->uvmode = !VP8GetBit(br, 142, "pred-modes-uv") ? DC_PRED
|
||||
: !VP8GetBit(br, 114, "pred-modes-uv") ? V_PRED
|
||||
: VP8GetBit(br, 183, "pred-modes-uv") ? TM_PRED
|
||||
: H_PRED;
|
||||
}
|
||||
|
||||
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
|
||||
for (mb_x = 0; mb_x < dec->mb_w; ++mb_x) {
|
||||
ParseIntraMode(br, dec, mb_x);
|
||||
}
|
||||
return !dec->br_.eof_;
|
||||
return !dec->br.eof;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -372,168 +343,131 @@ int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
|
||||
static const uint8_t
|
||||
CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
{ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 },
|
||||
{ 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
}
|
||||
};
|
||||
{{{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255},
|
||||
{250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255},
|
||||
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}},
|
||||
{{{217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255},
|
||||
{234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255}},
|
||||
{{255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}},
|
||||
{{{186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255},
|
||||
{251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255}},
|
||||
{{255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}},
|
||||
{{{248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255},
|
||||
{248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||
{{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}}};
|
||||
|
||||
// Paragraph 9.9
|
||||
|
||||
static const uint8_t kBands[16 + 1] = {
|
||||
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||
0 // extra entry as sentinel
|
||||
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||
0 // extra entry as sentinel
|
||||
};
|
||||
|
||||
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
VP8Proba* const proba = &dec->proba_;
|
||||
VP8Proba* const proba = &dec->proba;
|
||||
int t, b, c, p;
|
||||
for (t = 0; t < NUM_TYPES; ++t) {
|
||||
for (b = 0; b < NUM_BANDS; ++b) {
|
||||
for (c = 0; c < NUM_CTX; ++c) {
|
||||
for (p = 0; p < NUM_PROBAS; ++p) {
|
||||
const int v =
|
||||
VP8GetBit(br, CoeffsUpdateProba[t][b][c][p], "global-header") ?
|
||||
VP8GetValue(br, 8, "global-header") :
|
||||
CoeffsProba0[t][b][c][p];
|
||||
proba->bands_[t][b].probas_[c][p] = v;
|
||||
VP8GetBit(br, CoeffsUpdateProba[t][b][c][p], "global-header")
|
||||
? VP8GetValue(br, 8, "global-header")
|
||||
: CoeffsProba0[t][b][c][p];
|
||||
proba->bands[t][b].probas[c][p] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (b = 0; b < 16 + 1; ++b) {
|
||||
proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
|
||||
proba->bands_ptr[t][b] = &proba->bands[t][kBands[b]];
|
||||
}
|
||||
}
|
||||
dec->use_skip_proba_ = VP8Get(br, "global-header");
|
||||
if (dec->use_skip_proba_) {
|
||||
dec->skip_p_ = VP8GetValue(br, 8, "global-header");
|
||||
dec->use_skip_proba = VP8Get(br, "global-header");
|
||||
if (dec->use_skip_proba) {
|
||||
dec->skip_p = VP8GetValue(br, 8, "global-header");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,28 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "src/dec/vp8_dec.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/dec/alphai_dec.h"
|
||||
#include "src/dec/common_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/bit_reader_inl_utils.h"
|
||||
#include "src/utils/bit_reader_utils.h"
|
||||
#include "src/utils/thread_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -30,8 +44,8 @@ int WebPGetDecoderVersion(void) {
|
||||
// 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);
|
||||
const VP8BandProbas* const prob[], int ctx,
|
||||
const quant_t dq, int n, int16_t* out);
|
||||
static volatile GetCoeffsFunc GetCoeffs = NULL;
|
||||
|
||||
static void InitGetCoeffs(void);
|
||||
@@ -40,8 +54,8 @@ static void InitGetCoeffs(void);
|
||||
// VP8Decoder
|
||||
|
||||
static void SetOk(VP8Decoder* const dec) {
|
||||
dec->status_ = VP8_STATUS_OK;
|
||||
dec->error_msg_ = "OK";
|
||||
dec->status = VP8_STATUS_OK;
|
||||
dec->error_msg = "OK";
|
||||
}
|
||||
|
||||
int VP8InitIoInternal(VP8Io* const io, int version) {
|
||||
@@ -49,7 +63,7 @@ int VP8InitIoInternal(VP8Io* const io, int version) {
|
||||
return 0; // mismatch error
|
||||
}
|
||||
if (io != NULL) {
|
||||
memset(io, 0, sizeof(*io));
|
||||
WEBP_UNSAFE_MEMSET(io, 0, sizeof(*io));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -58,9 +72,9 @@ VP8Decoder* VP8New(void) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||
if (dec != NULL) {
|
||||
SetOk(dec);
|
||||
WebPGetWorkerInterface()->Init(&dec->worker_);
|
||||
dec->ready_ = 0;
|
||||
dec->num_parts_minus_one_ = 0;
|
||||
WebPGetWorkerInterface()->Init(&dec->worker);
|
||||
dec->ready = 0;
|
||||
dec->num_parts_minus_one = 0;
|
||||
InitGetCoeffs();
|
||||
}
|
||||
return dec;
|
||||
@@ -68,13 +82,13 @@ VP8Decoder* VP8New(void) {
|
||||
|
||||
VP8StatusCode VP8Status(VP8Decoder* const dec) {
|
||||
if (!dec) return VP8_STATUS_INVALID_PARAM;
|
||||
return dec->status_;
|
||||
return dec->status;
|
||||
}
|
||||
|
||||
const char* VP8StatusMessage(VP8Decoder* const dec) {
|
||||
if (dec == NULL) return "no object";
|
||||
if (!dec->error_msg_) return "OK";
|
||||
return dec->error_msg_;
|
||||
if (!dec->error_msg) return "OK";
|
||||
return dec->error_msg;
|
||||
}
|
||||
|
||||
void VP8Delete(VP8Decoder* const dec) {
|
||||
@@ -84,55 +98,56 @@ void VP8Delete(VP8Decoder* const dec) {
|
||||
}
|
||||
}
|
||||
|
||||
int VP8SetError(VP8Decoder* const dec,
|
||||
VP8StatusCode error, const char* const msg) {
|
||||
int VP8SetError(VP8Decoder* const dec, VP8StatusCode error,
|
||||
const char* const msg) {
|
||||
// VP8_STATUS_SUSPENDED is only meaningful in incremental decoding.
|
||||
assert(dec->incremental_ || error != VP8_STATUS_SUSPENDED);
|
||||
assert(dec->incremental || error != VP8_STATUS_SUSPENDED);
|
||||
// The oldest error reported takes precedence over the new one.
|
||||
if (dec->status_ == VP8_STATUS_OK) {
|
||||
dec->status_ = error;
|
||||
dec->error_msg_ = msg;
|
||||
dec->ready_ = 0;
|
||||
if (dec->status == VP8_STATUS_OK) {
|
||||
dec->status = error;
|
||||
dec->error_msg = msg;
|
||||
dec->ready = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int VP8CheckSignature(const uint8_t* const data, size_t data_size) {
|
||||
return (data_size >= 3 &&
|
||||
data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a);
|
||||
int VP8CheckSignature(const uint8_t* const WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size) {
|
||||
return (data_size >= 3 && data[0] == 0x9d && data[1] == 0x01 &&
|
||||
data[2] == 0x2a);
|
||||
}
|
||||
|
||||
int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
|
||||
int* const width, int* const height) {
|
||||
int VP8GetInfo(const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size,
|
||||
size_t chunk_size, int* const width, int* const height) {
|
||||
if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) {
|
||||
return 0; // not enough data
|
||||
return 0; // not enough data
|
||||
}
|
||||
// check signature
|
||||
if (!VP8CheckSignature(data + 3, data_size - 3)) {
|
||||
return 0; // Wrong signature.
|
||||
return 0; // Wrong signature.
|
||||
} else {
|
||||
const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
const int key_frame = !(bits & 1);
|
||||
const int w = ((data[7] << 8) | data[6]) & 0x3fff;
|
||||
const int h = ((data[9] << 8) | data[8]) & 0x3fff;
|
||||
|
||||
if (!key_frame) { // Not a keyframe.
|
||||
if (!key_frame) { // Not a keyframe.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((bits >> 1) & 7) > 3) {
|
||||
return 0; // unknown profile
|
||||
return 0; // unknown profile
|
||||
}
|
||||
if (!((bits >> 4) & 1)) {
|
||||
return 0; // first frame is invisible!
|
||||
return 0; // first frame is invisible!
|
||||
}
|
||||
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.
|
||||
return 0; // We don't support both width and height to be zero.
|
||||
}
|
||||
|
||||
if (width) {
|
||||
@@ -151,44 +166,48 @@ int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
|
||||
|
||||
static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
|
||||
assert(hdr != NULL);
|
||||
hdr->use_segment_ = 0;
|
||||
hdr->update_map_ = 0;
|
||||
hdr->absolute_delta_ = 1;
|
||||
memset(hdr->quantizer_, 0, sizeof(hdr->quantizer_));
|
||||
memset(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_));
|
||||
hdr->use_segment = 0;
|
||||
hdr->update_map = 0;
|
||||
hdr->absolute_delta = 1;
|
||||
WEBP_UNSAFE_MEMSET(hdr->quantizer, 0, sizeof(hdr->quantizer));
|
||||
WEBP_UNSAFE_MEMSET(hdr->filter_strength, 0, sizeof(hdr->filter_strength));
|
||||
}
|
||||
|
||||
// Paragraph 9.3
|
||||
static int ParseSegmentHeader(VP8BitReader* br,
|
||||
VP8SegmentHeader* hdr, VP8Proba* proba) {
|
||||
static int ParseSegmentHeader(VP8BitReader* br, VP8SegmentHeader* hdr,
|
||||
VP8Proba* proba) {
|
||||
assert(br != NULL);
|
||||
assert(hdr != NULL);
|
||||
hdr->use_segment_ = VP8Get(br, "global-header");
|
||||
if (hdr->use_segment_) {
|
||||
hdr->update_map_ = VP8Get(br, "global-header");
|
||||
if (VP8Get(br, "global-header")) { // update data
|
||||
hdr->use_segment = VP8Get(br, "global-header");
|
||||
if (hdr->use_segment) {
|
||||
hdr->update_map = VP8Get(br, "global-header");
|
||||
if (VP8Get(br, "global-header")) { // update data
|
||||
int s;
|
||||
hdr->absolute_delta_ = VP8Get(br, "global-header");
|
||||
hdr->absolute_delta = VP8Get(br, "global-header");
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
hdr->quantizer_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 7, "global-header") : 0;
|
||||
hdr->quantizer[s] = VP8Get(br, "global-header")
|
||||
? VP8GetSignedValue(br, 7, "global-header")
|
||||
: 0;
|
||||
}
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
hdr->filter_strength_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 6, "global-header") : 0;
|
||||
hdr->filter_strength[s] =
|
||||
VP8Get(br, "global-header")
|
||||
? VP8GetSignedValue(br, 6, "global-header")
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
if (hdr->update_map_) {
|
||||
if (hdr->update_map) {
|
||||
int s;
|
||||
for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
|
||||
proba->segments_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetValue(br, 8, "global-header") : 255u;
|
||||
proba->segments[s] = VP8Get(br, "global-header")
|
||||
? VP8GetValue(br, 8, "global-header")
|
||||
: 255u;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hdr->update_map_ = 0;
|
||||
hdr->update_map = 0;
|
||||
}
|
||||
return !br->eof_;
|
||||
return !br->eof;
|
||||
}
|
||||
|
||||
// Paragraph 9.5
|
||||
@@ -201,17 +220,18 @@ static int ParseSegmentHeader(VP8BitReader* br,
|
||||
// is returned, and this is an unrecoverable error.
|
||||
// If the partitions were positioned ok, VP8_STATUS_OK is returned.
|
||||
static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
const uint8_t* buf, size_t size) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const uint8_t* sz = buf;
|
||||
const uint8_t* WEBP_COUNTED_BY(size) buf,
|
||||
size_t size) {
|
||||
VP8BitReader* const br = &dec->br;
|
||||
const uint8_t* WEBP_BIDI_INDEXABLE sz = buf;
|
||||
const uint8_t* buf_end = buf + size;
|
||||
const uint8_t* part_start;
|
||||
const uint8_t* WEBP_BIDI_INDEXABLE part_start;
|
||||
size_t size_left = size;
|
||||
size_t last_part;
|
||||
size_t p;
|
||||
|
||||
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2, "global-header")) - 1;
|
||||
last_part = dec->num_parts_minus_one_;
|
||||
dec->num_parts_minus_one = (1 << VP8GetValue(br, 2, "global-header")) - 1;
|
||||
last_part = dec->num_parts_minus_one;
|
||||
if (size < 3 * last_part) {
|
||||
// we can't even read the sizes with sz[]! That's a failure.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
@@ -221,48 +241,48 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
for (p = 0; p < last_part; ++p) {
|
||||
size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
||||
if (psize > size_left) psize = size_left;
|
||||
VP8InitBitReader(dec->parts_ + p, part_start, psize);
|
||||
VP8InitBitReader(dec->parts + p, part_start, psize);
|
||||
part_start += psize;
|
||||
size_left -= psize;
|
||||
sz += 3;
|
||||
}
|
||||
VP8InitBitReader(dec->parts_ + last_part, part_start, size_left);
|
||||
VP8InitBitReader(dec->parts + last_part, part_start, size_left);
|
||||
if (part_start < buf_end) return VP8_STATUS_OK;
|
||||
return dec->incremental_
|
||||
return dec->incremental
|
||||
? VP8_STATUS_SUSPENDED // Init is ok, but there's not enough data
|
||||
: VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
|
||||
// Paragraph 9.4
|
||||
static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
|
||||
VP8FilterHeader* const hdr = &dec->filter_hdr_;
|
||||
hdr->simple_ = VP8Get(br, "global-header");
|
||||
hdr->level_ = VP8GetValue(br, 6, "global-header");
|
||||
hdr->sharpness_ = VP8GetValue(br, 3, "global-header");
|
||||
hdr->use_lf_delta_ = VP8Get(br, "global-header");
|
||||
if (hdr->use_lf_delta_) {
|
||||
if (VP8Get(br, "global-header")) { // update lf-delta?
|
||||
VP8FilterHeader* const hdr = &dec->filter_hdr;
|
||||
hdr->simple = VP8Get(br, "global-header");
|
||||
hdr->level = VP8GetValue(br, 6, "global-header");
|
||||
hdr->sharpness = VP8GetValue(br, 3, "global-header");
|
||||
hdr->use_lf_delta = VP8Get(br, "global-header");
|
||||
if (hdr->use_lf_delta) {
|
||||
if (VP8Get(br, "global-header")) { // update lf-delta?
|
||||
int i;
|
||||
for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
|
||||
if (VP8Get(br, "global-header")) {
|
||||
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header");
|
||||
hdr->ref_lf_delta[i] = VP8GetSignedValue(br, 6, "global-header");
|
||||
}
|
||||
}
|
||||
for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
|
||||
if (VP8Get(br, "global-header")) {
|
||||
hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header");
|
||||
hdr->mode_lf_delta[i] = VP8GetSignedValue(br, 6, "global-header");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2;
|
||||
return !br->eof_;
|
||||
dec->filter_type = (hdr->level == 0) ? 0 : hdr->simple ? 1 : 2;
|
||||
return !br->eof;
|
||||
}
|
||||
|
||||
// Topmost call
|
||||
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
const uint8_t* buf;
|
||||
size_t buf_size;
|
||||
const uint8_t* WEBP_COUNTED_BY(buf_size) buf;
|
||||
VP8FrameHeader* frm_hdr;
|
||||
VP8PictureHeader* pic_hdr;
|
||||
VP8BitReader* br;
|
||||
@@ -276,26 +296,26 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
||||
"null VP8Io passed to VP8GetHeaders()");
|
||||
}
|
||||
buf = io->data;
|
||||
buf_size = io->data_size;
|
||||
buf =
|
||||
WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, io->data, io->data_size);
|
||||
if (buf_size < 4) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Truncated header.");
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "Truncated header.");
|
||||
}
|
||||
|
||||
// Paragraph 9.1
|
||||
{
|
||||
const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16);
|
||||
frm_hdr = &dec->frm_hdr_;
|
||||
frm_hdr->key_frame_ = !(bits & 1);
|
||||
frm_hdr->profile_ = (bits >> 1) & 7;
|
||||
frm_hdr->show_ = (bits >> 4) & 1;
|
||||
frm_hdr->partition_length_ = (bits >> 5);
|
||||
if (frm_hdr->profile_ > 3) {
|
||||
frm_hdr = &dec->frm_hdr;
|
||||
frm_hdr->key_frame = !(bits & 1);
|
||||
frm_hdr->profile = (bits >> 1) & 7;
|
||||
frm_hdr->show = (bits >> 4) & 1;
|
||||
frm_hdr->partition_length = (bits >> 5);
|
||||
if (frm_hdr->profile > 3) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Incorrect keyframe parameters.");
|
||||
}
|
||||
if (!frm_hdr->show_) {
|
||||
if (!frm_hdr->show) {
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Frame not displayable.");
|
||||
}
|
||||
@@ -303,66 +323,64 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
buf_size -= 3;
|
||||
}
|
||||
|
||||
pic_hdr = &dec->pic_hdr_;
|
||||
if (frm_hdr->key_frame_) {
|
||||
pic_hdr = &dec->pic_hdr;
|
||||
if (frm_hdr->key_frame) {
|
||||
// Paragraph 9.2
|
||||
if (buf_size < 7) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"cannot parse picture header");
|
||||
}
|
||||
if (!VP8CheckSignature(buf, buf_size)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Bad code word");
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "Bad code word");
|
||||
}
|
||||
pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff;
|
||||
pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2
|
||||
pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff;
|
||||
pic_hdr->yscale_ = buf[6] >> 6;
|
||||
pic_hdr->width = ((buf[4] << 8) | buf[3]) & 0x3fff;
|
||||
pic_hdr->xscale = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2
|
||||
pic_hdr->height = ((buf[6] << 8) | buf[5]) & 0x3fff;
|
||||
pic_hdr->yscale = buf[6] >> 6;
|
||||
buf += 7;
|
||||
buf_size -= 7;
|
||||
|
||||
dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
|
||||
dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
|
||||
dec->mb_w = (pic_hdr->width + 15) >> 4;
|
||||
dec->mb_h = (pic_hdr->height + 15) >> 4;
|
||||
|
||||
// Setup default output area (can be later modified during io->setup())
|
||||
io->width = pic_hdr->width_;
|
||||
io->height = pic_hdr->height_;
|
||||
// IMPORTANT! use some sane dimensions in crop_* and scaled_* fields.
|
||||
io->width = pic_hdr->width;
|
||||
io->height = pic_hdr->height;
|
||||
// IMPORTANT! use some sane dimensions in crop* and scaled* fields.
|
||||
// So they can be used interchangeably without always testing for
|
||||
// 'use_cropping'.
|
||||
io->use_cropping = 0;
|
||||
io->crop_top = 0;
|
||||
io->crop_top = 0;
|
||||
io->crop_left = 0;
|
||||
io->crop_right = io->width;
|
||||
io->crop_right = io->width;
|
||||
io->crop_bottom = io->height;
|
||||
io->use_scaling = 0;
|
||||
io->use_scaling = 0;
|
||||
io->scaled_width = io->width;
|
||||
io->scaled_height = io->height;
|
||||
|
||||
io->mb_w = io->width; // for soundness
|
||||
io->mb_h = io->height; // ditto
|
||||
|
||||
VP8ResetProba(&dec->proba_);
|
||||
ResetSegmentHeader(&dec->segment_hdr_);
|
||||
VP8ResetProba(&dec->proba);
|
||||
ResetSegmentHeader(&dec->segment_hdr);
|
||||
}
|
||||
|
||||
// Check if we have all the partition #0 available, and initialize dec->br_
|
||||
// Check if we have all the partition #0 available, and initialize dec->br
|
||||
// to read this partition (and this partition only).
|
||||
if (frm_hdr->partition_length_ > buf_size) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"bad partition length");
|
||||
if (frm_hdr->partition_length > buf_size) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "bad partition length");
|
||||
}
|
||||
|
||||
br = &dec->br_;
|
||||
VP8InitBitReader(br, buf, frm_hdr->partition_length_);
|
||||
buf += frm_hdr->partition_length_;
|
||||
buf_size -= frm_hdr->partition_length_;
|
||||
br = &dec->br;
|
||||
VP8InitBitReader(br, buf, frm_hdr->partition_length);
|
||||
buf += frm_hdr->partition_length;
|
||||
buf_size -= frm_hdr->partition_length;
|
||||
|
||||
if (frm_hdr->key_frame_) {
|
||||
pic_hdr->colorspace_ = VP8Get(br, "global-header");
|
||||
pic_hdr->clamp_type_ = VP8Get(br, "global-header");
|
||||
if (frm_hdr->key_frame) {
|
||||
pic_hdr->colorspace = VP8Get(br, "global-header");
|
||||
pic_hdr->clamp_type = VP8Get(br, "global-header");
|
||||
}
|
||||
if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
|
||||
if (!ParseSegmentHeader(br, &dec->segment_hdr, &dec->proba)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"cannot parse segment header");
|
||||
}
|
||||
@@ -380,32 +398,30 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
VP8ParseQuant(dec);
|
||||
|
||||
// Frame buffer marking
|
||||
if (!frm_hdr->key_frame_) {
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Not a key frame.");
|
||||
if (!frm_hdr->key_frame) {
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, "Not a key frame.");
|
||||
}
|
||||
|
||||
VP8Get(br, "global-header"); // ignore the value of update_proba_
|
||||
VP8Get(br, "global-header"); // ignore the value of 'update_proba'
|
||||
|
||||
VP8ParseProba(br, dec);
|
||||
|
||||
// sanitized state
|
||||
dec->ready_ = 1;
|
||||
dec->ready = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Residual decoding (Paragraph 13.2 / 13.3)
|
||||
|
||||
static const uint8_t kCat3[] = { 173, 148, 140, 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 kCat6[] =
|
||||
{ 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 };
|
||||
static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 };
|
||||
static const uint8_t kZigzag[16] = {
|
||||
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
|
||||
};
|
||||
static const uint8_t kCat3[] = {173, 148, 140, 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 kCat6[] = {254, 254, 243, 230, 196, 177,
|
||||
153, 140, 133, 130, 129, 0};
|
||||
static const uint8_t* const kCat3456[] = {kCat3, kCat4, kCat5, kCat6};
|
||||
static const uint8_t kZigzag[16] = {0, 1, 4, 8, 5, 2, 3, 6,
|
||||
9, 12, 13, 10, 7, 11, 14, 15};
|
||||
|
||||
// See section 13-2: https://datatracker.ietf.org/doc/html/rfc6386#section-13.2
|
||||
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
||||
@@ -441,19 +457,19 @@ static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
||||
|
||||
// Returns the position of the last non-zero coeff plus one
|
||||
static int GetCoeffsFast(VP8BitReader* const br,
|
||||
const VP8BandProbas* const prob[],
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
const uint8_t* p = prob[n]->probas_[ctx];
|
||||
const 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 (!VP8GetBit(br, p[0], "coeffs")) {
|
||||
return n; // previous coeff was last non-zero coeff
|
||||
}
|
||||
while (!VP8GetBit(br, p[1], "coeffs")) { // sequence of zero coeffs
|
||||
p = prob[++n]->probas_[0];
|
||||
while (!VP8GetBit(br, p[1], "coeffs")) { // 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];
|
||||
{ // non zero coeff
|
||||
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas[0];
|
||||
int v;
|
||||
if (!VP8GetBit(br, p[2], "coeffs")) {
|
||||
v = 1;
|
||||
@@ -471,19 +487,19 @@ static int GetCoeffsFast(VP8BitReader* const br,
|
||||
// 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];
|
||||
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], "coeffs")) {
|
||||
return n; // previous coeff was last non-zero coeff
|
||||
}
|
||||
while (!VP8GetBitAlt(br, p[1], "coeffs")) { // sequence of zero coeffs
|
||||
p = prob[++n]->probas_[0];
|
||||
while (!VP8GetBitAlt(br, p[1], "coeffs")) { // 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];
|
||||
{ // non zero coeff
|
||||
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas[0];
|
||||
int v;
|
||||
if (!VP8GetBitAlt(br, p[2], "coeffs")) {
|
||||
v = 1;
|
||||
@@ -514,14 +530,14 @@ static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {
|
||||
return nz_coeffs;
|
||||
}
|
||||
|
||||
static int ParseResiduals(VP8Decoder* const dec,
|
||||
VP8MB* const mb, VP8BitReader* const token_br) {
|
||||
const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_;
|
||||
const VP8BandProbas* const * ac_proba;
|
||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||
const VP8QuantMatrix* const q = &dec->dqm_[block->segment_];
|
||||
int16_t* dst = block->coeffs_;
|
||||
VP8MB* const left_mb = dec->mb_info_ - 1;
|
||||
static int ParseResiduals(VP8Decoder* const dec, VP8MB* const mb,
|
||||
VP8BitReader* const token_br) {
|
||||
const VP8BandProbas*(*const bands)[16 + 1] = dec->proba.bands_ptr;
|
||||
const VP8BandProbas* const* ac_proba;
|
||||
VP8MBData* const block = dec->mb_data + dec->mb_x;
|
||||
const VP8QuantMatrix* const q = &dec->dqm[block->segment];
|
||||
int16_t* dst = block->coeffs;
|
||||
VP8MB* const left_mb = dec->mb_info - 1;
|
||||
uint8_t tnz, lnz;
|
||||
uint32_t non_zero_y = 0;
|
||||
uint32_t non_zero_uv = 0;
|
||||
@@ -529,15 +545,15 @@ static int ParseResiduals(VP8Decoder* const dec,
|
||||
uint32_t out_t_nz, out_l_nz;
|
||||
int first;
|
||||
|
||||
memset(dst, 0, 384 * sizeof(*dst));
|
||||
if (!block->is_i4x4_) { // parse DC
|
||||
int16_t dc[16] = { 0 };
|
||||
const int ctx = mb->nz_dc_ + left_mb->nz_dc_;
|
||||
const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc);
|
||||
mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0);
|
||||
if (nz > 1) { // more than just the DC -> perform the full transform
|
||||
WEBP_UNSAFE_MEMSET(dst, 0, 384 * sizeof(*dst));
|
||||
if (!block->is_i4x4) { // parse DC
|
||||
int16_t dc[16] = {0};
|
||||
const int ctx = mb->nz_dc + left_mb->nz_dc;
|
||||
const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat, 0, dc);
|
||||
mb->nz_dc = left_mb->nz_dc = (nz > 0);
|
||||
if (nz > 1) { // more than just the DC -> perform the full transform
|
||||
VP8TransformWHT(dc, dst);
|
||||
} else { // only DC is non-zero -> inlined simplified transform
|
||||
} 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;
|
||||
@@ -549,14 +565,14 @@ static int ParseResiduals(VP8Decoder* const dec,
|
||||
ac_proba = bands[3];
|
||||
}
|
||||
|
||||
tnz = mb->nz_ & 0x0f;
|
||||
lnz = left_mb->nz_ & 0x0f;
|
||||
tnz = mb->nz & 0x0f;
|
||||
lnz = left_mb->nz & 0x0f;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int l = lnz & 1;
|
||||
uint32_t nz_coeffs = 0;
|
||||
for (x = 0; x < 4; ++x) {
|
||||
const int ctx = l + (tnz & 1);
|
||||
const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst);
|
||||
const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat, first, dst);
|
||||
l = (nz > first);
|
||||
tnz = (tnz >> 1) | (l << 7);
|
||||
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
||||
@@ -571,13 +587,13 @@ static int ParseResiduals(VP8Decoder* const dec,
|
||||
|
||||
for (ch = 0; ch < 4; ch += 2) {
|
||||
uint32_t nz_coeffs = 0;
|
||||
tnz = mb->nz_ >> (4 + ch);
|
||||
lnz = left_mb->nz_ >> (4 + ch);
|
||||
tnz = mb->nz >> (4 + ch);
|
||||
lnz = left_mb->nz >> (4 + ch);
|
||||
for (y = 0; y < 2; ++y) {
|
||||
int l = lnz & 1;
|
||||
for (x = 0; x < 2; ++x) {
|
||||
const int ctx = l + (tnz & 1);
|
||||
const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst);
|
||||
const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat, 0, dst);
|
||||
l = (nz > 0);
|
||||
tnz = (tnz >> 1) | (l << 3);
|
||||
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
||||
@@ -591,16 +607,16 @@ static int ParseResiduals(VP8Decoder* const dec,
|
||||
out_t_nz |= (tnz << 4) << ch;
|
||||
out_l_nz |= (lnz & 0xf0) << ch;
|
||||
}
|
||||
mb->nz_ = out_t_nz;
|
||||
left_mb->nz_ = out_l_nz;
|
||||
mb->nz = out_t_nz;
|
||||
left_mb->nz = out_l_nz;
|
||||
|
||||
block->non_zero_y_ = non_zero_y;
|
||||
block->non_zero_uv_ = non_zero_uv;
|
||||
block->non_zero_y = non_zero_y;
|
||||
block->non_zero_uv = non_zero_uv;
|
||||
|
||||
// We look at the mode-code of each block and check if some blocks have less
|
||||
// than three non-zero coeffs (code < 2). This is to avoid dithering flat and
|
||||
// empty blocks.
|
||||
block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_;
|
||||
block->dither = (non_zero_uv & 0xaaaa) ? 0 : q->dither;
|
||||
|
||||
return !(non_zero_y | non_zero_uv); // will be used for further optimization
|
||||
}
|
||||
@@ -609,64 +625,64 @@ static int ParseResiduals(VP8Decoder* const dec,
|
||||
// Main loop
|
||||
|
||||
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
VP8MB* const mb = dec->mb_info_ + dec->mb_x_;
|
||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||
int skip = dec->use_skip_proba_ ? block->skip_ : 0;
|
||||
VP8MB* const left = dec->mb_info - 1;
|
||||
VP8MB* const mb = dec->mb_info + dec->mb_x;
|
||||
VP8MBData* const block = dec->mb_data + dec->mb_x;
|
||||
int skip = dec->use_skip_proba ? block->skip : 0;
|
||||
|
||||
if (!skip) {
|
||||
skip = ParseResiduals(dec, mb, token_br);
|
||||
} else {
|
||||
left->nz_ = mb->nz_ = 0;
|
||||
if (!block->is_i4x4_) {
|
||||
left->nz_dc_ = mb->nz_dc_ = 0;
|
||||
left->nz = mb->nz = 0;
|
||||
if (!block->is_i4x4) {
|
||||
left->nz_dc = mb->nz_dc = 0;
|
||||
}
|
||||
block->non_zero_y_ = 0;
|
||||
block->non_zero_uv_ = 0;
|
||||
block->dither_ = 0;
|
||||
block->non_zero_y = 0;
|
||||
block->non_zero_uv = 0;
|
||||
block->dither = 0;
|
||||
}
|
||||
|
||||
if (dec->filter_type_ > 0) { // store filter info
|
||||
VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_;
|
||||
*finfo = dec->fstrengths_[block->segment_][block->is_i4x4_];
|
||||
finfo->f_inner_ |= !skip;
|
||||
if (dec->filter_type > 0) { // store filter info
|
||||
VP8FInfo* const finfo = dec->f_info + dec->mb_x;
|
||||
*finfo = dec->fstrengths[block->segment][block->is_i4x4];
|
||||
finfo->f_inner |= !skip;
|
||||
}
|
||||
|
||||
return !token_br->eof_;
|
||||
return !token_br->eof;
|
||||
}
|
||||
|
||||
void VP8InitScanline(VP8Decoder* const dec) {
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
left->nz_ = 0;
|
||||
left->nz_dc_ = 0;
|
||||
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
|
||||
dec->mb_x_ = 0;
|
||||
VP8MB* const left = dec->mb_info - 1;
|
||||
left->nz = 0;
|
||||
left->nz_dc = 0;
|
||||
WEBP_UNSAFE_MEMSET(dec->intra_l, B_DC_PRED, sizeof(dec->intra_l));
|
||||
dec->mb_x = 0;
|
||||
}
|
||||
|
||||
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
||||
for (dec->mb_y = 0; dec->mb_y < dec->br_mb_y; ++dec->mb_y) {
|
||||
// Parse bitstream for this row.
|
||||
VP8BitReader* const token_br =
|
||||
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
|
||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||
&dec->parts[dec->mb_y & dec->num_parts_minus_one];
|
||||
if (!VP8ParseIntraModeRow(&dec->br, dec)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-partition0 encountered.");
|
||||
}
|
||||
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
||||
for (; dec->mb_x < dec->mb_w; ++dec->mb_x) {
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-file encountered.");
|
||||
}
|
||||
}
|
||||
VP8InitScanline(dec); // Prepare for next scanline
|
||||
VP8InitScanline(dec); // Prepare for next scanline
|
||||
|
||||
// Reconstruct, filter and emit the row.
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
|
||||
}
|
||||
}
|
||||
if (dec->mt_method_ > 0) {
|
||||
if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0;
|
||||
if (dec->mt_method > 0) {
|
||||
if (!WebPGetWorkerInterface()->Sync(&dec->worker)) return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -683,16 +699,16 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
|
||||
"NULL VP8Io parameter in VP8Decode().");
|
||||
}
|
||||
|
||||
if (!dec->ready_) {
|
||||
if (!dec->ready) {
|
||||
if (!VP8GetHeaders(dec, io)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
assert(dec->ready_);
|
||||
assert(dec->ready);
|
||||
|
||||
// Finish setting up the decoding parameter. Will call io->setup().
|
||||
ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK);
|
||||
if (ok) { // good to go.
|
||||
if (ok) { // good to go.
|
||||
// Will allocate memory and prepare everything.
|
||||
if (ok) ok = VP8InitFrame(dec, io);
|
||||
|
||||
@@ -708,7 +724,7 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dec->ready_ = 0;
|
||||
dec->ready = 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -716,13 +732,13 @@ void VP8Clear(VP8Decoder* const dec) {
|
||||
if (dec == NULL) {
|
||||
return;
|
||||
}
|
||||
WebPGetWorkerInterface()->End(&dec->worker_);
|
||||
WebPGetWorkerInterface()->End(&dec->worker);
|
||||
WebPDeallocateAlphaMemory(dec);
|
||||
WebPSafeFree(dec->mem_);
|
||||
dec->mem_ = NULL;
|
||||
dec->mem_size_ = 0;
|
||||
memset(&dec->br_, 0, sizeof(dec->br_));
|
||||
dec->ready_ = 0;
|
||||
WebPSafeFree(dec->mem);
|
||||
dec->mem = NULL;
|
||||
dec->mem_size = 0;
|
||||
WEBP_UNSAFE_MEMSET(&dec->br, 0, sizeof(dec->br));
|
||||
dec->ready = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -14,9 +14,13 @@
|
||||
#ifndef WEBP_DEC_VP8_DEC_H_
|
||||
#define WEBP_DEC_VP8_DEC_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -47,20 +51,20 @@ typedef void (*VP8IoTeardownHook)(const VP8Io* io);
|
||||
|
||||
struct VP8Io {
|
||||
// set by VP8GetHeaders()
|
||||
int width, height; // picture dimensions, in pixels (invariable).
|
||||
// These are the original, uncropped dimensions.
|
||||
// The actual area passed to put() is stored
|
||||
// in mb_w / mb_h fields.
|
||||
int width, height; // picture dimensions, in pixels (invariable).
|
||||
// These are the original, uncropped dimensions.
|
||||
// The actual area passed to put() is stored
|
||||
// in mb_w / mb_h fields.
|
||||
|
||||
// set before calling put()
|
||||
int mb_y; // position of the current rows (in pixels)
|
||||
int mb_w; // number of columns in the sample
|
||||
int mb_h; // number of rows in the sample
|
||||
const uint8_t* y, *u, *v; // rows to copy (in yuv420 format)
|
||||
const uint8_t *y, *u, *v; // rows to copy (in yuv420 format)
|
||||
int y_stride; // row stride for luma
|
||||
int uv_stride; // row stride for chroma
|
||||
|
||||
void* opaque; // user data
|
||||
void* opaque; // user data
|
||||
|
||||
// called when fresh samples are available. Currently, samples are in
|
||||
// YUV420 format, and can be up to width x 24 in size (depending on the
|
||||
@@ -156,29 +160,33 @@ void VP8Delete(VP8Decoder* const dec);
|
||||
// Miscellaneous VP8/VP8L bitstream probing functions.
|
||||
|
||||
// Returns true if the next 3 bytes in data contain the VP8 signature.
|
||||
WEBP_EXTERN int VP8CheckSignature(const uint8_t* const data, size_t data_size);
|
||||
WEBP_EXTERN int VP8CheckSignature(
|
||||
const uint8_t* const WEBP_COUNTED_BY(data_size) data, size_t data_size);
|
||||
|
||||
// Validates the VP8 data-header and retrieves basic header information viz
|
||||
// width and height. Returns 0 in case of formatting error. *width/*height
|
||||
// can be passed NULL.
|
||||
WEBP_EXTERN int VP8GetInfo(
|
||||
const uint8_t* data,
|
||||
size_t data_size, // data available so far
|
||||
size_t chunk_size, // total data size expected in the chunk
|
||||
const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, // data available so far
|
||||
size_t chunk_size, // total data size expected in the chunk
|
||||
int* const width, int* const height);
|
||||
|
||||
// Returns true if the next byte(s) in data is a VP8L signature.
|
||||
WEBP_EXTERN int VP8LCheckSignature(const uint8_t* const data, size_t size);
|
||||
WEBP_EXTERN int VP8LCheckSignature(const uint8_t* const WEBP_COUNTED_BY(size)
|
||||
data,
|
||||
size_t size);
|
||||
|
||||
// Validates the VP8L data-header and retrieves basic header information viz
|
||||
// width, height and alpha. Returns 0 in case of formatting error.
|
||||
// width/height/has_alpha can be passed NULL.
|
||||
WEBP_EXTERN int VP8LGetInfo(
|
||||
const uint8_t* data, size_t data_size, // data available so far
|
||||
int* const width, int* const height, int* const has_alpha);
|
||||
WEBP_EXTERN int VP8LGetInfo(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, // data available so far
|
||||
int* const width, int* const height,
|
||||
int* const has_alpha);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_DEC_VP8_DEC_H_
|
||||
|
||||
@@ -14,15 +14,21 @@
|
||||
#ifndef WEBP_DEC_VP8I_DEC_H_
|
||||
#define WEBP_DEC_VP8I_DEC_H_
|
||||
|
||||
#include <string.h> // for memcpy()
|
||||
#include <string.h> // for memcpy()
|
||||
|
||||
#include "src/dec/common_dec.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/bit_reader_utils.h"
|
||||
#include "src/utils/random_utils.h"
|
||||
#include "src/utils/thread_utils.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -32,7 +38,7 @@ extern "C" {
|
||||
|
||||
// version numbers
|
||||
#define DEC_MAJ_VERSION 1
|
||||
#define DEC_MIN_VERSION 4
|
||||
#define DEC_MIN_VERSION 6
|
||||
#define DEC_REV_VERSION 0
|
||||
|
||||
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||
@@ -58,9 +64,9 @@ extern "C" {
|
||||
// '|' = left sample, '-' = top sample, '+' = top-left sample
|
||||
// 't' = extra top-right sample for 4x4 modes
|
||||
#define YUV_SIZE (BPS * 17 + BPS * 9)
|
||||
#define Y_OFF (BPS * 1 + 8)
|
||||
#define U_OFF (Y_OFF + BPS * 16 + BPS)
|
||||
#define V_OFF (U_OFF + 16)
|
||||
#define Y_OFF (BPS * 1 + 8)
|
||||
#define U_OFF (Y_OFF + BPS * 16 + BPS)
|
||||
#define V_OFF (U_OFF + 16)
|
||||
|
||||
// minimal width under which lossy multi-threading is always disabled
|
||||
#define MIN_WIDTH_FOR_THREADS 512
|
||||
@@ -69,85 +75,85 @@ extern "C" {
|
||||
// Headers
|
||||
|
||||
typedef struct {
|
||||
uint8_t key_frame_;
|
||||
uint8_t profile_;
|
||||
uint8_t show_;
|
||||
uint32_t partition_length_;
|
||||
uint8_t key_frame;
|
||||
uint8_t profile;
|
||||
uint8_t show;
|
||||
uint32_t partition_length;
|
||||
} VP8FrameHeader;
|
||||
|
||||
typedef struct {
|
||||
uint16_t width_;
|
||||
uint16_t height_;
|
||||
uint8_t xscale_;
|
||||
uint8_t yscale_;
|
||||
uint8_t colorspace_; // 0 = YCbCr
|
||||
uint8_t clamp_type_;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t xscale;
|
||||
uint8_t yscale;
|
||||
uint8_t colorspace; // 0 = YCbCr
|
||||
uint8_t clamp_type;
|
||||
} VP8PictureHeader;
|
||||
|
||||
// segment features
|
||||
typedef struct {
|
||||
int use_segment_;
|
||||
int update_map_; // whether to update the segment map or not
|
||||
int absolute_delta_; // absolute or delta values for quantizer and filter
|
||||
int8_t quantizer_[NUM_MB_SEGMENTS]; // quantization changes
|
||||
int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
|
||||
int use_segment;
|
||||
int update_map; // whether to update the segment map or not
|
||||
int absolute_delta; // absolute or delta values for quantizer and filter
|
||||
int8_t quantizer[NUM_MB_SEGMENTS]; // quantization changes
|
||||
int8_t filter_strength[NUM_MB_SEGMENTS]; // filter strength for segments
|
||||
} VP8SegmentHeader;
|
||||
|
||||
// probas associated to one of the contexts
|
||||
typedef uint8_t VP8ProbaArray[NUM_PROBAS];
|
||||
|
||||
typedef struct { // all the probas associated to one band
|
||||
VP8ProbaArray probas_[NUM_CTX];
|
||||
typedef struct { // all the probas associated to one band
|
||||
VP8ProbaArray probas[NUM_CTX];
|
||||
} VP8BandProbas;
|
||||
|
||||
// Struct collecting all frame-persistent probabilities.
|
||||
typedef struct {
|
||||
uint8_t segments_[MB_FEATURE_TREE_PROBS];
|
||||
uint8_t segments[MB_FEATURE_TREE_PROBS];
|
||||
// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
|
||||
VP8BandProbas bands_[NUM_TYPES][NUM_BANDS];
|
||||
const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1];
|
||||
VP8BandProbas bands[NUM_TYPES][NUM_BANDS];
|
||||
const VP8BandProbas* bands_ptr[NUM_TYPES][16 + 1];
|
||||
} VP8Proba;
|
||||
|
||||
// Filter parameters
|
||||
typedef struct {
|
||||
int simple_; // 0=complex, 1=simple
|
||||
int level_; // [0..63]
|
||||
int sharpness_; // [0..7]
|
||||
int use_lf_delta_;
|
||||
int ref_lf_delta_[NUM_REF_LF_DELTAS];
|
||||
int mode_lf_delta_[NUM_MODE_LF_DELTAS];
|
||||
int simple; // 0=complex, 1=simple
|
||||
int level; // [0..63]
|
||||
int sharpness; // [0..7]
|
||||
int use_lf_delta;
|
||||
int ref_lf_delta[NUM_REF_LF_DELTAS];
|
||||
int mode_lf_delta[NUM_MODE_LF_DELTAS];
|
||||
} VP8FilterHeader;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Informations about the macroblocks.
|
||||
|
||||
typedef struct { // filter specs
|
||||
uint8_t f_limit_; // filter limit in [3..189], or 0 if no filtering
|
||||
uint8_t f_ilevel_; // inner limit in [1..63]
|
||||
uint8_t f_inner_; // do inner filtering?
|
||||
uint8_t hev_thresh_; // high edge variance threshold in [0..2]
|
||||
typedef struct { // filter specs
|
||||
uint8_t f_limit; // filter limit in [3..189], or 0 if no filtering
|
||||
uint8_t f_ilevel; // inner limit in [1..63]
|
||||
uint8_t f_inner; // do inner filtering?
|
||||
uint8_t hev_thresh; // high edge variance threshold in [0..2]
|
||||
} VP8FInfo;
|
||||
|
||||
typedef struct { // Top/Left Contexts used for syntax-parsing
|
||||
uint8_t nz_; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma)
|
||||
uint8_t nz_dc_; // non-zero DC coeff (1bit)
|
||||
uint8_t nz; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma)
|
||||
uint8_t nz_dc; // non-zero DC coeff (1bit)
|
||||
} VP8MB;
|
||||
|
||||
// Dequantization matrices
|
||||
typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
|
||||
typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
|
||||
typedef struct {
|
||||
quant_t y1_mat_, y2_mat_, uv_mat_;
|
||||
quant_t y1_mat, y2_mat, uv_mat;
|
||||
|
||||
int uv_quant_; // U/V quantizer value
|
||||
int dither_; // dithering amplitude (0 = off, max=255)
|
||||
int uv_quant; // U/V quantizer value
|
||||
int dither; // dithering amplitude (0 = off, max=255)
|
||||
} VP8QuantMatrix;
|
||||
|
||||
// Data needed to reconstruct a macroblock
|
||||
typedef struct {
|
||||
int16_t coeffs_[384]; // 384 coeffs = (16+4+4) * 4*4
|
||||
uint8_t is_i4x4_; // true if intra4x4
|
||||
uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
|
||||
uint8_t uvmode_; // chroma prediction mode
|
||||
int16_t coeffs[384]; // 384 coeffs = (16+4+4) * 4*4
|
||||
uint8_t is_i4x4; // true if intra4x4
|
||||
uint8_t imodes[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
|
||||
uint8_t uvmode; // chroma prediction mode
|
||||
// bit-wise info about the content of each sub-4x4 blocks (in decoding order).
|
||||
// Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
|
||||
// code=0 -> no coefficient
|
||||
@@ -155,21 +161,21 @@ typedef struct {
|
||||
// code=2 -> first three coefficients are non-zero
|
||||
// code=3 -> more than three coefficients are non-zero
|
||||
// This allows to call specialized transform functions.
|
||||
uint32_t non_zero_y_;
|
||||
uint32_t non_zero_uv_;
|
||||
uint8_t dither_; // local dithering strength (deduced from non_zero_*)
|
||||
uint8_t skip_;
|
||||
uint8_t segment_;
|
||||
uint32_t non_zero_y;
|
||||
uint32_t non_zero_uv;
|
||||
uint8_t dither; // local dithering strength (deduced from non_zero*)
|
||||
uint8_t skip;
|
||||
uint8_t segment;
|
||||
} VP8MBData;
|
||||
|
||||
// Persistent information needed by the parallel processing
|
||||
typedef struct {
|
||||
int id_; // cache row to process (in [0..2])
|
||||
int mb_y_; // macroblock position of the row
|
||||
int filter_row_; // true if row-filtering is needed
|
||||
VP8FInfo* f_info_; // filter strengths (swapped with dec->f_info_)
|
||||
VP8MBData* mb_data_; // reconstruction data (swapped with dec->mb_data_)
|
||||
VP8Io io_; // copy of the VP8Io to pass to put()
|
||||
int id; // cache row to process (in [0..2])
|
||||
int mb_y; // macroblock position of the row
|
||||
int filter_row; // true if row-filtering is needed
|
||||
VP8FInfo* f_info; // filter strengths (swapped with dec->f_info)
|
||||
VP8MBData* mb_data; // reconstruction data (swapped with dec->mb_data)
|
||||
VP8Io io; // copy of the VP8Io to pass to put()
|
||||
} VP8ThreadContext;
|
||||
|
||||
// Saved top samples, per macroblock. Fits into a cache-line.
|
||||
@@ -181,97 +187,98 @@ typedef struct {
|
||||
// VP8Decoder: the main opaque structure handed over to user
|
||||
|
||||
struct VP8Decoder {
|
||||
VP8StatusCode status_;
|
||||
int ready_; // true if ready to decode a picture with VP8Decode()
|
||||
const char* error_msg_; // set when status_ is not OK.
|
||||
VP8StatusCode status;
|
||||
int ready; // true if ready to decode a picture with VP8Decode()
|
||||
const char* error_msg; // set when status is not OK.
|
||||
|
||||
// Main data source
|
||||
VP8BitReader br_;
|
||||
int incremental_; // if true, incremental decoding is expected
|
||||
VP8BitReader br;
|
||||
int incremental; // if true, incremental decoding is expected
|
||||
|
||||
// headers
|
||||
VP8FrameHeader frm_hdr_;
|
||||
VP8PictureHeader pic_hdr_;
|
||||
VP8FilterHeader filter_hdr_;
|
||||
VP8SegmentHeader segment_hdr_;
|
||||
VP8FrameHeader frm_hdr;
|
||||
VP8PictureHeader pic_hdr;
|
||||
VP8FilterHeader filter_hdr;
|
||||
VP8SegmentHeader segment_hdr;
|
||||
|
||||
// Worker
|
||||
WebPWorker worker_;
|
||||
int mt_method_; // multi-thread method: 0=off, 1=[parse+recon][filter]
|
||||
// 2=[parse][recon+filter]
|
||||
int cache_id_; // current cache row
|
||||
int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3)
|
||||
VP8ThreadContext thread_ctx_; // Thread context
|
||||
WebPWorker worker;
|
||||
int mt_method; // multi-thread method: 0=off, 1=[parse+recon][filter]
|
||||
// 2=[parse][recon+filter]
|
||||
int cache_id; // current cache row
|
||||
int num_caches; // number of cached rows of 16 pixels (1, 2 or 3)
|
||||
VP8ThreadContext thread_ctx; // Thread context
|
||||
|
||||
// dimension, in macroblock units.
|
||||
int mb_w_, mb_h_;
|
||||
int mb_w, mb_h;
|
||||
|
||||
// Macroblock to process/filter, depending on cropping and filter_type.
|
||||
int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered
|
||||
int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded
|
||||
int tl_mb_x, tl_mb_y; // top-left MB that must be in-loop filtered
|
||||
int br_mb_x, br_mb_y; // last bottom-right MB that must be decoded
|
||||
|
||||
// number of partitions minus one.
|
||||
uint32_t num_parts_minus_one_;
|
||||
uint32_t num_parts_minus_one;
|
||||
// per-partition boolean decoders.
|
||||
VP8BitReader parts_[MAX_NUM_PARTITIONS];
|
||||
VP8BitReader parts[MAX_NUM_PARTITIONS];
|
||||
|
||||
// Dithering strength, deduced from decoding options
|
||||
int dither_; // whether to use dithering or not
|
||||
VP8Random dithering_rg_; // random generator for dithering
|
||||
int dither; // whether to use dithering or not
|
||||
VP8Random dithering_rg; // random generator for dithering
|
||||
|
||||
// dequantization (one set of DC/AC dequant factor per segment)
|
||||
VP8QuantMatrix dqm_[NUM_MB_SEGMENTS];
|
||||
VP8QuantMatrix dqm[NUM_MB_SEGMENTS];
|
||||
|
||||
// probabilities
|
||||
VP8Proba proba_;
|
||||
int use_skip_proba_;
|
||||
uint8_t skip_p_;
|
||||
VP8Proba proba;
|
||||
int use_skip_proba;
|
||||
uint8_t skip_p;
|
||||
|
||||
// Boundary data cache and persistent buffers.
|
||||
uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
|
||||
uint8_t intra_l_[4]; // left intra modes values
|
||||
uint8_t* intra_t; // top intra modes values: 4 * mb_w
|
||||
uint8_t intra_l[4]; // left intra modes values
|
||||
|
||||
VP8TopSamples* yuv_t_; // top y/u/v samples
|
||||
VP8TopSamples* yuv_t; // top y/u/v samples
|
||||
|
||||
VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
|
||||
VP8FInfo* f_info_; // filter strength info
|
||||
uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
|
||||
VP8MB* mb_info; // contextual macroblock info (mb_w + 1)
|
||||
VP8FInfo* f_info; // filter strength info
|
||||
uint8_t* yuv_b; // main block for Y/U/V (size = YUV_SIZE)
|
||||
|
||||
uint8_t* cache_y_; // macroblock row for storing unfiltered samples
|
||||
uint8_t* cache_u_;
|
||||
uint8_t* cache_v_;
|
||||
int cache_y_stride_;
|
||||
int cache_uv_stride_;
|
||||
uint8_t* cache_y; // macroblock row for storing unfiltered samples
|
||||
uint8_t* cache_u;
|
||||
uint8_t* cache_v;
|
||||
int cache_y_stride;
|
||||
int cache_uv_stride;
|
||||
|
||||
// main memory chunk for the above data. Persistent.
|
||||
void* mem_;
|
||||
size_t mem_size_;
|
||||
void* mem;
|
||||
size_t mem_size;
|
||||
|
||||
// Per macroblock non-persistent infos.
|
||||
int mb_x_, mb_y_; // current position, in macroblock units
|
||||
VP8MBData* mb_data_; // parsed reconstruction data
|
||||
int mb_x, mb_y; // current position, in macroblock units
|
||||
VP8MBData* mb_data; // parsed reconstruction data
|
||||
|
||||
// Filtering side-info
|
||||
int filter_type_; // 0=off, 1=simple, 2=complex
|
||||
VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type
|
||||
int filter_type; // 0=off, 1=simple, 2=complex
|
||||
VP8FInfo fstrengths[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type
|
||||
|
||||
// Alpha
|
||||
struct ALPHDecoder* alph_dec_; // alpha-plane decoder object
|
||||
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
||||
size_t alpha_data_size_;
|
||||
int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_
|
||||
uint8_t* alpha_plane_mem_; // memory allocated for alpha_plane_
|
||||
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
|
||||
const uint8_t* alpha_prev_line_; // last decoded alpha row (or NULL)
|
||||
int alpha_dithering_; // derived from decoding options (0=off, 100=full)
|
||||
struct ALPHDecoder* alph_dec; // alpha-plane decoder object
|
||||
const uint8_t* WEBP_COUNTED_BY(alpha_data_size)
|
||||
alpha_data; // compressed alpha data (if present)
|
||||
size_t alpha_data_size;
|
||||
int is_alpha_decoded; // true if alpha_data is decoded in alpha_plane
|
||||
uint8_t* alpha_plane_mem; // memory allocated for alpha_plane
|
||||
uint8_t* alpha_plane; // output. Persistent, contains the whole data.
|
||||
const uint8_t* alpha_prev_line; // last decoded alpha row (or NULL)
|
||||
int alpha_dithering; // derived from decoding options (0=off, 100=full)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
// in vp8.c
|
||||
int VP8SetError(VP8Decoder* const dec,
|
||||
VP8StatusCode error, const char* const msg);
|
||||
int VP8SetError(VP8Decoder* const dec, VP8StatusCode error,
|
||||
const char* const msg);
|
||||
|
||||
// in tree.c
|
||||
void VP8ResetProba(VP8Proba* const proba);
|
||||
@@ -295,8 +302,8 @@ WEBP_NODISCARD int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Return the multi-threading method to use (0=off), depending
|
||||
// on options and bitstream size. Only for lossy decoding.
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
const WebPHeaderStructure* const headers,
|
||||
int width, int height);
|
||||
const WebPHeaderStructure* const headers, int width,
|
||||
int height);
|
||||
// Initialize dithering post-process if needed.
|
||||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec);
|
||||
@@ -310,13 +317,13 @@ WEBP_NODISCARD int VP8DecodeMB(VP8Decoder* const dec,
|
||||
|
||||
// in alpha.c
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
const VP8Io* const io,
|
||||
int row, int num_rows);
|
||||
const VP8Io* const io, int row,
|
||||
int num_rows);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_DEC_VP8I_DEC_H_
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,80 +15,85 @@
|
||||
#ifndef WEBP_DEC_VP8LI_DEC_H_
|
||||
#define WEBP_DEC_VP8LI_DEC_H_
|
||||
|
||||
#include <string.h> // for memcpy()
|
||||
#include <string.h> // for memcpy()
|
||||
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/utils/bit_reader_utils.h"
|
||||
#include "src/utils/color_cache_utils.h"
|
||||
#include "src/utils/huffman_utils.h"
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
READ_DATA = 0,
|
||||
READ_HDR = 1,
|
||||
READ_DIM = 2
|
||||
} VP8LDecodeState;
|
||||
typedef enum { READ_DATA = 0, READ_HDR = 1, READ_DIM = 2 } VP8LDecodeState;
|
||||
|
||||
typedef struct VP8LTransform VP8LTransform;
|
||||
struct VP8LTransform {
|
||||
VP8LImageTransformType type_; // transform type.
|
||||
int bits_; // subsampling bits defining transform window.
|
||||
int xsize_; // transform window X index.
|
||||
int ysize_; // transform window Y index.
|
||||
uint32_t* data_; // transform data.
|
||||
VP8LImageTransformType type; // transform type.
|
||||
int bits; // subsampling bits defining transform window.
|
||||
int xsize; // transform window X index.
|
||||
int ysize; // transform window Y index.
|
||||
uint32_t* data; // transform data.
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int color_cache_size_;
|
||||
VP8LColorCache color_cache_;
|
||||
VP8LColorCache saved_color_cache_; // for incremental
|
||||
int color_cache_size;
|
||||
VP8LColorCache color_cache;
|
||||
VP8LColorCache saved_color_cache; // for incremental
|
||||
|
||||
int huffman_mask_;
|
||||
int huffman_subsample_bits_;
|
||||
int huffman_xsize_;
|
||||
uint32_t* huffman_image_;
|
||||
int num_htree_groups_;
|
||||
HTreeGroup* htree_groups_;
|
||||
HuffmanTables huffman_tables_;
|
||||
int huffman_mask;
|
||||
int huffman_subsample_bits;
|
||||
int huffman_xsize;
|
||||
uint32_t* huffman_image;
|
||||
int num_htree_groups;
|
||||
HTreeGroup* htree_groups;
|
||||
HuffmanTables huffman_tables;
|
||||
} VP8LMetadata;
|
||||
|
||||
typedef struct VP8LDecoder VP8LDecoder;
|
||||
struct VP8LDecoder {
|
||||
VP8StatusCode status_;
|
||||
VP8LDecodeState state_;
|
||||
VP8Io* io_;
|
||||
VP8StatusCode status;
|
||||
VP8LDecodeState state;
|
||||
VP8Io* io;
|
||||
|
||||
const WebPDecBuffer* output_; // shortcut to io->opaque->output
|
||||
const WebPDecBuffer* output; // shortcut to io->opaque->output
|
||||
|
||||
uint32_t* pixels_; // Internal data: either uint8_t* for alpha
|
||||
// or uint32_t* for BGRA.
|
||||
uint32_t* argb_cache_; // Scratch buffer for temporary BGRA storage.
|
||||
uint32_t* pixels; // Internal data: either uint8_t* for alpha
|
||||
// or uint32_t* for BGRA.
|
||||
uint32_t* argb_cache; // Scratch buffer for temporary BGRA storage.
|
||||
uint16_t* accumulated_rgb_pixels; // Scratch buffer for accumulated RGB for
|
||||
// YUV conversion.
|
||||
|
||||
VP8LBitReader br_;
|
||||
int incremental_; // if true, incremental decoding is expected
|
||||
VP8LBitReader saved_br_; // note: could be local variables too
|
||||
int saved_last_pixel_;
|
||||
VP8LBitReader br;
|
||||
int incremental; // if true, incremental decoding is expected
|
||||
VP8LBitReader saved_br; // note: could be local variables too
|
||||
int saved_last_pixel;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
int last_row_; // last input row decoded so far.
|
||||
int last_pixel_; // last pixel decoded so far. However, it may
|
||||
// not be transformed, scaled and
|
||||
// color-converted yet.
|
||||
int last_out_row_; // last row output so far.
|
||||
int width;
|
||||
int height;
|
||||
int last_row; // last input row decoded so far.
|
||||
int last_pixel; // last pixel decoded so far. However, it may
|
||||
// not be transformed, scaled and
|
||||
// color-converted yet.
|
||||
int last_out_row; // last row output so far.
|
||||
|
||||
VP8LMetadata hdr_;
|
||||
VP8LMetadata hdr;
|
||||
|
||||
int next_transform_;
|
||||
VP8LTransform transforms_[NUM_TRANSFORMS];
|
||||
int next_transform;
|
||||
VP8LTransform transforms[NUM_TRANSFORMS];
|
||||
// or'd bitset storing the transforms types.
|
||||
uint32_t transforms_seen_;
|
||||
uint32_t transforms_seen;
|
||||
|
||||
uint8_t* rescaler_memory; // Working memory for rescaling work.
|
||||
WebPRescaler* rescaler; // Common rescaler for all channels.
|
||||
uint8_t* rescaler_memory; // Working memory for rescaling work.
|
||||
WebPRescaler* rescaler; // Common rescaler for all channels.
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -100,9 +105,9 @@ struct ALPHDecoder; // Defined in dec/alphai.h.
|
||||
|
||||
// Decodes image header for alpha data stored using lossless compression.
|
||||
// Returns false in case of error.
|
||||
WEBP_NODISCARD int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
|
||||
const uint8_t* const data,
|
||||
size_t data_size);
|
||||
WEBP_NODISCARD int VP8LDecodeAlphaHeader(
|
||||
struct ALPHDecoder* const alph_dec,
|
||||
const uint8_t* const WEBP_COUNTED_BY(data_size) data, size_t data_size);
|
||||
|
||||
// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are
|
||||
// already decoded in previous call(s), it will resume decoding from where it
|
||||
@@ -118,7 +123,7 @@ WEBP_NODISCARD VP8LDecoder* VP8LNew(void);
|
||||
WEBP_NODISCARD int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io);
|
||||
|
||||
// Decodes an image. It's required to decode the lossless header before calling
|
||||
// this function. Returns false in case of error, with updated dec->status_.
|
||||
// this function. Returns false in case of error, with updated dec->status.
|
||||
WEBP_NODISCARD int VP8LDecodeImage(VP8LDecoder* const dec);
|
||||
|
||||
// Clears and deallocate a lossless decoder instance.
|
||||
@@ -139,7 +144,7 @@ WEBP_NODISCARD int ReadHuffmanCodesHelper(
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_DEC_VP8LI_DEC_H_
|
||||
|
||||
@@ -11,17 +11,24 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/dec/common_dec.h"
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/mux_types.h" // ALPHA_FLAG
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
#include "src/webp/mux_types.h" // ALPHA_FLAG
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RIFF layout is:
|
||||
// Offset tag
|
||||
@@ -54,9 +61,11 @@
|
||||
// and VP8_STATUS_OK otherwise.
|
||||
// In case there are not enough bytes (partial RIFF container), return 0 for
|
||||
// *riff_size. Else return the RIFF size extracted from the header.
|
||||
static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
size_t* const data_size, int have_all_data,
|
||||
size_t* const riff_size) {
|
||||
static VP8StatusCode ParseRIFF(const uint8_t* WEBP_COUNTED_BY(*data_size) *
|
||||
WEBP_SINGLE const data,
|
||||
size_t* WEBP_SINGLE const data_size,
|
||||
int have_all_data,
|
||||
size_t* WEBP_SINGLE const riff_size) {
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
assert(riff_size != NULL);
|
||||
@@ -79,8 +88,8 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
}
|
||||
// We have a RIFF container. Skip it.
|
||||
*riff_size = size;
|
||||
*data += RIFF_HEADER_SIZE;
|
||||
*data_size -= RIFF_HEADER_SIZE;
|
||||
*data += RIFF_HEADER_SIZE;
|
||||
}
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
@@ -93,11 +102,13 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
// If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
|
||||
// *height_ptr and *flags_ptr are set to the corresponding values extracted
|
||||
// from the VP8X chunk.
|
||||
static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
||||
size_t* const data_size,
|
||||
int* const found_vp8x,
|
||||
int* const width_ptr, int* const height_ptr,
|
||||
uint32_t* const flags_ptr) {
|
||||
static VP8StatusCode ParseVP8X(const uint8_t* WEBP_COUNTED_BY(*data_size) *
|
||||
WEBP_SINGLE const data,
|
||||
size_t* WEBP_SINGLE const data_size,
|
||||
int* WEBP_SINGLE const found_vp8x,
|
||||
int* WEBP_SINGLE const width_ptr,
|
||||
int* WEBP_SINGLE const height_ptr,
|
||||
uint32_t* WEBP_SINGLE const flags_ptr) {
|
||||
const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
@@ -132,8 +143,8 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
||||
if (width_ptr != NULL) *width_ptr = width;
|
||||
if (height_ptr != NULL) *height_ptr = height;
|
||||
// Skip over VP8X header bytes.
|
||||
*data += vp8x_size;
|
||||
*data_size -= vp8x_size;
|
||||
*data += vp8x_size;
|
||||
*found_vp8x = 1;
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
@@ -146,13 +157,13 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
||||
// VP8_STATUS_OK otherwise.
|
||||
// If an alpha chunk is found, *alpha_data and *alpha_size are set
|
||||
// appropriately.
|
||||
static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
size_t* const data_size,
|
||||
size_t const riff_size,
|
||||
const uint8_t** const alpha_data,
|
||||
size_t* const alpha_size) {
|
||||
const uint8_t* buf;
|
||||
static VP8StatusCode ParseOptionalChunks(
|
||||
const uint8_t* WEBP_COUNTED_BY(*data_size) * WEBP_SINGLE const data,
|
||||
size_t* WEBP_SINGLE const data_size, size_t const riff_size,
|
||||
const uint8_t* WEBP_COUNTED_BY(*alpha_size) * WEBP_SINGLE const alpha_data,
|
||||
size_t* WEBP_SINGLE const alpha_size) {
|
||||
size_t buf_size;
|
||||
const uint8_t* WEBP_COUNTED_BY(buf_size) buf;
|
||||
uint32_t total_size = TAG_SIZE + // "WEBP".
|
||||
CHUNK_HEADER_SIZE + // "VP8Xnnnn".
|
||||
VP8X_CHUNK_SIZE; // data.
|
||||
@@ -168,10 +179,10 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
|
||||
while (1) {
|
||||
uint32_t chunk_size;
|
||||
uint32_t disk_chunk_size; // chunk_size with padding
|
||||
uint32_t disk_chunk_size; // chunk_size with padding
|
||||
|
||||
*data = buf;
|
||||
*data_size = buf_size;
|
||||
*data = buf;
|
||||
|
||||
if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
@@ -179,7 +190,7 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
|
||||
chunk_size = GetLE32(buf + TAG_SIZE);
|
||||
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.
|
||||
}
|
||||
// For odd-sized chunk-payload, there's one byte padding at the end.
|
||||
disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1u;
|
||||
@@ -187,23 +198,22 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
|
||||
// Check that total bytes skipped so far does not exceed riff_size.
|
||||
if (riff_size > 0 && (total_size > riff_size)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
|
||||
}
|
||||
|
||||
// Start of a (possibly incomplete) VP8/VP8L chunk implies that we have
|
||||
// parsed all the optional chunks.
|
||||
// Note: This check must occur before the check 'buf_size < disk_chunk_size'
|
||||
// below to allow incomplete VP8/VP8L chunks.
|
||||
if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
|
||||
!memcmp(buf, "VP8L", TAG_SIZE)) {
|
||||
if (!memcmp(buf, "VP8 ", TAG_SIZE) || !memcmp(buf, "VP8L", TAG_SIZE)) {
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
if (buf_size < disk_chunk_size) { // Insufficient data.
|
||||
if (buf_size < disk_chunk_size) { // Insufficient data.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
|
||||
if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header.
|
||||
if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header.
|
||||
*alpha_data = buf + CHUNK_HEADER_SIZE;
|
||||
*alpha_size = chunk_size;
|
||||
}
|
||||
@@ -222,16 +232,20 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
// If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
|
||||
// extracted from the VP8/VP8L chunk header.
|
||||
// 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,
|
||||
size_t* const data_size, int have_all_data,
|
||||
size_t riff_size, size_t* const chunk_size,
|
||||
int* const is_lossless) {
|
||||
const uint8_t* const data = *data_ptr;
|
||||
static VP8StatusCode ParseVP8Header(const uint8_t* WEBP_COUNTED_BY(*data_size) *
|
||||
WEBP_SINGLE const data_ptr,
|
||||
size_t* WEBP_SINGLE const data_size,
|
||||
int have_all_data, size_t riff_size,
|
||||
size_t* WEBP_SINGLE const chunk_size,
|
||||
int* WEBP_SINGLE const is_lossless) {
|
||||
const size_t local_data_size = *data_size;
|
||||
const uint8_t* WEBP_COUNTED_BY(local_data_size) const data = *data_ptr;
|
||||
const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
|
||||
const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE);
|
||||
const uint32_t minimal_size =
|
||||
TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR
|
||||
// "WEBP" + "VP8Lnnnn"
|
||||
(void)local_data_size;
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
assert(chunk_size != NULL);
|
||||
@@ -252,8 +266,8 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
|
||||
}
|
||||
// Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
|
||||
*chunk_size = size;
|
||||
*data_ptr += CHUNK_HEADER_SIZE;
|
||||
*data_size -= CHUNK_HEADER_SIZE;
|
||||
*data_ptr += CHUNK_HEADER_SIZE;
|
||||
*is_lossless = is_vp8l;
|
||||
} else {
|
||||
// Raw VP8/VP8L bitstream (no header).
|
||||
@@ -277,14 +291,13 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
|
||||
// RIFF + VP8X + (optional chunks) + VP8(L)
|
||||
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
|
||||
// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
|
||||
static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
size_t data_size,
|
||||
int* const width,
|
||||
int* const height,
|
||||
int* const has_alpha,
|
||||
int* const has_animation,
|
||||
int* const format,
|
||||
WebPHeaderStructure* const headers) {
|
||||
static VP8StatusCode ParseHeadersInternal(
|
||||
const uint8_t* WEBP_COUNTED_BY(data_size_param) data_param,
|
||||
size_t data_size_param, int* const width, int* const height,
|
||||
int* const has_alpha, int* const has_animation, int* const format,
|
||||
WebPHeaderStructure* const headers) {
|
||||
size_t data_size = data_size_param;
|
||||
const uint8_t* WEBP_COUNTED_BY(data_size) data = data_param;
|
||||
int canvas_width = 0;
|
||||
int canvas_height = 0;
|
||||
int image_width = 0;
|
||||
@@ -300,22 +313,22 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
if (data == NULL || data_size < RIFF_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
memset(&hdrs, 0, sizeof(hdrs));
|
||||
WEBP_UNSAFE_MEMSET(&hdrs, 0, sizeof(hdrs));
|
||||
hdrs.data = data;
|
||||
hdrs.data_size = data_size;
|
||||
|
||||
// Skip over RIFF header.
|
||||
status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong RIFF header / insufficient data.
|
||||
return status; // Wrong RIFF header / insufficient data.
|
||||
}
|
||||
found_riff = (hdrs.riff_size > 0);
|
||||
|
||||
// Skip over VP8X.
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
status = ParseVP8X(&data, &data_size, &found_vp8x,
|
||||
&canvas_width, &canvas_height, &flags);
|
||||
status = ParseVP8X(&data, &data_size, &found_vp8x, &canvas_width,
|
||||
&canvas_height, &flags);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong VP8X / insufficient data.
|
||||
}
|
||||
@@ -327,7 +340,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
}
|
||||
if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
|
||||
if (has_animation != NULL) *has_animation = animation_present;
|
||||
if (format != NULL) *format = 0; // default = undefined
|
||||
if (format != NULL) *format = 0; // default = undefined
|
||||
|
||||
image_width = canvas_width;
|
||||
image_height = canvas_height;
|
||||
@@ -345,11 +358,16 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
// Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
|
||||
if ((found_riff && found_vp8x) ||
|
||||
(!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
|
||||
size_t local_alpha_data_size = 0;
|
||||
const uint8_t* WEBP_COUNTED_BY(local_alpha_data_size) local_alpha_data =
|
||||
NULL;
|
||||
status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
|
||||
&hdrs.alpha_data, &hdrs.alpha_data_size);
|
||||
&local_alpha_data, &local_alpha_data_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
goto ReturnWidthHeight; // Invalid chunk size / insufficient data.
|
||||
}
|
||||
hdrs.alpha_data = local_alpha_data;
|
||||
hdrs.alpha_data_size = local_alpha_data_size;
|
||||
}
|
||||
|
||||
// Skip over VP8/VP8L header.
|
||||
@@ -398,7 +416,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
|
||||
assert(headers->offset == headers->data_size - data_size);
|
||||
}
|
||||
ReturnWidthHeight:
|
||||
ReturnWidthHeight:
|
||||
if (status == VP8_STATUS_OK ||
|
||||
(status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) {
|
||||
if (has_alpha != NULL) {
|
||||
@@ -420,9 +438,13 @@ VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
|
||||
int has_animation = 0;
|
||||
assert(headers != NULL);
|
||||
// fill out headers, ignore width/height/has_alpha.
|
||||
status = ParseHeadersInternal(headers->data, headers->data_size,
|
||||
NULL, NULL, NULL, &has_animation,
|
||||
NULL, headers);
|
||||
{
|
||||
const uint8_t* WEBP_BIDI_INDEXABLE const bounded_data =
|
||||
WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, headers->data,
|
||||
headers->data_size);
|
||||
status = ParseHeadersInternal(bounded_data, headers->data_size, NULL, NULL,
|
||||
NULL, &has_animation, NULL, headers);
|
||||
}
|
||||
if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
// The WebPDemux API + libwebp can be used to decode individual
|
||||
// uncomposited frames or the WebPAnimDecoder can be used to fully
|
||||
@@ -439,7 +461,7 @@ VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
|
||||
|
||||
void WebPResetDecParams(WebPDecParams* const params) {
|
||||
if (params != NULL) {
|
||||
memset(params, 0, sizeof(*params));
|
||||
WEBP_UNSAFE_MEMSET(params, 0, sizeof(*params));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,9 +469,9 @@ void WebPResetDecParams(WebPDecParams* const params) {
|
||||
// "Into" decoding variants
|
||||
|
||||
// Main flow
|
||||
WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data,
|
||||
size_t data_size,
|
||||
WebPDecParams* const params) {
|
||||
WEBP_NODISCARD static VP8StatusCode DecodeInto(
|
||||
const uint8_t* WEBP_COUNTED_BY(data_size) const data, size_t data_size,
|
||||
WebPDecParams* const params) {
|
||||
VP8StatusCode status;
|
||||
VP8Io io;
|
||||
WebPHeaderStructure headers;
|
||||
@@ -457,7 +479,7 @@ WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data,
|
||||
headers.data = data;
|
||||
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) {
|
||||
return status;
|
||||
}
|
||||
@@ -475,23 +497,23 @@ WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data,
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
dec->alpha_data = headers.alpha_data;
|
||||
dec->alpha_data_size = headers.alpha_data_size;
|
||||
|
||||
// Decode bitstream header, update io->width/io->height.
|
||||
if (!VP8GetHeaders(dec, &io)) {
|
||||
status = dec->status_; // An error occurred. Grab error status.
|
||||
status = dec->status; // An error occurred. Grab error status.
|
||||
} else {
|
||||
// Allocate/check output buffers.
|
||||
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
|
||||
params->output);
|
||||
if (status == VP8_STATUS_OK) { // Decode
|
||||
// This change must be done before calling VP8Decode()
|
||||
dec->mt_method_ = VP8GetThreadMethod(params->options, &headers,
|
||||
io.width, io.height);
|
||||
dec->mt_method =
|
||||
VP8GetThreadMethod(params->options, &headers, io.width, io.height);
|
||||
VP8InitDithering(params->options, dec);
|
||||
if (!VP8Decode(dec, &io)) {
|
||||
status = dec->status_;
|
||||
status = dec->status;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,14 +524,14 @@ WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data,
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!VP8LDecodeHeader(dec, &io)) {
|
||||
status = dec->status_; // An error occurred. Grab error status.
|
||||
status = dec->status; // An error occurred. Grab error status.
|
||||
} else {
|
||||
// Allocate/check output buffers.
|
||||
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
|
||||
params->output);
|
||||
if (status == VP8_STATUS_OK) { // Decode
|
||||
if (!VP8LDecodeImage(dec)) {
|
||||
status = dec->status_;
|
||||
status = dec->status;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -529,11 +551,10 @@ WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data,
|
||||
}
|
||||
|
||||
// Helpers
|
||||
WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
|
||||
const uint8_t* const data,
|
||||
size_t data_size,
|
||||
uint8_t* const rgba,
|
||||
int stride, size_t size) {
|
||||
WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(
|
||||
WEBP_CSP_MODE colorspace,
|
||||
const uint8_t* WEBP_COUNTED_BY(data_size) const data, size_t data_size,
|
||||
uint8_t* WEBP_COUNTED_BY(size) const rgba, int stride, size_t size) {
|
||||
WebPDecParams params;
|
||||
WebPDecBuffer buf;
|
||||
if (rgba == NULL || !WebPInitDecBuffer(&buf)) {
|
||||
@@ -541,10 +562,10 @@ WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
|
||||
}
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &buf;
|
||||
buf.colorspace = colorspace;
|
||||
buf.u.RGBA.rgba = rgba;
|
||||
buf.colorspace = colorspace;
|
||||
buf.u.RGBA.rgba = rgba;
|
||||
buf.u.RGBA.stride = stride;
|
||||
buf.u.RGBA.size = size;
|
||||
buf.u.RGBA.size = size;
|
||||
buf.is_external_memory = 1;
|
||||
if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
@@ -552,50 +573,63 @@ WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
|
||||
return rgba;
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
uint8_t* WebPDecodeRGBInto(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size,
|
||||
uint8_t* WEBP_COUNTED_BY(size) output, size_t size,
|
||||
int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
uint8_t* WebPDecodeRGBAInto(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size,
|
||||
uint8_t* WEBP_COUNTED_BY(size) output, size_t size,
|
||||
int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
uint8_t* WebPDecodeARGBInto(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size,
|
||||
uint8_t* WEBP_COUNTED_BY(size) output, size_t size,
|
||||
int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
uint8_t* WebPDecodeBGRInto(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size,
|
||||
uint8_t* WEBP_COUNTED_BY(size) output, size_t size,
|
||||
int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
uint8_t* WebPDecodeBGRAInto(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size,
|
||||
uint8_t* WEBP_COUNTED_BY(size) output, size_t size,
|
||||
int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride) {
|
||||
uint8_t* WebPDecodeYUVInto(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size,
|
||||
uint8_t* WEBP_COUNTED_BY(luma_size) luma,
|
||||
size_t luma_size, int luma_stride,
|
||||
uint8_t* WEBP_COUNTED_BY(u_size) u, size_t u_size,
|
||||
int u_stride, uint8_t* WEBP_COUNTED_BY(v_size) v,
|
||||
size_t v_size, int v_stride) {
|
||||
WebPDecParams params;
|
||||
WebPDecBuffer output;
|
||||
if (luma == NULL || !WebPInitDecBuffer(&output)) return NULL;
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &output;
|
||||
output.colorspace = MODE_YUV;
|
||||
output.u.YUVA.y = luma;
|
||||
output.colorspace = MODE_YUV;
|
||||
output.u.YUVA.y = luma;
|
||||
output.u.YUVA.y_stride = luma_stride;
|
||||
output.u.YUVA.y_size = luma_size;
|
||||
output.u.YUVA.u = u;
|
||||
output.u.YUVA.y_size = luma_size;
|
||||
output.u.YUVA.u = u;
|
||||
output.u.YUVA.u_stride = u_stride;
|
||||
output.u.YUVA.u_size = u_size;
|
||||
output.u.YUVA.v = v;
|
||||
output.u.YUVA.u_size = u_size;
|
||||
output.u.YUVA.v = v;
|
||||
output.u.YUVA.v_stride = v_stride;
|
||||
output.u.YUVA.v_size = v_size;
|
||||
output.u.YUVA.v_size = v_size;
|
||||
output.is_external_memory = 1;
|
||||
if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
@@ -606,7 +640,8 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
WEBP_NODISCARD static uint8_t* Decode(WEBP_CSP_MODE mode,
|
||||
const uint8_t* const data,
|
||||
const uint8_t* WEBP_COUNTED_BY(data_size)
|
||||
const data,
|
||||
size_t data_size, int* const width,
|
||||
int* const height,
|
||||
WebPDecBuffer* const keep_info) {
|
||||
@@ -631,50 +666,50 @@ WEBP_NODISCARD static uint8_t* Decode(WEBP_CSP_MODE mode,
|
||||
if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
}
|
||||
if (keep_info != NULL) { // keep track of the side-info
|
||||
if (keep_info != NULL) { // keep track of the side-info
|
||||
WebPCopyDecBuffer(&output, keep_info);
|
||||
}
|
||||
// return decoded samples (don't clear 'output'!)
|
||||
return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
uint8_t* WebPDecodeRGB(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, int* width, int* height) {
|
||||
return Decode(MODE_RGB, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
uint8_t* WebPDecodeRGBA(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, int* width, int* height) {
|
||||
return Decode(MODE_RGBA, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
uint8_t* WebPDecodeARGB(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, int* width, int* height) {
|
||||
return Decode(MODE_ARGB, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
uint8_t* WebPDecodeBGR(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, int* width, int* height) {
|
||||
return Decode(MODE_BGR, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
uint8_t* WebPDecodeBGRA(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, int* width, int* height) {
|
||||
return Decode(MODE_BGRA, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height, uint8_t** u, uint8_t** v,
|
||||
int* stride, int* uv_stride) {
|
||||
uint8_t* WebPDecodeYUV(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, int* width, int* height, uint8_t** u,
|
||||
uint8_t** v, int* stride, int* uv_stride) {
|
||||
// data, width and height are checked by Decode().
|
||||
if (u == NULL || v == NULL || stride == NULL || uv_stride == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
WebPDecBuffer output; // only to preserve the side-infos
|
||||
uint8_t* const out = Decode(MODE_YUV, data, data_size,
|
||||
width, height, &output);
|
||||
WebPDecBuffer output; // only to preserve the side-infos
|
||||
uint8_t* const out =
|
||||
Decode(MODE_YUV, data, data_size, width, height, &output);
|
||||
|
||||
if (out != NULL) {
|
||||
const WebPYUVABuffer* const buf = &output.u.YUVA;
|
||||
@@ -690,10 +725,12 @@ uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
|
||||
static void DefaultFeatures(WebPBitstreamFeatures* const features) {
|
||||
assert(features != NULL);
|
||||
memset(features, 0, sizeof(*features));
|
||||
WEBP_UNSAFE_MEMSET(features, 0, sizeof(*features));
|
||||
}
|
||||
|
||||
static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
|
||||
static VP8StatusCode GetFeatures(const uint8_t* WEBP_COUNTED_BY(data_size)
|
||||
const data,
|
||||
size_t data_size,
|
||||
WebPBitstreamFeatures* const features) {
|
||||
if (features == NULL || data == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
@@ -701,17 +738,16 @@ static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
|
||||
DefaultFeatures(features);
|
||||
|
||||
// Only parse enough of the data to retrieve the features.
|
||||
return ParseHeadersInternal(data, data_size,
|
||||
&features->width, &features->height,
|
||||
&features->has_alpha, &features->has_animation,
|
||||
&features->format, NULL);
|
||||
return ParseHeadersInternal(
|
||||
data, data_size, &features->width, &features->height,
|
||||
&features->has_alpha, &features->has_animation, &features->format, NULL);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPGetInfo()
|
||||
|
||||
int WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
int WebPGetInfo(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, int* width, int* height) {
|
||||
WebPBitstreamFeatures features;
|
||||
|
||||
if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) {
|
||||
@@ -719,7 +755,7 @@ int WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
}
|
||||
|
||||
if (width != NULL) {
|
||||
*width = features.width;
|
||||
*width = features.width;
|
||||
}
|
||||
if (height != NULL) {
|
||||
*height = features.height;
|
||||
@@ -731,15 +767,14 @@ int WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
//------------------------------------------------------------------------------
|
||||
// Advance decoding API
|
||||
|
||||
int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
|
||||
int version) {
|
||||
int WebPInitDecoderConfigInternal(WebPDecoderConfig* config, int version) {
|
||||
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
|
||||
return 0; // version mismatch
|
||||
return 0; // version mismatch
|
||||
}
|
||||
if (config == NULL) {
|
||||
return 0;
|
||||
}
|
||||
memset(config, 0, sizeof(*config));
|
||||
WEBP_UNSAFE_MEMSET(config, 0, sizeof(*config));
|
||||
DefaultFeatures(&config->input);
|
||||
if (!WebPInitDecBuffer(&config->output)) {
|
||||
return 0;
|
||||
@@ -747,11 +782,68 @@ int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
|
||||
return 1;
|
||||
}
|
||||
|
||||
VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size,
|
||||
static int WebPCheckCropDimensionsBasic(int x, int y, int w, int h) {
|
||||
return !(x < 0 || y < 0 || w <= 0 || h <= 0);
|
||||
}
|
||||
|
||||
int WebPValidateDecoderConfig(const WebPDecoderConfig* config) {
|
||||
const WebPDecoderOptions* options;
|
||||
if (config == NULL) return 0;
|
||||
if (!IsValidColorspace(config->output.colorspace)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
options = &config->options;
|
||||
// bypass_filtering, no_fancy_upsampling, use_cropping, use_scaling,
|
||||
// use_threads, flip can be any integer and are interpreted as boolean.
|
||||
|
||||
// Check for cropping.
|
||||
if (options->use_cropping && !WebPCheckCropDimensionsBasic(
|
||||
options->crop_left, options->crop_top,
|
||||
options->crop_width, options->crop_height)) {
|
||||
return 0;
|
||||
}
|
||||
// Check for scaling.
|
||||
if (options->use_scaling &&
|
||||
(options->scaled_width < 0 || options->scaled_height < 0 ||
|
||||
(options->scaled_width == 0 && options->scaled_height == 0))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// In case the WebPBitstreamFeatures has been filled in, check further.
|
||||
if (config->input.width > 0 || config->input.height > 0) {
|
||||
int scaled_width = options->scaled_width;
|
||||
int scaled_height = options->scaled_height;
|
||||
if (options->use_cropping &&
|
||||
!WebPCheckCropDimensions(config->input.width, config->input.height,
|
||||
options->crop_left, options->crop_top,
|
||||
options->crop_width, options->crop_height)) {
|
||||
return 0;
|
||||
}
|
||||
if (options->use_scaling && !WebPRescalerGetScaledDimensions(
|
||||
config->input.width, config->input.height,
|
||||
&scaled_width, &scaled_height)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for dithering.
|
||||
if (options->dithering_strength < 0 || options->dithering_strength > 100 ||
|
||||
options->alpha_dithering_strength < 0 ||
|
||||
options->alpha_dithering_strength > 100) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
VP8StatusCode WebPGetFeaturesInternal(const uint8_t* WEBP_COUNTED_BY(data_size)
|
||||
data,
|
||||
size_t data_size,
|
||||
WebPBitstreamFeatures* features,
|
||||
int version) {
|
||||
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
|
||||
return VP8_STATUS_INVALID_PARAM; // version mismatch
|
||||
return VP8_STATUS_INVALID_PARAM; // version mismatch
|
||||
}
|
||||
if (features == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
@@ -759,8 +851,8 @@ VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size,
|
||||
return GetFeatures(data, data_size, features);
|
||||
}
|
||||
|
||||
VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config) {
|
||||
VP8StatusCode WebPDecode(const uint8_t* WEBP_COUNTED_BY(data_size) data,
|
||||
size_t data_size, WebPDecoderConfig* config) {
|
||||
WebPDecParams params;
|
||||
VP8StatusCode status;
|
||||
|
||||
@@ -804,10 +896,10 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
|
||||
//------------------------------------------------------------------------------
|
||||
// Cropping and rescaling.
|
||||
|
||||
int WebPCheckCropDimensions(int image_width, int image_height,
|
||||
int x, int y, int w, int h) {
|
||||
return !(x < 0 || y < 0 || w <= 0 || h <= 0 ||
|
||||
x >= image_width || w > image_width || w > image_width - x ||
|
||||
int WebPCheckCropDimensions(int image_width, int image_height, int x, int y,
|
||||
int w, int h) {
|
||||
return WebPCheckCropDimensionsBasic(x, y, w, h) &&
|
||||
!(x >= image_width || w > image_width || w > image_width - x ||
|
||||
y >= image_height || h > image_height || h > image_height - y);
|
||||
}
|
||||
|
||||
@@ -824,7 +916,7 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
h = options->crop_height;
|
||||
x = options->crop_left;
|
||||
y = options->crop_top;
|
||||
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420
|
||||
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420
|
||||
x &= ~1;
|
||||
y &= ~1;
|
||||
}
|
||||
@@ -832,9 +924,9 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
return 0; // out of frame boundary error
|
||||
}
|
||||
}
|
||||
io->crop_left = x;
|
||||
io->crop_top = y;
|
||||
io->crop_right = x + w;
|
||||
io->crop_left = x;
|
||||
io->crop_top = y;
|
||||
io->crop_right = x + w;
|
||||
io->crop_bottom = y + h;
|
||||
io->mb_w = w;
|
||||
io->mb_h = h;
|
||||
@@ -861,8 +953,8 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
|
||||
if (io->use_scaling) {
|
||||
// disable filter (only for large downscaling ratio).
|
||||
io->bypass_filtering |= (io->scaled_width < W * 3 / 4) &&
|
||||
(io->scaled_height < H * 3 / 4);
|
||||
io->bypass_filtering |=
|
||||
(io->scaled_width < W * 3 / 4) && (io->scaled_height < H * 3 / 4);
|
||||
io->fancy_upsampling = 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
@@ -18,9 +18,14 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#include "src/dec/vp8_dec.h"
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecParams: Decoding output parameters. Transient internal object.
|
||||
@@ -33,15 +38,15 @@ typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos,
|
||||
int max_out_lines);
|
||||
|
||||
struct WebPDecParams {
|
||||
WebPDecBuffer* output; // output buffer.
|
||||
uint8_t* tmp_y, *tmp_u, *tmp_v; // cache for the fancy upsampler
|
||||
// or used for tmp rescaling
|
||||
WebPDecBuffer* output; // output buffer.
|
||||
uint8_t *tmp_y, *tmp_u, *tmp_v; // cache for the fancy upsampler
|
||||
// or used for tmp rescaling
|
||||
|
||||
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
|
||||
|
||||
WebPRescaler* scaler_y, *scaler_u, *scaler_v, *scaler_a; // rescalers
|
||||
void* memory; // overall scratch memory for the output work.
|
||||
WebPRescaler *scaler_y, *scaler_u, *scaler_v, *scaler_a; // rescalers
|
||||
void* memory; // overall scratch memory for the output work.
|
||||
|
||||
OutputFunc emit; // output RGB or YUV samples
|
||||
OutputAlphaFunc emit_alpha; // output alpha channel
|
||||
@@ -56,15 +61,16 @@ void WebPResetDecParams(WebPDecParams* const params);
|
||||
|
||||
// Structure storing a description of the RIFF headers.
|
||||
typedef struct {
|
||||
const uint8_t* data; // input buffer
|
||||
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)
|
||||
const uint8_t* alpha_data; // points to alpha chunk (if present)
|
||||
size_t alpha_data_size; // alpha chunk size
|
||||
size_t compressed_size; // VP8/VP8L compressed data size
|
||||
size_t riff_size; // size of the riff payload (or 0 if absent)
|
||||
int is_lossless; // true if a VP8L chunk is present
|
||||
const uint8_t* WEBP_COUNTED_BY(data_size) data; // input buffer
|
||||
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)
|
||||
const uint8_t* WEBP_COUNTED_BY(alpha_data_size)
|
||||
alpha_data; // points to alpha chunk (if present)
|
||||
size_t alpha_data_size; // alpha chunk size
|
||||
size_t compressed_size; // VP8/VP8L compressed data size
|
||||
size_t riff_size; // size of the riff payload (or 0 if absent)
|
||||
int is_lossless; // true if a VP8L chunk is present
|
||||
} WebPHeaderStructure;
|
||||
|
||||
// Skips over all valid chunks prior to the first VP8/VP8L frame header.
|
||||
@@ -79,8 +85,8 @@ VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers);
|
||||
// Misc utils
|
||||
|
||||
// Returns true if crop dimensions are within image bounds.
|
||||
int WebPCheckCropDimensions(int image_width, int image_height,
|
||||
int x, int y, int w, int h);
|
||||
int WebPCheckCropDimensions(int image_width, int image_height, int x, int y,
|
||||
int w, int h);
|
||||
|
||||
// Initializes VP8Io with custom setup, io and teardown functions. The default
|
||||
// hooks will use the supplied 'params' as io->opaque handle.
|
||||
@@ -133,7 +139,7 @@ int WebPAvoidSlowMemory(const WebPDecBuffer* const output,
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_DEC_WEBPI_DEC_H_
|
||||
|
||||
@@ -13,6 +13,6 @@ noinst_HEADERS =
|
||||
noinst_HEADERS += ../webp/format_constants.h
|
||||
|
||||
libwebpdemux_la_LIBADD = ../libwebp.la
|
||||
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:15:0
|
||||
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:17:0
|
||||
libwebpdemuxincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebpdemux.pc
|
||||
|
||||
@@ -20,8 +20,12 @@
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/demux.h"
|
||||
#include "src/webp/mux.h"
|
||||
#include "src/webp/mux_types.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
#define NUM_CHANNELS 4
|
||||
|
||||
// Channel extraction from a uint32_t representation of a uint8_t RGBA/BGRA
|
||||
@@ -39,19 +43,19 @@ static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
|
||||
int num_pixels);
|
||||
|
||||
struct WebPAnimDecoder {
|
||||
WebPDemuxer* demux_; // Demuxer created from given WebP bitstream.
|
||||
WebPDecoderConfig config_; // Decoder config.
|
||||
WebPDemuxer* demux; // Demuxer created from given WebP bitstream.
|
||||
WebPDecoderConfig config; // Decoder config.
|
||||
// Note: we use a pointer to a function blending multiple pixels at a time to
|
||||
// allow possible inlining of per-pixel blending function.
|
||||
BlendRowFunc blend_func_; // Pointer to the chose blend row function.
|
||||
WebPAnimInfo info_; // Global info about the animation.
|
||||
uint8_t* curr_frame_; // Current canvas (not disposed).
|
||||
uint8_t* prev_frame_disposed_; // Previous canvas (properly disposed).
|
||||
int prev_frame_timestamp_; // Previous frame timestamp (milliseconds).
|
||||
WebPIterator prev_iter_; // Iterator object for previous frame.
|
||||
int prev_frame_was_keyframe_; // True if previous frame was a keyframe.
|
||||
int next_frame_; // Index of the next frame to be decoded
|
||||
// (starting from 1).
|
||||
BlendRowFunc blend_func; // Pointer to the chose blend row function.
|
||||
WebPAnimInfo info; // Global info about the animation.
|
||||
uint8_t* curr_frame; // Current canvas (not disposed).
|
||||
uint8_t* prev_frame_disposed; // Previous canvas (properly disposed).
|
||||
int prev_frame_timestamp; // Previous frame timestamp (milliseconds).
|
||||
WebPIterator prev_iter; // Iterator object for previous frame.
|
||||
int prev_frame_was_keyframe; // True if previous frame was a keyframe.
|
||||
int next_frame; // Index of the next frame to be decoded
|
||||
// (starting from 1).
|
||||
};
|
||||
|
||||
static void DefaultDecoderOptions(WebPAnimDecoderOptions* const dec_options) {
|
||||
@@ -73,17 +77,17 @@ WEBP_NODISCARD static int ApplyDecoderOptions(
|
||||
const WebPAnimDecoderOptions* const dec_options,
|
||||
WebPAnimDecoder* const dec) {
|
||||
WEBP_CSP_MODE mode;
|
||||
WebPDecoderConfig* config = &dec->config_;
|
||||
WebPDecoderConfig* config = &dec->config;
|
||||
assert(dec_options != NULL);
|
||||
|
||||
mode = dec_options->color_mode;
|
||||
if (mode != MODE_RGBA && mode != MODE_BGRA &&
|
||||
mode != MODE_rgbA && mode != MODE_bgrA) {
|
||||
if (mode != MODE_RGBA && mode != MODE_BGRA && mode != MODE_rgbA &&
|
||||
mode != MODE_bgrA) {
|
||||
return 0;
|
||||
}
|
||||
dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA)
|
||||
? &BlendPixelRowNonPremult
|
||||
: &BlendPixelRowPremult;
|
||||
dec->blend_func = (mode == MODE_RGBA || mode == MODE_BGRA)
|
||||
? &BlendPixelRowNonPremult
|
||||
: &BlendPixelRowPremult;
|
||||
if (!WebPInitDecoderConfig(config)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -123,34 +127,34 @@ WebPAnimDecoder* WebPAnimDecoderNewInternal(
|
||||
}
|
||||
if (!ApplyDecoderOptions(&options, dec)) goto Error;
|
||||
|
||||
dec->demux_ = WebPDemux(webp_data);
|
||||
if (dec->demux_ == NULL) goto Error;
|
||||
dec->demux = WebPDemux(webp_data);
|
||||
if (dec->demux == NULL) goto Error;
|
||||
|
||||
dec->info_.canvas_width = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_WIDTH);
|
||||
dec->info_.canvas_height = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_HEIGHT);
|
||||
dec->info_.loop_count = WebPDemuxGetI(dec->demux_, WEBP_FF_LOOP_COUNT);
|
||||
dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR);
|
||||
dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT);
|
||||
dec->info.canvas_width = WebPDemuxGetI(dec->demux, WEBP_FF_CANVAS_WIDTH);
|
||||
dec->info.canvas_height = WebPDemuxGetI(dec->demux, WEBP_FF_CANVAS_HEIGHT);
|
||||
dec->info.loop_count = WebPDemuxGetI(dec->demux, WEBP_FF_LOOP_COUNT);
|
||||
dec->info.bgcolor = WebPDemuxGetI(dec->demux, WEBP_FF_BACKGROUND_COLOR);
|
||||
dec->info.frame_count = WebPDemuxGetI(dec->demux, WEBP_FF_FRAME_COUNT);
|
||||
|
||||
// Note: calloc() because we fill frame with zeroes as well.
|
||||
dec->curr_frame_ = (uint8_t*)WebPSafeCalloc(
|
||||
dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
|
||||
if (dec->curr_frame_ == NULL) goto Error;
|
||||
dec->prev_frame_disposed_ = (uint8_t*)WebPSafeCalloc(
|
||||
dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
|
||||
if (dec->prev_frame_disposed_ == NULL) goto Error;
|
||||
dec->curr_frame = (uint8_t*)WebPSafeCalloc(
|
||||
dec->info.canvas_width * NUM_CHANNELS, dec->info.canvas_height);
|
||||
if (dec->curr_frame == NULL) goto Error;
|
||||
dec->prev_frame_disposed = (uint8_t*)WebPSafeCalloc(
|
||||
dec->info.canvas_width * NUM_CHANNELS, dec->info.canvas_height);
|
||||
if (dec->prev_frame_disposed == NULL) goto Error;
|
||||
|
||||
WebPAnimDecoderReset(dec);
|
||||
return dec;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
WebPAnimDecoderDelete(dec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) {
|
||||
if (dec == NULL || info == NULL) return 0;
|
||||
*info = dec->info_;
|
||||
*info = dec->info;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -166,7 +170,7 @@ WEBP_NODISCARD static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
|
||||
const uint64_t size =
|
||||
(uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf);
|
||||
if (!CheckSizeOverflow(size)) return 0;
|
||||
memset(buf, 0, (size_t)size);
|
||||
WEBP_UNSAFE_MEMSET(buf, 0, (size_t)size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -177,7 +181,7 @@ static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset,
|
||||
assert(width * NUM_CHANNELS <= buf_stride);
|
||||
buf += y_offset * buf_stride + x_offset * NUM_CHANNELS;
|
||||
for (j = 0; j < height; ++j) {
|
||||
memset(buf, 0, width * NUM_CHANNELS);
|
||||
WEBP_UNSAFE_MEMSET(buf, 0, width * NUM_CHANNELS);
|
||||
buf += buf_stride;
|
||||
}
|
||||
}
|
||||
@@ -188,20 +192,20 @@ WEBP_NODISCARD static int CopyCanvas(const uint8_t* src, uint8_t* dst,
|
||||
const uint64_t size = (uint64_t)width * height * NUM_CHANNELS;
|
||||
if (!CheckSizeOverflow(size)) return 0;
|
||||
assert(src != NULL && dst != NULL);
|
||||
memcpy(dst, src, (size_t)size);
|
||||
WEBP_UNSAFE_MEMCPY(dst, src, (size_t)size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns true if the current frame is a key-frame.
|
||||
static int IsKeyFrame(const WebPIterator* const curr,
|
||||
const WebPIterator* const prev,
|
||||
int prev_frame_was_key_frame,
|
||||
int canvas_width, int canvas_height) {
|
||||
int prev_frame_was_key_frame, int canvas_width,
|
||||
int canvas_height) {
|
||||
if (curr->frame_num == 1) {
|
||||
return 1;
|
||||
} else if ((!curr->has_alpha || curr->blend_method == WEBP_MUX_NO_BLEND) &&
|
||||
IsFullFrame(curr->width, curr->height,
|
||||
canvas_width, canvas_height)) {
|
||||
IsFullFrame(curr->width, curr->height, canvas_width,
|
||||
canvas_height)) {
|
||||
return 1;
|
||||
} else {
|
||||
return (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
|
||||
@@ -211,12 +215,11 @@ static int IsKeyFrame(const WebPIterator* const curr,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Blend a single channel of 'src' over 'dst', given their alpha channel values.
|
||||
// 'src' and 'dst' are assumed to be NOT pre-multiplied by alpha.
|
||||
static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a,
|
||||
uint32_t dst, uint8_t dst_a,
|
||||
uint32_t scale, int shift) {
|
||||
static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a, uint32_t dst,
|
||||
uint8_t dst_a, uint32_t scale,
|
||||
int shift) {
|
||||
const uint8_t src_channel = (src >> shift) & 0xff;
|
||||
const uint8_t dst_channel = (dst >> shift) & 0xff;
|
||||
const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a;
|
||||
@@ -326,8 +329,8 @@ static void FindBlendRangeAtRow(const WebPIterator* const src,
|
||||
}
|
||||
}
|
||||
|
||||
int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
uint8_t** buf_ptr, int* timestamp_ptr) {
|
||||
int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, uint8_t** buf_ptr,
|
||||
int* timestamp_ptr) {
|
||||
WebPIterator iter;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
@@ -338,26 +341,25 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
if (dec == NULL || buf_ptr == NULL || timestamp_ptr == NULL) return 0;
|
||||
if (!WebPAnimDecoderHasMoreFrames(dec)) return 0;
|
||||
|
||||
width = dec->info_.canvas_width;
|
||||
height = dec->info_.canvas_height;
|
||||
blend_row = dec->blend_func_;
|
||||
width = dec->info.canvas_width;
|
||||
height = dec->info.canvas_height;
|
||||
blend_row = dec->blend_func;
|
||||
|
||||
// Get compressed frame.
|
||||
if (!WebPDemuxGetFrame(dec->demux_, dec->next_frame_, &iter)) {
|
||||
if (!WebPDemuxGetFrame(dec->demux, dec->next_frame, &iter)) {
|
||||
return 0;
|
||||
}
|
||||
timestamp = dec->prev_frame_timestamp_ + iter.duration;
|
||||
timestamp = dec->prev_frame_timestamp + iter.duration;
|
||||
|
||||
// Initialize.
|
||||
is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_,
|
||||
dec->prev_frame_was_keyframe_, width, height);
|
||||
is_key_frame = IsKeyFrame(&iter, &dec->prev_iter,
|
||||
dec->prev_frame_was_keyframe, width, height);
|
||||
if (is_key_frame) {
|
||||
if (!ZeroFillCanvas(dec->curr_frame_, width, height)) {
|
||||
if (!ZeroFillCanvas(dec->curr_frame, width, height)) {
|
||||
goto Error;
|
||||
}
|
||||
} else {
|
||||
if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_,
|
||||
width, height)) {
|
||||
if (!CopyCanvas(dec->prev_frame_disposed, dec->curr_frame, width, height)) {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
@@ -370,12 +372,12 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
const uint64_t out_offset = (uint64_t)iter.y_offset * stride +
|
||||
(uint64_t)iter.x_offset * NUM_CHANNELS; // 53b
|
||||
const uint64_t size = (uint64_t)iter.height * stride; // at most 25 + 27b
|
||||
WebPDecoderConfig* const config = &dec->config_;
|
||||
WebPDecoderConfig* const config = &dec->config;
|
||||
WebPRGBABuffer* const buf = &config->output.u.RGBA;
|
||||
if ((size_t)size != size) goto Error;
|
||||
buf->stride = (int)stride;
|
||||
buf->size = (size_t)size;
|
||||
buf->rgba = dec->curr_frame_ + out_offset;
|
||||
buf->rgba = dec->curr_frame + out_offset;
|
||||
|
||||
if (WebPDecode(in, in_size, config) != VP8_STATUS_OK) {
|
||||
goto Error;
|
||||
@@ -388,18 +390,17 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
// that pixel in the previous frame if blending method of is WEBP_MUX_BLEND.
|
||||
if (iter.frame_num > 1 && iter.blend_method == WEBP_MUX_BLEND &&
|
||||
!is_key_frame) {
|
||||
if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_NONE) {
|
||||
if (dec->prev_iter.dispose_method == WEBP_MUX_DISPOSE_NONE) {
|
||||
int y;
|
||||
// Blend transparent pixels with pixels in previous canvas.
|
||||
for (y = 0; y < iter.height; ++y) {
|
||||
const size_t offset =
|
||||
(iter.y_offset + y) * width + iter.x_offset;
|
||||
blend_row((uint32_t*)dec->curr_frame_ + offset,
|
||||
(uint32_t*)dec->prev_frame_disposed_ + offset, iter.width);
|
||||
const size_t offset = (iter.y_offset + y) * width + iter.x_offset;
|
||||
blend_row((uint32_t*)dec->curr_frame + offset,
|
||||
(uint32_t*)dec->prev_frame_disposed + offset, iter.width);
|
||||
}
|
||||
} else {
|
||||
int y;
|
||||
assert(dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND);
|
||||
assert(dec->prev_iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND);
|
||||
// We need to blend a transparent pixel with its value just after
|
||||
// initialization. That is, blend it with:
|
||||
// * Fully transparent pixel if it belongs to prevRect <-- No-op.
|
||||
@@ -407,73 +408,73 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
for (y = 0; y < iter.height; ++y) {
|
||||
const int canvas_y = iter.y_offset + y;
|
||||
int left1, width1, left2, width2;
|
||||
FindBlendRangeAtRow(&iter, &dec->prev_iter_, canvas_y, &left1, &width1,
|
||||
FindBlendRangeAtRow(&iter, &dec->prev_iter, canvas_y, &left1, &width1,
|
||||
&left2, &width2);
|
||||
if (width1 > 0) {
|
||||
const size_t offset1 = canvas_y * width + left1;
|
||||
blend_row((uint32_t*)dec->curr_frame_ + offset1,
|
||||
(uint32_t*)dec->prev_frame_disposed_ + offset1, width1);
|
||||
blend_row((uint32_t*)dec->curr_frame + offset1,
|
||||
(uint32_t*)dec->prev_frame_disposed + offset1, width1);
|
||||
}
|
||||
if (width2 > 0) {
|
||||
const size_t offset2 = canvas_y * width + left2;
|
||||
blend_row((uint32_t*)dec->curr_frame_ + offset2,
|
||||
(uint32_t*)dec->prev_frame_disposed_ + offset2, width2);
|
||||
blend_row((uint32_t*)dec->curr_frame + offset2,
|
||||
(uint32_t*)dec->prev_frame_disposed + offset2, width2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update info of the previous frame and dispose it for the next iteration.
|
||||
dec->prev_frame_timestamp_ = timestamp;
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||
dec->prev_iter_ = iter;
|
||||
dec->prev_frame_was_keyframe_ = is_key_frame;
|
||||
if (!CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height)) {
|
||||
dec->prev_frame_timestamp = timestamp;
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter);
|
||||
dec->prev_iter = iter;
|
||||
dec->prev_frame_was_keyframe = is_key_frame;
|
||||
if (!CopyCanvas(dec->curr_frame, dec->prev_frame_disposed, width, height)) {
|
||||
goto Error;
|
||||
}
|
||||
if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||
ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS,
|
||||
dec->prev_iter_.x_offset, dec->prev_iter_.y_offset,
|
||||
dec->prev_iter_.width, dec->prev_iter_.height);
|
||||
if (dec->prev_iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||
ZeroFillFrameRect(dec->prev_frame_disposed, width * NUM_CHANNELS,
|
||||
dec->prev_iter.x_offset, dec->prev_iter.y_offset,
|
||||
dec->prev_iter.width, dec->prev_iter.height);
|
||||
}
|
||||
++dec->next_frame_;
|
||||
++dec->next_frame;
|
||||
|
||||
// All OK, fill in the values.
|
||||
*buf_ptr = dec->curr_frame_;
|
||||
*buf_ptr = dec->curr_frame;
|
||||
*timestamp_ptr = timestamp;
|
||||
return 1;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) {
|
||||
if (dec == NULL) return 0;
|
||||
return (dec->next_frame_ <= (int)dec->info_.frame_count);
|
||||
return (dec->next_frame <= (int)dec->info.frame_count);
|
||||
}
|
||||
|
||||
void WebPAnimDecoderReset(WebPAnimDecoder* dec) {
|
||||
if (dec != NULL) {
|
||||
dec->prev_frame_timestamp_ = 0;
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||
memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_));
|
||||
dec->prev_frame_was_keyframe_ = 0;
|
||||
dec->next_frame_ = 1;
|
||||
dec->prev_frame_timestamp = 0;
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter);
|
||||
WEBP_UNSAFE_MEMSET(&dec->prev_iter, 0, sizeof(dec->prev_iter));
|
||||
dec->prev_frame_was_keyframe = 0;
|
||||
dec->next_frame = 1;
|
||||
}
|
||||
}
|
||||
|
||||
const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) {
|
||||
if (dec == NULL) return NULL;
|
||||
return dec->demux_;
|
||||
return dec->demux;
|
||||
}
|
||||
|
||||
void WebPAnimDecoderDelete(WebPAnimDecoder* dec) {
|
||||
if (dec != NULL) {
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||
WebPDemuxDelete(dec->demux_);
|
||||
WebPSafeFree(dec->curr_frame_);
|
||||
WebPSafeFree(dec->prev_frame_disposed_);
|
||||
WebPDemuxReleaseIterator(&dec->prev_iter);
|
||||
WebPDemuxDelete(dec->demux);
|
||||
WebPSafeFree(dec->curr_frame);
|
||||
WebPSafeFree(dec->prev_frame_disposed);
|
||||
WebPSafeFree(dec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,65 +19,66 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/decode.h" // WebPGetFeatures
|
||||
#include "src/webp/decode.h" // WebPGetFeatures
|
||||
#include "src/webp/demux.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
#include "src/webp/mux.h"
|
||||
#include "src/webp/mux_types.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
|
||||
|
||||
#define DMUX_MAJ_VERSION 1
|
||||
#define DMUX_MIN_VERSION 4
|
||||
#define DMUX_MIN_VERSION 6
|
||||
#define DMUX_REV_VERSION 0
|
||||
|
||||
typedef struct {
|
||||
size_t start_; // start location of the data
|
||||
size_t end_; // end location
|
||||
size_t riff_end_; // riff chunk end location, can be > end_.
|
||||
size_t buf_size_; // size of the buffer
|
||||
const uint8_t* buf_;
|
||||
size_t start; // start location of the data
|
||||
size_t end; // end location
|
||||
size_t riff_end; // riff chunk end location, can be > end.
|
||||
size_t buf_size; // size of the buffer
|
||||
const uint8_t* buf;
|
||||
} MemBuffer;
|
||||
|
||||
typedef struct {
|
||||
size_t offset_;
|
||||
size_t size_;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
} ChunkData;
|
||||
|
||||
typedef struct Frame {
|
||||
int x_offset_, y_offset_;
|
||||
int width_, height_;
|
||||
int has_alpha_;
|
||||
int duration_;
|
||||
WebPMuxAnimDispose dispose_method_;
|
||||
WebPMuxAnimBlend blend_method_;
|
||||
int frame_num_;
|
||||
int complete_; // img_components_ contains a full image.
|
||||
ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
|
||||
struct Frame* next_;
|
||||
int x_offset, y_offset;
|
||||
int width, height;
|
||||
int has_alpha;
|
||||
int duration;
|
||||
WebPMuxAnimDispose dispose_method;
|
||||
WebPMuxAnimBlend blend_method;
|
||||
int frame_num;
|
||||
int complete; // img_components contains a full image.
|
||||
ChunkData img_components[2]; // 0=VP8{,L} 1=ALPH
|
||||
struct Frame* next;
|
||||
} Frame;
|
||||
|
||||
typedef struct Chunk {
|
||||
ChunkData data_;
|
||||
struct Chunk* next_;
|
||||
ChunkData data;
|
||||
struct Chunk* next;
|
||||
} Chunk;
|
||||
|
||||
struct WebPDemuxer {
|
||||
MemBuffer mem_;
|
||||
WebPDemuxState state_;
|
||||
int is_ext_format_;
|
||||
uint32_t feature_flags_;
|
||||
int canvas_width_, canvas_height_;
|
||||
int loop_count_;
|
||||
uint32_t bgcolor_;
|
||||
int num_frames_;
|
||||
Frame* frames_;
|
||||
Frame** frames_tail_;
|
||||
Chunk* chunks_; // non-image chunks
|
||||
Chunk** chunks_tail_;
|
||||
MemBuffer mem;
|
||||
WebPDemuxState state;
|
||||
int is_ext_format;
|
||||
uint32_t feature_flags;
|
||||
int canvas_width, canvas_height;
|
||||
int loop_count;
|
||||
uint32_t bgcolor;
|
||||
int num_frames;
|
||||
Frame* frames;
|
||||
Frame** frames_tail;
|
||||
Chunk* chunks; // non-image chunks
|
||||
Chunk** chunks_tail;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PARSE_OK,
|
||||
PARSE_NEED_MORE_DATA,
|
||||
PARSE_ERROR
|
||||
} ParseStatus;
|
||||
typedef enum { PARSE_OK, PARSE_NEED_MORE_DATA, PARSE_ERROR } ParseStatus;
|
||||
|
||||
typedef struct ChunkParser {
|
||||
uint8_t id[4];
|
||||
@@ -91,10 +92,10 @@ static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
|
||||
static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
|
||||
|
||||
static const ChunkParser kMasterChunks[] = {
|
||||
{ { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
|
||||
{ { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
|
||||
{ { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat },
|
||||
{ { '0', '0', '0', '0' }, NULL, NULL },
|
||||
{{'V', 'P', '8', ' '}, ParseSingleImage, IsValidSimpleFormat},
|
||||
{{'V', 'P', '8', 'L'}, ParseSingleImage, IsValidSimpleFormat},
|
||||
{{'V', 'P', '8', 'X'}, ParseVP8X, IsValidExtendedFormat},
|
||||
{{'0', '0', '0', '0'}, NULL, NULL},
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -106,66 +107,66 @@ int WebPGetDemuxVersion(void) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// MemBuffer
|
||||
|
||||
static int RemapMemBuffer(MemBuffer* const mem,
|
||||
const uint8_t* data, size_t size) {
|
||||
if (size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
|
||||
static int RemapMemBuffer(MemBuffer* const mem, const uint8_t* data,
|
||||
size_t size) {
|
||||
if (size < mem->buf_size) return 0; // can't remap to a shorter buffer!
|
||||
|
||||
mem->buf_ = data;
|
||||
mem->end_ = mem->buf_size_ = size;
|
||||
mem->buf = data;
|
||||
mem->end = mem->buf_size = size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int InitMemBuffer(MemBuffer* const mem,
|
||||
const uint8_t* data, size_t size) {
|
||||
memset(mem, 0, sizeof(*mem));
|
||||
static int InitMemBuffer(MemBuffer* const mem, const uint8_t* data,
|
||||
size_t size) {
|
||||
WEBP_UNSAFE_MEMSET(mem, 0, sizeof(*mem));
|
||||
return RemapMemBuffer(mem, data, size);
|
||||
}
|
||||
|
||||
// Return the remaining data size available in 'mem'.
|
||||
static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
|
||||
return (mem->end_ - mem->start_);
|
||||
return (mem->end - mem->start);
|
||||
}
|
||||
|
||||
// Return true if 'size' exceeds the end of the RIFF chunk.
|
||||
static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
|
||||
return (size > mem->riff_end_ - mem->start_);
|
||||
return (size > mem->riff_end - mem->start);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
|
||||
mem->start_ += size;
|
||||
mem->start += size;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
|
||||
mem->start_ -= size;
|
||||
mem->start -= size;
|
||||
}
|
||||
|
||||
static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
|
||||
return mem->buf_ + mem->start_;
|
||||
return mem->buf + mem->start;
|
||||
}
|
||||
|
||||
// Read from 'mem' and skip the read bytes.
|
||||
static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
|
||||
const uint8_t byte = mem->buf_[mem->start_];
|
||||
const uint8_t byte = mem->buf[mem->start];
|
||||
Skip(mem, 1);
|
||||
return byte;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {
|
||||
const uint8_t* const data = mem->buf_ + mem->start_;
|
||||
const uint8_t* const data = mem->buf + mem->start;
|
||||
const int val = GetLE16(data);
|
||||
Skip(mem, 2);
|
||||
return val;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {
|
||||
const uint8_t* const data = mem->buf_ + mem->start_;
|
||||
const uint8_t* const data = mem->buf + mem->start;
|
||||
const int val = GetLE24(data);
|
||||
Skip(mem, 3);
|
||||
return val;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
|
||||
const uint8_t* const data = mem->buf_ + mem->start_;
|
||||
const uint8_t* const data = mem->buf + mem->start;
|
||||
const uint32_t val = GetLE32(data);
|
||||
Skip(mem, 4);
|
||||
return val;
|
||||
@@ -175,34 +176,34 @@ static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
|
||||
// Secondary chunk parsing
|
||||
|
||||
static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
|
||||
*dmux->chunks_tail_ = chunk;
|
||||
chunk->next_ = NULL;
|
||||
dmux->chunks_tail_ = &chunk->next_;
|
||||
*dmux->chunks_tail = chunk;
|
||||
chunk->next = NULL;
|
||||
dmux->chunks_tail = &chunk->next;
|
||||
}
|
||||
|
||||
// Add a frame to the end of the list, ensuring the last frame is complete.
|
||||
// Returns true on success, false otherwise.
|
||||
static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
|
||||
const Frame* const last_frame = *dmux->frames_tail_;
|
||||
if (last_frame != NULL && !last_frame->complete_) return 0;
|
||||
const Frame* const last_frame = *dmux->frames_tail;
|
||||
if (last_frame != NULL && !last_frame->complete) return 0;
|
||||
|
||||
*dmux->frames_tail_ = frame;
|
||||
frame->next_ = NULL;
|
||||
dmux->frames_tail_ = &frame->next_;
|
||||
*dmux->frames_tail = frame;
|
||||
frame->next = NULL;
|
||||
dmux->frames_tail = &frame->next;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void SetFrameInfo(size_t start_offset, size_t size,
|
||||
int frame_num, int complete,
|
||||
static void SetFrameInfo(size_t start_offset, size_t size, int frame_num,
|
||||
int complete,
|
||||
const WebPBitstreamFeatures* const features,
|
||||
Frame* const frame) {
|
||||
frame->img_components_[0].offset_ = start_offset;
|
||||
frame->img_components_[0].size_ = size;
|
||||
frame->width_ = features->width;
|
||||
frame->height_ = features->height;
|
||||
frame->has_alpha_ |= features->has_alpha;
|
||||
frame->frame_num_ = frame_num;
|
||||
frame->complete_ = complete;
|
||||
frame->img_components[0].offset = start_offset;
|
||||
frame->img_components[0].size = size;
|
||||
frame->width = features->width;
|
||||
frame->height = features->height;
|
||||
frame->has_alpha |= features->has_alpha;
|
||||
frame->frame_num = frame_num;
|
||||
frame->complete = complete;
|
||||
}
|
||||
|
||||
// Store image bearing chunks to 'frame'. 'min_size' is an optional size
|
||||
@@ -211,14 +212,14 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
MemBuffer* const mem, Frame* const frame) {
|
||||
int alpha_chunks = 0;
|
||||
int image_chunks = 0;
|
||||
int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE ||
|
||||
MemDataSize(mem) < min_size);
|
||||
int done =
|
||||
(MemDataSize(mem) < CHUNK_HEADER_SIZE || MemDataSize(mem) < min_size);
|
||||
ParseStatus status = PARSE_OK;
|
||||
|
||||
if (done) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
do {
|
||||
const size_t chunk_start_offset = mem->start_;
|
||||
const size_t chunk_start_offset = mem->start;
|
||||
const uint32_t fourcc = ReadLE32(mem);
|
||||
const uint32_t payload_size = ReadLE32(mem);
|
||||
uint32_t payload_size_padded;
|
||||
@@ -229,7 +230,8 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
|
||||
payload_size_padded = payload_size + (payload_size & 1);
|
||||
payload_available = (payload_size_padded > MemDataSize(mem))
|
||||
? MemDataSize(mem) : payload_size_padded;
|
||||
? MemDataSize(mem)
|
||||
: payload_size_padded;
|
||||
chunk_size = CHUNK_HEADER_SIZE + payload_available;
|
||||
if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
|
||||
if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
|
||||
@@ -238,10 +240,10 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
case MKFOURCC('A', 'L', 'P', 'H'):
|
||||
if (alpha_chunks == 0) {
|
||||
++alpha_chunks;
|
||||
frame->img_components_[1].offset_ = chunk_start_offset;
|
||||
frame->img_components_[1].size_ = chunk_size;
|
||||
frame->has_alpha_ = 1;
|
||||
frame->frame_num_ = frame_num;
|
||||
frame->img_components[1].offset = chunk_start_offset;
|
||||
frame->img_components[1].size = chunk_size;
|
||||
frame->has_alpha = 1;
|
||||
frame->frame_num = frame_num;
|
||||
Skip(mem, payload_available);
|
||||
} else {
|
||||
goto Done;
|
||||
@@ -255,9 +257,8 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
// Extract the bitstream features, tolerating failures when the data
|
||||
// is incomplete.
|
||||
WebPBitstreamFeatures features;
|
||||
const VP8StatusCode vp8_status =
|
||||
WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
|
||||
&features);
|
||||
const VP8StatusCode vp8_status = WebPGetFeatures(
|
||||
mem->buf + chunk_start_offset, chunk_size, &features);
|
||||
if (status == PARSE_NEED_MORE_DATA &&
|
||||
vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return PARSE_NEED_MORE_DATA;
|
||||
@@ -273,7 +274,7 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
goto Done;
|
||||
}
|
||||
break;
|
||||
Done:
|
||||
Done:
|
||||
default:
|
||||
// Restore fourcc/size when moving up one level in parsing.
|
||||
Rewind(mem, CHUNK_HEADER_SIZE);
|
||||
@@ -281,7 +282,7 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
break;
|
||||
}
|
||||
|
||||
if (mem->start_ == mem->riff_end_) {
|
||||
if (mem->start == mem->riff_end) {
|
||||
done = 1;
|
||||
} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
|
||||
status = PARSE_NEED_MORE_DATA;
|
||||
@@ -295,12 +296,11 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
// enough data ('min_size') to parse the payload.
|
||||
// Returns PARSE_OK on success with *frame pointing to the new Frame.
|
||||
// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
|
||||
static ParseStatus NewFrame(const MemBuffer* const mem,
|
||||
uint32_t min_size, uint32_t actual_size,
|
||||
Frame** frame) {
|
||||
static ParseStatus NewFrame(const MemBuffer* const mem, uint32_t min_size,
|
||||
uint32_t actual_size, Frame** frame) {
|
||||
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
|
||||
if (actual_size < min_size) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
|
||||
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
*frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame));
|
||||
return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
|
||||
@@ -308,44 +308,43 @@ static ParseStatus NewFrame(const MemBuffer* const mem,
|
||||
|
||||
// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
|
||||
// 'frame_chunk_size' is the previously validated, padded chunk size.
|
||||
static ParseStatus ParseAnimationFrame(
|
||||
WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
|
||||
const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||
static ParseStatus ParseAnimationFrame(WebPDemuxer* const dmux,
|
||||
uint32_t frame_chunk_size) {
|
||||
const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);
|
||||
const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
|
||||
int added_frame = 0;
|
||||
int bits;
|
||||
MemBuffer* const mem = &dmux->mem_;
|
||||
MemBuffer* const mem = &dmux->mem;
|
||||
Frame* frame;
|
||||
size_t start_offset;
|
||||
ParseStatus status =
|
||||
NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
|
||||
ParseStatus status = NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
|
||||
if (status != PARSE_OK) return status;
|
||||
|
||||
frame->x_offset_ = 2 * ReadLE24s(mem);
|
||||
frame->y_offset_ = 2 * ReadLE24s(mem);
|
||||
frame->width_ = 1 + ReadLE24s(mem);
|
||||
frame->height_ = 1 + ReadLE24s(mem);
|
||||
frame->duration_ = ReadLE24s(mem);
|
||||
frame->x_offset = 2 * ReadLE24s(mem);
|
||||
frame->y_offset = 2 * ReadLE24s(mem);
|
||||
frame->width = 1 + ReadLE24s(mem);
|
||||
frame->height = 1 + ReadLE24s(mem);
|
||||
frame->duration = ReadLE24s(mem);
|
||||
bits = ReadByte(mem);
|
||||
frame->dispose_method_ =
|
||||
frame->dispose_method =
|
||||
(bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
|
||||
frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
|
||||
if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
|
||||
frame->blend_method = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
|
||||
if (frame->width * (uint64_t)frame->height >= MAX_IMAGE_AREA) {
|
||||
WebPSafeFree(frame);
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
// Store a frame only if the animation flag is set there is some data for
|
||||
// this frame is available.
|
||||
start_offset = mem->start_;
|
||||
status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame);
|
||||
if (status != PARSE_ERROR && mem->start_ - start_offset > anmf_payload_size) {
|
||||
start_offset = mem->start;
|
||||
status = StoreFrame(dmux->num_frames + 1, anmf_payload_size, mem, frame);
|
||||
if (status != PARSE_ERROR && mem->start - start_offset > anmf_payload_size) {
|
||||
status = PARSE_ERROR;
|
||||
}
|
||||
if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) {
|
||||
if (status != PARSE_ERROR && is_animation && frame->frame_num > 0) {
|
||||
added_frame = AddFrame(dmux, frame);
|
||||
if (added_frame) {
|
||||
++dmux->num_frames_;
|
||||
++dmux->num_frames;
|
||||
} else {
|
||||
status = PARSE_ERROR;
|
||||
}
|
||||
@@ -359,13 +358,13 @@ static ParseStatus ParseAnimationFrame(
|
||||
// the user to request the payload via a fourcc string. 'size' includes the
|
||||
// header and the unpadded payload size.
|
||||
// Returns true on success, false otherwise.
|
||||
static int StoreChunk(WebPDemuxer* const dmux,
|
||||
size_t start_offset, uint32_t size) {
|
||||
static int StoreChunk(WebPDemuxer* const dmux, size_t start_offset,
|
||||
uint32_t size) {
|
||||
Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk));
|
||||
if (chunk == NULL) return 0;
|
||||
|
||||
chunk->data_.offset_ = start_offset;
|
||||
chunk->data_.size_ = size;
|
||||
chunk->data.offset = start_offset;
|
||||
chunk->data.size = size;
|
||||
AddChunk(dmux, chunk);
|
||||
return 1;
|
||||
}
|
||||
@@ -389,9 +388,9 @@ static ParseStatus ReadHeader(MemBuffer* const mem) {
|
||||
if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
|
||||
|
||||
// There's no point in reading past the end of the RIFF chunk
|
||||
mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
|
||||
if (mem->buf_size_ > mem->riff_end_) {
|
||||
mem->buf_size_ = mem->end_ = mem->riff_end_;
|
||||
mem->riff_end = riff_size + CHUNK_HEADER_SIZE;
|
||||
if (mem->buf_size > mem->riff_end) {
|
||||
mem->buf_size = mem->end = mem->riff_end;
|
||||
}
|
||||
|
||||
Skip(mem, RIFF_HEADER_SIZE);
|
||||
@@ -400,12 +399,12 @@ static ParseStatus ReadHeader(MemBuffer* const mem) {
|
||||
|
||||
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
|
||||
const size_t min_size = CHUNK_HEADER_SIZE;
|
||||
MemBuffer* const mem = &dmux->mem_;
|
||||
MemBuffer* const mem = &dmux->mem;
|
||||
Frame* frame;
|
||||
ParseStatus status;
|
||||
int image_added = 0;
|
||||
|
||||
if (dmux->frames_ != NULL) return PARSE_ERROR;
|
||||
if (dmux->frames != NULL) return PARSE_ERROR;
|
||||
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
@@ -414,29 +413,29 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
|
||||
|
||||
// For the single image case we allow parsing of a partial frame, so no
|
||||
// minimum size is imposed here.
|
||||
status = StoreFrame(1, 0, &dmux->mem_, frame);
|
||||
status = StoreFrame(1, 0, &dmux->mem, frame);
|
||||
if (status != PARSE_ERROR) {
|
||||
const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
|
||||
const int has_alpha = !!(dmux->feature_flags & ALPHA_FLAG);
|
||||
// Clear any alpha when the alpha flag is missing.
|
||||
if (!has_alpha && frame->img_components_[1].size_ > 0) {
|
||||
frame->img_components_[1].offset_ = 0;
|
||||
frame->img_components_[1].size_ = 0;
|
||||
frame->has_alpha_ = 0;
|
||||
if (!has_alpha && frame->img_components[1].size > 0) {
|
||||
frame->img_components[1].offset = 0;
|
||||
frame->img_components[1].size = 0;
|
||||
frame->has_alpha = 0;
|
||||
}
|
||||
|
||||
// Use the frame width/height as the canvas values for non-vp8x files.
|
||||
// Also, set ALPHA_FLAG if this is a lossless image with alpha.
|
||||
if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
|
||||
dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
|
||||
dmux->canvas_width_ = frame->width_;
|
||||
dmux->canvas_height_ = frame->height_;
|
||||
dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
|
||||
if (!dmux->is_ext_format && frame->width > 0 && frame->height > 0) {
|
||||
dmux->state = WEBP_DEMUX_PARSED_HEADER;
|
||||
dmux->canvas_width = frame->width;
|
||||
dmux->canvas_height = frame->height;
|
||||
dmux->feature_flags |= frame->has_alpha ? ALPHA_FLAG : 0;
|
||||
}
|
||||
if (!AddFrame(dmux, frame)) {
|
||||
status = PARSE_ERROR; // last frame was left incomplete
|
||||
} else {
|
||||
image_added = 1;
|
||||
dmux->num_frames_ = 1;
|
||||
dmux->num_frames = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,14 +444,14 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
|
||||
}
|
||||
|
||||
static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
|
||||
const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||
MemBuffer* const mem = &dmux->mem_;
|
||||
const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);
|
||||
MemBuffer* const mem = &dmux->mem;
|
||||
int anim_chunks = 0;
|
||||
ParseStatus status = PARSE_OK;
|
||||
|
||||
do {
|
||||
int store_chunk = 1;
|
||||
const size_t chunk_start_offset = mem->start_;
|
||||
const size_t chunk_start_offset = mem->start;
|
||||
const uint32_t fourcc = ReadLE32(mem);
|
||||
const uint32_t chunk_size = ReadLE32(mem);
|
||||
uint32_t chunk_size_padded;
|
||||
@@ -483,8 +482,8 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
|
||||
status = PARSE_NEED_MORE_DATA;
|
||||
} else if (anim_chunks == 0) {
|
||||
++anim_chunks;
|
||||
dmux->bgcolor_ = ReadLE32(mem);
|
||||
dmux->loop_count_ = ReadLE16s(mem);
|
||||
dmux->bgcolor = ReadLE32(mem);
|
||||
dmux->loop_count = ReadLE16s(mem);
|
||||
Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
|
||||
} else {
|
||||
store_chunk = 0;
|
||||
@@ -498,18 +497,18 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
|
||||
break;
|
||||
}
|
||||
case MKFOURCC('I', 'C', 'C', 'P'): {
|
||||
store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
|
||||
store_chunk = !!(dmux->feature_flags & ICCP_FLAG);
|
||||
goto Skip;
|
||||
}
|
||||
case MKFOURCC('E', 'X', 'I', 'F'): {
|
||||
store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
|
||||
store_chunk = !!(dmux->feature_flags & EXIF_FLAG);
|
||||
goto Skip;
|
||||
}
|
||||
case MKFOURCC('X', 'M', 'P', ' '): {
|
||||
store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
|
||||
store_chunk = !!(dmux->feature_flags & XMP_FLAG);
|
||||
goto Skip;
|
||||
}
|
||||
Skip:
|
||||
Skip:
|
||||
default: {
|
||||
if (chunk_size_padded <= MemDataSize(mem)) {
|
||||
if (store_chunk) {
|
||||
@@ -527,7 +526,7 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
|
||||
}
|
||||
}
|
||||
|
||||
if (mem->start_ == mem->riff_end_) {
|
||||
if (mem->start == mem->riff_end) {
|
||||
break;
|
||||
} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
|
||||
status = PARSE_NEED_MORE_DATA;
|
||||
@@ -538,12 +537,12 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
|
||||
}
|
||||
|
||||
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||
MemBuffer* const mem = &dmux->mem_;
|
||||
MemBuffer* const mem = &dmux->mem;
|
||||
uint32_t vp8x_size;
|
||||
|
||||
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
dmux->is_ext_format_ = 1;
|
||||
dmux->is_ext_format = 1;
|
||||
Skip(mem, TAG_SIZE); // VP8X
|
||||
vp8x_size = ReadLE32(mem);
|
||||
if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
|
||||
@@ -552,15 +551,15 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||
if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
dmux->feature_flags_ = ReadByte(mem);
|
||||
dmux->feature_flags = ReadByte(mem);
|
||||
Skip(mem, 3); // Reserved.
|
||||
dmux->canvas_width_ = 1 + ReadLE24s(mem);
|
||||
dmux->canvas_height_ = 1 + ReadLE24s(mem);
|
||||
if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
|
||||
dmux->canvas_width = 1 + ReadLE24s(mem);
|
||||
dmux->canvas_height = 1 + ReadLE24s(mem);
|
||||
if (dmux->canvas_width * (uint64_t)dmux->canvas_height >= MAX_IMAGE_AREA) {
|
||||
return PARSE_ERROR; // image final dimension is too large
|
||||
}
|
||||
Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
|
||||
dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
|
||||
dmux->state = WEBP_DEMUX_PARSED_HEADER;
|
||||
|
||||
if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
|
||||
@@ -572,13 +571,13 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||
// Format validation
|
||||
|
||||
static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
|
||||
const Frame* const frame = dmux->frames_;
|
||||
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
|
||||
const Frame* const frame = dmux->frames;
|
||||
if (dmux->state == WEBP_DEMUX_PARSING_HEADER) return 1;
|
||||
|
||||
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
|
||||
if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
|
||||
if (dmux->canvas_width <= 0 || dmux->canvas_height <= 0) return 0;
|
||||
if (dmux->state == WEBP_DEMUX_DONE && frame == NULL) return 0;
|
||||
|
||||
if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
|
||||
if (frame->width <= 0 || frame->height <= 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -587,65 +586,65 @@ static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
|
||||
static int CheckFrameBounds(const Frame* const frame, int exact,
|
||||
int canvas_width, int canvas_height) {
|
||||
if (exact) {
|
||||
if (frame->x_offset_ != 0 || frame->y_offset_ != 0) {
|
||||
if (frame->x_offset != 0 || frame->y_offset != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (frame->width_ != canvas_width || frame->height_ != canvas_height) {
|
||||
if (frame->width != canvas_width || frame->height != canvas_height) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0;
|
||||
if (frame->width_ + frame->x_offset_ > canvas_width) return 0;
|
||||
if (frame->height_ + frame->y_offset_ > canvas_height) return 0;
|
||||
if (frame->x_offset < 0 || frame->y_offset < 0) return 0;
|
||||
if (frame->width + frame->x_offset > canvas_width) return 0;
|
||||
if (frame->height + frame->y_offset > canvas_height) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
||||
const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||
const Frame* f = dmux->frames_;
|
||||
const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);
|
||||
const Frame* f = dmux->frames;
|
||||
|
||||
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
|
||||
if (dmux->state == WEBP_DEMUX_PARSING_HEADER) return 1;
|
||||
|
||||
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
|
||||
if (dmux->loop_count_ < 0) return 0;
|
||||
if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
|
||||
if (dmux->feature_flags_ & ~ALL_VALID_FLAGS) return 0; // invalid bitstream
|
||||
if (dmux->canvas_width <= 0 || dmux->canvas_height <= 0) return 0;
|
||||
if (dmux->loop_count < 0) return 0;
|
||||
if (dmux->state == WEBP_DEMUX_DONE && dmux->frames == NULL) return 0;
|
||||
if (dmux->feature_flags & ~ALL_VALID_FLAGS) return 0; // invalid bitstream
|
||||
|
||||
while (f != NULL) {
|
||||
const int cur_frame_set = f->frame_num_;
|
||||
const int cur_frame_set = f->frame_num;
|
||||
|
||||
// Check frame properties.
|
||||
for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
|
||||
const ChunkData* const image = f->img_components_;
|
||||
const ChunkData* const alpha = f->img_components_ + 1;
|
||||
for (; f != NULL && f->frame_num == cur_frame_set; f = f->next) {
|
||||
const ChunkData* const image = f->img_components;
|
||||
const ChunkData* const alpha = f->img_components + 1;
|
||||
|
||||
if (!is_animation && f->frame_num_ > 1) return 0;
|
||||
if (!is_animation && f->frame_num > 1) return 0;
|
||||
|
||||
if (f->complete_) {
|
||||
if (alpha->size_ == 0 && image->size_ == 0) return 0;
|
||||
if (f->complete) {
|
||||
if (alpha->size == 0 && image->size == 0) return 0;
|
||||
// Ensure alpha precedes image bitstream.
|
||||
if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
|
||||
if (alpha->size > 0 && alpha->offset > image->offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (f->width_ <= 0 || f->height_ <= 0) return 0;
|
||||
if (f->width <= 0 || f->height <= 0) return 0;
|
||||
} else {
|
||||
// There shouldn't be a partial frame in a complete file.
|
||||
if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
|
||||
if (dmux->state == WEBP_DEMUX_DONE) return 0;
|
||||
|
||||
// Ensure alpha precedes image bitstream.
|
||||
if (alpha->size_ > 0 && image->size_ > 0 &&
|
||||
alpha->offset_ > image->offset_) {
|
||||
if (alpha->size > 0 && image->size > 0 &&
|
||||
alpha->offset > image->offset) {
|
||||
return 0;
|
||||
}
|
||||
// There shouldn't be any frames after an incomplete one.
|
||||
if (f->next_ != NULL) return 0;
|
||||
if (f->next != NULL) return 0;
|
||||
}
|
||||
|
||||
if (f->width_ > 0 && f->height_ > 0 &&
|
||||
!CheckFrameBounds(f, !is_animation,
|
||||
dmux->canvas_width_, dmux->canvas_height_)) {
|
||||
if (f->width > 0 && f->height > 0 &&
|
||||
!CheckFrameBounds(f, !is_animation, dmux->canvas_width,
|
||||
dmux->canvas_height)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -657,21 +656,21 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
||||
// WebPDemuxer object
|
||||
|
||||
static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
|
||||
dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
|
||||
dmux->loop_count_ = 1;
|
||||
dmux->bgcolor_ = 0xFFFFFFFF; // White background by default.
|
||||
dmux->canvas_width_ = -1;
|
||||
dmux->canvas_height_ = -1;
|
||||
dmux->frames_tail_ = &dmux->frames_;
|
||||
dmux->chunks_tail_ = &dmux->chunks_;
|
||||
dmux->mem_ = *mem;
|
||||
dmux->state = WEBP_DEMUX_PARSING_HEADER;
|
||||
dmux->loop_count = 1;
|
||||
dmux->bgcolor = 0xFFFFFFFF; // White background by default.
|
||||
dmux->canvas_width = -1;
|
||||
dmux->canvas_height = -1;
|
||||
dmux->frames_tail = &dmux->frames;
|
||||
dmux->chunks_tail = &dmux->chunks;
|
||||
dmux->mem = *mem;
|
||||
}
|
||||
|
||||
static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem,
|
||||
WebPDemuxer** demuxer) {
|
||||
WebPBitstreamFeatures features;
|
||||
const VP8StatusCode status =
|
||||
WebPGetFeatures(mem->buf_, mem->buf_size_, &features);
|
||||
WebPGetFeatures(mem->buf, mem->buf_size, &features);
|
||||
*demuxer = NULL;
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA
|
||||
@@ -683,19 +682,19 @@ static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem,
|
||||
Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
|
||||
if (dmux == NULL || frame == NULL) goto Error;
|
||||
InitDemux(dmux, mem);
|
||||
SetFrameInfo(0, mem->buf_size_, 1 /*frame_num*/, 1 /*complete*/, &features,
|
||||
SetFrameInfo(0, mem->buf_size, 1 /*frame_num*/, 1 /*complete*/, &features,
|
||||
frame);
|
||||
if (!AddFrame(dmux, frame)) goto Error;
|
||||
dmux->state_ = WEBP_DEMUX_DONE;
|
||||
dmux->canvas_width_ = frame->width_;
|
||||
dmux->canvas_height_ = frame->height_;
|
||||
dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
|
||||
dmux->num_frames_ = 1;
|
||||
dmux->state = WEBP_DEMUX_DONE;
|
||||
dmux->canvas_width = frame->width;
|
||||
dmux->canvas_height = frame->height;
|
||||
dmux->feature_flags |= frame->has_alpha ? ALPHA_FLAG : 0;
|
||||
dmux->num_frames = 1;
|
||||
assert(IsValidSimpleFormat(dmux));
|
||||
*demuxer = dmux;
|
||||
return PARSE_OK;
|
||||
|
||||
Error:
|
||||
Error:
|
||||
WebPSafeFree(dmux);
|
||||
WebPSafeFree(frame);
|
||||
return PARSE_ERROR;
|
||||
@@ -734,7 +733,7 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
partial = (mem.buf_size_ < mem.riff_end_);
|
||||
partial = (mem.buf_size < mem.riff_end);
|
||||
if (!allow_partial && partial) return NULL;
|
||||
|
||||
dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
|
||||
@@ -743,16 +742,16 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
|
||||
|
||||
status = PARSE_ERROR;
|
||||
for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
|
||||
if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
|
||||
if (!memcmp(parser->id, GetBuffer(&dmux->mem), TAG_SIZE)) {
|
||||
status = parser->parse(dmux);
|
||||
if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
|
||||
if (status == PARSE_OK) dmux->state = WEBP_DEMUX_DONE;
|
||||
if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
|
||||
if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
|
||||
if (status == PARSE_ERROR) dmux->state_ = WEBP_DEMUX_PARSE_ERROR;
|
||||
if (status == PARSE_ERROR) dmux->state = WEBP_DEMUX_PARSE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state != NULL) *state = dmux->state_;
|
||||
if (state != NULL) *state = dmux->state;
|
||||
|
||||
if (status == PARSE_ERROR) {
|
||||
WebPDemuxDelete(dmux);
|
||||
@@ -766,14 +765,14 @@ void WebPDemuxDelete(WebPDemuxer* dmux) {
|
||||
Frame* f;
|
||||
if (dmux == NULL) return;
|
||||
|
||||
for (f = dmux->frames_; f != NULL;) {
|
||||
for (f = dmux->frames; f != NULL;) {
|
||||
Frame* const cur_frame = f;
|
||||
f = f->next_;
|
||||
f = f->next;
|
||||
WebPSafeFree(cur_frame);
|
||||
}
|
||||
for (c = dmux->chunks_; c != NULL;) {
|
||||
for (c = dmux->chunks; c != NULL;) {
|
||||
Chunk* const cur_chunk = c;
|
||||
c = c->next_;
|
||||
c = c->next;
|
||||
WebPSafeFree(cur_chunk);
|
||||
}
|
||||
WebPSafeFree(dmux);
|
||||
@@ -785,12 +784,18 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
|
||||
if (dmux == NULL) return 0;
|
||||
|
||||
switch (feature) {
|
||||
case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags_;
|
||||
case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_;
|
||||
case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_;
|
||||
case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_;
|
||||
case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
|
||||
case WEBP_FF_FRAME_COUNT: return (uint32_t)dmux->num_frames_;
|
||||
case WEBP_FF_FORMAT_FLAGS:
|
||||
return dmux->feature_flags;
|
||||
case WEBP_FF_CANVAS_WIDTH:
|
||||
return (uint32_t)dmux->canvas_width;
|
||||
case WEBP_FF_CANVAS_HEIGHT:
|
||||
return (uint32_t)dmux->canvas_height;
|
||||
case WEBP_FF_LOOP_COUNT:
|
||||
return (uint32_t)dmux->loop_count;
|
||||
case WEBP_FF_BACKGROUND_COLOR:
|
||||
return dmux->bgcolor;
|
||||
case WEBP_FF_FRAME_COUNT:
|
||||
return (uint32_t)dmux->num_frames;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -800,8 +805,8 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
|
||||
|
||||
static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
|
||||
const Frame* f;
|
||||
for (f = dmux->frames_; f != NULL; f = f->next_) {
|
||||
if (frame_num == f->frame_num_) break;
|
||||
for (f = dmux->frames; f != NULL; f = f->next) {
|
||||
if (frame_num == f->frame_num) break;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
@@ -811,19 +816,19 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
|
||||
size_t* const data_size) {
|
||||
*data_size = 0;
|
||||
if (frame != NULL) {
|
||||
const ChunkData* const image = frame->img_components_;
|
||||
const ChunkData* const alpha = frame->img_components_ + 1;
|
||||
size_t start_offset = image->offset_;
|
||||
*data_size = image->size_;
|
||||
const ChunkData* const image = frame->img_components;
|
||||
const ChunkData* const alpha = frame->img_components + 1;
|
||||
size_t start_offset = image->offset;
|
||||
*data_size = image->size;
|
||||
|
||||
// if alpha exists it precedes image, update the size allowing for
|
||||
// intervening chunks.
|
||||
if (alpha->size_ > 0) {
|
||||
const size_t inter_size = (image->offset_ > 0)
|
||||
? image->offset_ - (alpha->offset_ + alpha->size_)
|
||||
if (alpha->size > 0) {
|
||||
const size_t inter_size =
|
||||
(image->offset > 0) ? image->offset - (alpha->offset + alpha->size)
|
||||
: 0;
|
||||
start_offset = alpha->offset_;
|
||||
*data_size += alpha->size_ + inter_size;
|
||||
start_offset = alpha->offset;
|
||||
*data_size += alpha->size + inter_size;
|
||||
}
|
||||
return mem_buf + start_offset;
|
||||
}
|
||||
@@ -832,27 +837,26 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
|
||||
|
||||
// Create a whole 'frame' from VP8 (+ alpha) or lossless.
|
||||
static int SynthesizeFrame(const WebPDemuxer* const dmux,
|
||||
const Frame* const frame,
|
||||
WebPIterator* const iter) {
|
||||
const uint8_t* const mem_buf = dmux->mem_.buf_;
|
||||
const Frame* const frame, WebPIterator* const iter) {
|
||||
const uint8_t* const mem_buf = dmux->mem.buf;
|
||||
size_t payload_size = 0;
|
||||
const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size);
|
||||
if (payload == NULL) return 0;
|
||||
assert(frame != NULL);
|
||||
|
||||
iter->frame_num = frame->frame_num_;
|
||||
iter->num_frames = dmux->num_frames_;
|
||||
iter->x_offset = frame->x_offset_;
|
||||
iter->y_offset = frame->y_offset_;
|
||||
iter->width = frame->width_;
|
||||
iter->height = frame->height_;
|
||||
iter->has_alpha = frame->has_alpha_;
|
||||
iter->duration = frame->duration_;
|
||||
iter->dispose_method = frame->dispose_method_;
|
||||
iter->blend_method = frame->blend_method_;
|
||||
iter->complete = frame->complete_;
|
||||
iter->frame_num = frame->frame_num;
|
||||
iter->num_frames = dmux->num_frames;
|
||||
iter->x_offset = frame->x_offset;
|
||||
iter->y_offset = frame->y_offset;
|
||||
iter->width = frame->width;
|
||||
iter->height = frame->height;
|
||||
iter->has_alpha = frame->has_alpha;
|
||||
iter->duration = frame->duration;
|
||||
iter->dispose_method = frame->dispose_method;
|
||||
iter->blend_method = frame->blend_method;
|
||||
iter->complete = frame->complete;
|
||||
iter->fragment.bytes = payload;
|
||||
iter->fragment.size = payload_size;
|
||||
iter->fragment.size = payload_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -860,8 +864,8 @@ static int SetFrame(int frame_num, WebPIterator* const iter) {
|
||||
const Frame* frame;
|
||||
const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
|
||||
if (dmux == NULL || frame_num < 0) return 0;
|
||||
if (frame_num > dmux->num_frames_) return 0;
|
||||
if (frame_num == 0) frame_num = dmux->num_frames_;
|
||||
if (frame_num > dmux->num_frames) return 0;
|
||||
if (frame_num == 0) frame_num = dmux->num_frames;
|
||||
|
||||
frame = GetFrame(dmux, frame_num);
|
||||
if (frame == NULL) return 0;
|
||||
@@ -872,7 +876,7 @@ static int SetFrame(int frame_num, WebPIterator* const iter) {
|
||||
int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
|
||||
if (iter == NULL) return 0;
|
||||
|
||||
memset(iter, 0, sizeof(*iter));
|
||||
WEBP_UNSAFE_MEMSET(iter, 0, sizeof(*iter));
|
||||
iter->private_ = (void*)dmux;
|
||||
return SetFrame(frame, iter);
|
||||
}
|
||||
@@ -888,19 +892,17 @@ int WebPDemuxPrevFrame(WebPIterator* iter) {
|
||||
return SetFrame(iter->frame_num - 1, iter);
|
||||
}
|
||||
|
||||
void WebPDemuxReleaseIterator(WebPIterator* iter) {
|
||||
(void)iter;
|
||||
}
|
||||
void WebPDemuxReleaseIterator(WebPIterator* iter) { (void)iter; }
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Chunk iteration
|
||||
|
||||
static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
|
||||
const uint8_t* const mem_buf = dmux->mem_.buf_;
|
||||
const uint8_t* const mem_buf = dmux->mem.buf;
|
||||
const Chunk* c;
|
||||
int count = 0;
|
||||
for (c = dmux->chunks_; c != NULL; c = c->next_) {
|
||||
const uint8_t* const header = mem_buf + c->data_.offset_;
|
||||
for (c = dmux->chunks; c != NULL; c = c->next) {
|
||||
const uint8_t* const header = mem_buf + c->data.offset;
|
||||
if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
|
||||
}
|
||||
return count;
|
||||
@@ -908,11 +910,11 @@ static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
|
||||
|
||||
static const Chunk* GetChunk(const WebPDemuxer* const dmux,
|
||||
const char fourcc[4], int chunk_num) {
|
||||
const uint8_t* const mem_buf = dmux->mem_.buf_;
|
||||
const uint8_t* const mem_buf = dmux->mem.buf;
|
||||
const Chunk* c;
|
||||
int count = 0;
|
||||
for (c = dmux->chunks_; c != NULL; c = c->next_) {
|
||||
const uint8_t* const header = mem_buf + c->data_.offset_;
|
||||
for (c = dmux->chunks; c != NULL; c = c->next) {
|
||||
const uint8_t* const header = mem_buf + c->data.offset;
|
||||
if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
|
||||
if (count == chunk_num) break;
|
||||
}
|
||||
@@ -930,23 +932,22 @@ static int SetChunk(const char fourcc[4], int chunk_num,
|
||||
if (chunk_num == 0) chunk_num = count;
|
||||
|
||||
if (chunk_num <= count) {
|
||||
const uint8_t* const mem_buf = dmux->mem_.buf_;
|
||||
const uint8_t* const mem_buf = dmux->mem.buf;
|
||||
const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
|
||||
iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
|
||||
iter->chunk.size = chunk->data_.size_ - CHUNK_HEADER_SIZE;
|
||||
iter->num_chunks = count;
|
||||
iter->chunk_num = chunk_num;
|
||||
iter->chunk.bytes = mem_buf + chunk->data.offset + CHUNK_HEADER_SIZE;
|
||||
iter->chunk.size = chunk->data.size - CHUNK_HEADER_SIZE;
|
||||
iter->num_chunks = count;
|
||||
iter->chunk_num = chunk_num;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WebPDemuxGetChunk(const WebPDemuxer* dmux,
|
||||
const char fourcc[4], int chunk_num,
|
||||
WebPChunkIterator* iter) {
|
||||
int WebPDemuxGetChunk(const WebPDemuxer* dmux, const char fourcc[4],
|
||||
int chunk_num, WebPChunkIterator* iter) {
|
||||
if (iter == NULL) return 0;
|
||||
|
||||
memset(iter, 0, sizeof(*iter));
|
||||
WEBP_UNSAFE_MEMSET(iter, 0, sizeof(*iter));
|
||||
iter->private_ = (void*)dmux;
|
||||
return SetChunk(fourcc, chunk_num, iter);
|
||||
}
|
||||
@@ -969,7 +970,4 @@ int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
|
||||
(void)iter;
|
||||
}
|
||||
|
||||
void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { (void)iter; }
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,4,0
|
||||
PRODUCTVERSION 1,0,4,0
|
||||
FILEVERSION 1,0,6,0
|
||||
PRODUCTVERSION 1,0,6,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -24,12 +24,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libwebpdemux DLL"
|
||||
VALUE "FileVersion", "1.4.0"
|
||||
VALUE "FileVersion", "1.6.0"
|
||||
VALUE "InternalName", "libwebpdemux.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2024"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2025"
|
||||
VALUE "OriginalFilename", "libwebpdemux.dll"
|
||||
VALUE "ProductName", "WebP Image Demuxer"
|
||||
VALUE "ProductVersion", "1.4.0"
|
||||
VALUE "ProductVersion", "1.6.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -5,6 +5,8 @@ noinst_LTLIBRARIES += libwebpdsp_sse2.la
|
||||
noinst_LTLIBRARIES += libwebpdspdecode_sse2.la
|
||||
noinst_LTLIBRARIES += libwebpdsp_sse41.la
|
||||
noinst_LTLIBRARIES += libwebpdspdecode_sse41.la
|
||||
noinst_LTLIBRARIES += libwebpdsp_avx2.la
|
||||
noinst_LTLIBRARIES += libwebpdspdecode_avx2.la
|
||||
noinst_LTLIBRARIES += libwebpdsp_neon.la
|
||||
noinst_LTLIBRARIES += libwebpdspdecode_neon.la
|
||||
noinst_LTLIBRARIES += libwebpdsp_msa.la
|
||||
@@ -44,6 +46,11 @@ ENC_SOURCES += lossless_enc.c
|
||||
ENC_SOURCES += quant.h
|
||||
ENC_SOURCES += ssim.c
|
||||
|
||||
libwebpdspdecode_avx2_la_SOURCES =
|
||||
libwebpdspdecode_avx2_la_SOURCES += lossless_avx2.c
|
||||
libwebpdspdecode_avx2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdspdecode_avx2_la_CFLAGS = $(AM_CFLAGS) $(AVX2_FLAGS)
|
||||
|
||||
libwebpdspdecode_sse41_la_SOURCES =
|
||||
libwebpdspdecode_sse41_la_SOURCES += alpha_processing_sse41.c
|
||||
libwebpdspdecode_sse41_la_SOURCES += dec_sse41.c
|
||||
@@ -123,6 +130,12 @@ libwebpdsp_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdsp_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
|
||||
libwebpdsp_sse41_la_LIBADD = libwebpdspdecode_sse41.la
|
||||
|
||||
libwebpdsp_avx2_la_SOURCES =
|
||||
libwebpdsp_avx2_la_SOURCES += lossless_enc_avx2.c
|
||||
libwebpdsp_avx2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdsp_avx2_la_CFLAGS = $(AM_CFLAGS) $(AVX2_FLAGS)
|
||||
libwebpdsp_avx2_la_LIBADD = libwebpdspdecode_avx2.la
|
||||
|
||||
libwebpdsp_neon_la_SOURCES =
|
||||
libwebpdsp_neon_la_SOURCES += cost_neon.c
|
||||
libwebpdsp_neon_la_SOURCES += enc_neon.c
|
||||
@@ -167,6 +180,7 @@ libwebpdsp_la_LDFLAGS = -lm
|
||||
libwebpdsp_la_LIBADD =
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_sse2.la
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_sse41.la
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_avx2.la
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_neon.la
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_msa.la
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_mips32.la
|
||||
@@ -180,6 +194,7 @@ if BUILD_LIBWEBPDECODER
|
||||
libwebpdspdecode_la_LIBADD =
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse2.la
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse41.la
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_avx2.la
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_neon.la
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_msa.la
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_mips32.la
|
||||
|
||||
@@ -12,17 +12,20 @@
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
// Tables can be faster on some platform but incur some extra binary size (~2k).
|
||||
#if !defined(USE_TABLES_FOR_ALPHA_MULT)
|
||||
#define USE_TABLES_FOR_ALPHA_MULT 0 // ALTERNATE_CODE
|
||||
#define USE_TABLES_FOR_ALPHA_MULT 0 // ALTERNATE_CODE
|
||||
#endif
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||
#define HALF ((1u << MFIX) >> 1)
|
||||
#define KINV_255 ((1u << MFIX) / 255u)
|
||||
|
||||
@@ -35,95 +38,94 @@ static uint32_t Mult(uint8_t x, uint32_t mult) {
|
||||
#if (USE_TABLES_FOR_ALPHA_MULT == 1)
|
||||
|
||||
static const uint32_t kMultTables[2][256] = {
|
||||
{ // (255u << MFIX) / alpha
|
||||
0x00000000, 0xff000000, 0x7f800000, 0x55000000, 0x3fc00000, 0x33000000,
|
||||
0x2a800000, 0x246db6db, 0x1fe00000, 0x1c555555, 0x19800000, 0x172e8ba2,
|
||||
0x15400000, 0x139d89d8, 0x1236db6d, 0x11000000, 0x0ff00000, 0x0f000000,
|
||||
0x0e2aaaaa, 0x0d6bca1a, 0x0cc00000, 0x0c249249, 0x0b9745d1, 0x0b1642c8,
|
||||
0x0aa00000, 0x0a333333, 0x09cec4ec, 0x0971c71c, 0x091b6db6, 0x08cb08d3,
|
||||
0x08800000, 0x0839ce73, 0x07f80000, 0x07ba2e8b, 0x07800000, 0x07492492,
|
||||
0x07155555, 0x06e45306, 0x06b5e50d, 0x0689d89d, 0x06600000, 0x063831f3,
|
||||
0x06124924, 0x05ee23b8, 0x05cba2e8, 0x05aaaaaa, 0x058b2164, 0x056cefa8,
|
||||
0x05500000, 0x05343eb1, 0x05199999, 0x05000000, 0x04e76276, 0x04cfb2b7,
|
||||
0x04b8e38e, 0x04a2e8ba, 0x048db6db, 0x0479435e, 0x04658469, 0x045270d0,
|
||||
0x04400000, 0x042e29f7, 0x041ce739, 0x040c30c3, 0x03fc0000, 0x03ec4ec4,
|
||||
0x03dd1745, 0x03ce540f, 0x03c00000, 0x03b21642, 0x03a49249, 0x03976fc6,
|
||||
0x038aaaaa, 0x037e3f1f, 0x03722983, 0x03666666, 0x035af286, 0x034fcace,
|
||||
0x0344ec4e, 0x033a5440, 0x03300000, 0x0325ed09, 0x031c18f9, 0x0312818a,
|
||||
0x03092492, 0x03000000, 0x02f711dc, 0x02ee5846, 0x02e5d174, 0x02dd7baf,
|
||||
0x02d55555, 0x02cd5cd5, 0x02c590b2, 0x02bdef7b, 0x02b677d4, 0x02af286b,
|
||||
0x02a80000, 0x02a0fd5c, 0x029a1f58, 0x029364d9, 0x028ccccc, 0x0286562d,
|
||||
0x02800000, 0x0279c952, 0x0273b13b, 0x026db6db, 0x0267d95b, 0x026217ec,
|
||||
0x025c71c7, 0x0256e62a, 0x0251745d, 0x024c1bac, 0x0246db6d, 0x0241b2f9,
|
||||
0x023ca1af, 0x0237a6f4, 0x0232c234, 0x022df2df, 0x02293868, 0x02249249,
|
||||
0x02200000, 0x021b810e, 0x021714fb, 0x0212bb51, 0x020e739c, 0x020a3d70,
|
||||
0x02061861, 0x02020408, 0x01fe0000, 0x01fa0be8, 0x01f62762, 0x01f25213,
|
||||
0x01ee8ba2, 0x01ead3ba, 0x01e72a07, 0x01e38e38, 0x01e00000, 0x01dc7f10,
|
||||
0x01d90b21, 0x01d5a3e9, 0x01d24924, 0x01cefa8d, 0x01cbb7e3, 0x01c880e5,
|
||||
0x01c55555, 0x01c234f7, 0x01bf1f8f, 0x01bc14e5, 0x01b914c1, 0x01b61eed,
|
||||
0x01b33333, 0x01b05160, 0x01ad7943, 0x01aaaaaa, 0x01a7e567, 0x01a5294a,
|
||||
0x01a27627, 0x019fcbd2, 0x019d2a20, 0x019a90e7, 0x01980000, 0x01957741,
|
||||
0x0192f684, 0x01907da4, 0x018e0c7c, 0x018ba2e8, 0x018940c5, 0x0186e5f0,
|
||||
0x01849249, 0x018245ae, 0x01800000, 0x017dc11f, 0x017b88ee, 0x0179574e,
|
||||
0x01772c23, 0x01750750, 0x0172e8ba, 0x0170d045, 0x016ebdd7, 0x016cb157,
|
||||
0x016aaaaa, 0x0168a9b9, 0x0166ae6a, 0x0164b8a7, 0x0162c859, 0x0160dd67,
|
||||
0x015ef7bd, 0x015d1745, 0x015b3bea, 0x01596596, 0x01579435, 0x0155c7b4,
|
||||
0x01540000, 0x01523d03, 0x01507eae, 0x014ec4ec, 0x014d0fac, 0x014b5edc,
|
||||
0x0149b26c, 0x01480a4a, 0x01466666, 0x0144c6af, 0x01432b16, 0x0141938b,
|
||||
0x01400000, 0x013e7063, 0x013ce4a9, 0x013b5cc0, 0x0139d89d, 0x01385830,
|
||||
0x0136db6d, 0x01356246, 0x0133ecad, 0x01327a97, 0x01310bf6, 0x012fa0be,
|
||||
0x012e38e3, 0x012cd459, 0x012b7315, 0x012a150a, 0x0128ba2e, 0x01276276,
|
||||
0x01260dd6, 0x0124bc44, 0x01236db6, 0x01222222, 0x0120d97c, 0x011f93bc,
|
||||
0x011e50d7, 0x011d10c4, 0x011bd37a, 0x011a98ef, 0x0119611a, 0x01182bf2,
|
||||
0x0116f96f, 0x0115c988, 0x01149c34, 0x0113716a, 0x01124924, 0x01112358,
|
||||
0x01100000, 0x010edf12, 0x010dc087, 0x010ca458, 0x010b8a7d, 0x010a72f0,
|
||||
0x01095da8, 0x01084a9f, 0x010739ce, 0x01062b2e, 0x01051eb8, 0x01041465,
|
||||
0x01030c30, 0x01020612, 0x01010204, 0x01000000 },
|
||||
{ // alpha * KINV_255
|
||||
0x00000000, 0x00010101, 0x00020202, 0x00030303, 0x00040404, 0x00050505,
|
||||
0x00060606, 0x00070707, 0x00080808, 0x00090909, 0x000a0a0a, 0x000b0b0b,
|
||||
0x000c0c0c, 0x000d0d0d, 0x000e0e0e, 0x000f0f0f, 0x00101010, 0x00111111,
|
||||
0x00121212, 0x00131313, 0x00141414, 0x00151515, 0x00161616, 0x00171717,
|
||||
0x00181818, 0x00191919, 0x001a1a1a, 0x001b1b1b, 0x001c1c1c, 0x001d1d1d,
|
||||
0x001e1e1e, 0x001f1f1f, 0x00202020, 0x00212121, 0x00222222, 0x00232323,
|
||||
0x00242424, 0x00252525, 0x00262626, 0x00272727, 0x00282828, 0x00292929,
|
||||
0x002a2a2a, 0x002b2b2b, 0x002c2c2c, 0x002d2d2d, 0x002e2e2e, 0x002f2f2f,
|
||||
0x00303030, 0x00313131, 0x00323232, 0x00333333, 0x00343434, 0x00353535,
|
||||
0x00363636, 0x00373737, 0x00383838, 0x00393939, 0x003a3a3a, 0x003b3b3b,
|
||||
0x003c3c3c, 0x003d3d3d, 0x003e3e3e, 0x003f3f3f, 0x00404040, 0x00414141,
|
||||
0x00424242, 0x00434343, 0x00444444, 0x00454545, 0x00464646, 0x00474747,
|
||||
0x00484848, 0x00494949, 0x004a4a4a, 0x004b4b4b, 0x004c4c4c, 0x004d4d4d,
|
||||
0x004e4e4e, 0x004f4f4f, 0x00505050, 0x00515151, 0x00525252, 0x00535353,
|
||||
0x00545454, 0x00555555, 0x00565656, 0x00575757, 0x00585858, 0x00595959,
|
||||
0x005a5a5a, 0x005b5b5b, 0x005c5c5c, 0x005d5d5d, 0x005e5e5e, 0x005f5f5f,
|
||||
0x00606060, 0x00616161, 0x00626262, 0x00636363, 0x00646464, 0x00656565,
|
||||
0x00666666, 0x00676767, 0x00686868, 0x00696969, 0x006a6a6a, 0x006b6b6b,
|
||||
0x006c6c6c, 0x006d6d6d, 0x006e6e6e, 0x006f6f6f, 0x00707070, 0x00717171,
|
||||
0x00727272, 0x00737373, 0x00747474, 0x00757575, 0x00767676, 0x00777777,
|
||||
0x00787878, 0x00797979, 0x007a7a7a, 0x007b7b7b, 0x007c7c7c, 0x007d7d7d,
|
||||
0x007e7e7e, 0x007f7f7f, 0x00808080, 0x00818181, 0x00828282, 0x00838383,
|
||||
0x00848484, 0x00858585, 0x00868686, 0x00878787, 0x00888888, 0x00898989,
|
||||
0x008a8a8a, 0x008b8b8b, 0x008c8c8c, 0x008d8d8d, 0x008e8e8e, 0x008f8f8f,
|
||||
0x00909090, 0x00919191, 0x00929292, 0x00939393, 0x00949494, 0x00959595,
|
||||
0x00969696, 0x00979797, 0x00989898, 0x00999999, 0x009a9a9a, 0x009b9b9b,
|
||||
0x009c9c9c, 0x009d9d9d, 0x009e9e9e, 0x009f9f9f, 0x00a0a0a0, 0x00a1a1a1,
|
||||
0x00a2a2a2, 0x00a3a3a3, 0x00a4a4a4, 0x00a5a5a5, 0x00a6a6a6, 0x00a7a7a7,
|
||||
0x00a8a8a8, 0x00a9a9a9, 0x00aaaaaa, 0x00ababab, 0x00acacac, 0x00adadad,
|
||||
0x00aeaeae, 0x00afafaf, 0x00b0b0b0, 0x00b1b1b1, 0x00b2b2b2, 0x00b3b3b3,
|
||||
0x00b4b4b4, 0x00b5b5b5, 0x00b6b6b6, 0x00b7b7b7, 0x00b8b8b8, 0x00b9b9b9,
|
||||
0x00bababa, 0x00bbbbbb, 0x00bcbcbc, 0x00bdbdbd, 0x00bebebe, 0x00bfbfbf,
|
||||
0x00c0c0c0, 0x00c1c1c1, 0x00c2c2c2, 0x00c3c3c3, 0x00c4c4c4, 0x00c5c5c5,
|
||||
0x00c6c6c6, 0x00c7c7c7, 0x00c8c8c8, 0x00c9c9c9, 0x00cacaca, 0x00cbcbcb,
|
||||
0x00cccccc, 0x00cdcdcd, 0x00cecece, 0x00cfcfcf, 0x00d0d0d0, 0x00d1d1d1,
|
||||
0x00d2d2d2, 0x00d3d3d3, 0x00d4d4d4, 0x00d5d5d5, 0x00d6d6d6, 0x00d7d7d7,
|
||||
0x00d8d8d8, 0x00d9d9d9, 0x00dadada, 0x00dbdbdb, 0x00dcdcdc, 0x00dddddd,
|
||||
0x00dedede, 0x00dfdfdf, 0x00e0e0e0, 0x00e1e1e1, 0x00e2e2e2, 0x00e3e3e3,
|
||||
0x00e4e4e4, 0x00e5e5e5, 0x00e6e6e6, 0x00e7e7e7, 0x00e8e8e8, 0x00e9e9e9,
|
||||
0x00eaeaea, 0x00ebebeb, 0x00ececec, 0x00ededed, 0x00eeeeee, 0x00efefef,
|
||||
0x00f0f0f0, 0x00f1f1f1, 0x00f2f2f2, 0x00f3f3f3, 0x00f4f4f4, 0x00f5f5f5,
|
||||
0x00f6f6f6, 0x00f7f7f7, 0x00f8f8f8, 0x00f9f9f9, 0x00fafafa, 0x00fbfbfb,
|
||||
0x00fcfcfc, 0x00fdfdfd, 0x00fefefe, 0x00ffffff }
|
||||
};
|
||||
// (255u << MFIX) / alpha
|
||||
{0x00000000, 0xff000000, 0x7f800000, 0x55000000, 0x3fc00000, 0x33000000,
|
||||
0x2a800000, 0x246db6db, 0x1fe00000, 0x1c555555, 0x19800000, 0x172e8ba2,
|
||||
0x15400000, 0x139d89d8, 0x1236db6d, 0x11000000, 0x0ff00000, 0x0f000000,
|
||||
0x0e2aaaaa, 0x0d6bca1a, 0x0cc00000, 0x0c249249, 0x0b9745d1, 0x0b1642c8,
|
||||
0x0aa00000, 0x0a333333, 0x09cec4ec, 0x0971c71c, 0x091b6db6, 0x08cb08d3,
|
||||
0x08800000, 0x0839ce73, 0x07f80000, 0x07ba2e8b, 0x07800000, 0x07492492,
|
||||
0x07155555, 0x06e45306, 0x06b5e50d, 0x0689d89d, 0x06600000, 0x063831f3,
|
||||
0x06124924, 0x05ee23b8, 0x05cba2e8, 0x05aaaaaa, 0x058b2164, 0x056cefa8,
|
||||
0x05500000, 0x05343eb1, 0x05199999, 0x05000000, 0x04e76276, 0x04cfb2b7,
|
||||
0x04b8e38e, 0x04a2e8ba, 0x048db6db, 0x0479435e, 0x04658469, 0x045270d0,
|
||||
0x04400000, 0x042e29f7, 0x041ce739, 0x040c30c3, 0x03fc0000, 0x03ec4ec4,
|
||||
0x03dd1745, 0x03ce540f, 0x03c00000, 0x03b21642, 0x03a49249, 0x03976fc6,
|
||||
0x038aaaaa, 0x037e3f1f, 0x03722983, 0x03666666, 0x035af286, 0x034fcace,
|
||||
0x0344ec4e, 0x033a5440, 0x03300000, 0x0325ed09, 0x031c18f9, 0x0312818a,
|
||||
0x03092492, 0x03000000, 0x02f711dc, 0x02ee5846, 0x02e5d174, 0x02dd7baf,
|
||||
0x02d55555, 0x02cd5cd5, 0x02c590b2, 0x02bdef7b, 0x02b677d4, 0x02af286b,
|
||||
0x02a80000, 0x02a0fd5c, 0x029a1f58, 0x029364d9, 0x028ccccc, 0x0286562d,
|
||||
0x02800000, 0x0279c952, 0x0273b13b, 0x026db6db, 0x0267d95b, 0x026217ec,
|
||||
0x025c71c7, 0x0256e62a, 0x0251745d, 0x024c1bac, 0x0246db6d, 0x0241b2f9,
|
||||
0x023ca1af, 0x0237a6f4, 0x0232c234, 0x022df2df, 0x02293868, 0x02249249,
|
||||
0x02200000, 0x021b810e, 0x021714fb, 0x0212bb51, 0x020e739c, 0x020a3d70,
|
||||
0x02061861, 0x02020408, 0x01fe0000, 0x01fa0be8, 0x01f62762, 0x01f25213,
|
||||
0x01ee8ba2, 0x01ead3ba, 0x01e72a07, 0x01e38e38, 0x01e00000, 0x01dc7f10,
|
||||
0x01d90b21, 0x01d5a3e9, 0x01d24924, 0x01cefa8d, 0x01cbb7e3, 0x01c880e5,
|
||||
0x01c55555, 0x01c234f7, 0x01bf1f8f, 0x01bc14e5, 0x01b914c1, 0x01b61eed,
|
||||
0x01b33333, 0x01b05160, 0x01ad7943, 0x01aaaaaa, 0x01a7e567, 0x01a5294a,
|
||||
0x01a27627, 0x019fcbd2, 0x019d2a20, 0x019a90e7, 0x01980000, 0x01957741,
|
||||
0x0192f684, 0x01907da4, 0x018e0c7c, 0x018ba2e8, 0x018940c5, 0x0186e5f0,
|
||||
0x01849249, 0x018245ae, 0x01800000, 0x017dc11f, 0x017b88ee, 0x0179574e,
|
||||
0x01772c23, 0x01750750, 0x0172e8ba, 0x0170d045, 0x016ebdd7, 0x016cb157,
|
||||
0x016aaaaa, 0x0168a9b9, 0x0166ae6a, 0x0164b8a7, 0x0162c859, 0x0160dd67,
|
||||
0x015ef7bd, 0x015d1745, 0x015b3bea, 0x01596596, 0x01579435, 0x0155c7b4,
|
||||
0x01540000, 0x01523d03, 0x01507eae, 0x014ec4ec, 0x014d0fac, 0x014b5edc,
|
||||
0x0149b26c, 0x01480a4a, 0x01466666, 0x0144c6af, 0x01432b16, 0x0141938b,
|
||||
0x01400000, 0x013e7063, 0x013ce4a9, 0x013b5cc0, 0x0139d89d, 0x01385830,
|
||||
0x0136db6d, 0x01356246, 0x0133ecad, 0x01327a97, 0x01310bf6, 0x012fa0be,
|
||||
0x012e38e3, 0x012cd459, 0x012b7315, 0x012a150a, 0x0128ba2e, 0x01276276,
|
||||
0x01260dd6, 0x0124bc44, 0x01236db6, 0x01222222, 0x0120d97c, 0x011f93bc,
|
||||
0x011e50d7, 0x011d10c4, 0x011bd37a, 0x011a98ef, 0x0119611a, 0x01182bf2,
|
||||
0x0116f96f, 0x0115c988, 0x01149c34, 0x0113716a, 0x01124924, 0x01112358,
|
||||
0x01100000, 0x010edf12, 0x010dc087, 0x010ca458, 0x010b8a7d, 0x010a72f0,
|
||||
0x01095da8, 0x01084a9f, 0x010739ce, 0x01062b2e, 0x01051eb8, 0x01041465,
|
||||
0x01030c30, 0x01020612, 0x01010204, 0x01000000},
|
||||
// alpha * KINV_255
|
||||
{0x00000000, 0x00010101, 0x00020202, 0x00030303, 0x00040404, 0x00050505,
|
||||
0x00060606, 0x00070707, 0x00080808, 0x00090909, 0x000a0a0a, 0x000b0b0b,
|
||||
0x000c0c0c, 0x000d0d0d, 0x000e0e0e, 0x000f0f0f, 0x00101010, 0x00111111,
|
||||
0x00121212, 0x00131313, 0x00141414, 0x00151515, 0x00161616, 0x00171717,
|
||||
0x00181818, 0x00191919, 0x001a1a1a, 0x001b1b1b, 0x001c1c1c, 0x001d1d1d,
|
||||
0x001e1e1e, 0x001f1f1f, 0x00202020, 0x00212121, 0x00222222, 0x00232323,
|
||||
0x00242424, 0x00252525, 0x00262626, 0x00272727, 0x00282828, 0x00292929,
|
||||
0x002a2a2a, 0x002b2b2b, 0x002c2c2c, 0x002d2d2d, 0x002e2e2e, 0x002f2f2f,
|
||||
0x00303030, 0x00313131, 0x00323232, 0x00333333, 0x00343434, 0x00353535,
|
||||
0x00363636, 0x00373737, 0x00383838, 0x00393939, 0x003a3a3a, 0x003b3b3b,
|
||||
0x003c3c3c, 0x003d3d3d, 0x003e3e3e, 0x003f3f3f, 0x00404040, 0x00414141,
|
||||
0x00424242, 0x00434343, 0x00444444, 0x00454545, 0x00464646, 0x00474747,
|
||||
0x00484848, 0x00494949, 0x004a4a4a, 0x004b4b4b, 0x004c4c4c, 0x004d4d4d,
|
||||
0x004e4e4e, 0x004f4f4f, 0x00505050, 0x00515151, 0x00525252, 0x00535353,
|
||||
0x00545454, 0x00555555, 0x00565656, 0x00575757, 0x00585858, 0x00595959,
|
||||
0x005a5a5a, 0x005b5b5b, 0x005c5c5c, 0x005d5d5d, 0x005e5e5e, 0x005f5f5f,
|
||||
0x00606060, 0x00616161, 0x00626262, 0x00636363, 0x00646464, 0x00656565,
|
||||
0x00666666, 0x00676767, 0x00686868, 0x00696969, 0x006a6a6a, 0x006b6b6b,
|
||||
0x006c6c6c, 0x006d6d6d, 0x006e6e6e, 0x006f6f6f, 0x00707070, 0x00717171,
|
||||
0x00727272, 0x00737373, 0x00747474, 0x00757575, 0x00767676, 0x00777777,
|
||||
0x00787878, 0x00797979, 0x007a7a7a, 0x007b7b7b, 0x007c7c7c, 0x007d7d7d,
|
||||
0x007e7e7e, 0x007f7f7f, 0x00808080, 0x00818181, 0x00828282, 0x00838383,
|
||||
0x00848484, 0x00858585, 0x00868686, 0x00878787, 0x00888888, 0x00898989,
|
||||
0x008a8a8a, 0x008b8b8b, 0x008c8c8c, 0x008d8d8d, 0x008e8e8e, 0x008f8f8f,
|
||||
0x00909090, 0x00919191, 0x00929292, 0x00939393, 0x00949494, 0x00959595,
|
||||
0x00969696, 0x00979797, 0x00989898, 0x00999999, 0x009a9a9a, 0x009b9b9b,
|
||||
0x009c9c9c, 0x009d9d9d, 0x009e9e9e, 0x009f9f9f, 0x00a0a0a0, 0x00a1a1a1,
|
||||
0x00a2a2a2, 0x00a3a3a3, 0x00a4a4a4, 0x00a5a5a5, 0x00a6a6a6, 0x00a7a7a7,
|
||||
0x00a8a8a8, 0x00a9a9a9, 0x00aaaaaa, 0x00ababab, 0x00acacac, 0x00adadad,
|
||||
0x00aeaeae, 0x00afafaf, 0x00b0b0b0, 0x00b1b1b1, 0x00b2b2b2, 0x00b3b3b3,
|
||||
0x00b4b4b4, 0x00b5b5b5, 0x00b6b6b6, 0x00b7b7b7, 0x00b8b8b8, 0x00b9b9b9,
|
||||
0x00bababa, 0x00bbbbbb, 0x00bcbcbc, 0x00bdbdbd, 0x00bebebe, 0x00bfbfbf,
|
||||
0x00c0c0c0, 0x00c1c1c1, 0x00c2c2c2, 0x00c3c3c3, 0x00c4c4c4, 0x00c5c5c5,
|
||||
0x00c6c6c6, 0x00c7c7c7, 0x00c8c8c8, 0x00c9c9c9, 0x00cacaca, 0x00cbcbcb,
|
||||
0x00cccccc, 0x00cdcdcd, 0x00cecece, 0x00cfcfcf, 0x00d0d0d0, 0x00d1d1d1,
|
||||
0x00d2d2d2, 0x00d3d3d3, 0x00d4d4d4, 0x00d5d5d5, 0x00d6d6d6, 0x00d7d7d7,
|
||||
0x00d8d8d8, 0x00d9d9d9, 0x00dadada, 0x00dbdbdb, 0x00dcdcdc, 0x00dddddd,
|
||||
0x00dedede, 0x00dfdfdf, 0x00e0e0e0, 0x00e1e1e1, 0x00e2e2e2, 0x00e3e3e3,
|
||||
0x00e4e4e4, 0x00e5e5e5, 0x00e6e6e6, 0x00e7e7e7, 0x00e8e8e8, 0x00e9e9e9,
|
||||
0x00eaeaea, 0x00ebebeb, 0x00ececec, 0x00ededed, 0x00eeeeee, 0x00efefef,
|
||||
0x00f0f0f0, 0x00f1f1f1, 0x00f2f2f2, 0x00f3f3f3, 0x00f4f4f4, 0x00f5f5f5,
|
||||
0x00f6f6f6, 0x00f7f7f7, 0x00f8f8f8, 0x00f9f9f9, 0x00fafafa, 0x00fbfbfb,
|
||||
0x00fcfcfc, 0x00fdfdfd, 0x00fefefe, 0x00ffffff}};
|
||||
|
||||
static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) {
|
||||
return kMultTables[!inverse][a];
|
||||
@@ -141,15 +143,15 @@ void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse) {
|
||||
int x;
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t argb = ptr[x];
|
||||
if (argb < 0xff000000u) { // alpha < 255
|
||||
if (argb <= 0x00ffffffu) { // alpha == 0
|
||||
if (argb < 0xff000000u) { // alpha < 255
|
||||
if (argb <= 0x00ffffffu) { // alpha == 0
|
||||
ptr[x] = 0;
|
||||
} else {
|
||||
const uint32_t alpha = (argb >> 24) & 0xff;
|
||||
const uint32_t scale = GetScale(alpha, inverse);
|
||||
uint32_t out = argb & 0xff000000u;
|
||||
out |= Mult(argb >> 0, scale) << 0;
|
||||
out |= Mult(argb >> 8, scale) << 8;
|
||||
out |= Mult(argb >> 0, scale) << 0;
|
||||
out |= Mult(argb >> 8, scale) << 8;
|
||||
out |= Mult(argb >> 16, scale) << 16;
|
||||
ptr[x] = out;
|
||||
}
|
||||
@@ -158,8 +160,8 @@ void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse) {
|
||||
}
|
||||
|
||||
void WebPMultRow_C(uint8_t* WEBP_RESTRICT const ptr,
|
||||
const uint8_t* WEBP_RESTRICT const alpha,
|
||||
int width, int inverse) {
|
||||
const uint8_t* WEBP_RESTRICT const alpha, int width,
|
||||
int inverse) {
|
||||
int x;
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t a = alpha[x];
|
||||
@@ -180,8 +182,8 @@ void WebPMultRow_C(uint8_t* WEBP_RESTRICT const ptr,
|
||||
|
||||
void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse);
|
||||
void (*WebPMultRow)(uint8_t* WEBP_RESTRICT const ptr,
|
||||
const uint8_t* WEBP_RESTRICT const alpha,
|
||||
int width, int inverse);
|
||||
const uint8_t* WEBP_RESTRICT const alpha, int width,
|
||||
int inverse);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Generic per-plane calls
|
||||
@@ -214,17 +216,17 @@ void WebPMultRows(uint8_t* WEBP_RESTRICT ptr, int stride,
|
||||
// (x * a * 32897) >> 23 is bit-wise equivalent to (int)(x * a / 255.)
|
||||
// for all 8bit x or a. For bit-wise equivalence to (int)(x * a / 255. + .5),
|
||||
// one can use instead: (x * a * 65793 + (1 << 23)) >> 24
|
||||
#if 1 // (int)(x * a / 255.)
|
||||
#define MULTIPLIER(a) ((a) * 32897U)
|
||||
#if 1 // (int)(x * a / 255.)
|
||||
#define MULTIPLIER(a) ((a) * 32897U)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
|
||||
#else // (int)(x * a / 255. + .5)
|
||||
#else // (int)(x * a / 255. + .5)
|
||||
#define MULTIPLIER(a) ((a) * 65793U)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m) + (1U << 23)) >> 24)
|
||||
#endif
|
||||
|
||||
#if !WEBP_NEON_OMIT_C_CODE
|
||||
static void ApplyAlphaMultiply_C(uint8_t* rgba, int alpha_first,
|
||||
int w, int h, int stride) {
|
||||
static void ApplyAlphaMultiply_C(uint8_t* rgba, int alpha_first, int w, int h,
|
||||
int stride) {
|
||||
while (h-- > 0) {
|
||||
uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
|
||||
const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
|
||||
@@ -247,7 +249,7 @@ static void ApplyAlphaMultiply_C(uint8_t* rgba, int alpha_first,
|
||||
|
||||
// rgbA4444
|
||||
|
||||
#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
|
||||
#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
|
||||
|
||||
static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
|
||||
return (x & 0xf0) | (x >> 4);
|
||||
@@ -261,8 +263,8 @@ static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) {
|
||||
return (x * m) >> 16;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void ApplyAlphaMultiply4444_C(uint8_t* rgba4444,
|
||||
int w, int h, int stride,
|
||||
static WEBP_INLINE void ApplyAlphaMultiply4444_C(uint8_t* rgba4444, int w,
|
||||
int h, int stride,
|
||||
int rg_byte_pos /* 0 or 1 */) {
|
||||
while (h-- > 0) {
|
||||
int i;
|
||||
@@ -282,8 +284,8 @@ static WEBP_INLINE void ApplyAlphaMultiply4444_C(uint8_t* rgba4444,
|
||||
}
|
||||
#undef MULTIPLIER
|
||||
|
||||
static void ApplyAlphaMultiply_16b_C(uint8_t* rgba4444,
|
||||
int w, int h, int stride) {
|
||||
static void ApplyAlphaMultiply_16b_C(uint8_t* rgba4444, int w, int h,
|
||||
int stride) {
|
||||
#if (WEBP_SWAP_16BIT_CSP == 1)
|
||||
ApplyAlphaMultiply4444_C(rgba4444, w, h, stride, 1);
|
||||
#else
|
||||
@@ -293,8 +295,8 @@ static void ApplyAlphaMultiply_16b_C(uint8_t* rgba4444,
|
||||
|
||||
#if !WEBP_NEON_OMIT_C_CODE
|
||||
static int DispatchAlpha_C(const uint8_t* WEBP_RESTRICT alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint8_t* WEBP_RESTRICT dst, int dst_stride) {
|
||||
int width, int height, uint8_t* WEBP_RESTRICT dst,
|
||||
int dst_stride) {
|
||||
uint32_t alpha_mask = 0xff;
|
||||
int i, j;
|
||||
|
||||
@@ -326,8 +328,8 @@ static void DispatchAlphaToGreen_C(const uint8_t* WEBP_RESTRICT alpha,
|
||||
}
|
||||
|
||||
static int ExtractAlpha_C(const uint8_t* WEBP_RESTRICT argb, int argb_stride,
|
||||
int width, int height,
|
||||
uint8_t* WEBP_RESTRICT alpha, int alpha_stride) {
|
||||
int width, int height, uint8_t* WEBP_RESTRICT alpha,
|
||||
int alpha_stride) {
|
||||
uint8_t alpha_mask = 0xff;
|
||||
int i, j;
|
||||
|
||||
@@ -353,19 +355,25 @@ static void ExtractGreen_C(const uint32_t* WEBP_RESTRICT argb,
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int HasAlpha8b_C(const uint8_t* src, int length) {
|
||||
while (length-- > 0) if (*src++ != 0xff) return 1;
|
||||
while (length-- > 0) {
|
||||
if (*src++ != 0xff) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int HasAlpha32b_C(const uint8_t* src, int length) {
|
||||
int x;
|
||||
for (x = 0; length-- > 0; x += 4) if (src[x] != 0xff) return 1;
|
||||
for (x = 0; length-- > 0; x += 4) {
|
||||
if (src[x] != 0xff) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void AlphaReplace_C(uint32_t* src, int length, uint32_t color) {
|
||||
int x;
|
||||
for (x = 0; x < length; ++x) if ((src[x] >> 24) == 0) src[x] = color;
|
||||
for (x = 0; x < length; ++x) {
|
||||
if ((src[x] >> 24) == 0) src[x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -379,8 +387,8 @@ static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) {
|
||||
static void PackARGB_C(const uint8_t* WEBP_RESTRICT a,
|
||||
const uint8_t* WEBP_RESTRICT r,
|
||||
const uint8_t* WEBP_RESTRICT g,
|
||||
const uint8_t* WEBP_RESTRICT b,
|
||||
int len, uint32_t* WEBP_RESTRICT out) {
|
||||
const uint8_t* WEBP_RESTRICT b, int len,
|
||||
uint32_t* WEBP_RESTRICT out) {
|
||||
int i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
out[i] = MakeARGB32(a[4 * i], r[4 * i], g[4 * i], b[4 * i]);
|
||||
@@ -390,8 +398,8 @@ static void PackARGB_C(const uint8_t* WEBP_RESTRICT a,
|
||||
|
||||
static void PackRGB_C(const uint8_t* WEBP_RESTRICT r,
|
||||
const uint8_t* WEBP_RESTRICT g,
|
||||
const uint8_t* WEBP_RESTRICT b,
|
||||
int len, int step, uint32_t* WEBP_RESTRICT out) {
|
||||
const uint8_t* WEBP_RESTRICT b, int len, int step,
|
||||
uint32_t* WEBP_RESTRICT out) {
|
||||
int i, offset = 0;
|
||||
for (i = 0; i < len; ++i) {
|
||||
out[i] = MakeARGB32(0xff, r[offset], g[offset], b[offset]);
|
||||
@@ -415,8 +423,8 @@ void (*WebPPackARGB)(const uint8_t* a, const uint8_t* r, const uint8_t* g,
|
||||
#endif
|
||||
void (*WebPPackRGB)(const uint8_t* WEBP_RESTRICT r,
|
||||
const uint8_t* WEBP_RESTRICT g,
|
||||
const uint8_t* WEBP_RESTRICT b,
|
||||
int len, int step, uint32_t* WEBP_RESTRICT out);
|
||||
const uint8_t* WEBP_RESTRICT b, int len, int step,
|
||||
uint32_t* WEBP_RESTRICT out);
|
||||
|
||||
int (*WebPHasAlpha8b)(const uint8_t* src, int length);
|
||||
int (*WebPHasAlpha32b)(const uint8_t* src, int length);
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
|
||||
static int DispatchAlpha_MIPSdspR2(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint8_t* dst, int dst_stride) {
|
||||
int width, int height, uint8_t* dst,
|
||||
int dst_stride) {
|
||||
uint32_t alpha_mask = 0xffffffff;
|
||||
int i, j, temp0;
|
||||
|
||||
@@ -28,97 +28,92 @@ static int DispatchAlpha_MIPSdspR2(const uint8_t* alpha, int alpha_stride,
|
||||
for (i = 0; i < (width >> 2); ++i) {
|
||||
int temp1, temp2, temp3;
|
||||
|
||||
__asm__ volatile (
|
||||
"ulw %[temp0], 0(%[palpha]) \n\t"
|
||||
"addiu %[palpha], %[palpha], 4 \n\t"
|
||||
"addiu %[pdst], %[pdst], 16 \n\t"
|
||||
"srl %[temp1], %[temp0], 8 \n\t"
|
||||
"srl %[temp2], %[temp0], 16 \n\t"
|
||||
"srl %[temp3], %[temp0], 24 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"sb %[temp0], -16(%[pdst]) \n\t"
|
||||
"sb %[temp1], -12(%[pdst]) \n\t"
|
||||
"sb %[temp2], -8(%[pdst]) \n\t"
|
||||
"sb %[temp3], -4(%[pdst]) \n\t"
|
||||
: [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
|
||||
[temp3]"=&r"(temp3), [palpha]"+r"(palpha), [pdst]"+r"(pdst),
|
||||
[alpha_mask]"+r"(alpha_mask)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
__asm__ volatile(
|
||||
"ulw %[temp0], 0(%[palpha]) \n\t"
|
||||
"addiu %[palpha], %[palpha], 4 \n\t"
|
||||
"addiu %[pdst], %[pdst], 16 \n\t"
|
||||
"srl %[temp1], %[temp0], 8 \n\t"
|
||||
"srl %[temp2], %[temp0], 16 \n\t"
|
||||
"srl %[temp3], %[temp0], 24 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"sb %[temp0], -16(%[pdst]) \n\t"
|
||||
"sb %[temp1], -12(%[pdst]) \n\t"
|
||||
"sb %[temp2], -8(%[pdst]) \n\t"
|
||||
"sb %[temp3], -4(%[pdst]) \n\t"
|
||||
: [temp0] "=&r"(temp0), [temp1] "=&r"(temp1), [temp2] "=&r"(temp2),
|
||||
[temp3] "=&r"(temp3), [palpha] "+r"(palpha), [pdst] "+r"(pdst),
|
||||
[alpha_mask] "+r"(alpha_mask)
|
||||
:
|
||||
: "memory");
|
||||
}
|
||||
|
||||
for (i = 0; i < (width & 3); ++i) {
|
||||
__asm__ volatile (
|
||||
"lbu %[temp0], 0(%[palpha]) \n\t"
|
||||
"addiu %[palpha], %[palpha], 1 \n\t"
|
||||
"sb %[temp0], 0(%[pdst]) \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"addiu %[pdst], %[pdst], 4 \n\t"
|
||||
: [temp0]"=&r"(temp0), [palpha]"+r"(palpha), [pdst]"+r"(pdst),
|
||||
[alpha_mask]"+r"(alpha_mask)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
__asm__ volatile(
|
||||
"lbu %[temp0], 0(%[palpha]) \n\t"
|
||||
"addiu %[palpha], %[palpha], 1 \n\t"
|
||||
"sb %[temp0], 0(%[pdst]) \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"addiu %[pdst], %[pdst], 4 \n\t"
|
||||
: [temp0] "=&r"(temp0), [palpha] "+r"(palpha), [pdst] "+r"(pdst),
|
||||
[alpha_mask] "+r"(alpha_mask)
|
||||
:
|
||||
: "memory");
|
||||
}
|
||||
alpha += alpha_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
|
||||
__asm__ volatile (
|
||||
"ext %[temp0], %[alpha_mask], 0, 16 \n\t"
|
||||
"srl %[alpha_mask], %[alpha_mask], 16 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"ext %[temp0], %[alpha_mask], 0, 8 \n\t"
|
||||
"srl %[alpha_mask], %[alpha_mask], 8 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
: [temp0]"=&r"(temp0), [alpha_mask]"+r"(alpha_mask)
|
||||
:
|
||||
);
|
||||
__asm__ volatile(
|
||||
"ext %[temp0], %[alpha_mask], 0, 16 \n\t"
|
||||
"srl %[alpha_mask], %[alpha_mask], 16 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"ext %[temp0], %[alpha_mask], 0, 8 \n\t"
|
||||
"srl %[alpha_mask], %[alpha_mask], 8 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
: [temp0] "=&r"(temp0), [alpha_mask] "+r"(alpha_mask)
|
||||
:);
|
||||
|
||||
return (alpha_mask != 0xff);
|
||||
}
|
||||
|
||||
static void MultARGBRow_MIPSdspR2(uint32_t* const ptr, int width,
|
||||
int inverse) {
|
||||
static void MultARGBRow_MIPSdspR2(uint32_t* const ptr, int width, int inverse) {
|
||||
int x;
|
||||
const uint32_t c_00ffffff = 0x00ffffffu;
|
||||
const uint32_t c_ff000000 = 0xff000000u;
|
||||
const uint32_t c_8000000 = 0x00800000u;
|
||||
const uint32_t c_8000080 = 0x00800080u;
|
||||
const uint32_t c_8000000 = 0x00800000u;
|
||||
const uint32_t c_8000080 = 0x00800080u;
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t argb = ptr[x];
|
||||
if (argb < 0xff000000u) { // alpha < 255
|
||||
if (argb <= 0x00ffffffu) { // alpha == 0
|
||||
if (argb < 0xff000000u) { // alpha < 255
|
||||
if (argb <= 0x00ffffffu) { // alpha == 0
|
||||
ptr[x] = 0;
|
||||
} else {
|
||||
int temp0, temp1, temp2, temp3, alpha;
|
||||
__asm__ volatile (
|
||||
"srl %[alpha], %[argb], 24 \n\t"
|
||||
"replv.qb %[temp0], %[alpha] \n\t"
|
||||
"and %[temp0], %[temp0], %[c_00ffffff] \n\t"
|
||||
"beqz %[inverse], 0f \n\t"
|
||||
"divu $zero, %[c_ff000000], %[alpha] \n\t"
|
||||
"mflo %[temp0] \n\t"
|
||||
"0: \n\t"
|
||||
"andi %[temp1], %[argb], 0xff \n\t"
|
||||
"ext %[temp2], %[argb], 8, 8 \n\t"
|
||||
"ext %[temp3], %[argb], 16, 8 \n\t"
|
||||
"mul %[temp1], %[temp1], %[temp0] \n\t"
|
||||
"mul %[temp2], %[temp2], %[temp0] \n\t"
|
||||
"mul %[temp3], %[temp3], %[temp0] \n\t"
|
||||
"precrq.ph.w %[temp1], %[temp2], %[temp1] \n\t"
|
||||
"addu %[temp3], %[temp3], %[c_8000000] \n\t"
|
||||
"addu %[temp1], %[temp1], %[c_8000080] \n\t"
|
||||
"precrq.ph.w %[temp3], %[argb], %[temp3] \n\t"
|
||||
"precrq.qb.ph %[temp1], %[temp3], %[temp1] \n\t"
|
||||
: [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
|
||||
[temp3]"=&r"(temp3), [alpha]"=&r"(alpha)
|
||||
: [inverse]"r"(inverse), [c_00ffffff]"r"(c_00ffffff),
|
||||
[c_8000000]"r"(c_8000000), [c_8000080]"r"(c_8000080),
|
||||
[c_ff000000]"r"(c_ff000000), [argb]"r"(argb)
|
||||
: "memory", "hi", "lo"
|
||||
);
|
||||
__asm__ volatile(
|
||||
"srl %[alpha], %[argb], 24 \n\t"
|
||||
"replv.qb %[temp0], %[alpha] \n\t"
|
||||
"and %[temp0], %[temp0], %[c_00ffffff] \n\t"
|
||||
"beqz %[inverse], 0f \n\t"
|
||||
"divu $zero, %[c_ff000000], %[alpha] \n\t"
|
||||
"mflo %[temp0] \n\t"
|
||||
"0: \n\t"
|
||||
"andi %[temp1], %[argb], 0xff \n\t"
|
||||
"ext %[temp2], %[argb], 8, 8 \n\t"
|
||||
"ext %[temp3], %[argb], 16, 8 \n\t"
|
||||
"mul %[temp1], %[temp1], %[temp0] \n\t"
|
||||
"mul %[temp2], %[temp2], %[temp0] \n\t"
|
||||
"mul %[temp3], %[temp3], %[temp0] \n\t"
|
||||
"precrq.ph.w %[temp1], %[temp2], %[temp1] \n\t"
|
||||
"addu %[temp3], %[temp3], %[c_8000000] \n\t"
|
||||
"addu %[temp1], %[temp1], %[c_8000080] \n\t"
|
||||
"precrq.ph.w %[temp3], %[argb], %[temp3] \n\t"
|
||||
"precrq.qb.ph %[temp1], %[temp3], %[temp1] \n\t"
|
||||
: [temp0] "=&r"(temp0), [temp1] "=&r"(temp1), [temp2] "=&r"(temp2),
|
||||
[temp3] "=&r"(temp3), [alpha] "=&r"(alpha)
|
||||
: [inverse] "r"(inverse), [c_00ffffff] "r"(c_00ffffff),
|
||||
[c_8000000] "r"(c_8000000), [c_8000080] "r"(c_8000080),
|
||||
[c_ff000000] "r"(c_ff000000), [argb] "r"(argb)
|
||||
: "memory", "hi", "lo");
|
||||
ptr[x] = temp1;
|
||||
}
|
||||
}
|
||||
@@ -133,38 +128,37 @@ static void PackARGB_MIPSdspR2(const uint8_t* a, const uint8_t* r,
|
||||
const int rest = len & 1;
|
||||
const uint32_t* const loop_end = out + len - rest;
|
||||
const int step = 4;
|
||||
__asm__ volatile (
|
||||
"xor %[offset], %[offset], %[offset] \n\t"
|
||||
"beq %[loop_end], %[out], 0f \n\t"
|
||||
"2: \n\t"
|
||||
"lbux %[temp0], %[offset](%[a]) \n\t"
|
||||
"lbux %[temp1], %[offset](%[r]) \n\t"
|
||||
"lbux %[temp2], %[offset](%[g]) \n\t"
|
||||
"lbux %[temp3], %[offset](%[b]) \n\t"
|
||||
"ins %[temp1], %[temp0], 16, 16 \n\t"
|
||||
"ins %[temp3], %[temp2], 16, 16 \n\t"
|
||||
"addiu %[out], %[out], 4 \n\t"
|
||||
"precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t"
|
||||
"sw %[temp0], -4(%[out]) \n\t"
|
||||
"addu %[offset], %[offset], %[step] \n\t"
|
||||
"bne %[loop_end], %[out], 2b \n\t"
|
||||
"0: \n\t"
|
||||
"beq %[rest], $zero, 1f \n\t"
|
||||
"lbux %[temp0], %[offset](%[a]) \n\t"
|
||||
"lbux %[temp1], %[offset](%[r]) \n\t"
|
||||
"lbux %[temp2], %[offset](%[g]) \n\t"
|
||||
"lbux %[temp3], %[offset](%[b]) \n\t"
|
||||
"ins %[temp1], %[temp0], 16, 16 \n\t"
|
||||
"ins %[temp3], %[temp2], 16, 16 \n\t"
|
||||
"precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t"
|
||||
"sw %[temp0], 0(%[out]) \n\t"
|
||||
"1: \n\t"
|
||||
: [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
|
||||
[temp3]"=&r"(temp3), [offset]"=&r"(offset), [out]"+&r"(out)
|
||||
: [a]"r"(a), [r]"r"(r), [g]"r"(g), [b]"r"(b), [step]"r"(step),
|
||||
[loop_end]"r"(loop_end), [rest]"r"(rest)
|
||||
: "memory"
|
||||
);
|
||||
__asm__ volatile(
|
||||
"xor %[offset], %[offset], %[offset] \n\t"
|
||||
"beq %[loop_end], %[out], 0f \n\t"
|
||||
"2: \n\t"
|
||||
"lbux %[temp0], %[offset](%[a]) \n\t"
|
||||
"lbux %[temp1], %[offset](%[r]) \n\t"
|
||||
"lbux %[temp2], %[offset](%[g]) \n\t"
|
||||
"lbux %[temp3], %[offset](%[b]) \n\t"
|
||||
"ins %[temp1], %[temp0], 16, 16 \n\t"
|
||||
"ins %[temp3], %[temp2], 16, 16 \n\t"
|
||||
"addiu %[out], %[out], 4 \n\t"
|
||||
"precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t"
|
||||
"sw %[temp0], -4(%[out]) \n\t"
|
||||
"addu %[offset], %[offset], %[step] \n\t"
|
||||
"bne %[loop_end], %[out], 2b \n\t"
|
||||
"0: \n\t"
|
||||
"beq %[rest], $zero, 1f \n\t"
|
||||
"lbux %[temp0], %[offset](%[a]) \n\t"
|
||||
"lbux %[temp1], %[offset](%[r]) \n\t"
|
||||
"lbux %[temp2], %[offset](%[g]) \n\t"
|
||||
"lbux %[temp3], %[offset](%[b]) \n\t"
|
||||
"ins %[temp1], %[temp0], 16, 16 \n\t"
|
||||
"ins %[temp3], %[temp2], 16, 16 \n\t"
|
||||
"precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t"
|
||||
"sw %[temp0], 0(%[out]) \n\t"
|
||||
"1: \n\t"
|
||||
: [temp0] "=&r"(temp0), [temp1] "=&r"(temp1), [temp2] "=&r"(temp2),
|
||||
[temp3] "=&r"(temp3), [offset] "=&r"(offset), [out] "+&r"(out)
|
||||
: [a] "r"(a), [r] "r"(r), [g] "r"(g), [b] "r"(b), [step] "r"(step),
|
||||
[loop_end] "r"(loop_end), [rest] "r"(rest)
|
||||
: "memory");
|
||||
}
|
||||
#endif // WORDS_BIGENDIAN
|
||||
|
||||
@@ -175,36 +169,35 @@ static void PackRGB_MIPSdspR2(const uint8_t* r, const uint8_t* g,
|
||||
const int rest = len & 1;
|
||||
const int a = 0xff;
|
||||
const uint32_t* const loop_end = out + len - rest;
|
||||
__asm__ volatile (
|
||||
"xor %[offset], %[offset], %[offset] \n\t"
|
||||
"beq %[loop_end], %[out], 0f \n\t"
|
||||
"2: \n\t"
|
||||
"lbux %[temp0], %[offset](%[r]) \n\t"
|
||||
"lbux %[temp1], %[offset](%[g]) \n\t"
|
||||
"lbux %[temp2], %[offset](%[b]) \n\t"
|
||||
"ins %[temp0], %[a], 16, 16 \n\t"
|
||||
"ins %[temp2], %[temp1], 16, 16 \n\t"
|
||||
"addiu %[out], %[out], 4 \n\t"
|
||||
"precr.qb.ph %[temp0], %[temp0], %[temp2] \n\t"
|
||||
"sw %[temp0], -4(%[out]) \n\t"
|
||||
"addu %[offset], %[offset], %[step] \n\t"
|
||||
"bne %[loop_end], %[out], 2b \n\t"
|
||||
"0: \n\t"
|
||||
"beq %[rest], $zero, 1f \n\t"
|
||||
"lbux %[temp0], %[offset](%[r]) \n\t"
|
||||
"lbux %[temp1], %[offset](%[g]) \n\t"
|
||||
"lbux %[temp2], %[offset](%[b]) \n\t"
|
||||
"ins %[temp0], %[a], 16, 16 \n\t"
|
||||
"ins %[temp2], %[temp1], 16, 16 \n\t"
|
||||
"precr.qb.ph %[temp0], %[temp0], %[temp2] \n\t"
|
||||
"sw %[temp0], 0(%[out]) \n\t"
|
||||
"1: \n\t"
|
||||
: [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
|
||||
[offset]"=&r"(offset), [out]"+&r"(out)
|
||||
: [a]"r"(a), [r]"r"(r), [g]"r"(g), [b]"r"(b), [step]"r"(step),
|
||||
[loop_end]"r"(loop_end), [rest]"r"(rest)
|
||||
: "memory"
|
||||
);
|
||||
__asm__ volatile(
|
||||
"xor %[offset], %[offset], %[offset] \n\t"
|
||||
"beq %[loop_end], %[out], 0f \n\t"
|
||||
"2: \n\t"
|
||||
"lbux %[temp0], %[offset](%[r]) \n\t"
|
||||
"lbux %[temp1], %[offset](%[g]) \n\t"
|
||||
"lbux %[temp2], %[offset](%[b]) \n\t"
|
||||
"ins %[temp0], %[a], 16, 16 \n\t"
|
||||
"ins %[temp2], %[temp1], 16, 16 \n\t"
|
||||
"addiu %[out], %[out], 4 \n\t"
|
||||
"precr.qb.ph %[temp0], %[temp0], %[temp2] \n\t"
|
||||
"sw %[temp0], -4(%[out]) \n\t"
|
||||
"addu %[offset], %[offset], %[step] \n\t"
|
||||
"bne %[loop_end], %[out], 2b \n\t"
|
||||
"0: \n\t"
|
||||
"beq %[rest], $zero, 1f \n\t"
|
||||
"lbux %[temp0], %[offset](%[r]) \n\t"
|
||||
"lbux %[temp1], %[offset](%[g]) \n\t"
|
||||
"lbux %[temp2], %[offset](%[b]) \n\t"
|
||||
"ins %[temp0], %[a], 16, 16 \n\t"
|
||||
"ins %[temp2], %[temp1], 16, 16 \n\t"
|
||||
"precr.qb.ph %[temp0], %[temp0], %[temp2] \n\t"
|
||||
"sw %[temp0], 0(%[out]) \n\t"
|
||||
"1: \n\t"
|
||||
: [temp0] "=&r"(temp0), [temp1] "=&r"(temp1), [temp2] "=&r"(temp2),
|
||||
[offset] "=&r"(offset), [out] "+&r"(out)
|
||||
: [a] "r"(a), [r] "r"(r), [g] "r"(g), [b] "r"(b), [step] "r"(step),
|
||||
[loop_end] "r"(loop_end), [rest] "r"(rest)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user