mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-15 13:29:54 +02:00
Compare commits
40 Commits
Author | SHA1 | Date | |
---|---|---|---|
7b75522d5e | |||
3180b6f4b7 | |||
16f36e4e39 | |||
df9e129b0d | |||
28b53efd6f | |||
81efa2a3f1 | |||
d8481223b3 | |||
4fdc903597 | |||
fa58371c6a | |||
84fdd0d12e | |||
2b98df90cb | |||
61e372b7e3 | |||
7ae658a026 | |||
51c4907d32 | |||
666bd6c654 | |||
561cdce5bd | |||
aec2cf02d1 | |||
928a75deca | |||
5173d4ee6f | |||
5b081219c9 | |||
381b7b54a0 | |||
6ed15ea1cd | |||
22bbb24ea8 | |||
8b3fb2389b | |||
f435de9575 | |||
41521aed47 | |||
9f4d4a3f49 | |||
0fd7514b55 | |||
f95a996c64 | |||
fd198f7370 | |||
485ff86fbb | |||
4cd0582d50 | |||
6752904b2f | |||
b6284d8247 | |||
decf6f6b87 | |||
dea3e89983 | |||
632798ae6f | |||
dc1a9518bc | |||
9cf9841b5e | |||
a376e7b96a |
5
.mailmap
5
.mailmap
@ -1,7 +1,8 @@
|
||||
<johann.koenig@duck.com> <johannkoenig@google.com>
|
||||
Johann Koenig <johann.koenig@duck.com>
|
||||
Johann Koenig <johann.koenig@duck.com> <johannkoenig@google.com>
|
||||
Mikołaj Zalewski <mikolajz@google.com>
|
||||
Pascal Massimino <pascal.massimino@gmail.com>
|
||||
<pascal.massimino@gmail.com> <skal@google.com>
|
||||
Pascal Massimino <pascal.massimino@gmail.com> <skal@google.com>
|
||||
Vikas Arora <vikasa@google.com>
|
||||
<vikasa@google.com> <vikasa@gmail.com>
|
||||
<vikasa@google.com> <vikaas.arora@gmail.com>
|
||||
|
2
AUTHORS
2
AUTHORS
@ -7,7 +7,7 @@ Contributors:
|
||||
- James Zern (jzern at google dot com)
|
||||
- Jan Engelhardt (jengelh at medozas dot de)
|
||||
- Jehan (jehan at girinstud dot io)
|
||||
- Johann (johann dot koenig at duck dot com)
|
||||
- Johann Koenig (johann dot koenig at duck dot com)
|
||||
- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
|
||||
- Jyrki Alakuijala (jyrki at google dot com)
|
||||
- Konstantin Ivlev (tomskside at gmail dot com)
|
||||
|
@ -97,6 +97,7 @@ dsp_enc_srcs := \
|
||||
src/dsp/cost.c \
|
||||
src/dsp/cost_mips32.c \
|
||||
src/dsp/cost_mips_dsp_r2.c \
|
||||
src/dsp/cost_neon.$(NEON) \
|
||||
src/dsp/cost_sse2.c \
|
||||
src/dsp/enc.c \
|
||||
src/dsp/enc_mips32.c \
|
||||
|
@ -18,6 +18,11 @@ option(WEBP_NEAR_LOSSLESS "Enable near-lossless encoding" ON)
|
||||
option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces."
|
||||
OFF)
|
||||
|
||||
# Option needed for handling Unicode file names on Windows.
|
||||
if(WIN32)
|
||||
option(WEBP_UNICODE "Build Unicode executables." ON)
|
||||
endif()
|
||||
|
||||
if(WEBP_BUILD_WEBP_JS)
|
||||
set(WEBP_ENABLE_SIMD OFF)
|
||||
set(WEBP_BUILD_ANIM_UTILS OFF)
|
||||
@ -50,6 +55,11 @@ if(WEBP_ENABLE_SWAP_16BIT_CSP)
|
||||
add_definitions(-DWEBP_SWAP_16BIT_CSP=1)
|
||||
endif()
|
||||
|
||||
if(WEBP_UNICODE)
|
||||
# Windows recommends setting both UNICODE and _UNICODE.
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
endif()
|
||||
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix "\$\{prefix\}")
|
||||
set(libdir "\$\{prefix\}/lib")
|
||||
@ -546,7 +556,7 @@ if(WEBP_BUILD_WEBP_JS)
|
||||
target_include_directories(webp_wasm PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set_target_properties(
|
||||
webp_wasm
|
||||
PROPERTIES LINK_FLAGS "-s WASM=1 -s 'BINARYEN_METHOD=\"native-wasm\"' \
|
||||
PROPERTIES LINK_FLAGS "-s WASM=1 \
|
||||
-s EXPORTED_FUNCTIONS='[\"_WebpToSDL\"]' -s INVOKE_RUN=0 \
|
||||
-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
|
||||
target_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
|
||||
|
30
ChangeLog
30
ChangeLog
@ -1,15 +1,45 @@
|
||||
61e372b7 update NEWS
|
||||
7ae658a0 bump version to 1.0.2
|
||||
51c4907d update AUTHORS
|
||||
666bd6c6 man/cwebp.1: refine near-lossless text
|
||||
561cdce5 Clarify the doc about GetFeatures.
|
||||
aec2cf02 near_lossless: fix fuzzing-detected integer overflow
|
||||
928a75de webp: Fix VP8LBitWriterClone() bug
|
||||
5173d4ee neon IsFlat
|
||||
5b081219 IsFlat: inline when possible
|
||||
381b7b54 IsFlat: use int for thresh
|
||||
6ed15ea1 fix unprobable leak in webp_sdl.c
|
||||
22bbb24e Merge "IsFlat: return int"
|
||||
8b3fb238 Merge tag 'v1.0.1'
|
||||
f435de95 IsFlat: return int
|
||||
41521aed utils.h: only define WEBP_NEED_LOG_TABLE_8BIT when needed
|
||||
9f4d4a3f neon: GetResidualCost
|
||||
0fd7514b neon: SetResidualCoeffs
|
||||
f95a996c Simpler histogram clustering.
|
||||
e85d3313 update ChangeLog (tag: v1.0.1-rc2, tag: v1.0.1, origin/1.0.1, 1.0.1)
|
||||
fa8210e4 Fix pair update in stochastic entropy merging.
|
||||
fd198f73 add codereview.settings
|
||||
825389ac README.mux: add a reference to the AnimDecoder API
|
||||
3be698c3 CMake: fix webp_js compilation
|
||||
485ff86f Fix pair update in stochastic entropy merging.
|
||||
4cd0582d CMake: fix webp_js compilation
|
||||
4cbb4caf update NEWS
|
||||
f5a5918d bump version to 1.0.1
|
||||
d61385db Speed-up: Make sure we only initialize histograms when needed.
|
||||
6752904b Speed-up: Make sure we only initialize histograms when needed.
|
||||
0c570316 update AUTHORS
|
||||
301a2dda img2webp: add help note about arguments from a file
|
||||
f0abab92 Speedups for empty histograms.
|
||||
f2dfd925 Split HistogramAdd to only have the high level logic in C.
|
||||
06b7bc7d Fix compilation on windows and clang-cl+ninja.
|
||||
b6284d82 img2webp: add help note about arguments from a file
|
||||
decf6f6b Speedups for empty histograms.
|
||||
dea3e899 Split HistogramAdd to only have the high level logic in C.
|
||||
632798ae Merge "Fix compilation on windows and clang-cl+ninja."
|
||||
dc1a9518 Merge "libwebp: Unicode command tools on Windows"
|
||||
9cf9841b libwebp: Unicode command tools on Windows
|
||||
98179495 remove some minor TODOs
|
||||
a376e7b9 Fix compilation on windows and clang-cl+ninja.
|
||||
cbf82cc0 Remove AVX2 files.
|
||||
5030e902 Merge "TIFF decoder: remove unused KINV definition"
|
||||
ac543311 Remove a few more useless #defines
|
||||
|
18
Makefile.vc
18
Makefile.vc
@ -129,12 +129,16 @@ LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME)_dll.pdb
|
||||
CFGSET = TRUE
|
||||
!ENDIF
|
||||
|
||||
!IF "$(UNICODE)" == "1"
|
||||
CFLAGS = $(CFLAGS) /D_UNICODE /DUNICODE
|
||||
!ENDIF
|
||||
|
||||
#######################
|
||||
# Usage
|
||||
#
|
||||
!IF "$(CFGSET)" == "FALSE"
|
||||
!MESSAGE Usage: nmake /f Makefile.vc [CFG=<config>]
|
||||
!MESSAGE . [OBJDIR=<path>] [RTLIBCFG=<rtlib>] [<target>]
|
||||
!MESSAGE . [OBJDIR=<path>] [RTLIBCFG=<rtlib>] [UNICODE=1] [<target>]
|
||||
!MESSAGE
|
||||
!MESSAGE where <config> is one of:
|
||||
!MESSAGE - release-static - release static library
|
||||
@ -234,6 +238,7 @@ DSP_ENC_OBJS = \
|
||||
$(DIROBJ)\dsp\cost.obj \
|
||||
$(DIROBJ)\dsp\cost_mips32.obj \
|
||||
$(DIROBJ)\dsp\cost_mips_dsp_r2.obj \
|
||||
$(DIROBJ)\dsp\cost_neon.obj \
|
||||
$(DIROBJ)\dsp\cost_sse2.obj \
|
||||
$(DIROBJ)\dsp\enc.obj \
|
||||
$(DIROBJ)\dsp\enc_mips32.obj \
|
||||
@ -483,15 +488,18 @@ $(DIROBJ)\examples\gifdec.obj: examples\gifdec.c
|
||||
{src\utils}.c{$(DIROBJ)\utils}.obj::
|
||||
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\utils\ $<
|
||||
|
||||
LNKLIBS = ole32.lib windowscodecs.lib shlwapi.lib
|
||||
!IF "$(UNICODE)" == "1"
|
||||
LNKLIBS = $(LNKLIBS) Shell32.lib
|
||||
!ENDIF
|
||||
|
||||
{$(DIROBJ)\examples}.obj{$(DIRBIN)}.exe:
|
||||
$(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
|
||||
ole32.lib windowscodecs.lib shlwapi.lib
|
||||
$(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS)
|
||||
$(MT) -manifest $@.manifest -outputresource:$@;1
|
||||
del $@.manifest
|
||||
|
||||
{$(DIROBJ)\extras}.obj{$(DIRBIN)}.exe:
|
||||
$(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
|
||||
ole32.lib windowscodecs.lib shlwapi.lib
|
||||
$(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS)
|
||||
$(MT) -manifest $@.manifest -outputresource:$@;1
|
||||
del $@.manifest
|
||||
|
||||
|
8
NEWS
8
NEWS
@ -1,3 +1,11 @@
|
||||
- 1/14/2019: version 1.0.2
|
||||
This is a binary compatible release.
|
||||
* (Windows) unicode file support in the tools (linux and mac already had
|
||||
support, issue #398)
|
||||
* lossless encoder speedups
|
||||
* lossy encoder speedup on ARM
|
||||
* lossless multi-threaded security fix (chromium:917029)
|
||||
|
||||
- 11/2/2018: version 1.0.1
|
||||
This is a binary compatible release.
|
||||
* lossless encoder speedups
|
||||
|
4
README
4
README
@ -4,7 +4,7 @@
|
||||
\__\__/\____/\_____/__/ ____ ___
|
||||
/ _/ / \ \ / _ \/ _/
|
||||
/ \_/ / / \ \ __/ \__
|
||||
\____/____/\_____/_____/____/v1.0.1
|
||||
\____/____/\_____/_____/____/v1.0.2
|
||||
|
||||
Description:
|
||||
============
|
||||
@ -136,6 +136,8 @@ cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
|
||||
|
||||
or through your favorite interface (like ccmake or cmake-qt-gui).
|
||||
|
||||
Use option -DWEBP_UNICODE=ON for Unicode support on Windows (with chcp 65001).
|
||||
|
||||
Finally, once installed, you can also use WebP in your CMake project by doing:
|
||||
|
||||
find_package(WebP)
|
||||
|
@ -1,7 +1,7 @@
|
||||
__ __ ____ ____ ____ __ __ _ __ __
|
||||
/ \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
|
||||
\ / __/ _ \ __/ / / (_/ /__
|
||||
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v1.0.1
|
||||
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v1.0.2
|
||||
|
||||
|
||||
Description:
|
||||
|
@ -173,6 +173,7 @@ model {
|
||||
include "cost.c"
|
||||
include "cost_mips32.c"
|
||||
include "cost_mips_dsp_r2.c"
|
||||
include "cost_neon.$NEON"
|
||||
include "cost_sse2.c"
|
||||
include "enc.c"
|
||||
include "enc_mips32.c"
|
||||
|
4
codereview.settings
Normal file
4
codereview.settings
Normal file
@ -0,0 +1,4 @@
|
||||
# This file is used by git cl to get repository specific information.
|
||||
GERRIT_HOST: True
|
||||
CODE_REVIEW_SERVER: chromium-review.googlesource.com
|
||||
GERRIT_SQUASH_UPLOADS: False
|
@ -1,4 +1,4 @@
|
||||
AC_INIT([libwebp], [1.0.1],
|
||||
AC_INIT([libwebp], [1.0.2],
|
||||
[https://bugs.chromium.org/p/webp],,
|
||||
[http://developers.google.com/speed/webp])
|
||||
AC_CANONICAL_HOST
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "./anim_util.h"
|
||||
#include "./example_util.h"
|
||||
#include "./unicode.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@ -218,12 +219,14 @@ int main(int argc, const char* argv[]) {
|
||||
const char* files[2] = { NULL, NULL };
|
||||
AnimatedImage images[2];
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-dump_frames")) {
|
||||
if (c < argc - 1) {
|
||||
dump_frames = 1;
|
||||
dump_folder = argv[++c];
|
||||
dump_folder = (const char*)GET_WARGV(argv, ++c);
|
||||
} else {
|
||||
parse_error = 1;
|
||||
}
|
||||
@ -243,7 +246,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
} else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
Help();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
int dec_version, demux_version;
|
||||
GetAnimatedImageVersions(&dec_version, &demux_version);
|
||||
@ -252,13 +255,13 @@ int main(int argc, const char* argv[]) {
|
||||
(dec_version >> 0) & 0xff,
|
||||
(demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
|
||||
(demux_version >> 0) & 0xff);
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
if (!got_input1) {
|
||||
files[0] = argv[c];
|
||||
files[0] = (const char*)GET_WARGV(argv, c);
|
||||
got_input1 = 1;
|
||||
} else if (!got_input2) {
|
||||
files[1] = argv[c];
|
||||
files[1] = (const char*)GET_WARGV(argv, c);
|
||||
got_input2 = 1;
|
||||
} else {
|
||||
parse_error = 1;
|
||||
@ -266,29 +269,30 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
if (parse_error) {
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
if (argc < 3) {
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
|
||||
if (!got_input2) {
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
if (dump_frames) {
|
||||
printf("Dumping decoded frames in: %s\n", dump_folder);
|
||||
WPRINTF("Dumping decoded frames in: %s\n", (const W_CHAR*)dump_folder);
|
||||
}
|
||||
|
||||
memset(images, 0, sizeof(images));
|
||||
for (i = 0; i < 2; ++i) {
|
||||
printf("Decoding file: %s\n", files[i]);
|
||||
WPRINTF("Decoding file: %s\n", (const W_CHAR*)files[i]);
|
||||
if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) {
|
||||
fprintf(stderr, "Error decoding file: %s\n Aborting.\n", files[i]);
|
||||
WFPRINTF(stderr, "Error decoding file: %s\n Aborting.\n",
|
||||
(const W_CHAR*)files[i]);
|
||||
return_code = -2;
|
||||
goto End;
|
||||
} else {
|
||||
@ -298,14 +302,16 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (!CompareAnimatedImagePair(&images[0], &images[1],
|
||||
premultiply, min_psnr)) {
|
||||
fprintf(stderr, "\nFiles %s and %s differ.\n", files[0], files[1]);
|
||||
WFPRINTF(stderr, "\nFiles %s and %s differ.\n", (const W_CHAR*)files[0],
|
||||
(const W_CHAR*)files[1]);
|
||||
return_code = -3;
|
||||
} else {
|
||||
printf("\nFiles %s and %s are identical.\n", files[0], files[1]);
|
||||
WPRINTF("\nFiles %s and %s are identical.\n", (const W_CHAR*)files[0],
|
||||
(const W_CHAR*)files[1]);
|
||||
return_code = 0;
|
||||
}
|
||||
End:
|
||||
ClearAnimatedImage(&images[0]);
|
||||
ClearAnimatedImage(&images[1]);
|
||||
return return_code;
|
||||
FREE_WARGV_AND_RETURN(return_code);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "./anim_util.h"
|
||||
#include "webp/decode.h"
|
||||
#include "../imageio/image_enc.h"
|
||||
#include "./unicode.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@ -36,15 +37,17 @@ static void Help(void) {
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
int error = 0;
|
||||
const char* dump_folder = ".";
|
||||
const char* prefix = "dump_";
|
||||
const char* suffix = "png";
|
||||
const W_CHAR* dump_folder = TO_W_CHAR(".");
|
||||
const W_CHAR* prefix = TO_W_CHAR("dump_");
|
||||
const W_CHAR* suffix = TO_W_CHAR("png");
|
||||
WebPOutputFileFormat format = PNG;
|
||||
int c;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
if (argc < 2) {
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
for (c = 1; !error && c < argc; ++c) {
|
||||
@ -54,23 +57,23 @@ int main(int argc, const char* argv[]) {
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
dump_folder = argv[++c];
|
||||
dump_folder = GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-prefix")) {
|
||||
if (c + 1 == argc) {
|
||||
fprintf(stderr, "missing argument after option '%s'\n", argv[c]);
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
prefix = argv[++c];
|
||||
prefix = GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-tiff")) {
|
||||
format = TIFF;
|
||||
suffix = "tiff";
|
||||
suffix = TO_W_CHAR("tiff");
|
||||
} else if (!strcmp(argv[c], "-pam")) {
|
||||
format = PAM;
|
||||
suffix = "pam";
|
||||
suffix = TO_W_CHAR("pam");
|
||||
} else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
Help();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-version")) {
|
||||
int dec_version, demux_version;
|
||||
GetAnimatedImageVersions(&dec_version, &demux_version);
|
||||
@ -79,21 +82,21 @@ int main(int argc, const char* argv[]) {
|
||||
(dec_version >> 0) & 0xff,
|
||||
(demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
|
||||
(demux_version >> 0) & 0xff);
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
uint32_t i;
|
||||
AnimatedImage image;
|
||||
const char* const file = argv[c];
|
||||
const W_CHAR* const file = GET_WARGV(argv, c);
|
||||
memset(&image, 0, sizeof(image));
|
||||
printf("Decoding file: %s as %s/%sxxxx.%s\n",
|
||||
file, dump_folder, prefix, suffix);
|
||||
if (!ReadAnimatedImage(file, &image, 0, NULL)) {
|
||||
fprintf(stderr, "Error decoding file: %s\n Aborting.\n", file);
|
||||
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;
|
||||
break;
|
||||
}
|
||||
for (i = 0; !error && i < image.num_frames; ++i) {
|
||||
char out_file[1024];
|
||||
W_CHAR out_file[1024];
|
||||
WebPDecBuffer buffer;
|
||||
WebPInitDecBuffer(&buffer);
|
||||
buffer.colorspace = MODE_RGBA;
|
||||
@ -103,10 +106,10 @@ 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;
|
||||
snprintf(out_file, sizeof(out_file), "%s/%s%.4d.%s",
|
||||
dump_folder, prefix, i, suffix);
|
||||
if (!WebPSaveImage(&buffer, format, out_file)) {
|
||||
fprintf(stderr, "Error while saving image '%s'\n", out_file);
|
||||
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;
|
||||
}
|
||||
WebPFreeDecBuffer(&buffer);
|
||||
@ -114,5 +117,5 @@ int main(int argc, const char* argv[]) {
|
||||
ClearAnimatedImage(&image);
|
||||
}
|
||||
}
|
||||
return error ? 1 : 0;
|
||||
FREE_WARGV_AND_RETURN(error ? 1 : 0);
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "webp/demux.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./gifdec.h"
|
||||
#include "./unicode.h"
|
||||
#include "./unicode_gif.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@ -152,42 +154,42 @@ static int DumpFrame(const char filename[], const char dump_folder[],
|
||||
int ok = 0;
|
||||
size_t max_len;
|
||||
int y;
|
||||
const char* base_name = NULL;
|
||||
char* file_name = NULL;
|
||||
const W_CHAR* base_name = NULL;
|
||||
W_CHAR* file_name = NULL;
|
||||
FILE* f = NULL;
|
||||
const char* row;
|
||||
|
||||
if (dump_folder == NULL) dump_folder = ".";
|
||||
if (dump_folder == NULL) dump_folder = (const char*)TO_W_CHAR(".");
|
||||
|
||||
base_name = strrchr(filename, '/');
|
||||
base_name = (base_name == NULL) ? filename : base_name + 1;
|
||||
max_len = strlen(dump_folder) + 1 + strlen(base_name)
|
||||
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;
|
||||
file_name = (char*)malloc(max_len * sizeof(*file_name));
|
||||
file_name = (W_CHAR*)malloc(max_len * sizeof(*file_name));
|
||||
if (file_name == NULL) goto End;
|
||||
|
||||
if (snprintf(file_name, max_len, "%s/%s_frame_%d.pam",
|
||||
dump_folder, base_name, frame_num) < 0) {
|
||||
if (WSNPRINTF(file_name, max_len, "%s/%s_frame_%d.pam",
|
||||
(const W_CHAR*)dump_folder, base_name, frame_num) < 0) {
|
||||
fprintf(stderr, "Error while generating file name\n");
|
||||
goto End;
|
||||
}
|
||||
|
||||
f = fopen(file_name, "wb");
|
||||
f = WFOPEN(file_name, "wb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "Error opening file for writing: %s\n", file_name);
|
||||
WFPRINTF(stderr, "Error opening file for writing: %s\n", file_name);
|
||||
ok = 0;
|
||||
goto End;
|
||||
}
|
||||
if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n"
|
||||
"DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
|
||||
canvas_width, canvas_height) < 0) {
|
||||
fprintf(stderr, "Write error for file %s\n", file_name);
|
||||
WFPRINTF(stderr, "Write error for file %s\n", file_name);
|
||||
goto End;
|
||||
}
|
||||
row = (const char*)rgba;
|
||||
for (y = 0; y < canvas_height; ++y) {
|
||||
if (fwrite(row, canvas_width * kNumChannels, 1, f) != 1) {
|
||||
fprintf(stderr, "Error writing to file: %s\n", file_name);
|
||||
WFPRINTF(stderr, "Error writing to file: %s\n", file_name);
|
||||
goto End;
|
||||
}
|
||||
row += canvas_width * kNumChannels;
|
||||
@ -223,7 +225,7 @@ static int ReadAnimatedWebP(const char filename[],
|
||||
|
||||
dec = WebPAnimDecoderNew(webp_data, NULL);
|
||||
if (dec == NULL) {
|
||||
fprintf(stderr, "Error parsing image: %s\n", filename);
|
||||
WFPRINTF(stderr, "Error parsing image: %s\n", (const W_CHAR*)filename);
|
||||
goto End;
|
||||
}
|
||||
|
||||
@ -511,15 +513,15 @@ static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||
int gif_error;
|
||||
GifFileType* gif;
|
||||
|
||||
gif = DGifOpenFileName(filename, NULL);
|
||||
gif = DGifOpenFileUnicode((const W_CHAR*)filename, NULL);
|
||||
if (gif == NULL) {
|
||||
fprintf(stderr, "Could not read file: %s.\n", filename);
|
||||
WFPRINTF(stderr, "Could not read file: %s.\n", (const W_CHAR*)filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gif_error = DGifSlurp(gif);
|
||||
if (gif_error != GIF_OK) {
|
||||
fprintf(stderr, "Could not parse image: %s.\n", filename);
|
||||
WFPRINTF(stderr, "Could not parse image: %s.\n", (const W_CHAR*)filename);
|
||||
GIFDisplayError(gif, gif_error);
|
||||
DGifCloseFile(gif, NULL);
|
||||
return 0;
|
||||
@ -705,7 +707,7 @@ int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
|
||||
memset(image, 0, sizeof(*image));
|
||||
|
||||
if (!ImgIoUtilReadFile(filename, &webp_data.bytes, &webp_data.size)) {
|
||||
fprintf(stderr, "Error reading file: %s\n", filename);
|
||||
WFPRINTF(stderr, "Error reading file: %s\n", (const W_CHAR*)filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -715,9 +717,9 @@ int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
|
||||
} else if (IsGIF(&webp_data)) {
|
||||
ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Unknown file type: %s. Supported file types are WebP and GIF\n",
|
||||
filename);
|
||||
WFPRINTF(stderr,
|
||||
"Unknown file type: %s. Supported file types are WebP and GIF\n",
|
||||
(const W_CHAR*)filename);
|
||||
ok = 0;
|
||||
}
|
||||
if (!ok) ClearAnimatedImage(image);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "../imageio/image_dec.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./stopwatch.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/encode.h"
|
||||
|
||||
#ifndef WEBP_DLL
|
||||
@ -88,7 +89,8 @@ static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
||||
WFPRINTF(stderr, "Error! Could not process file %s\n",
|
||||
(const W_CHAR*)filename);
|
||||
}
|
||||
free((void*)data);
|
||||
return ok;
|
||||
@ -114,7 +116,8 @@ static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
}
|
||||
End:
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
||||
WFPRINTF(stderr, "Error! Could not process file %s\n",
|
||||
(const W_CHAR*)filename);
|
||||
}
|
||||
free((void*)data);
|
||||
return ok;
|
||||
@ -185,7 +188,7 @@ static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
||||
if (short_output) {
|
||||
fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
|
||||
} else {
|
||||
fprintf(stderr, "File: %s\n", file_name);
|
||||
WFPRINTF(stderr, "File: %s\n", (const W_CHAR*)file_name);
|
||||
fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
|
||||
fprintf(stderr, "Output: %d bytes (%.2f bpp)\n", stats->coded_size,
|
||||
8.f * stats->coded_size / pic->width / pic->height);
|
||||
@ -204,7 +207,7 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
||||
const int num_i16 = stats->block_count[1];
|
||||
const int num_skip = stats->block_count[2];
|
||||
const int total = num_i4 + num_i16;
|
||||
fprintf(stderr, "File: %s\n", file_name);
|
||||
WFPRINTF(stderr, "File: %s\n", (const W_CHAR*)file_name);
|
||||
fprintf(stderr, "Dimension: %d x %d%s\n",
|
||||
pic->width, pic->height,
|
||||
stats->alpha_data_size ? " (with alpha)" : "");
|
||||
@ -309,7 +312,7 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
|
||||
const int alpha_height =
|
||||
WebPPictureHasTransparency(picture) ? picture->height : 0;
|
||||
const int height = picture->height + uv_height + alpha_height;
|
||||
FILE* const f = fopen(PGM_name, "wb");
|
||||
FILE* const f = WFOPEN(PGM_name, "wb");
|
||||
if (f == NULL) return 0;
|
||||
fprintf(f, "P5\n%d %d\n255\n", stride, height);
|
||||
for (y = 0; y < picture->height; ++y) {
|
||||
@ -663,32 +666,34 @@ int main(int argc, const char *argv[]) {
|
||||
Metadata metadata;
|
||||
Stopwatch stop_watch;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
MetadataInit(&metadata);
|
||||
WebPMemoryWriterInit(&memory_writer);
|
||||
if (!WebPPictureInit(&picture) ||
|
||||
!WebPPictureInit(&original_picture) ||
|
||||
!WebPConfigInit(&config)) {
|
||||
fprintf(stderr, "Error! Version mismatch!\n");
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
HelpShort();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
HelpShort();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
|
||||
HelpLong();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
|
||||
out_file = argv[++c];
|
||||
out_file = (const char*)GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-d") && c < argc - 1) {
|
||||
dump_file = argv[++c];
|
||||
dump_file = (const char*)GET_WARGV(argv, ++c);
|
||||
config.show_compressed = 1;
|
||||
} else if (!strcmp(argv[c], "-print_psnr")) {
|
||||
config.show_compressed = 1;
|
||||
@ -816,7 +821,7 @@ int main(int argc, const char *argv[]) {
|
||||
const int version = WebPGetEncoderVersion();
|
||||
printf("%d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-progress")) {
|
||||
show_progress = 1;
|
||||
} else if (!strcmp(argv[c], "-quiet")) {
|
||||
@ -878,8 +883,7 @@ int main(int argc, const char *argv[]) {
|
||||
if (i == kNumTokens) {
|
||||
fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
|
||||
(int)(token - start), start);
|
||||
HelpLong();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
start = token + 1;
|
||||
}
|
||||
@ -893,19 +897,19 @@ int main(int argc, const char *argv[]) {
|
||||
} else if (!strcmp(argv[c], "-v")) {
|
||||
verbose = 1;
|
||||
} else if (!strcmp(argv[c], "--")) {
|
||||
if (c < argc - 1) in_file = argv[++c];
|
||||
if (c < argc - 1) in_file = (const char*)GET_WARGV(argv, ++c);
|
||||
break;
|
||||
} else if (argv[c][0] == '-') {
|
||||
fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
|
||||
HelpLong();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
} else {
|
||||
in_file = argv[c];
|
||||
in_file = (const char*)GET_WARGV(argv, c);
|
||||
}
|
||||
|
||||
if (parse_error) {
|
||||
HelpLong();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
if (in_file == NULL) {
|
||||
@ -955,7 +959,8 @@ int main(int argc, const char *argv[]) {
|
||||
}
|
||||
if (!ReadPicture(in_file, &picture, keep_alpha,
|
||||
(keep_metadata == 0) ? NULL : &metadata)) {
|
||||
fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
|
||||
WFPRINTF(stderr, "Error! Cannot read input picture file '%s'\n",
|
||||
(const W_CHAR*)in_file);
|
||||
goto Error;
|
||||
}
|
||||
picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL;
|
||||
@ -971,14 +976,15 @@ int main(int argc, const char *argv[]) {
|
||||
|
||||
// Open the output
|
||||
if (out_file != NULL) {
|
||||
const int use_stdout = !strcmp(out_file, "-");
|
||||
out = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(out_file, "wb");
|
||||
const int use_stdout = !WSTRCMP(out_file, "-");
|
||||
out = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : WFOPEN(out_file, "wb");
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
|
||||
WFPRINTF(stderr, "Error! Cannot open output file '%s'\n",
|
||||
(const W_CHAR*)out_file);
|
||||
goto Error;
|
||||
} else {
|
||||
if (!short_output && !quiet) {
|
||||
fprintf(stderr, "Saving file '%s'\n", out_file);
|
||||
WFPRINTF(stderr, "Saving file '%s'\n", (const W_CHAR*)out_file);
|
||||
}
|
||||
}
|
||||
if (keep_metadata == 0) {
|
||||
@ -1093,7 +1099,8 @@ int main(int argc, const char *argv[]) {
|
||||
fprintf(stderr, "Warning: can't dump file (-d option) "
|
||||
"in lossless mode.\n");
|
||||
} else if (!DumpPicture(&picture, dump_file)) {
|
||||
fprintf(stderr, "Warning, couldn't dump picture %s\n", dump_file);
|
||||
WFPRINTF(stderr, "Warning, couldn't dump picture %s\n",
|
||||
(const W_CHAR*)dump_file);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1169,7 +1176,7 @@ int main(int argc, const char *argv[]) {
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
FREE_WARGV_AND_RETURN(return_value);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "../imageio/image_enc.h"
|
||||
#include "../imageio/webpdec.h"
|
||||
#include "./stopwatch.h"
|
||||
#include "./unicode.h"
|
||||
|
||||
static int verbose = 0;
|
||||
static int quiet = 0;
|
||||
@ -42,7 +43,7 @@ extern void* VP8GetCPUInfo; // opaque forward declaration.
|
||||
|
||||
static int SaveOutput(const WebPDecBuffer* const buffer,
|
||||
WebPOutputFileFormat format, const char* const out_file) {
|
||||
const int use_stdout = (out_file != NULL) && !strcmp(out_file, "-");
|
||||
const int use_stdout = (out_file != NULL) && !WSTRCMP(out_file, "-");
|
||||
int ok = 1;
|
||||
Stopwatch stop_watch;
|
||||
|
||||
@ -56,7 +57,7 @@ static int SaveOutput(const WebPDecBuffer* const buffer,
|
||||
if (use_stdout) {
|
||||
fprintf(stderr, "Saved to stdout\n");
|
||||
} else {
|
||||
fprintf(stderr, "Saved file %s\n", out_file);
|
||||
WFPRINTF(stderr, "Saved file %s\n", (const W_CHAR*)out_file);
|
||||
}
|
||||
}
|
||||
if (verbose) {
|
||||
@ -67,7 +68,7 @@ static int SaveOutput(const WebPDecBuffer* const buffer,
|
||||
if (use_stdout) {
|
||||
fprintf(stderr, "Error writing to stdout !!\n");
|
||||
} else {
|
||||
fprintf(stderr, "Error writing file %s !!\n", out_file);
|
||||
WFPRINTF(stderr, "Error writing file %s !!\n", (const W_CHAR*)out_file);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
@ -191,18 +192,20 @@ int main(int argc, const char *argv[]) {
|
||||
int incremental = 0;
|
||||
int c;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
if (!WebPInitDecoderConfig(&config)) {
|
||||
fprintf(stderr, "Library version mismatch!\n");
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
Help();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
|
||||
out_file = argv[++c];
|
||||
out_file = (const char*)GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-alpha")) {
|
||||
format = ALPHA_PLANE_ONLY;
|
||||
} else if (!strcmp(argv[c], "-nofancy")) {
|
||||
@ -223,7 +226,7 @@ int main(int argc, const char *argv[]) {
|
||||
const int version = WebPGetDecoderVersion();
|
||||
printf("%d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-pgm")) {
|
||||
format = PGM;
|
||||
} else if (!strcmp(argv[c], "-yuv")) {
|
||||
@ -284,26 +287,26 @@ int main(int argc, const char *argv[]) {
|
||||
} else if (!strcmp(argv[c], "-incremental")) {
|
||||
incremental = 1;
|
||||
} else if (!strcmp(argv[c], "--")) {
|
||||
if (c < argc - 1) in_file = argv[++c];
|
||||
if (c < argc - 1) in_file = (const char*)GET_WARGV(argv, ++c);
|
||||
break;
|
||||
} else if (argv[c][0] == '-') {
|
||||
fprintf(stderr, "Unknown option '%s'\n", argv[c]);
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
} else {
|
||||
in_file = argv[c];
|
||||
in_file = (const char*)GET_WARGV(argv, c);
|
||||
}
|
||||
|
||||
if (parse_error) {
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_file == NULL) {
|
||||
fprintf(stderr, "missing input file!!\n");
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
if (quiet) verbose = 0;
|
||||
@ -312,7 +315,7 @@ int main(int argc, const char *argv[]) {
|
||||
VP8StatusCode status = VP8_STATUS_OK;
|
||||
size_t data_size = 0;
|
||||
if (!LoadWebP(in_file, &data, &data_size, bitstream)) {
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
@ -389,18 +392,18 @@ int main(int argc, const char *argv[]) {
|
||||
|
||||
if (out_file != NULL) {
|
||||
if (!quiet) {
|
||||
fprintf(stderr, "Decoded %s. Dimensions: %d x %d %s. Format: %s. "
|
||||
"Now saving...\n",
|
||||
in_file, output_buffer->width, output_buffer->height,
|
||||
WFPRINTF(stderr, "Decoded %s.", (const W_CHAR*)in_file);
|
||||
fprintf(stderr, " Dimensions: %d x %d %s. Format: %s. Now saving...\n",
|
||||
output_buffer->width, output_buffer->height,
|
||||
bitstream->has_alpha ? " (with alpha)" : "",
|
||||
kFormatType[bitstream->format]);
|
||||
}
|
||||
ok = SaveOutput(output_buffer, format, out_file);
|
||||
} else {
|
||||
if (!quiet) {
|
||||
fprintf(stderr, "File %s can be decoded "
|
||||
"(dimensions: %d x %d %s. Format: %s).\n",
|
||||
in_file, output_buffer->width, output_buffer->height,
|
||||
WFPRINTF(stderr, "File %s can be decoded ", (const W_CHAR*)in_file);
|
||||
fprintf(stderr, "(dimensions: %d x %d %s. Format: %s).\n",
|
||||
output_buffer->width, output_buffer->height,
|
||||
bitstream->has_alpha ? " (with alpha)" : "",
|
||||
kFormatType[bitstream->format]);
|
||||
fprintf(stderr, "Nothing written; "
|
||||
@ -411,7 +414,7 @@ int main(int argc, const char *argv[]) {
|
||||
WebPFreeDecBuffer(output_buffer);
|
||||
free((void*)external_buffer);
|
||||
free((void*)data);
|
||||
return ok ? 0 : -1;
|
||||
FREE_WARGV_AND_RETURN(ok ? 0 : -1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -90,6 +90,14 @@ int ExUtilInitCommandLineArguments(int argc, const char* argv[],
|
||||
if (argc == 1 && argv[0][0] != '-') {
|
||||
char* cur;
|
||||
const char sep[] = " \t\r\n\f\v";
|
||||
|
||||
#if defined(_WIN32) && defined(_UNICODE)
|
||||
fprintf(stderr,
|
||||
"Error: Reading arguments from a file is a feature unavailable "
|
||||
"with Unicode binaries.\n");
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data_)) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include "../examples/example_util.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./gifdec.h"
|
||||
#include "./unicode.h"
|
||||
#include "./unicode_gif.h"
|
||||
|
||||
#if !defined(STDIN_FILENO)
|
||||
#define STDIN_FILENO 0
|
||||
@ -99,7 +101,7 @@ int main(int argc, const char *argv[]) {
|
||||
int gif_error = GIF_ERROR;
|
||||
WebPMuxError err = WEBP_MUX_OK;
|
||||
int ok = 0;
|
||||
const 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;
|
||||
@ -132,11 +134,13 @@ int main(int argc, const char *argv[]) {
|
||||
int default_kmin = 1; // Whether to use default kmin value.
|
||||
int default_kmax = 1;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
if (!WebPConfigInit(&config) || !WebPAnimEncoderOptionsInit(&enc_options) ||
|
||||
!WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) ||
|
||||
!WebPPictureInit(&prev_canvas)) {
|
||||
fprintf(stderr, "Error! Version mismatch!\n");
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
config.lossless = 1; // Use lossless compression by default.
|
||||
|
||||
@ -146,16 +150,16 @@ int main(int argc, const char *argv[]) {
|
||||
|
||||
if (argc == 1) {
|
||||
Help();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
Help();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
|
||||
out_file = argv[++c];
|
||||
out_file = GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-lossy")) {
|
||||
config.lossless = 0;
|
||||
} else if (!strcmp(argv[c], "-mixed")) {
|
||||
@ -212,7 +216,7 @@ int main(int argc, const char *argv[]) {
|
||||
fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
|
||||
(int)(token - start), start);
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
start = token + 1;
|
||||
}
|
||||
@ -225,7 +229,7 @@ int main(int argc, const char *argv[]) {
|
||||
(enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
|
||||
enc_version & 0xff, (mux_version >> 16) & 0xff,
|
||||
(mux_version >> 8) & 0xff, mux_version & 0xff);
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-quiet")) {
|
||||
quiet = 1;
|
||||
enc_options.verbose = 0;
|
||||
@ -233,19 +237,19 @@ int main(int argc, const char *argv[]) {
|
||||
verbose = 1;
|
||||
enc_options.verbose = 1;
|
||||
} else if (!strcmp(argv[c], "--")) {
|
||||
if (c < argc - 1) in_file = argv[++c];
|
||||
if (c < argc - 1) in_file = GET_WARGV(argv, ++c);
|
||||
break;
|
||||
} else if (argv[c][0] == '-') {
|
||||
fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
} else {
|
||||
in_file = argv[c];
|
||||
in_file = GET_WARGV(argv, c);
|
||||
}
|
||||
|
||||
if (parse_error) {
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,13 +273,7 @@ int main(int argc, const char *argv[]) {
|
||||
}
|
||||
|
||||
// Start the decoder object
|
||||
#if LOCAL_GIF_PREREQ(5,0)
|
||||
gif = !strcmp(in_file, "-") ? DGifOpenFileHandle(STDIN_FILENO, &gif_error)
|
||||
: DGifOpenFileName(in_file, &gif_error);
|
||||
#else
|
||||
gif = !strcmp(in_file, "-") ? DGifOpenFileHandle(STDIN_FILENO)
|
||||
: DGifOpenFileName(in_file);
|
||||
#endif
|
||||
gif = DGifOpenFileUnicode(in_file, &gif_error);
|
||||
if (gif == NULL) goto End;
|
||||
|
||||
// Loop over GIF images
|
||||
@ -544,17 +542,18 @@ int main(int argc, const char *argv[]) {
|
||||
}
|
||||
|
||||
if (out_file != NULL) {
|
||||
if (!ImgIoUtilWriteFile(out_file, webp_data.bytes, webp_data.size)) {
|
||||
fprintf(stderr, "Error writing output file: %s\n", out_file);
|
||||
if (!ImgIoUtilWriteFile((const char*)out_file, webp_data.bytes,
|
||||
webp_data.size)) {
|
||||
WFPRINTF(stderr, "Error writing output file: %s\n", out_file);
|
||||
goto End;
|
||||
}
|
||||
if (!quiet) {
|
||||
if (!strcmp(out_file, "-")) {
|
||||
if (!WSTRCMP(out_file, "-")) {
|
||||
fprintf(stderr, "Saved %d bytes to STDIO\n",
|
||||
(int)webp_data.size);
|
||||
} else {
|
||||
fprintf(stderr, "Saved output file (%d bytes): %s\n",
|
||||
(int)webp_data.size, out_file);
|
||||
WFPRINTF(stderr, "Saved output file (%d bytes): %s\n",
|
||||
(int)webp_data.size, out_file);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -589,7 +588,7 @@ int main(int argc, const char *argv[]) {
|
||||
#endif
|
||||
}
|
||||
|
||||
return !ok;
|
||||
FREE_WARGV_AND_RETURN(!ok);
|
||||
}
|
||||
|
||||
#else // !WEBP_HAVE_GIF
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "../imageio/image_dec.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./stopwatch.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/encode.h"
|
||||
#include "webp/mux.h"
|
||||
|
||||
@ -138,8 +139,13 @@ int main(int argc, const char* argv[]) {
|
||||
int c;
|
||||
int have_input = 0;
|
||||
CommandLineArguments cmd_args;
|
||||
int ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args);
|
||||
if (!ok) return 1;
|
||||
int ok;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args);
|
||||
if (!ok) FREE_WARGV_AND_RETURN(1);
|
||||
|
||||
argc = cmd_args.argc_;
|
||||
argv = cmd_args.argv_;
|
||||
|
||||
@ -158,7 +164,7 @@ int main(int argc, const char* argv[]) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-o") && c + 1 < argc) {
|
||||
argv[c] = NULL;
|
||||
output = argv[++c];
|
||||
output = (const char*)GET_WARGV_SHIFTED(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) {
|
||||
argv[c] = NULL;
|
||||
anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
@ -245,7 +251,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
// read next input image
|
||||
pic.use_argb = 1;
|
||||
ok = ReadImage(argv[c], &pic);
|
||||
ok = ReadImage((const char*)GET_WARGV_SHIFTED(argv, c), &pic);
|
||||
if (!ok) goto End;
|
||||
|
||||
if (enc == NULL) {
|
||||
@ -277,8 +283,8 @@ int main(int argc, const char* argv[]) {
|
||||
if (!ok) goto End;
|
||||
|
||||
if (verbose) {
|
||||
fprintf(stderr, "Added frame #%3d at time %4d (file: %s)\n",
|
||||
pic_num, timestamp_ms, 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;
|
||||
@ -302,7 +308,7 @@ int main(int argc, const char* argv[]) {
|
||||
if (ok) {
|
||||
if (output != NULL) {
|
||||
ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size);
|
||||
if (ok) fprintf(stderr, "output file: %s ", output);
|
||||
if (ok) WFPRINTF(stderr, "output file: %s ", (const W_CHAR*)output);
|
||||
} else {
|
||||
fprintf(stderr, "[no output file specified] ");
|
||||
}
|
||||
@ -314,5 +320,5 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
WebPDataClear(&webp_data);
|
||||
ExUtilDeleteCommandLineArguments(&cmd_args);
|
||||
return ok ? 0 : 1;
|
||||
FREE_WARGV_AND_RETURN(ok ? 0 : 1);
|
||||
}
|
||||
|
102
examples/unicode.h
Normal file
102
examples/unicode.h
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2018 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Unicode support for Windows. The main idea is to maintain an array of Unicode
|
||||
// arguments (wargv) and use it only for file paths. The regular argv is used
|
||||
// for everything else.
|
||||
//
|
||||
// Author: Yannis Guyon (yguyon@google.com)
|
||||
|
||||
#ifndef WEBP_EXAMPLES_UNICODE_H_
|
||||
#define WEBP_EXAMPLES_UNICODE_H_
|
||||
|
||||
#if defined(_WIN32) && defined(_UNICODE)
|
||||
|
||||
// wchar_t is used instead of TCHAR because we only perform additional work when
|
||||
// Unicode is enabled and because the output of CommandLineToArgvW() is wchar_t.
|
||||
|
||||
#include <wchar.h>
|
||||
#include <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); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Use this to get a Unicode argument (e.g. file path).
|
||||
#define GET_WARGV(UNUSED, C) wargv[C]
|
||||
// For cases where argv is shifted by one compared to wargv.
|
||||
#define GET_WARGV_SHIFTED(UNUSED, C) wargv[(C) + 1]
|
||||
#define GET_WARGV_OR_NULL() wargv
|
||||
|
||||
// Release resources. LocalFree() is needed after CommandLineToArgvW().
|
||||
#define FREE_WARGV() LOCAL_FREE((W_CHAR** const)wargv)
|
||||
#define LOCAL_FREE(WARGV) \
|
||||
do { \
|
||||
if ((WARGV) != NULL) LocalFree(WARGV); \
|
||||
} while (0)
|
||||
|
||||
#define W_CHAR wchar_t // WCHAR without underscore might already be defined.
|
||||
#define TO_W_CHAR(STR) (L##STR)
|
||||
|
||||
#define WFOPEN(ARG, OPT) _wfopen((const W_CHAR*)ARG, TO_W_CHAR(OPT))
|
||||
|
||||
#define WPRINTF(STR, ...) wprintf(TO_W_CHAR(STR), __VA_ARGS__)
|
||||
#define WFPRINTF(STDERR, STR, ...) fwprintf(STDERR, TO_W_CHAR(STR), __VA_ARGS__)
|
||||
|
||||
#define WSTRLEN(FILENAME) wcslen((const W_CHAR*)FILENAME)
|
||||
#define WSTRCMP(FILENAME, STR) wcscmp((const W_CHAR*)FILENAME, TO_W_CHAR(STR))
|
||||
#define WSTRRCHR(FILENAME, STR) wcsrchr((const W_CHAR*)FILENAME, TO_W_CHAR(STR))
|
||||
#define WSNPRINTF(A, B, STR, ...) _snwprintf(A, B, TO_W_CHAR(STR), __VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
// Unicode file paths work as is on Unix platforms, and no extra work is done on
|
||||
// Windows either if Unicode is disabled.
|
||||
|
||||
#define INIT_WARGV(ARGC, ARGV)
|
||||
|
||||
#define GET_WARGV(ARGV, C) (ARGV)[C]
|
||||
#define GET_WARGV_SHIFTED(ARGV, C) (ARGV)[C]
|
||||
#define GET_WARGV_OR_NULL() NULL
|
||||
|
||||
#define FREE_WARGV()
|
||||
#define LOCAL_FREE(WARGV)
|
||||
|
||||
#define W_CHAR char
|
||||
#define TO_W_CHAR(STR) (STR)
|
||||
|
||||
#define WFOPEN(ARG, OPT) fopen(ARG, OPT)
|
||||
|
||||
#define WPRINTF(STR, ...) printf(STR, __VA_ARGS__)
|
||||
#define WFPRINTF(STDERR, STR, ...) fprintf(STDERR, STR, __VA_ARGS__)
|
||||
|
||||
#define WSTRLEN(FILENAME) strlen(FILENAME)
|
||||
#define WSTRCMP(FILENAME, STR) strcmp(FILENAME, STR)
|
||||
#define WSTRRCHR(FILENAME, STR) strrchr(FILENAME, STR)
|
||||
#define WSNPRINTF(A, B, STR, ...) snprintf(A, B, STR, __VA_ARGS__)
|
||||
|
||||
#endif // defined(_WIN32) && defined(_UNICODE)
|
||||
|
||||
// Don't forget to free wargv before returning (e.g. from main).
|
||||
#define FREE_WARGV_AND_RETURN(VALUE) \
|
||||
do { \
|
||||
FREE_WARGV(); \
|
||||
return (VALUE); \
|
||||
} while (0)
|
||||
|
||||
#endif // WEBP_EXAMPLES_UNICODE_H_
|
75
examples/unicode_gif.h
Normal file
75
examples/unicode_gif.h
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2018 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// giflib doesn't have a Unicode DGifOpenFileName(). Let's make one.
|
||||
//
|
||||
// Author: Yannis Guyon (yguyon@google.com)
|
||||
|
||||
#ifndef WEBP_EXAMPLES_UNICODE_GIF_H_
|
||||
#define WEBP_EXAMPLES_UNICODE_GIF_H_
|
||||
|
||||
#include "./unicode.h"
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "webp/config.h" // For WEBP_HAVE_GIF
|
||||
#endif
|
||||
|
||||
#if defined(WEBP_HAVE_GIF)
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <fcntl.h> // Not standard, needed for _topen and flags.
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#include <gif_lib.h>
|
||||
#include <string.h>
|
||||
#include "./gifdec.h"
|
||||
|
||||
#if !defined(STDIN_FILENO)
|
||||
#define STDIN_FILENO 0
|
||||
#endif
|
||||
|
||||
static GifFileType* DGifOpenFileUnicode(const W_CHAR* file_name, int* error) {
|
||||
if (!WSTRCMP(file_name, "-")) {
|
||||
#if LOCAL_GIF_PREREQ(5, 0)
|
||||
return DGifOpenFileHandle(STDIN_FILENO, error);
|
||||
#else
|
||||
(void)error;
|
||||
return DGifOpenFileHandle(STDIN_FILENO);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && defined(_UNICODE)
|
||||
|
||||
int file_handle = _wopen(file_name, _O_RDONLY | _O_BINARY);
|
||||
if (file_handle == -1) {
|
||||
if (error != NULL) *error = D_GIF_ERR_OPEN_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if LOCAL_GIF_PREREQ(5, 0)
|
||||
return DGifOpenFileHandle(file_handle, error);
|
||||
#else
|
||||
return DGifOpenFileHandle(file_handle);
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if LOCAL_GIF_PREREQ(5, 0)
|
||||
return DGifOpenFileName(file_name, error);
|
||||
#else
|
||||
return DGifOpenFileName(file_name);
|
||||
#endif
|
||||
|
||||
#endif // defined(_WIN32) && defined(_UNICODE)
|
||||
// DGifCloseFile() is called later.
|
||||
}
|
||||
|
||||
#endif // defined(WEBP_HAVE_GIF)
|
||||
|
||||
#endif // WEBP_EXAMPLES_UNICODE_GIF_H_
|
@ -42,6 +42,7 @@
|
||||
|
||||
#include "../examples/example_util.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./unicode.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
@ -470,9 +471,11 @@ int main(int argc, char *argv[]) {
|
||||
WebPDecoderConfig* const config = &kParams.config;
|
||||
WebPIterator* const curr = &kParams.curr_frame;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
if (!WebPInitDecoderConfig(config)) {
|
||||
fprintf(stderr, "Library version mismatch!\n");
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
config->options.dithering_strength = 50;
|
||||
config->options.alpha_dithering_strength = 100;
|
||||
@ -484,7 +487,7 @@ int main(int argc, char *argv[]) {
|
||||
int parse_error = 0;
|
||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
Help();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-noicc")) {
|
||||
kParams.use_color_profile = 0;
|
||||
} else if (!strcmp(argv[c], "-nofancy")) {
|
||||
@ -507,30 +510,30 @@ int main(int argc, char *argv[]) {
|
||||
(dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
|
||||
dec_version & 0xff, (dmux_version >> 16) & 0xff,
|
||||
(dmux_version >> 8) & 0xff, dmux_version & 0xff);
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-mt")) {
|
||||
config->options.use_threads = 1;
|
||||
} else if (!strcmp(argv[c], "--")) {
|
||||
if (c < argc - 1) kParams.file_name = argv[++c];
|
||||
if (c < argc - 1) kParams.file_name = (const char*)GET_WARGV(argv, ++c);
|
||||
break;
|
||||
} else if (argv[c][0] == '-') {
|
||||
printf("Unknown option '%s'\n", argv[c]);
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
} else {
|
||||
kParams.file_name = argv[c];
|
||||
kParams.file_name = (const char*)GET_WARGV(argv, c);
|
||||
}
|
||||
|
||||
if (parse_error) {
|
||||
Help();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (kParams.file_name == NULL) {
|
||||
printf("missing input file!!\n");
|
||||
Help();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
}
|
||||
|
||||
if (!ImgIoUtilReadFile(kParams.file_name,
|
||||
@ -605,11 +608,11 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Should only be reached when using FREEGLUT:
|
||||
ClearParams();
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
|
||||
Error:
|
||||
ClearParams();
|
||||
return -1;
|
||||
FREE_WARGV_AND_RETURN(-1);
|
||||
}
|
||||
|
||||
#else // !WEBP_HAVE_GL
|
||||
|
@ -20,6 +20,7 @@
|
||||
#endif
|
||||
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./unicode.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/format_constants.h"
|
||||
#include "webp/mux_types.h"
|
||||
@ -1119,19 +1120,21 @@ int main(int argc, const char* argv[]) {
|
||||
WebPInfoStatus webp_info_status = WEBP_INFO_OK;
|
||||
WebPInfo webp_info;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
if (argc == 1) {
|
||||
HelpShort();
|
||||
return WEBP_INFO_OK;
|
||||
FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
|
||||
}
|
||||
|
||||
// Parse command-line input.
|
||||
for (c = 1; c < argc; ++c) {
|
||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||
HelpShort();
|
||||
return WEBP_INFO_OK;
|
||||
FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
|
||||
} else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
|
||||
HelpLong();
|
||||
return WEBP_INFO_OK;
|
||||
FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
|
||||
} else if (!strcmp(argv[c], "-quiet")) {
|
||||
quiet = 1;
|
||||
} else if (!strcmp(argv[c], "-diag")) {
|
||||
@ -1144,7 +1147,7 @@ int main(int argc, const char* argv[]) {
|
||||
const int version = WebPGetDecoderVersion();
|
||||
printf("WebP Decoder version: %d.%d.%d\n",
|
||||
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else { // Assume the remaining are all input files.
|
||||
break;
|
||||
}
|
||||
@ -1152,27 +1155,28 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
if (c == argc) {
|
||||
HelpShort();
|
||||
return WEBP_INFO_INVALID_COMMAND;
|
||||
FREE_WARGV_AND_RETURN(WEBP_INFO_INVALID_COMMAND);
|
||||
}
|
||||
|
||||
// Process input files one by one.
|
||||
for (; c < argc; ++c) {
|
||||
WebPData webp_data;
|
||||
const char* in_file = NULL;
|
||||
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;
|
||||
in_file = argv[c];
|
||||
if (in_file == NULL || !ReadFileToWebPData(in_file, &webp_data)) {
|
||||
in_file = GET_WARGV(argv, c);
|
||||
if (in_file == NULL ||
|
||||
!ReadFileToWebPData((const char*)in_file, &webp_data)) {
|
||||
webp_info_status = WEBP_INFO_INVALID_COMMAND;
|
||||
fprintf(stderr, "Failed to open input file %s.\n", in_file);
|
||||
WFPRINTF(stderr, "Failed to open input file %s.\n", in_file);
|
||||
continue;
|
||||
}
|
||||
if (!webp_info.quiet_) printf("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);
|
||||
}
|
||||
return webp_info_status;
|
||||
FREE_WARGV_AND_RETURN(webp_info_status);
|
||||
}
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "webp/mux.h"
|
||||
#include "../examples/example_util.h"
|
||||
#include "../imageio/imageio_util.h"
|
||||
#include "./unicode.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Config object to parse command-line arguments.
|
||||
@ -390,23 +391,25 @@ static int CreateMux(const char* const filename, WebPMux** mux) {
|
||||
*mux = WebPMuxCreate(&bitstream, 1);
|
||||
WebPDataClear(&bitstream);
|
||||
if (*mux != NULL) return 1;
|
||||
fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
|
||||
WFPRINTF(stderr, "Failed to create mux object from file %s.\n",
|
||||
(const W_CHAR*)filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WriteData(const char* filename, const WebPData* const webpdata) {
|
||||
int ok = 0;
|
||||
FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb")
|
||||
: ImgIoUtilSetBinaryMode(stdout);
|
||||
FILE* fout = WSTRCMP(filename, "-") ? WFOPEN(filename, "wb")
|
||||
: ImgIoUtilSetBinaryMode(stdout);
|
||||
if (fout == NULL) {
|
||||
fprintf(stderr, "Error opening output WebP file %s!\n", filename);
|
||||
WFPRINTF(stderr, "Error opening output WebP file %s!\n",
|
||||
(const W_CHAR*)filename);
|
||||
return 0;
|
||||
}
|
||||
if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
|
||||
fprintf(stderr, "Error writing file %s!\n", filename);
|
||||
WFPRINTF(stderr, "Error writing file %s!\n", (const W_CHAR*)filename);
|
||||
} else {
|
||||
fprintf(stderr, "Saved file %s (%d bytes)\n",
|
||||
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);
|
||||
@ -612,13 +615,16 @@ static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
|
||||
CHECK_NUM_ARGS_AT_MOST(NUM, LABEL);
|
||||
|
||||
// Parses command-line arguments to fill up config object. Also performs some
|
||||
// semantic checks.
|
||||
static int ParseCommandLine(Config* config) {
|
||||
// semantic checks. unicode_argv contains wchar_t arguments or is null.
|
||||
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_;
|
||||
// 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];
|
||||
@ -696,7 +702,7 @@ static int ParseCommandLine(Config* config) {
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "-o")) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
config->output_ = argv[i + 1];
|
||||
config->output_ = wargv[i + 1];
|
||||
i += 2;
|
||||
} else if (!strcmp(argv[i], "-info")) {
|
||||
CHECK_NUM_ARGS_EXACTLY(2, ErrParse);
|
||||
@ -705,24 +711,26 @@ static int ParseCommandLine(Config* config) {
|
||||
} else {
|
||||
config->action_type_ = ACTION_INFO;
|
||||
config->arg_count_ = 0;
|
||||
config->input_ = argv[i + 1];
|
||||
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);
|
||||
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);
|
||||
DeleteConfig(config);
|
||||
LOCAL_FREE((W_CHAR** const)unicode_argv);
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
if (i < argc - 1) {
|
||||
++i;
|
||||
if (config->input_ == NULL) {
|
||||
config->input_ = argv[i];
|
||||
config->input_ = wargv[i];
|
||||
} else {
|
||||
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
|
||||
argv[i], ErrParse);
|
||||
@ -747,7 +755,7 @@ static int ParseCommandLine(Config* config) {
|
||||
}
|
||||
if (config->action_type_ == ACTION_SET) {
|
||||
CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
|
||||
arg->filename_ = argv[i + 1];
|
||||
arg->filename_ = wargv[i + 1];
|
||||
++feature_arg_index;
|
||||
i += 2;
|
||||
} else {
|
||||
@ -762,7 +770,7 @@ static int ParseCommandLine(Config* config) {
|
||||
i += 2;
|
||||
} else { // Assume input file.
|
||||
if (config->input_ == NULL) {
|
||||
config->input_ = argv[i];
|
||||
config->input_ = wargv[i];
|
||||
} else {
|
||||
ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
|
||||
argv[i], ErrParse);
|
||||
@ -808,8 +816,8 @@ static int ValidateConfig(Config* const config) {
|
||||
}
|
||||
|
||||
// Create config object from command-line arguments.
|
||||
static int InitializeConfig(int argc, const char* argv[],
|
||||
Config* const config) {
|
||||
static int InitializeConfig(int argc, const char* argv[], Config* const config,
|
||||
const W_CHAR** const unicode_argv) {
|
||||
int num_feature_args = 0;
|
||||
int ok;
|
||||
|
||||
@ -830,7 +838,7 @@ static int InitializeConfig(int argc, const char* argv[],
|
||||
}
|
||||
|
||||
// Parse command-line.
|
||||
if (!ParseCommandLine(config) || !ValidateConfig(config)) {
|
||||
if (!ParseCommandLine(config, unicode_argv) || !ValidateConfig(config)) {
|
||||
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
||||
}
|
||||
|
||||
@ -1140,14 +1148,18 @@ static int Process(const Config* config) {
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
Config config;
|
||||
int ok = InitializeConfig(argc - 1, argv + 1, &config);
|
||||
int ok;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
ok = InitializeConfig(argc - 1, argv + 1, &config, GET_WARGV_OR_NULL());
|
||||
if (ok) {
|
||||
ok = Process(&config);
|
||||
} else {
|
||||
PrintHelp();
|
||||
}
|
||||
DeleteConfig(&config);
|
||||
return !ok;
|
||||
FREE_WARGV_AND_RETURN(!ok);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#define XTRA_MAJ_VERSION 1
|
||||
#define XTRA_MIN_VERSION 0
|
||||
#define XTRA_REV_VERSION 1
|
||||
#define XTRA_REV_VERSION 2
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "webp/encode.h"
|
||||
#include "imageio/image_dec.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
int keep_alpha) {
|
||||
@ -48,7 +49,8 @@ static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||
|
||||
End:
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
||||
WFPRINTF(stderr, "Error! Could not process file %s\n",
|
||||
(const W_CHAR*)filename);
|
||||
}
|
||||
free((void*)data);
|
||||
return ok ? data_size : 0;
|
||||
@ -239,9 +241,11 @@ int main(int argc, const char *argv[]) {
|
||||
const char* name2 = NULL;
|
||||
const char* output = NULL;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) {
|
||||
fprintf(stderr, "Can't init pictures\n");
|
||||
return 1;
|
||||
FREE_WARGV_AND_RETURN(1);
|
||||
}
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
@ -263,11 +267,11 @@ int main(int argc, const char *argv[]) {
|
||||
fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]);
|
||||
goto End;
|
||||
}
|
||||
output = argv[c];
|
||||
output = (const char*)GET_WARGV(argv, c);
|
||||
} else if (name1 == NULL) {
|
||||
name1 = argv[c];
|
||||
name1 = (const char*)GET_WARGV(argv, c);
|
||||
} else {
|
||||
name2 = argv[c];
|
||||
name2 = (const char*)GET_WARGV(argv, c);
|
||||
}
|
||||
}
|
||||
if (help || name1 == NULL || name2 == NULL) {
|
||||
@ -347,5 +351,5 @@ int main(int argc, const char *argv[]) {
|
||||
End:
|
||||
WebPPictureFree(&pic1);
|
||||
WebPPictureFree(&pic2);
|
||||
return ret;
|
||||
FREE_WARGV_AND_RETURN(ret);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "webp_to_sdl.h"
|
||||
#include "webp/decode.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
#if defined(WEBP_HAVE_JUST_SDL_H)
|
||||
#include <SDL.h>
|
||||
@ -51,29 +52,33 @@ static void ProcessEvents(void) {
|
||||
int main(int argc, char* argv[]) {
|
||||
int c;
|
||||
int ok = 0;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
for (c = 1; c < argc; ++c) {
|
||||
const char* file = NULL;
|
||||
const uint8_t* webp = NULL;
|
||||
size_t webp_size = 0;
|
||||
if (!strcmp(argv[c], "-h")) {
|
||||
printf("Usage: %s [-h] image.webp [more_files.webp...]\n", argv[0]);
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
file = argv[c];
|
||||
file = (const char*)GET_WARGV(argv, c);
|
||||
}
|
||||
if (file == NULL) continue;
|
||||
if (!ImgIoUtilReadFile(file, &webp, &webp_size)) {
|
||||
fprintf(stderr, "Error opening file: %s\n", file);
|
||||
WFPRINTF(stderr, "Error opening file: %s\n", (const W_CHAR*)file);
|
||||
goto Error;
|
||||
}
|
||||
if (webp_size != (size_t)(int)webp_size) {
|
||||
free((void*)webp);
|
||||
fprintf(stderr, "File too large.\n");
|
||||
goto Error;
|
||||
}
|
||||
ok = WebpToSDL((const char*)webp, (int)webp_size);
|
||||
free((void*)webp);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Error decoding file %s\n", file);
|
||||
WFPRINTF(stderr, "Error decoding file %s\n", (const W_CHAR*)file);
|
||||
goto Error;
|
||||
}
|
||||
ProcessEvents();
|
||||
@ -82,7 +87,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
Error:
|
||||
SDL_Quit();
|
||||
return ok ? 0 : 1;
|
||||
FREE_WARGV_AND_RETURN(ok ? 0 : 1);
|
||||
}
|
||||
|
||||
#else // !WEBP_HAVE_SDL
|
||||
|
@ -13,26 +13,30 @@
|
||||
|
||||
#include "extras/extras.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
int c;
|
||||
int quiet = 0;
|
||||
int ok = 1;
|
||||
|
||||
INIT_WARGV(argc, argv);
|
||||
|
||||
for (c = 1; ok && c < argc; ++c) {
|
||||
if (!strcmp(argv[c], "-quiet")) {
|
||||
quiet = 1;
|
||||
} else if (!strcmp(argv[c], "-help") || !strcmp(argv[c], "-h")) {
|
||||
printf("webp_quality [-h][-quiet] webp_files...\n");
|
||||
return 0;
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else {
|
||||
const char* const filename = argv[c];
|
||||
const char* const filename = (const char*)GET_WARGV(argv, c);
|
||||
const uint8_t* data = NULL;
|
||||
size_t data_size = 0;
|
||||
int q;
|
||||
ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||
if (!ok) break;
|
||||
q = VP8EstimateQuality(data, data_size);
|
||||
if (!quiet) printf("[%s] ", filename);
|
||||
if (!quiet) WPRINTF("[%s] ", (const W_CHAR*)filename);
|
||||
if (q < 0) {
|
||||
fprintf(stderr, "Not a WebP file, or not a lossy WebP file.\n");
|
||||
ok = 0;
|
||||
@ -46,5 +50,5 @@ int main(int argc, const char *argv[]) {
|
||||
free((void*)data);
|
||||
}
|
||||
}
|
||||
return ok ? 0 : 1;
|
||||
FREE_WARGV_AND_RETURN(ok ? 0 : 1);
|
||||
}
|
||||
|
@ -29,11 +29,13 @@
|
||||
// code with COBJMACROS.
|
||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||
#include <shlwapi.h>
|
||||
#include <tchar.h>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
#endif
|
||||
|
||||
#include "./imageio_util.h"
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PNG
|
||||
@ -61,11 +63,12 @@ static HRESULT CreateOutputStream(const char* out_file_name,
|
||||
// Output to a memory buffer. This is freed when 'stream' is released.
|
||||
IFS(CreateStreamOnHGlobal(NULL, TRUE, stream));
|
||||
} else {
|
||||
IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream));
|
||||
IFS(SHCreateStreamOnFile((const LPTSTR)out_file_name,
|
||||
STGM_WRITE | STGM_CREATE, stream));
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
fprintf(stderr, "Error opening output file %s (%08lx)\n",
|
||||
out_file_name, hr);
|
||||
_ftprintf(stderr, _T("Error opening output file %s (%08lx)\n"),
|
||||
(const LPTSTR)out_file_name, hr);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
@ -549,7 +552,8 @@ int WebPSaveImage(const WebPDecBuffer* const buffer,
|
||||
const char* const out_file_name) {
|
||||
FILE* fout = NULL;
|
||||
int needs_open_file = 1;
|
||||
const int use_stdout = (out_file_name != NULL) && !strcmp(out_file_name, "-");
|
||||
const int use_stdout =
|
||||
(out_file_name != NULL) && !WSTRCMP(out_file_name, "-");
|
||||
int ok = 1;
|
||||
|
||||
if (buffer == NULL || out_file_name == NULL) return 0;
|
||||
@ -560,9 +564,10 @@ int WebPSaveImage(const WebPDecBuffer* const buffer,
|
||||
|
||||
if (needs_open_file) {
|
||||
fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout)
|
||||
: fopen(out_file_name, "wb");
|
||||
: WFOPEN(out_file_name, "wb");
|
||||
if (fout == NULL) {
|
||||
fprintf(stderr, "Error opening output file %s\n", out_file_name);
|
||||
WFPRINTF(stderr, "Error opening output file %s\n",
|
||||
(const W_CHAR*)out_file_name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../examples/unicode.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// File I/O
|
||||
@ -73,7 +74,7 @@ int ImgIoUtilReadFile(const char* const file_name,
|
||||
uint8_t* file_data;
|
||||
size_t file_size;
|
||||
FILE* in;
|
||||
const int from_stdin = (file_name == NULL) || !strcmp(file_name, "-");
|
||||
const int from_stdin = (file_name == NULL) || !WSTRCMP(file_name, "-");
|
||||
|
||||
if (from_stdin) return ImgIoUtilReadFromStdin(data, data_size);
|
||||
|
||||
@ -81,9 +82,9 @@ int ImgIoUtilReadFile(const char* const file_name,
|
||||
*data = NULL;
|
||||
*data_size = 0;
|
||||
|
||||
in = fopen(file_name, "rb");
|
||||
in = WFOPEN(file_name, "rb");
|
||||
if (in == NULL) {
|
||||
fprintf(stderr, "cannot open input file '%s'\n", file_name);
|
||||
WFPRINTF(stderr, "cannot open input file '%s'\n", (const W_CHAR*)file_name);
|
||||
return 0;
|
||||
}
|
||||
fseek(in, 0, SEEK_END);
|
||||
@ -93,16 +94,16 @@ int ImgIoUtilReadFile(const char* const file_name,
|
||||
file_data = (uint8_t*)malloc(file_size + 1);
|
||||
if (file_data == NULL) {
|
||||
fclose(in);
|
||||
fprintf(stderr, "memory allocation failure when reading file %s\n",
|
||||
file_name);
|
||||
WFPRINTF(stderr, "memory allocation failure when reading file %s\n",
|
||||
(const W_CHAR*)file_name);
|
||||
return 0;
|
||||
}
|
||||
ok = (fread(file_data, file_size, 1, in) == 1);
|
||||
fclose(in);
|
||||
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Could not read %d bytes of data from file %s\n",
|
||||
(int)file_size, file_name);
|
||||
WFPRINTF(stderr, "Could not read %d bytes of data from file %s\n",
|
||||
(int)file_size, (const W_CHAR*)file_name);
|
||||
free(file_data);
|
||||
return 0;
|
||||
}
|
||||
@ -118,14 +119,15 @@ int ImgIoUtilWriteFile(const char* const file_name,
|
||||
const uint8_t* data, size_t data_size) {
|
||||
int ok;
|
||||
FILE* out;
|
||||
const int to_stdout = (file_name == NULL) || !strcmp(file_name, "-");
|
||||
const int to_stdout = (file_name == NULL) || !WSTRCMP(file_name, "-");
|
||||
|
||||
if (data == NULL) {
|
||||
return 0;
|
||||
}
|
||||
out = to_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(file_name, "wb");
|
||||
out = to_stdout ? ImgIoUtilSetBinaryMode(stdout) : WFOPEN(file_name, "wb");
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "Error! Cannot open output file '%s'\n", file_name);
|
||||
WFPRINTF(stderr, "Error! Cannot open output file '%s'\n",
|
||||
(const W_CHAR*)file_name);
|
||||
return 0;
|
||||
}
|
||||
ok = (fwrite(data, data_size, 1, out) == 1);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
#include "webp/encode.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
|
||||
@ -43,7 +44,7 @@ static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
|
||||
}
|
||||
|
||||
void PrintWebPError(const char* const in_file, int status) {
|
||||
fprintf(stderr, "Decoding of %s failed.\n", in_file);
|
||||
WFPRINTF(stderr, "Decoding of %s failed.\n", (const W_CHAR*)in_file);
|
||||
fprintf(stderr, "Status: %d", status);
|
||||
if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
fprintf(stderr, "(%s)", kStatusMessages[status]);
|
||||
|
@ -29,12 +29,14 @@
|
||||
// code with COBJMACROS.
|
||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||
#include <shlwapi.h>
|
||||
#include <tchar.h>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
#include "webp/encode.h"
|
||||
#include "../examples/unicode.h"
|
||||
#include "./imageio_util.h"
|
||||
#include "./metadata.h"
|
||||
#include "webp/encode.h"
|
||||
|
||||
#define IFS(fn) \
|
||||
do { \
|
||||
@ -85,7 +87,7 @@ WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
|
||||
|
||||
static HRESULT OpenInputStream(const char* filename, IStream** stream) {
|
||||
HRESULT hr = S_OK;
|
||||
if (!strcmp(filename, "-")) {
|
||||
if (!WSTRCMP(filename, "-")) {
|
||||
const uint8_t* data = NULL;
|
||||
size_t data_size = 0;
|
||||
const int ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||
@ -108,11 +110,12 @@ static HRESULT OpenInputStream(const char* filename, IStream** stream) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
} else {
|
||||
IFS(SHCreateStreamOnFileA(filename, STGM_READ, stream));
|
||||
IFS(SHCreateStreamOnFile((const LPTSTR)filename, STGM_READ, stream));
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
fprintf(stderr, "Error opening input file %s (%08lx)\n", filename, hr);
|
||||
_ftprintf(stderr, _T("Error opening input file %s (%08lx)\n"),
|
||||
(const LPTSTR)filename, hr);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
@ -185,6 +185,7 @@ DSP_ENC_OBJS = \
|
||||
src/dsp/cost.o \
|
||||
src/dsp/cost_mips32.o \
|
||||
src/dsp/cost_mips_dsp_r2.o \
|
||||
src/dsp/cost_neon.o \
|
||||
src/dsp/cost_sse2.o \
|
||||
src/dsp/enc.o \
|
||||
src/dsp/enc_mips32.o \
|
||||
|
12
man/cwebp.1
12
man/cwebp.1
@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH CWEBP 1 "January 20, 2017"
|
||||
.TH CWEBP 1 "January 11, 2019"
|
||||
.SH NAME
|
||||
cwebp \- compress an image file to a WebP file
|
||||
.SH SYNOPSIS
|
||||
@ -41,10 +41,12 @@ the invisible pixel values (R/G/B or Y/U/V) will be preserved only if the
|
||||
\-exact option is used.
|
||||
.TP
|
||||
.BI \-near_lossless " int
|
||||
Use near\-lossless image preprocessing. This option adjusts pixel values
|
||||
to help compressibility, but has minimal impact on the visual quality.
|
||||
It triggers lossless compression mode automatically.
|
||||
Range is 0 (maximum preprocessing) to 100 (no preprocessing, the default).
|
||||
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
|
||||
.BI \-q " float
|
||||
Specify the compression factor for RGB channels between 0 and 100. The default
|
||||
|
@ -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 7:3:0
|
||||
libwebp_la_LDFLAGS = -no-undefined -version-info 7:4:0
|
||||
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 3:3:0
|
||||
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 3:4:0
|
||||
pkgconfig_DATA += libwebpdecoder.pc
|
||||
endif
|
||||
|
||||
|
@ -32,7 +32,7 @@ extern "C" {
|
||||
// version numbers
|
||||
#define DEC_MAJ_VERSION 1
|
||||
#define DEC_MIN_VERSION 0
|
||||
#define DEC_REV_VERSION 1
|
||||
#define DEC_REV_VERSION 2
|
||||
|
||||
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||
// Constraints are: We need to store one 16x16 block of luma samples (y),
|
||||
|
@ -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:5:0
|
||||
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:6:0
|
||||
libwebpdemuxincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebpdemux.pc
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#define DMUX_MAJ_VERSION 1
|
||||
#define DMUX_MIN_VERSION 0
|
||||
#define DMUX_REV_VERSION 1
|
||||
#define DMUX_REV_VERSION 2
|
||||
|
||||
typedef struct {
|
||||
size_t start_; // start location of the data
|
||||
|
@ -6,8 +6,8 @@
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,1
|
||||
PRODUCTVERSION 1,0,0,1
|
||||
FILEVERSION 1,0,0,2
|
||||
PRODUCTVERSION 1,0,0,2
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@ -24,12 +24,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libwebpdemux DLL"
|
||||
VALUE "FileVersion", "1.0.1"
|
||||
VALUE "FileVersion", "1.0.2"
|
||||
VALUE "InternalName", "libwebpdemux.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2018"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2019"
|
||||
VALUE "OriginalFilename", "libwebpdemux.dll"
|
||||
VALUE "ProductName", "WebP Image Demuxer"
|
||||
VALUE "ProductVersion", "1.0.1"
|
||||
VALUE "ProductVersion", "1.0.2"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -40,6 +40,7 @@ ENC_SOURCES =
|
||||
ENC_SOURCES += cost.c
|
||||
ENC_SOURCES += enc.c
|
||||
ENC_SOURCES += lossless_enc.c
|
||||
ENC_SOURCES += quant.h
|
||||
ENC_SOURCES += ssim.c
|
||||
|
||||
libwebpdspdecode_sse41_la_SOURCES =
|
||||
@ -121,6 +122,7 @@ libwebpdsp_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
|
||||
libwebpdsp_sse41_la_LIBADD = libwebpdspdecode_sse41.la
|
||||
|
||||
libwebpdsp_neon_la_SOURCES =
|
||||
libwebpdsp_neon_la_SOURCES += cost_neon.c
|
||||
libwebpdsp_neon_la_SOURCES += enc_neon.c
|
||||
libwebpdsp_neon_la_SOURCES += lossless_enc_neon.c
|
||||
libwebpdsp_neon_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
|
@ -377,6 +377,7 @@ VP8SetResidualCoeffsFunc VP8SetResidualCoeffs;
|
||||
extern void VP8EncDspCostInitMIPS32(void);
|
||||
extern void VP8EncDspCostInitMIPSdspR2(void);
|
||||
extern void VP8EncDspCostInitSSE2(void);
|
||||
extern void VP8EncDspCostInitNEON(void);
|
||||
|
||||
WEBP_DSP_INIT_FUNC(VP8EncDspCostInit) {
|
||||
VP8GetResidualCost = GetResidualCost_C;
|
||||
@ -398,6 +399,11 @@ WEBP_DSP_INIT_FUNC(VP8EncDspCostInit) {
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
VP8EncDspCostInitSSE2();
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_NEON)
|
||||
if (VP8GetCPUInfo(kNEON)) {
|
||||
VP8EncDspCostInitNEON();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
122
src/dsp/cost_neon.c
Normal file
122
src/dsp/cost_neon.c
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2018 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// ARM NEON version of cost functions
|
||||
|
||||
#include "src/dsp/dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_NEON)
|
||||
|
||||
#include "src/dsp/neon.h"
|
||||
#include "src/enc/cost_enc.h"
|
||||
|
||||
static const uint8_t position[16] = { 1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14, 15, 16 };
|
||||
|
||||
static void SetResidualCoeffs_NEON(const int16_t* const coeffs,
|
||||
VP8Residual* const res) {
|
||||
const int16x8_t minus_one = vdupq_n_s16(-1);
|
||||
const int16x8_t coeffs_0 = vld1q_s16(coeffs);
|
||||
const int16x8_t coeffs_1 = vld1q_s16(coeffs + 8);
|
||||
const uint16x8_t eob_0 = vtstq_s16(coeffs_0, minus_one);
|
||||
const uint16x8_t eob_1 = vtstq_s16(coeffs_1, minus_one);
|
||||
const uint8x16_t eob = vcombine_u8(vqmovn_u16(eob_0), vqmovn_u16(eob_1));
|
||||
const uint8x16_t masked = vandq_u8(eob, vld1q_u8(position));
|
||||
|
||||
#ifdef __aarch64__
|
||||
res->last = vmaxvq_u8(masked) - 1;
|
||||
#else
|
||||
const uint8x8_t eob_8x8 = vmax_u8(vget_low_u8(masked), vget_high_u8(masked));
|
||||
const uint16x8_t eob_16x8 = vmovl_u8(eob_8x8);
|
||||
const uint16x4_t eob_16x4 =
|
||||
vmax_u16(vget_low_u16(eob_16x8), vget_high_u16(eob_16x8));
|
||||
const uint32x4_t eob_32x4 = vmovl_u16(eob_16x4);
|
||||
uint32x2_t eob_32x2 =
|
||||
vmax_u32(vget_low_u32(eob_32x4), vget_high_u32(eob_32x4));
|
||||
eob_32x2 = vpmax_u32(eob_32x2, eob_32x2);
|
||||
|
||||
vst1_lane_s32(&res->last, vreinterpret_s32_u32(eob_32x2), 0);
|
||||
--res->last;
|
||||
#endif // __aarch64__
|
||||
|
||||
res->coeffs = coeffs;
|
||||
}
|
||||
|
||||
static int GetResidualCost_NEON(int ctx0, const VP8Residual* const res) {
|
||||
uint8_t levels[16], ctxs[16];
|
||||
uint16_t abs_levels[16];
|
||||
int n = res->first;
|
||||
// should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
|
||||
const int p0 = res->prob[n][ctx0][0];
|
||||
CostArrayPtr const costs = res->costs;
|
||||
const uint16_t* t = costs[n][ctx0];
|
||||
// bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0
|
||||
// (as required by the syntax). For ctx0 == 0, we need to add it here or it'll
|
||||
// be missing during the loop.
|
||||
int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0;
|
||||
|
||||
if (res->last < 0) {
|
||||
return VP8BitCost(0, p0);
|
||||
}
|
||||
|
||||
{ // precompute clamped levels and contexts, packed to 8b.
|
||||
const uint8x16_t kCst2 = vdupq_n_u8(2);
|
||||
const uint8x16_t kCst67 = vdupq_n_u8(MAX_VARIABLE_LEVEL);
|
||||
const int16x8_t c0 = vld1q_s16(res->coeffs);
|
||||
const int16x8_t c1 = vld1q_s16(res->coeffs + 8);
|
||||
const uint16x8_t E0 = vreinterpretq_u16_s16(vabsq_s16(c0));
|
||||
const uint16x8_t E1 = vreinterpretq_u16_s16(vabsq_s16(c1));
|
||||
const uint8x16_t F = vcombine_u8(vqmovn_u16(E0), vqmovn_u16(E1));
|
||||
const uint8x16_t G = vminq_u8(F, kCst2); // context = 0,1,2
|
||||
const uint8x16_t H = vminq_u8(F, kCst67); // clamp_level in [0..67]
|
||||
|
||||
vst1q_u8(ctxs, G);
|
||||
vst1q_u8(levels, H);
|
||||
|
||||
vst1q_u16(abs_levels, E0);
|
||||
vst1q_u16(abs_levels + 8, E1);
|
||||
}
|
||||
for (; n < res->last; ++n) {
|
||||
const int ctx = ctxs[n];
|
||||
const int level = levels[n];
|
||||
const int flevel = abs_levels[n]; // full level
|
||||
cost += VP8LevelFixedCosts[flevel] + t[level]; // simplified VP8LevelCost()
|
||||
t = costs[n + 1][ctx];
|
||||
}
|
||||
// Last coefficient is always non-zero
|
||||
{
|
||||
const int level = levels[n];
|
||||
const int flevel = abs_levels[n];
|
||||
assert(flevel != 0);
|
||||
cost += VP8LevelFixedCosts[flevel] + t[level];
|
||||
if (n < 15) {
|
||||
const int b = VP8EncBands[n + 1];
|
||||
const int ctx = ctxs[n];
|
||||
const int last_p0 = res->prob[b][ctx][0];
|
||||
cost += VP8BitCost(0, last_p0);
|
||||
}
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Entry point
|
||||
|
||||
extern void VP8EncDspCostInitNEON(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInitNEON(void) {
|
||||
VP8SetResidualCoeffs = SetResidualCoeffs_NEON;
|
||||
VP8GetResidualCost = GetResidualCost_NEON;
|
||||
}
|
||||
|
||||
#else // !WEBP_USE_NEON
|
||||
|
||||
WEBP_DSP_INIT_STUB(VP8EncDspCostInitNEON)
|
||||
|
||||
#endif // WEBP_USE_NEON
|
70
src/dsp/quant.h
Normal file
70
src/dsp/quant.h
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright 2018 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#ifndef WEBP_DSP_QUANT_H_
|
||||
#define WEBP_DSP_QUANT_H_
|
||||
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#if defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) && \
|
||||
!defined(WEBP_HAVE_NEON_RTCD)
|
||||
#include <arm_neon.h>
|
||||
|
||||
#define IsFlat IsFlat_NEON
|
||||
|
||||
static uint32x2_t horizontal_add_uint32x4(const uint32x4_t a) {
|
||||
const uint64x2_t b = vpaddlq_u32(a);
|
||||
return vadd_u32(vreinterpret_u32_u64(vget_low_u64(b)),
|
||||
vreinterpret_u32_u64(vget_high_u64(b)));
|
||||
}
|
||||
|
||||
static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks,
|
||||
int thresh) {
|
||||
const int16x8_t tst_ones = vdupq_n_s16(-1);
|
||||
uint32x4_t sum = vdupq_n_u32(0);
|
||||
|
||||
for (int i = 0; i < num_blocks; ++i) {
|
||||
// Set DC to zero.
|
||||
const int16x8_t a_0 = vsetq_lane_s16(0, vld1q_s16(levels), 0);
|
||||
const int16x8_t a_1 = vld1q_s16(levels + 8);
|
||||
|
||||
const uint16x8_t b_0 = vshrq_n_u16(vtstq_s16(a_0, tst_ones), 15);
|
||||
const uint16x8_t b_1 = vshrq_n_u16(vtstq_s16(a_1, tst_ones), 15);
|
||||
|
||||
sum = vpadalq_u16(sum, b_0);
|
||||
sum = vpadalq_u16(sum, b_1);
|
||||
|
||||
levels += 16;
|
||||
}
|
||||
return thresh >= (int32_t)vget_lane_u32(horizontal_add_uint32x4(sum), 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define IsFlat IsFlat_C
|
||||
|
||||
static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks,
|
||||
int thresh) {
|
||||
int score = 0;
|
||||
while (num_blocks-- > 0) { // TODO(skal): refine positional scoring?
|
||||
int i;
|
||||
for (i = 1; i < 16; ++i) { // omit DC, we're only interested in AC
|
||||
score += (levels[i] != 0);
|
||||
if (score > thresh) return 0;
|
||||
}
|
||||
levels += 16;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) &&
|
||||
// !defined(WEBP_HAVE_NEON_RTCD)
|
||||
|
||||
#endif // WEBP_DSP_QUANT_H_
|
@ -165,7 +165,7 @@ VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
|
||||
void VP8LHistogramSetClear(VP8LHistogramSet* const set) {
|
||||
int i;
|
||||
const int cache_bits = set->histograms[0]->palette_code_bits_;
|
||||
const int size = set->size;
|
||||
const int size = set->max_size;
|
||||
const size_t total_size = HistogramSetTotalSize(size, cache_bits);
|
||||
uint8_t* memory = (uint8_t*)set;
|
||||
|
||||
@ -180,6 +180,20 @@ void VP8LHistogramSetClear(VP8LHistogramSet* const set) {
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the histogram 'i' from 'set' by setting it to NULL.
|
||||
static void HistogramSetRemoveHistogram(VP8LHistogramSet* const set, int i,
|
||||
int* const num_used) {
|
||||
assert(set->histograms[i] != NULL);
|
||||
set->histograms[i] = NULL;
|
||||
--*num_used;
|
||||
// If we remove the last valid one, shrink until the next valid one.
|
||||
if (i == set->size - 1) {
|
||||
while (set->size >= 1 && set->histograms[set->size - 1] == NULL) {
|
||||
--set->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
|
||||
@ -447,7 +461,9 @@ static double HistogramAddEval(const VP8LHistogram* const a,
|
||||
static double HistogramAddThresh(const VP8LHistogram* const a,
|
||||
const VP8LHistogram* const b,
|
||||
double cost_threshold) {
|
||||
double cost = -a->bit_cost_;
|
||||
double cost;
|
||||
assert(a != NULL && b != NULL);
|
||||
cost = -a->bit_cost_;
|
||||
GetCombinedHistogramEntropy(a, b, cost_threshold, &cost);
|
||||
return cost;
|
||||
}
|
||||
@ -561,14 +577,17 @@ static void HistogramBuild(
|
||||
}
|
||||
|
||||
// Copies the histograms and computes its bit_cost.
|
||||
static void HistogramCopyAndAnalyze(
|
||||
VP8LHistogramSet* const orig_histo, VP8LHistogramSet* const image_histo) {
|
||||
int i;
|
||||
const int histo_size = orig_histo->size;
|
||||
static const uint16_t kInvalidHistogramSymbol = (uint16_t)(-1);
|
||||
static void HistogramCopyAndAnalyze(VP8LHistogramSet* const orig_histo,
|
||||
VP8LHistogramSet* const image_histo,
|
||||
int* const num_used,
|
||||
uint16_t* const histogram_symbols) {
|
||||
int i, cluster_id;
|
||||
int num_used_orig = *num_used;
|
||||
VP8LHistogram** const orig_histograms = orig_histo->histograms;
|
||||
VP8LHistogram** const histograms = image_histo->histograms;
|
||||
image_histo->size = 0;
|
||||
for (i = 0; i < histo_size; ++i) {
|
||||
assert(image_histo->max_size == orig_histo->max_size);
|
||||
for (cluster_id = 0, i = 0; i < orig_histo->max_size; ++i) {
|
||||
VP8LHistogram* const histo = orig_histograms[i];
|
||||
UpdateHistogramCost(histo);
|
||||
|
||||
@ -576,10 +595,19 @@ static void HistogramCopyAndAnalyze(
|
||||
// with no information (when they are skipped because of LZ77).
|
||||
if (!histo->is_used_[0] && !histo->is_used_[1] && !histo->is_used_[2]
|
||||
&& !histo->is_used_[3] && !histo->is_used_[4]) {
|
||||
continue;
|
||||
// The first histogram is always used. If an histogram is empty, we set
|
||||
// its id to be the same as the previous one: this will improve
|
||||
// compressibility for later LZ77.
|
||||
assert(i > 0);
|
||||
HistogramSetRemoveHistogram(image_histo, i, num_used);
|
||||
HistogramSetRemoveHistogram(orig_histo, i, &num_used_orig);
|
||||
histogram_symbols[i] = kInvalidHistogramSymbol;
|
||||
} else {
|
||||
// Copy histograms from orig_histo[] to image_histo[].
|
||||
HistogramCopy(histo, histograms[i]);
|
||||
histogram_symbols[i] = cluster_id++;
|
||||
assert(cluster_id <= image_histo->max_size);
|
||||
}
|
||||
// Copy histograms from orig_histo[] to image_histo[].
|
||||
HistogramCopy(histo, histograms[image_histo->size++]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,29 +624,33 @@ static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo,
|
||||
|
||||
// Analyze the dominant (literal, red and blue) entropy costs.
|
||||
for (i = 0; i < histo_size; ++i) {
|
||||
if (histograms[i] == NULL) continue;
|
||||
UpdateDominantCostRange(histograms[i], &cost_range);
|
||||
}
|
||||
|
||||
// bin-hash histograms on three of the dominant (literal, red and blue)
|
||||
// symbol costs and store the resulting bin_id for each histogram.
|
||||
for (i = 0; i < histo_size; ++i) {
|
||||
// bin_map[i] is not set to a special value as its use will later be guarded
|
||||
// by another (histograms[i] == NULL).
|
||||
if (histograms[i] == NULL) continue;
|
||||
bin_map[i] = GetHistoBinIndex(histograms[i], &cost_range, low_effort);
|
||||
}
|
||||
}
|
||||
|
||||
// Compact image_histo[] by merging some histograms with same bin_id together if
|
||||
// it's advantageous.
|
||||
// Merges some histograms with same bin_id together if it's advantageous.
|
||||
// Sets the remaining histograms to NULL.
|
||||
static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
|
||||
int *num_used,
|
||||
const uint16_t* const clusters,
|
||||
uint16_t* const cluster_mappings,
|
||||
VP8LHistogram* cur_combo,
|
||||
const uint16_t* const bin_map,
|
||||
int bin_map_size, int num_bins,
|
||||
int num_bins,
|
||||
double combine_cost_factor,
|
||||
int low_effort) {
|
||||
VP8LHistogram** const histograms = image_histo->histograms;
|
||||
int idx;
|
||||
// Work in-place: processed histograms are put at the beginning of
|
||||
// image_histo[]. At the end, we just have to truncate the array.
|
||||
int size = 0;
|
||||
struct {
|
||||
int16_t first; // position of the histogram that accumulates all
|
||||
// histograms with the same bin_id
|
||||
@ -631,16 +663,19 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
|
||||
bin_info[idx].num_combine_failures = 0;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < bin_map_size; ++idx) {
|
||||
const int bin_id = bin_map[idx];
|
||||
const int first = bin_info[bin_id].first;
|
||||
assert(size <= idx);
|
||||
// By default, a cluster matches itself.
|
||||
for (idx = 0; idx < *num_used; ++idx) cluster_mappings[idx] = idx;
|
||||
for (idx = 0; idx < image_histo->size; ++idx) {
|
||||
int bin_id, first;
|
||||
if (histograms[idx] == NULL) continue;
|
||||
bin_id = bin_map[idx];
|
||||
first = bin_info[bin_id].first;
|
||||
if (first == -1) {
|
||||
// just move histogram #idx to its final position
|
||||
histograms[size] = histograms[idx];
|
||||
bin_info[bin_id].first = size++;
|
||||
bin_info[bin_id].first = idx;
|
||||
} else if (low_effort) {
|
||||
HistogramAdd(histograms[idx], histograms[first], histograms[first]);
|
||||
HistogramSetRemoveHistogram(image_histo, idx, num_used);
|
||||
cluster_mappings[clusters[idx]] = clusters[first];
|
||||
} else {
|
||||
// try to merge #idx into #first (both share the same bin_id)
|
||||
const double bit_cost = histograms[idx]->bit_cost_;
|
||||
@ -663,19 +698,18 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
|
||||
bin_info[bin_id].num_combine_failures >= max_combine_failures) {
|
||||
// move the (better) merged histogram to its final slot
|
||||
HistogramSwap(&cur_combo, &histograms[first]);
|
||||
HistogramSetRemoveHistogram(image_histo, idx, num_used);
|
||||
cluster_mappings[clusters[idx]] = clusters[first];
|
||||
} else {
|
||||
histograms[size++] = histograms[idx];
|
||||
++bin_info[bin_id].num_combine_failures;
|
||||
}
|
||||
} else {
|
||||
histograms[size++] = histograms[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
image_histo->size = size;
|
||||
if (low_effort) {
|
||||
// for low_effort case, update the final cost when everything is merged
|
||||
for (idx = 0; idx < size; ++idx) {
|
||||
for (idx = 0; idx < image_histo->size; ++idx) {
|
||||
if (histograms[idx] == NULL) continue;
|
||||
UpdateHistogramCost(histograms[idx]);
|
||||
}
|
||||
}
|
||||
@ -706,16 +740,9 @@ typedef struct {
|
||||
int max_size;
|
||||
} HistoQueue;
|
||||
|
||||
static int HistoQueueInit(HistoQueue* const histo_queue, const int max_index) {
|
||||
static int HistoQueueInit(HistoQueue* const histo_queue, const int max_size) {
|
||||
histo_queue->size = 0;
|
||||
// max_index^2 for the queue size is safe. If you look at
|
||||
// HistogramCombineGreedy, and imagine that UpdateQueueFront always pushes
|
||||
// data to the queue, you insert at most:
|
||||
// - max_index*(max_index-1)/2 (the first two for loops)
|
||||
// - max_index - 1 in the last for loop at the first iteration of the while
|
||||
// loop, max_index - 2 at the second iteration ... therefore
|
||||
// max_index*(max_index-1)/2 overall too
|
||||
histo_queue->max_size = max_index * max_index;
|
||||
histo_queue->max_size = max_size;
|
||||
// We allocate max_size + 1 because the last element at index "size" is
|
||||
// used as temporary data (and it could be up to max_size).
|
||||
histo_queue->queue = (HistogramPair*)WebPSafeMalloc(
|
||||
@ -778,6 +805,8 @@ static double HistoQueuePush(HistoQueue* const histo_queue,
|
||||
const VP8LHistogram* h2;
|
||||
HistogramPair pair;
|
||||
|
||||
// Stop here if the queue is full.
|
||||
if (histo_queue->size == histo_queue->max_size) return 0.;
|
||||
assert(threshold <= 0.);
|
||||
if (idx1 > idx2) {
|
||||
const int tmp = idx2;
|
||||
@ -794,8 +823,6 @@ static double HistoQueuePush(HistoQueue* const histo_queue,
|
||||
// Do not even consider the pair if it does not improve the entropy.
|
||||
if (pair.cost_diff >= threshold) return 0.;
|
||||
|
||||
// We cannot add more elements than the capacity.
|
||||
assert(histo_queue->size < histo_queue->max_size);
|
||||
histo_queue->queue[histo_queue->size++] = pair;
|
||||
HistoQueueUpdateHead(histo_queue, &histo_queue->queue[histo_queue->size - 1]);
|
||||
|
||||
@ -806,42 +833,43 @@ static double HistoQueuePush(HistoQueue* const histo_queue,
|
||||
|
||||
// Combines histograms by continuously choosing the one with the highest cost
|
||||
// reduction.
|
||||
static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) {
|
||||
static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo,
|
||||
int* const num_used) {
|
||||
int ok = 0;
|
||||
int image_histo_size = image_histo->size;
|
||||
const int image_histo_size = image_histo->size;
|
||||
int i, j;
|
||||
VP8LHistogram** const histograms = image_histo->histograms;
|
||||
// Indexes of remaining histograms.
|
||||
int* const clusters =
|
||||
(int*)WebPSafeMalloc(image_histo_size, sizeof(*clusters));
|
||||
// Priority queue of histogram pairs.
|
||||
HistoQueue histo_queue;
|
||||
|
||||
if (!HistoQueueInit(&histo_queue, image_histo_size) || clusters == NULL) {
|
||||
// image_histo_size^2 for the queue size is safe. If you look at
|
||||
// HistogramCombineGreedy, and imagine that UpdateQueueFront always pushes
|
||||
// data to the queue, you insert at most:
|
||||
// - image_histo_size*(image_histo_size-1)/2 (the first two for loops)
|
||||
// - image_histo_size - 1 in the last for loop at the first iteration of
|
||||
// the while loop, image_histo_size - 2 at the second iteration ...
|
||||
// therefore image_histo_size*(image_histo_size-1)/2 overall too
|
||||
if (!HistoQueueInit(&histo_queue, image_histo_size * image_histo_size)) {
|
||||
goto End;
|
||||
}
|
||||
|
||||
for (i = 0; i < image_histo_size; ++i) {
|
||||
// Initialize clusters indexes.
|
||||
clusters[i] = i;
|
||||
if (image_histo->histograms[i] == NULL) continue;
|
||||
for (j = i + 1; j < image_histo_size; ++j) {
|
||||
// Initialize positions array.
|
||||
// Initialize queue.
|
||||
if (image_histo->histograms[j] == NULL) continue;
|
||||
HistoQueuePush(&histo_queue, histograms, i, j, 0.);
|
||||
}
|
||||
}
|
||||
|
||||
while (image_histo_size > 1 && histo_queue.size > 0) {
|
||||
while (histo_queue.size > 0) {
|
||||
const int idx1 = histo_queue.queue[0].idx1;
|
||||
const int idx2 = histo_queue.queue[0].idx2;
|
||||
HistogramAdd(histograms[idx2], histograms[idx1], histograms[idx1]);
|
||||
histograms[idx1]->bit_cost_ = histo_queue.queue[0].cost_combo;
|
||||
|
||||
// Remove merged histogram.
|
||||
for (i = 0; i + 1 < image_histo_size; ++i) {
|
||||
if (clusters[i] >= idx2) {
|
||||
clusters[i] = clusters[i + 1];
|
||||
}
|
||||
}
|
||||
--image_histo_size;
|
||||
HistogramSetRemoveHistogram(image_histo, idx2, num_used);
|
||||
|
||||
// Remove pairs intersecting the just combined best pair.
|
||||
for (i = 0; i < histo_queue.size;) {
|
||||
@ -856,24 +884,15 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) {
|
||||
}
|
||||
|
||||
// Push new pairs formed with combined histogram to the queue.
|
||||
for (i = 0; i < image_histo_size; ++i) {
|
||||
if (clusters[i] != idx1) {
|
||||
HistoQueuePush(&histo_queue, histograms, idx1, clusters[i], 0.);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Move remaining histograms to the beginning of the array.
|
||||
for (i = 0; i < image_histo_size; ++i) {
|
||||
if (i != clusters[i]) { // swap the two histograms
|
||||
HistogramSwap(&histograms[i], &histograms[clusters[i]]);
|
||||
for (i = 0; i < image_histo->size; ++i) {
|
||||
if (i == idx1 || image_histo->histograms[i] == NULL) continue;
|
||||
HistoQueuePush(&histo_queue, image_histo->histograms, idx1, i, 0.);
|
||||
}
|
||||
}
|
||||
|
||||
image_histo->size = image_histo_size;
|
||||
ok = 1;
|
||||
|
||||
End:
|
||||
WebPSafeFree(clusters);
|
||||
HistoQueueClear(&histo_queue);
|
||||
return ok;
|
||||
}
|
||||
@ -881,47 +900,68 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) {
|
||||
// Perform histogram aggregation using a stochastic approach.
|
||||
// 'do_greedy' is set to 1 if a greedy approach needs to be performed
|
||||
// afterwards, 0 otherwise.
|
||||
static int PairComparison(const void* idx1, const void* idx2) {
|
||||
// To be used with bsearch: <0 when *idx1<*idx2, >0 if >, 0 when ==.
|
||||
return (*(int*) idx1 - *(int*) idx2);
|
||||
}
|
||||
static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
|
||||
int min_cluster_size,
|
||||
int* const num_used, int min_cluster_size,
|
||||
int* const do_greedy) {
|
||||
int iter;
|
||||
int j, iter;
|
||||
uint32_t seed = 1;
|
||||
int tries_with_no_success = 0;
|
||||
int image_histo_size = image_histo->size;
|
||||
const int outer_iters = image_histo_size;
|
||||
const int outer_iters = *num_used;
|
||||
const int num_tries_no_success = outer_iters / 2;
|
||||
VP8LHistogram** const histograms = image_histo->histograms;
|
||||
// Priority queue of histogram pairs. Its size of "kCostHeapSizeSqrt"^2
|
||||
// Priority queue of histogram pairs. Its size of 'kHistoQueueSize'
|
||||
// impacts the quality of the compression and the speed: the smaller the
|
||||
// faster but the worse for the compression.
|
||||
HistoQueue histo_queue;
|
||||
const int kHistoQueueSizeSqrt = 3;
|
||||
const int kHistoQueueSize = 9;
|
||||
int ok = 0;
|
||||
// mapping from an index in image_histo with no NULL histogram to the full
|
||||
// blown image_histo.
|
||||
int* mappings;
|
||||
|
||||
if (!HistoQueueInit(&histo_queue, kHistoQueueSizeSqrt)) {
|
||||
goto End;
|
||||
if (*num_used < min_cluster_size) {
|
||||
*do_greedy = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
mappings = (int*) WebPSafeMalloc(*num_used, sizeof(*mappings));
|
||||
if (mappings == NULL) return 0;
|
||||
if (!HistoQueueInit(&histo_queue, kHistoQueueSize)) goto End;
|
||||
// Fill the initial mapping.
|
||||
for (j = 0, iter = 0; iter < image_histo->size; ++iter) {
|
||||
if (histograms[iter] == NULL) continue;
|
||||
mappings[j++] = iter;
|
||||
}
|
||||
assert(j == *num_used);
|
||||
|
||||
// Collapse similar histograms in 'image_histo'.
|
||||
++min_cluster_size;
|
||||
for (iter = 0; iter < outer_iters && image_histo_size >= min_cluster_size &&
|
||||
++tries_with_no_success < num_tries_no_success;
|
||||
for (iter = 0;
|
||||
iter < outer_iters && *num_used >= min_cluster_size &&
|
||||
++tries_with_no_success < num_tries_no_success;
|
||||
++iter) {
|
||||
int* mapping_index;
|
||||
double best_cost =
|
||||
(histo_queue.size == 0) ? 0. : histo_queue.queue[0].cost_diff;
|
||||
int best_idx1 = -1, best_idx2 = 1;
|
||||
int j;
|
||||
const uint32_t rand_range = (image_histo_size - 1) * image_histo_size;
|
||||
// image_histo_size / 2 was chosen empirically. Less means faster but worse
|
||||
const uint32_t rand_range = (*num_used - 1) * (*num_used);
|
||||
// (*num_used) / 2 was chosen empirically. Less means faster but worse
|
||||
// compression.
|
||||
const int num_tries = image_histo_size / 2;
|
||||
const int num_tries = (*num_used) / 2;
|
||||
|
||||
for (j = 0; j < num_tries; ++j) {
|
||||
// Pick random samples.
|
||||
for (j = 0; *num_used >= 2 && j < num_tries; ++j) {
|
||||
double curr_cost;
|
||||
// Choose two different histograms at random and try to combine them.
|
||||
const uint32_t tmp = MyRand(&seed) % rand_range;
|
||||
const uint32_t idx1 = tmp / (image_histo_size - 1);
|
||||
uint32_t idx2 = tmp % (image_histo_size - 1);
|
||||
uint32_t idx1 = tmp / (*num_used - 1);
|
||||
uint32_t idx2 = tmp % (*num_used - 1);
|
||||
if (idx2 >= idx1) ++idx2;
|
||||
idx1 = mappings[idx1];
|
||||
idx2 = mappings[idx2];
|
||||
|
||||
// Calculate cost reduction on combination.
|
||||
curr_cost =
|
||||
@ -934,18 +974,21 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
|
||||
}
|
||||
if (histo_queue.size == 0) continue;
|
||||
|
||||
// Merge the two best histograms.
|
||||
// Get the best histograms.
|
||||
best_idx1 = histo_queue.queue[0].idx1;
|
||||
best_idx2 = histo_queue.queue[0].idx2;
|
||||
assert(best_idx1 < best_idx2);
|
||||
HistogramAddEval(histograms[best_idx1], histograms[best_idx2],
|
||||
histograms[best_idx1], 0);
|
||||
// Swap the best_idx2 histogram with the last one (which is now unused).
|
||||
--image_histo_size;
|
||||
if (best_idx2 != image_histo_size) {
|
||||
HistogramSwap(&histograms[image_histo_size], &histograms[best_idx2]);
|
||||
}
|
||||
histograms[image_histo_size] = NULL;
|
||||
// Pop best_idx2 from mappings.
|
||||
mapping_index = (int*) bsearch(&best_idx2, mappings, *num_used,
|
||||
sizeof(best_idx2), &PairComparison);
|
||||
assert(mapping_index != NULL);
|
||||
memmove(mapping_index, mapping_index + 1, sizeof(*mapping_index) *
|
||||
((*num_used) - (mapping_index - mappings) - 1));
|
||||
// Merge the histograms and remove best_idx2 from the queue.
|
||||
HistogramAdd(histograms[best_idx2], histograms[best_idx1],
|
||||
histograms[best_idx1]);
|
||||
histograms[best_idx1]->bit_cost_ = histo_queue.queue[0].cost_combo;
|
||||
HistogramSetRemoveHistogram(image_histo, best_idx2, num_used);
|
||||
// Parse the queue and update each pair that deals with best_idx1,
|
||||
// best_idx2 or image_histo_size.
|
||||
for (j = 0; j < histo_queue.size;) {
|
||||
@ -968,12 +1011,6 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
|
||||
p->idx2 = best_idx1;
|
||||
do_eval = 1;
|
||||
}
|
||||
if (p->idx2 == image_histo_size) {
|
||||
// No need to re-evaluate here as it does not involve a pair
|
||||
// containing best_idx1 or best_idx2.
|
||||
p->idx2 = best_idx2;
|
||||
}
|
||||
assert(p->idx2 < image_histo_size);
|
||||
// Make sure the index order is respected.
|
||||
if (p->idx1 > p->idx2) {
|
||||
const int tmp = p->idx2;
|
||||
@ -991,15 +1028,14 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
|
||||
HistoQueueUpdateHead(&histo_queue, p);
|
||||
++j;
|
||||
}
|
||||
|
||||
tries_with_no_success = 0;
|
||||
}
|
||||
image_histo->size = image_histo_size;
|
||||
*do_greedy = (image_histo->size <= min_cluster_size);
|
||||
*do_greedy = (*num_used <= min_cluster_size);
|
||||
ok = 1;
|
||||
|
||||
End:
|
||||
HistoQueueClear(&histo_queue);
|
||||
WebPSafeFree(mappings);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -1007,23 +1043,29 @@ End:
|
||||
// Histogram refinement
|
||||
|
||||
// Find the best 'out' histogram for each of the 'in' histograms.
|
||||
// At call-time, 'out' contains the histograms of the clusters.
|
||||
// Note: we assume that out[]->bit_cost_ is already up-to-date.
|
||||
static void HistogramRemap(const VP8LHistogramSet* const in,
|
||||
const VP8LHistogramSet* const out,
|
||||
VP8LHistogramSet* const out,
|
||||
uint16_t* const symbols) {
|
||||
int i;
|
||||
VP8LHistogram** const in_histo = in->histograms;
|
||||
VP8LHistogram** const out_histo = out->histograms;
|
||||
const int in_size = in->size;
|
||||
const int in_size = out->max_size;
|
||||
const int out_size = out->size;
|
||||
if (out_size > 1) {
|
||||
for (i = 0; i < in_size; ++i) {
|
||||
int best_out = 0;
|
||||
double best_bits = MAX_COST;
|
||||
int k;
|
||||
if (in_histo[i] == NULL) {
|
||||
// Arbitrarily set to the previous value if unused to help future LZ77.
|
||||
symbols[i] = symbols[i - 1];
|
||||
continue;
|
||||
}
|
||||
for (k = 0; k < out_size; ++k) {
|
||||
const double cur_bits =
|
||||
HistogramAddThresh(out_histo[k], in_histo[i], best_bits);
|
||||
double cur_bits;
|
||||
cur_bits = HistogramAddThresh(out_histo[k], in_histo[i], best_bits);
|
||||
if (k == 0 || cur_bits < best_bits) {
|
||||
best_bits = cur_bits;
|
||||
best_out = k;
|
||||
@ -1039,12 +1081,13 @@ static void HistogramRemap(const VP8LHistogramSet* const in,
|
||||
}
|
||||
|
||||
// Recompute each out based on raw and symbols.
|
||||
for (i = 0; i < out_size; ++i) {
|
||||
HistogramClear(out_histo[i]);
|
||||
}
|
||||
VP8LHistogramSetClear(out);
|
||||
out->size = out_size;
|
||||
|
||||
for (i = 0; i < in_size; ++i) {
|
||||
const int idx = symbols[i];
|
||||
int idx;
|
||||
if (in_histo[i] == NULL) continue;
|
||||
idx = symbols[i];
|
||||
HistogramAdd(in_histo[i], out_histo[idx], out_histo[idx]);
|
||||
}
|
||||
}
|
||||
@ -1060,6 +1103,70 @@ static double GetCombineCostFactor(int histo_size, int quality) {
|
||||
return combine_cost_factor;
|
||||
}
|
||||
|
||||
// Given a HistogramSet 'set', the mapping of clusters 'cluster_mapping' and the
|
||||
// current assignment of the cells in 'symbols', merge the clusters and
|
||||
// assign the smallest possible clusters values.
|
||||
static void OptimizeHistogramSymbols(const VP8LHistogramSet* const set,
|
||||
uint16_t* const cluster_mappings,
|
||||
int num_clusters,
|
||||
uint16_t* const cluster_mappings_tmp,
|
||||
uint16_t* const symbols) {
|
||||
int i, cluster_max;
|
||||
int do_continue = 1;
|
||||
// First, assign the lowest cluster to each pixel.
|
||||
while (do_continue) {
|
||||
do_continue = 0;
|
||||
for (i = 0; i < num_clusters; ++i) {
|
||||
int k;
|
||||
k = cluster_mappings[i];
|
||||
while (k != cluster_mappings[k]) {
|
||||
cluster_mappings[k] = cluster_mappings[cluster_mappings[k]];
|
||||
k = cluster_mappings[k];
|
||||
}
|
||||
if (k != cluster_mappings[i]) {
|
||||
do_continue = 1;
|
||||
cluster_mappings[i] = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Create a mapping from a cluster id to its minimal version.
|
||||
cluster_max = 0;
|
||||
memset(cluster_mappings_tmp, 0,
|
||||
set->max_size * sizeof(*cluster_mappings_tmp));
|
||||
assert(cluster_mappings[0] == 0);
|
||||
// Re-map the ids.
|
||||
for (i = 0; i < set->max_size; ++i) {
|
||||
int cluster;
|
||||
if (symbols[i] == kInvalidHistogramSymbol) continue;
|
||||
cluster = cluster_mappings[symbols[i]];
|
||||
assert(symbols[i] < num_clusters);
|
||||
if (cluster > 0 && cluster_mappings_tmp[cluster] == 0) {
|
||||
++cluster_max;
|
||||
cluster_mappings_tmp[cluster] = cluster_max;
|
||||
}
|
||||
symbols[i] = cluster_mappings_tmp[cluster];
|
||||
}
|
||||
|
||||
// Make sure all cluster values are used.
|
||||
cluster_max = 0;
|
||||
for (i = 0; i < set->max_size; ++i) {
|
||||
if (symbols[i] == kInvalidHistogramSymbol) continue;
|
||||
if (symbols[i] <= cluster_max) continue;
|
||||
++cluster_max;
|
||||
assert(symbols[i] == cluster_max);
|
||||
}
|
||||
}
|
||||
|
||||
static void RemoveEmptyHistograms(VP8LHistogramSet* const image_histo) {
|
||||
uint32_t size;
|
||||
int i;
|
||||
for (i = 0, size = 0; i < image_histo->size; ++i) {
|
||||
if (image_histo->histograms[i] == NULL) continue;
|
||||
image_histo->histograms[size++] = image_histo->histograms[i];
|
||||
}
|
||||
image_histo->size = size;
|
||||
}
|
||||
|
||||
int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
||||
const VP8LBackwardRefs* const refs,
|
||||
int quality, int low_effort,
|
||||
@ -1078,27 +1185,36 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
||||
// maximum quality q==100 (to preserve the compression gains at that level).
|
||||
const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE;
|
||||
int entropy_combine;
|
||||
|
||||
if (orig_histo == NULL) goto Error;
|
||||
uint16_t* const map_tmp =
|
||||
WebPSafeMalloc(2 * image_histo_raw_size, sizeof(map_tmp));
|
||||
uint16_t* const cluster_mappings = map_tmp + image_histo_raw_size;
|
||||
int num_used = image_histo_raw_size;
|
||||
if (orig_histo == NULL || map_tmp == NULL) goto Error;
|
||||
|
||||
// Construct the histograms from backward references.
|
||||
HistogramBuild(xsize, histo_bits, refs, orig_histo);
|
||||
// Copies the histograms and computes its bit_cost.
|
||||
HistogramCopyAndAnalyze(orig_histo, image_histo);
|
||||
// histogram_symbols is optimized
|
||||
HistogramCopyAndAnalyze(orig_histo, image_histo, &num_used,
|
||||
histogram_symbols);
|
||||
|
||||
entropy_combine =
|
||||
(image_histo->size > entropy_combine_num_bins * 2) && (quality < 100);
|
||||
(num_used > entropy_combine_num_bins * 2) && (quality < 100);
|
||||
|
||||
if (entropy_combine) {
|
||||
const int bin_map_size = image_histo->size;
|
||||
// Reuse histogram_symbols storage. By definition, it's guaranteed to be ok.
|
||||
uint16_t* const bin_map = histogram_symbols;
|
||||
uint16_t* const bin_map = map_tmp;
|
||||
const double combine_cost_factor =
|
||||
GetCombineCostFactor(image_histo_raw_size, quality);
|
||||
const uint32_t num_clusters = num_used;
|
||||
|
||||
HistogramAnalyzeEntropyBin(image_histo, bin_map, low_effort);
|
||||
// Collapse histograms with similar entropy.
|
||||
HistogramCombineEntropyBin(image_histo, tmp_histo, bin_map, bin_map_size,
|
||||
HistogramCombineEntropyBin(image_histo, &num_used, histogram_symbols,
|
||||
cluster_mappings, tmp_histo, bin_map,
|
||||
entropy_combine_num_bins, combine_cost_factor,
|
||||
low_effort);
|
||||
OptimizeHistogramSymbols(image_histo, cluster_mappings, num_clusters,
|
||||
map_tmp, histogram_symbols);
|
||||
}
|
||||
|
||||
// Don't combine the histograms using stochastic and greedy heuristics for
|
||||
@ -1108,21 +1224,26 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
||||
// cubic ramp between 1 and MAX_HISTO_GREEDY:
|
||||
const int threshold_size = (int)(1 + (x * x * x) * (MAX_HISTO_GREEDY - 1));
|
||||
int do_greedy;
|
||||
if (!HistogramCombineStochastic(image_histo, threshold_size, &do_greedy)) {
|
||||
if (!HistogramCombineStochastic(image_histo, &num_used, threshold_size,
|
||||
&do_greedy)) {
|
||||
goto Error;
|
||||
}
|
||||
if (do_greedy && !HistogramCombineGreedy(image_histo)) {
|
||||
goto Error;
|
||||
if (do_greedy) {
|
||||
RemoveEmptyHistograms(image_histo);
|
||||
if (!HistogramCombineGreedy(image_histo, &num_used)) {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(vrabaud): Optimize HistogramRemap for low-effort compression mode.
|
||||
// Find the optimal map from original histograms to the final ones.
|
||||
RemoveEmptyHistograms(image_histo);
|
||||
HistogramRemap(orig_histo, image_histo, histogram_symbols);
|
||||
|
||||
ok = 1;
|
||||
|
||||
Error:
|
||||
VP8LFreeHistogramSet(orig_histo);
|
||||
WebPSafeFree(map_tmp);
|
||||
return ok;
|
||||
}
|
||||
|
@ -177,12 +177,15 @@ static uint8_t NearLosslessComponent(uint8_t value, uint8_t predict,
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t NearLosslessDiff(uint8_t a, uint8_t b) {
|
||||
return (uint8_t)((((int)(a) - (int)(b))) & 0xff);
|
||||
}
|
||||
|
||||
// Quantize every component of the difference between the actual pixel value and
|
||||
// its prediction to a multiple of a quantization (a power of 2, not larger than
|
||||
// max_quantization which is a power of 2, smaller than max_diff). Take care if
|
||||
// value and predict have undergone subtract green, which means that red and
|
||||
// blue are represented as offsets from green.
|
||||
#define NEAR_LOSSLESS_DIFF(a, b) (uint8_t)((((int)(a) - (int)(b))) & 0xff)
|
||||
static uint32_t NearLossless(uint32_t value, uint32_t predict,
|
||||
int max_quantization, int max_diff,
|
||||
int used_subtract_green) {
|
||||
@ -199,7 +202,7 @@ static uint32_t NearLossless(uint32_t value, uint32_t predict,
|
||||
}
|
||||
if ((value >> 24) == 0 || (value >> 24) == 0xff) {
|
||||
// Preserve transparency of fully transparent or fully opaque pixels.
|
||||
a = NEAR_LOSSLESS_DIFF(value >> 24, predict >> 24);
|
||||
a = NearLosslessDiff(value >> 24, predict >> 24);
|
||||
} else {
|
||||
a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization);
|
||||
}
|
||||
@ -212,16 +215,15 @@ static uint32_t NearLossless(uint32_t value, uint32_t predict,
|
||||
// The amount by which green has been adjusted during quantization. It is
|
||||
// subtracted from red and blue for compensation, to avoid accumulating two
|
||||
// quantization errors in them.
|
||||
green_diff = NEAR_LOSSLESS_DIFF(new_green, value >> 8);
|
||||
green_diff = NearLosslessDiff(new_green, value >> 8);
|
||||
}
|
||||
r = NearLosslessComponent(NEAR_LOSSLESS_DIFF(value >> 16, green_diff),
|
||||
r = NearLosslessComponent(NearLosslessDiff(value >> 16, green_diff),
|
||||
(predict >> 16) & 0xff, 0xff - new_green,
|
||||
quantization);
|
||||
b = NearLosslessComponent(NEAR_LOSSLESS_DIFF(value, green_diff),
|
||||
b = NearLosslessComponent(NearLosslessDiff(value, green_diff),
|
||||
predict & 0xff, 0xff - new_green, quantization);
|
||||
return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||
}
|
||||
#undef NEAR_LOSSLESS_DIFF
|
||||
#endif // (WEBP_NEAR_LOSSLESS == 1)
|
||||
|
||||
// Stores the difference between the pixel and its prediction in "out".
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <math.h>
|
||||
#include <stdlib.h> // for abs()
|
||||
|
||||
#include "src/dsp/quant.h"
|
||||
#include "src/enc/vp8i_enc.h"
|
||||
#include "src/enc/cost_enc.h"
|
||||
|
||||
@ -977,19 +978,6 @@ static void SwapOut(VP8EncIterator* const it) {
|
||||
SwapPtr(&it->yuv_out_, &it->yuv_out2_);
|
||||
}
|
||||
|
||||
static score_t IsFlat(const int16_t* levels, int num_blocks, score_t thresh) {
|
||||
score_t score = 0;
|
||||
while (num_blocks-- > 0) { // TODO(skal): refine positional scoring?
|
||||
int i;
|
||||
for (i = 1; i < 16; ++i) { // omit DC, we're only interested in AC
|
||||
score += (levels[i] != 0);
|
||||
if (score > thresh) return 0;
|
||||
}
|
||||
levels += 16;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
|
||||
const int kNumBlocks = 16;
|
||||
VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_];
|
||||
|
@ -32,7 +32,7 @@ extern "C" {
|
||||
// version numbers
|
||||
#define ENC_MAJ_VERSION 1
|
||||
#define ENC_MIN_VERSION 0
|
||||
#define ENC_REV_VERSION 1
|
||||
#define ENC_REV_VERSION 2
|
||||
|
||||
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
|
||||
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
|
||||
|
@ -462,6 +462,7 @@ static int GetHuffBitLengthsAndCodes(
|
||||
for (i = 0; i < histogram_image_size; ++i) {
|
||||
const VP8LHistogram* const histo = histogram_image->histograms[i];
|
||||
HuffmanTreeCode* const codes = &huffman_codes[5 * i];
|
||||
assert(histo != NULL);
|
||||
for (k = 0; k < 5; ++k) {
|
||||
const int num_symbols =
|
||||
(k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits_) :
|
||||
|
@ -6,8 +6,8 @@
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,1
|
||||
PRODUCTVERSION 1,0,0,1
|
||||
FILEVERSION 1,0,0,2
|
||||
PRODUCTVERSION 1,0,0,2
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@ -24,12 +24,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libwebp DLL"
|
||||
VALUE "FileVersion", "1.0.1"
|
||||
VALUE "FileVersion", "1.0.2"
|
||||
VALUE "InternalName", "libwebp.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2018"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2019"
|
||||
VALUE "OriginalFilename", "libwebp.dll"
|
||||
VALUE "ProductName", "WebP Image Codec"
|
||||
VALUE "ProductVersion", "1.0.1"
|
||||
VALUE "ProductVersion", "1.0.2"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -6,8 +6,8 @@
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,1
|
||||
PRODUCTVERSION 1,0,0,1
|
||||
FILEVERSION 1,0,0,2
|
||||
PRODUCTVERSION 1,0,0,2
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@ -24,12 +24,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libwebpdecoder DLL"
|
||||
VALUE "FileVersion", "1.0.1"
|
||||
VALUE "FileVersion", "1.0.2"
|
||||
VALUE "InternalName", "libwebpdecoder.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2018"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2019"
|
||||
VALUE "OriginalFilename", "libwebpdecoder.dll"
|
||||
VALUE "ProductName", "WebP Image Decoder"
|
||||
VALUE "ProductVersion", "1.0.1"
|
||||
VALUE "ProductVersion", "1.0.2"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -17,6 +17,6 @@ noinst_HEADERS =
|
||||
noinst_HEADERS += ../webp/format_constants.h
|
||||
|
||||
libwebpmux_la_LIBADD = ../libwebp.la
|
||||
libwebpmux_la_LDFLAGS = -no-undefined -version-info 3:3:0 -lm
|
||||
libwebpmux_la_LDFLAGS = -no-undefined -version-info 3:4:0 -lm
|
||||
libwebpmuxincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebpmux.pc
|
||||
|
@ -6,8 +6,8 @@
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,1
|
||||
PRODUCTVERSION 1,0,0,1
|
||||
FILEVERSION 1,0,0,2
|
||||
PRODUCTVERSION 1,0,0,2
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@ -24,12 +24,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libwebpmux DLL"
|
||||
VALUE "FileVersion", "1.0.1"
|
||||
VALUE "FileVersion", "1.0.2"
|
||||
VALUE "InternalName", "libwebpmux.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2018"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2019"
|
||||
VALUE "OriginalFilename", "libwebpmux.dll"
|
||||
VALUE "ProductName", "WebP Image Muxer"
|
||||
VALUE "ProductVersion", "1.0.1"
|
||||
VALUE "ProductVersion", "1.0.2"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -29,7 +29,7 @@ extern "C" {
|
||||
|
||||
#define MUX_MAJ_VERSION 1
|
||||
#define MUX_MIN_VERSION 0
|
||||
#define MUX_REV_VERSION 1
|
||||
#define MUX_REV_VERSION 2
|
||||
|
||||
// Chunk object.
|
||||
typedef struct WebPChunk WebPChunk;
|
||||
|
@ -248,6 +248,7 @@ int VP8LBitWriterClone(const VP8LBitWriter* const src,
|
||||
dst->bits_ = src->bits_;
|
||||
dst->used_ = src->used_;
|
||||
dst->error_ = src->error_;
|
||||
dst->cur_ = dst->buf_ + current_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -107,19 +107,6 @@ static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
|
||||
PutLE16(data + 2, (int)(val >> 16));
|
||||
}
|
||||
|
||||
// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either
|
||||
// based on table or not. Can be used as fallback if clz() is not available.
|
||||
#define WEBP_NEED_LOG_TABLE_8BIT
|
||||
extern const uint8_t WebPLogTable8bit[256];
|
||||
static WEBP_INLINE int WebPLog2FloorC(uint32_t n) {
|
||||
int log_value = 0;
|
||||
while (n >= 256) {
|
||||
log_value += 8;
|
||||
n >>= 8;
|
||||
}
|
||||
return log_value + WebPLogTable8bit[n];
|
||||
}
|
||||
|
||||
// Returns (int)floor(log2(n)). n must be > 0.
|
||||
// use GNU builtins where available.
|
||||
#if defined(__GNUC__) && \
|
||||
@ -138,6 +125,19 @@ static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
|
||||
return first_set_bit;
|
||||
}
|
||||
#else // default: use the C-version.
|
||||
// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either
|
||||
// based on table or not. Can be used as fallback if clz() is not available.
|
||||
#define WEBP_NEED_LOG_TABLE_8BIT
|
||||
extern const uint8_t WebPLogTable8bit[256];
|
||||
static WEBP_INLINE int WebPLog2FloorC(uint32_t n) {
|
||||
int log_value = 0;
|
||||
while (n >= 256) {
|
||||
log_value += 8;
|
||||
n >>= 8;
|
||||
}
|
||||
return log_value + WebPLogTable8bit[n];
|
||||
}
|
||||
|
||||
static WEBP_INLINE int BitsLog2Floor(uint32_t n) { return WebPLog2FloorC(n); }
|
||||
#endif
|
||||
|
||||
|
@ -42,6 +42,12 @@ WEBP_EXTERN int WebPGetDecoderVersion(void);
|
||||
// This function will also validate the header, returning true on success,
|
||||
// false otherwise. '*width' and '*height' are only valid on successful return.
|
||||
// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
|
||||
// Note: The following chunk sequences (before the raw VP8/VP8L data) are
|
||||
// considered valid by this function:
|
||||
// RIFF + VP8(L)
|
||||
// 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.
|
||||
WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
@ -425,6 +431,12 @@ WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal(
|
||||
// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
|
||||
// features from headers. Returns error in other cases.
|
||||
// Note: The following chunk sequences (before the raw VP8/VP8L data) are
|
||||
// considered valid by this function:
|
||||
// RIFF + VP8(L)
|
||||
// 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 WEBP_INLINE VP8StatusCode WebPGetFeatures(
|
||||
const uint8_t* data, size_t data_size,
|
||||
WebPBitstreamFeatures* features) {
|
||||
|
Reference in New Issue
Block a user