mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 05:38:22 +01:00
Merge branch '0.2.0'
* 0.2.0: (42 commits) Update ChangeLog dec/io.c: cosmetics RGBA4444: harmonize lossless/lossy alpha values fix RGBA4444 output w/fancy upsampling Alignment fix avoid rgb-premultiply if there's only trivial alpha values fix the ARGB4444 premultiply arithmetic Lossless decoder fix for a special transform order Update encoding heuristic w.r.t palette colors. remove unused ApplyInverseTransform() Update ChangeLog update AUTHORS update NEWS add support for ARGB -> YUVA conversion for lossless decoder bump version to 0.2.0 fix alpha-plane check + add extra checks MODE_YUVA: set alpha to opaque if the image has none silence one more warning move some RGB->YUV functions to yuv.h README: sync [cd]webp help output ... Change-Id: I4c4e3be2e88655d25d4dd2eae46c580d960b12ac
This commit is contained in:
commit
e55fbd6d2c
1
AUTHORS
1
AUTHORS
@ -2,6 +2,7 @@ Contributors:
|
|||||||
- James Zern (jzern at google dot com)
|
- James Zern (jzern at google dot com)
|
||||||
- Jan Engelhardt (jengelh at medozas dot de)
|
- Jan Engelhardt (jengelh at medozas dot de)
|
||||||
- Johann (johannkoenig at google dot com)
|
- Johann (johannkoenig at google dot com)
|
||||||
|
- Jyrki Alakuijala (jyrki at google dot com)
|
||||||
- Lou Quillio (louquillio at google dot com)
|
- Lou Quillio (louquillio at google dot com)
|
||||||
- Martin Olsson (mnemo at minimum dot se)
|
- Martin Olsson (mnemo at minimum dot se)
|
||||||
- Mikołaj Zalewski (mikolajz at google dot com)
|
- Mikołaj Zalewski (mikolajz at google dot com)
|
||||||
|
@ -47,9 +47,10 @@ LOCAL_SRC_FILES := \
|
|||||||
src/utils/quant_levels.c \
|
src/utils/quant_levels.c \
|
||||||
src/utils/rescaler.c \
|
src/utils/rescaler.c \
|
||||||
src/utils/thread.c \
|
src/utils/thread.c \
|
||||||
|
src/utils/utils.c \
|
||||||
|
|
||||||
LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD \
|
LOCAL_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD \
|
||||||
-DNOT_HAVE_LOG2 -DWEBP_USE_THREAD \
|
-DWEBP_USE_THREAD \
|
||||||
-finline-functions -frename-registers -ffast-math \
|
-finline-functions -frename-registers -ffast-math \
|
||||||
-s -fomit-frame-pointer -Isrc/webp
|
-s -fomit-frame-pointer -Isrc/webp
|
||||||
|
|
||||||
|
56
ChangeLog
56
ChangeLog
@ -1,3 +1,57 @@
|
|||||||
|
c655380 dec/io.c: cosmetics
|
||||||
|
fe1958f RGBA4444: harmonize lossless/lossy alpha values
|
||||||
|
681cb30 fix RGBA4444 output w/fancy upsampling
|
||||||
|
f06c1d8 Merge "Alignment fix" into 0.2.0
|
||||||
|
f56e98f Alignment fix
|
||||||
|
6fe843b avoid rgb-premultiply if there's only trivial alpha values
|
||||||
|
528a11a fix the ARGB4444 premultiply arithmetic
|
||||||
|
a0a4885 Lossless decoder fix for a special transform order
|
||||||
|
62dd9bb Update encoding heuristic w.r.t palette colors.
|
||||||
|
6f4272b remove unused ApplyInverseTransform()
|
||||||
|
93bf0fa Update ChangeLog (v0.2.0-rc1)
|
||||||
|
5934fc5 update AUTHORS
|
||||||
|
014a711 update NEWS
|
||||||
|
43b0d61 add support for ARGB -> YUVA conversion for lossless decoder
|
||||||
|
33705ca bump version to 0.2.0
|
||||||
|
c40d7ef fix alpha-plane check + add extra checks
|
||||||
|
a06f802 MODE_YUVA: set alpha to opaque if the image has none
|
||||||
|
52a87dd Merge "silence one more warning" into 0.2.0
|
||||||
|
3b02309 silence one more warning
|
||||||
|
f94b04f move some RGB->YUV functions to yuv.h
|
||||||
|
4b71ba0 README: sync [cd]webp help output
|
||||||
|
c9ae57f man/dwebp.1: add links to output file format details
|
||||||
|
292ec5c quiet a few 'uninitialized' warnings
|
||||||
|
4af3f6c fix indentation
|
||||||
|
9b261bf remove the last NOT_HAVE_LOG2 instances
|
||||||
|
323dc4d remove use of log2(). Use VP8LFastLog2() instead.
|
||||||
|
8c515d5 Merge "harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc" into 0.2.0
|
||||||
|
d4b4bb0 Merge changes I46090628,I1a41b2ce into 0.2.0
|
||||||
|
bff34ac harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc
|
||||||
|
a3c063c Merge "extra size check for security" into 0.2.0
|
||||||
|
5e79630 Merge "WebPEncode: clear stats at the start of encode" into 0.2.0
|
||||||
|
f1edf62 Merge "rationalize use of color-cache" into 0.2.0
|
||||||
|
c193331 extra size check for security
|
||||||
|
906be65 rationalize use of color-cache
|
||||||
|
dd1c387 Add image-hint for low-color images.
|
||||||
|
4eb7aa6 Merge "WebPCheckMalloc() and WebPCheckCalloc():" into 0.2.0
|
||||||
|
80cc730 WebPCheckMalloc() and WebPCheckCalloc():
|
||||||
|
183cba8 check VP8LBitWriterInit return
|
||||||
|
cbfa9ee lossless: fix crash on user abort
|
||||||
|
256afef cwebp: exit immediately on version mismatch
|
||||||
|
475d87d WebPEncode: clear stats at the start of encode
|
||||||
|
a7cc729 fix type and conversion warnings
|
||||||
|
7d853d7 add stats for lossless
|
||||||
|
d39177b make QuantizeLevels() store the sum of squared error
|
||||||
|
5955cf5 replace x*155/100 by x*101581>>16
|
||||||
|
7d732f9 make QuantizeLevels() store the sum of squared error
|
||||||
|
e45a446 replace x*155/100 by x*101581>>16
|
||||||
|
159b75d cwebp output size consistency:
|
||||||
|
cbee59e Merge commit 'v0.1.99'
|
||||||
|
1889e9b dwebp: report -alpha option
|
||||||
|
3bc3f7c Merge "dwebp: add PAM output support" into 0.2.0
|
||||||
|
d919ed0 dwebp: add PAM output support
|
||||||
|
85e215d README/manpages/configure: update website link
|
||||||
|
c3a207b Update ChangeLog (v0.1.99)
|
||||||
d1fd782 Merge "add extra precision about default values and behaviour" into 0.2.0
|
d1fd782 Merge "add extra precision about default values and behaviour" into 0.2.0
|
||||||
efc826e add extra precision about default values and behaviour
|
efc826e add extra precision about default values and behaviour
|
||||||
9f29635 header/doc clean up
|
9f29635 header/doc clean up
|
||||||
@ -14,6 +68,7 @@ c37c23e README: cosmetics
|
|||||||
ce90847 Merge "add some padding bytes areas for later use" into 0.2.0
|
ce90847 Merge "add some padding bytes areas for later use" into 0.2.0
|
||||||
2390dab Merge "fixing the findings by Frederic Kayser to the bitstream spec" into 0.2.0
|
2390dab Merge "fixing the findings by Frederic Kayser to the bitstream spec" into 0.2.0
|
||||||
0275159 add a very crude progress report for lossless
|
0275159 add a very crude progress report for lossless
|
||||||
|
a4b9b1c Remove some unused enum values.
|
||||||
dd10817 rename 'use_argb_input' to 'use_argb'
|
dd10817 rename 'use_argb_input' to 'use_argb'
|
||||||
90516ae add some padding bytes areas for later use
|
90516ae add some padding bytes areas for later use
|
||||||
d03b250 fixing the findings by Frederic Kayser to the bitstream spec
|
d03b250 fixing the findings by Frederic Kayser to the bitstream spec
|
||||||
@ -46,6 +101,7 @@ c3b014d Android.mk: add missing lossless files
|
|||||||
8c1cc6b makefile.unix dist: explicitly name installed includes
|
8c1cc6b makefile.unix dist: explicitly name installed includes
|
||||||
7f4647e Merge "clarify the colorspace naming and byte ordering of decoded samples" into 0.2.0
|
7f4647e Merge "clarify the colorspace naming and byte ordering of decoded samples" into 0.2.0
|
||||||
cbf6972 clarify the colorspace naming and byte ordering of decoded samples
|
cbf6972 clarify the colorspace naming and byte ordering of decoded samples
|
||||||
|
857650c Mux: Add WebPDataInit() and remove WebPImageInfo
|
||||||
ff771e7 don't install webp/decode_vp8.h
|
ff771e7 don't install webp/decode_vp8.h
|
||||||
596dff7 VP8LFillBitWindow: use 64-bit path for msvc x64 builds
|
596dff7 VP8LFillBitWindow: use 64-bit path for msvc x64 builds
|
||||||
3ca7ce9 Merge "doc: remove non-finalized chunk references" into 0.2.0
|
3ca7ce9 Merge "doc: remove non-finalized chunk references" into 0.2.0
|
||||||
|
@ -27,7 +27,7 @@ CCNODBG = cl.exe /nologo /O2 /DNDEBUG
|
|||||||
CCDEBUG = cl.exe /nologo /Od /Gm /Zi /D_DEBUG /RTC1
|
CCDEBUG = cl.exe /nologo /Od /Gm /Zi /D_DEBUG /RTC1
|
||||||
CFLAGS = /Isrc /nologo /W3 /EHsc /FD /c /GS
|
CFLAGS = /Isrc /nologo /W3 /EHsc /FD /c /GS
|
||||||
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
|
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
|
||||||
CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD /DNOT_HAVE_LOG2
|
CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
|
||||||
LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE
|
LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE
|
||||||
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
|
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
|
||||||
LNKDLL = link.exe /DLL
|
LNKDLL = link.exe /DLL
|
||||||
@ -199,6 +199,7 @@ UTILS_OBJS = \
|
|||||||
$(DIROBJ)\utils\quant_levels.obj \
|
$(DIROBJ)\utils\quant_levels.obj \
|
||||||
$(DIROBJ)\utils\rescaler.obj \
|
$(DIROBJ)\utils\rescaler.obj \
|
||||||
$(DIROBJ)\utils\thread.obj \
|
$(DIROBJ)\utils\thread.obj \
|
||||||
|
$(DIROBJ)\utils\utils.obj \
|
||||||
|
|
||||||
LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS) $(LIBWEBP_OBJS)
|
LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS) $(LIBWEBP_OBJS)
|
||||||
LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
|
LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
|
||||||
|
7
NEWS
7
NEWS
@ -1,3 +1,10 @@
|
|||||||
|
- 8/3/12: version 0.2.0
|
||||||
|
* Add support for ARGB -> YUVA conversion for lossless decoder
|
||||||
|
New functions: WebPINewYUVA, WebPIDecGetYUVA
|
||||||
|
* Add stats for lossless and alpha encoding
|
||||||
|
* Security related hardening: allocation and size checks
|
||||||
|
* Add PAM output support to dwebp
|
||||||
|
|
||||||
- 7/19/12: version 0.1.99
|
- 7/19/12: version 0.1.99
|
||||||
* This is a pre-release of 0.2.0, not an rc to allow for further
|
* This is a pre-release of 0.2.0, not an rc to allow for further
|
||||||
incompatible changes based on user feedback.
|
incompatible changes based on user feedback.
|
||||||
|
14
README
14
README
@ -4,7 +4,7 @@
|
|||||||
\__\__/\____/\_____/__/ ____ ___
|
\__\__/\____/\_____/__/ ____ ___
|
||||||
/ _/ / \ \ / _ \/ _/
|
/ _/ / \ \ / _ \/ _/
|
||||||
/ \_/ / / \ \ __/ \__
|
/ \_/ / / \ \ __/ \__
|
||||||
\____/____/\_____/_____/____/v0.1.99
|
\____/____/\_____/_____/____/v0.2.0
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
============
|
============
|
||||||
@ -13,7 +13,7 @@ WebP codec: library to encode and decode images in WebP format. This package
|
|||||||
contains the library that can be used in other programs to add WebP support,
|
contains the library that can be used in other programs to add WebP support,
|
||||||
as well as the command line tools 'cwebp' and 'dwebp'.
|
as well as the command line tools 'cwebp' and 'dwebp'.
|
||||||
|
|
||||||
See http://code.google.com/speed/webp
|
See http://developers.google.com/speed/webp
|
||||||
|
|
||||||
Latest sources are available from http://www.webmproject.org/code/
|
Latest sources are available from http://www.webmproject.org/code/
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ options:
|
|||||||
-noalpha ............... discard any transparency information.
|
-noalpha ............... discard any transparency information.
|
||||||
-lossless .............. Encode image losslessly.
|
-lossless .............. Encode image losslessly.
|
||||||
-hint <string> ......... Specify image characteristics hint.
|
-hint <string> ......... Specify image characteristics hint.
|
||||||
One of: photo or picture
|
One of: photo, picture or graph
|
||||||
|
|
||||||
-short ................. condense printed message
|
-short ................. condense printed message
|
||||||
-quiet ................. don't print anything.
|
-quiet ................. don't print anything.
|
||||||
@ -231,7 +231,8 @@ Usage: dwebp in_file [options] [-o out_file]
|
|||||||
|
|
||||||
Decodes the WebP image file to PNG format [Default]
|
Decodes the WebP image file to PNG format [Default]
|
||||||
Use following options to convert into alternate image formats:
|
Use following options to convert into alternate image formats:
|
||||||
-ppm ......... save the raw RGB samples as color PPM
|
-pam ......... save the raw RGBA samples as a color PAM
|
||||||
|
-ppm ......... save the raw RGB samples as a color PPM
|
||||||
-pgm ......... save the raw YUV samples as a grayscale PGM
|
-pgm ......... save the raw YUV samples as a grayscale PGM
|
||||||
file with IMC4 layout.
|
file with IMC4 layout.
|
||||||
Other options are:
|
Other options are:
|
||||||
@ -241,6 +242,7 @@ Use following options to convert into alternate image formats:
|
|||||||
-mt .......... use multi-threading
|
-mt .......... use multi-threading
|
||||||
-crop <x> <y> <w> <h> ... crop output with the given rectangle
|
-crop <x> <y> <w> <h> ... crop output with the given rectangle
|
||||||
-scale <w> <h> .......... scale the output (*after* any cropping)
|
-scale <w> <h> .......... scale the output (*after* any cropping)
|
||||||
|
-alpha ....... only save the alpha plane.
|
||||||
-h ....... this help message.
|
-h ....... this help message.
|
||||||
-v ....... verbose (e.g. print encoding/decoding times)
|
-v ....... verbose (e.g. print encoding/decoding times)
|
||||||
-noasm ....... disable all assembly optimizations.
|
-noasm ....... disable all assembly optimizations.
|
||||||
@ -403,12 +405,12 @@ The 'idec' object must always be released (even upon an error condition) by
|
|||||||
calling: WebPDelete(idec).
|
calling: WebPDelete(idec).
|
||||||
|
|
||||||
To retrieve partially decoded picture samples, one must use the corresponding
|
To retrieve partially decoded picture samples, one must use the corresponding
|
||||||
method: WebPIDecGetRGB or WebPIDecGetYUV.
|
method: WebPIDecGetRGB or WebPIDecGetYUVA.
|
||||||
It will return the last displayable pixel row.
|
It will return the last displayable pixel row.
|
||||||
|
|
||||||
Lastly, note that decoding can also be performed into a pre-allocated pixel
|
Lastly, note that decoding can also be performed into a pre-allocated pixel
|
||||||
buffer. This buffer must be passed when creating a WebPIDecoder, calling
|
buffer. This buffer must be passed when creating a WebPIDecoder, calling
|
||||||
WebPINewRGB() or WebPINewYUV().
|
WebPINewRGB() or WebPINewYUVA().
|
||||||
|
|
||||||
Please have a look at the src/webp/decode.h header for further details.
|
Please have a look at the src/webp/decode.h header for further details.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
AC_INIT([libwebp], [0.1.99],
|
AC_INIT([libwebp], [0.2.0],
|
||||||
[http://code.google.com/p/webp/issues],,
|
[http://code.google.com/p/webp/issues],,
|
||||||
[http://code.google.com/speed/webp])
|
[http://developers.google.com/speed/webp])
|
||||||
AC_CANONICAL_TARGET
|
AC_CANONICAL_TARGET
|
||||||
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
|
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
|
||||||
AC_PROG_LIBTOOL
|
AC_PROG_LIBTOOL
|
||||||
@ -29,11 +29,6 @@ AC_DEFUN([WITHLIB_OPTION],
|
|||||||
[use $2 libraries from DIR]),
|
[use $2 libraries from DIR]),
|
||||||
[$2_LIBS="-L$withval"])])
|
[$2_LIBS="-L$withval"])])
|
||||||
|
|
||||||
dnl === Check for native log2
|
|
||||||
AC_SEARCH_LIBS([log2], [m],,
|
|
||||||
[AC_DEFINE([NOT_HAVE_LOG2], [1],
|
|
||||||
[Undefine this if you have log2().])])
|
|
||||||
|
|
||||||
dnl === Check for pthread support
|
dnl === Check for pthread support
|
||||||
AC_ARG_ENABLE([threading],
|
AC_ARG_ENABLE([threading],
|
||||||
AS_HELP_STRING([--disable-threading],
|
AS_HELP_STRING([--disable-threading],
|
||||||
|
@ -132,7 +132,7 @@ static HRESULT ReadPictureWithWIC(const char* filename,
|
|||||||
IWICBitmapDecoder* pDecoder = NULL;
|
IWICBitmapDecoder* pDecoder = NULL;
|
||||||
IStream* pStream = NULL;
|
IStream* pStream = NULL;
|
||||||
UINT frameCount = 0;
|
UINT frameCount = 0;
|
||||||
UINT width, height = 0;
|
UINT width = 0, height = 0;
|
||||||
BYTE* rgb = NULL;
|
BYTE* rgb = NULL;
|
||||||
WICPixelFormatGUID srcPixelFormat = { 0 };
|
WICPixelFormatGUID srcPixelFormat = { 0 };
|
||||||
GUID srcContainerFormat = { 0 };
|
GUID srcContainerFormat = { 0 };
|
||||||
@ -614,6 +614,25 @@ static void PrintValues(const int values[4]) {
|
|||||||
fprintf(stderr, "|\n");
|
fprintf(stderr, "|\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
|
||||||
|
const char* const description) {
|
||||||
|
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
|
||||||
|
description, stats->lossless_size);
|
||||||
|
if (stats->lossless_features) {
|
||||||
|
fprintf(stderr, " * Lossless features used:");
|
||||||
|
if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
|
||||||
|
if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM");
|
||||||
|
if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN");
|
||||||
|
if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n",
|
||||||
|
stats->histogram_bits, stats->transform_bits, stats->cache_bits);
|
||||||
|
if (stats->palette_size > 0) {
|
||||||
|
fprintf(stderr, " * Palette size: %d\n", stats->palette_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
||||||
int short_output,
|
int short_output,
|
||||||
const char* const file_name) {
|
const char* const file_name) {
|
||||||
@ -624,6 +643,7 @@ static void PrintExtraInfoLossless(const WebPPicture* const pic,
|
|||||||
fprintf(stderr, "File: %s\n", file_name);
|
fprintf(stderr, "File: %s\n", file_name);
|
||||||
fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
|
fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
|
||||||
fprintf(stderr, "Output: %d bytes\n", stats->coded_size);
|
fprintf(stderr, "Output: %d bytes\n", stats->coded_size);
|
||||||
|
PrintFullLosslessInfo(stats, "ARGB");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,9 +678,9 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
|||||||
100.f * stats->header_bytes[0] / stats->coded_size,
|
100.f * stats->header_bytes[0] / stats->coded_size,
|
||||||
stats->header_bytes[1],
|
stats->header_bytes[1],
|
||||||
100.f * stats->header_bytes[1] / stats->coded_size);
|
100.f * stats->header_bytes[1] / stats->coded_size);
|
||||||
if (stats->alpha_data_size) {
|
if (stats->alpha_data_size > 0) {
|
||||||
fprintf(stderr, " transparency: %6d\n",
|
fprintf(stderr, " transparency: %6d (%.1f dB)\n",
|
||||||
stats->alpha_data_size);
|
stats->alpha_data_size, stats->PSNR[4]);
|
||||||
}
|
}
|
||||||
if (stats->layer_data_size) {
|
if (stats->layer_data_size) {
|
||||||
fprintf(stderr, " enhancement: %6d\n",
|
fprintf(stderr, " enhancement: %6d\n",
|
||||||
@ -686,8 +706,11 @@ static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
|
|||||||
fprintf(stderr, " segments total: ");
|
fprintf(stderr, " segments total: ");
|
||||||
PrintByteCount(totals, stats->coded_size, NULL);
|
PrintByteCount(totals, stats->coded_size, NULL);
|
||||||
}
|
}
|
||||||
|
if (stats->lossless_size > 0) {
|
||||||
|
PrintFullLosslessInfo(stats, "alpha");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pic->extra_info) {
|
if (pic->extra_info != NULL) {
|
||||||
const int mb_w = (pic->width + 15) / 16;
|
const int mb_w = (pic->width + 15) / 16;
|
||||||
const int mb_h = (pic->height + 15) / 16;
|
const int mb_h = (pic->height + 15) / 16;
|
||||||
const int type = pic->extra_info_type;
|
const int type = pic->extra_info_type;
|
||||||
@ -756,7 +779,7 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
|
|||||||
|
|
||||||
static int ProgressReport(int percent, const WebPPicture* const picture) {
|
static int ProgressReport(int percent, const WebPPicture* const picture) {
|
||||||
printf("[%s]: %3d %% \r",
|
printf("[%s]: %3d %% \r",
|
||||||
(char*)picture->stats->user_data, percent);
|
(char*)picture->user_data, percent);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
return 1; // all ok
|
return 1; // all ok
|
||||||
}
|
}
|
||||||
@ -821,7 +844,7 @@ static void HelpLong(void) {
|
|||||||
printf(" -noalpha ............... discard any transparency information.\n");
|
printf(" -noalpha ............... discard any transparency information.\n");
|
||||||
printf(" -lossless .............. Encode image losslessly.\n");
|
printf(" -lossless .............. Encode image losslessly.\n");
|
||||||
printf(" -hint <string> ......... Specify image characteristics hint.\n");
|
printf(" -hint <string> ......... Specify image characteristics hint.\n");
|
||||||
printf(" One of: photo or picture\n");
|
printf(" One of: photo, picture or graph\n");
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf(" -short ................. condense printed message\n");
|
printf(" -short ................. condense printed message\n");
|
||||||
@ -886,7 +909,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
!WebPPictureInit(&original_picture) ||
|
!WebPPictureInit(&original_picture) ||
|
||||||
!WebPConfigInit(&config)) {
|
!WebPConfigInit(&config)) {
|
||||||
fprintf(stderr, "Error! Version mismatch!\n");
|
fprintf(stderr, "Error! Version mismatch!\n");
|
||||||
goto Error;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
@ -950,6 +973,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
config.image_hint = WEBP_HINT_PHOTO;
|
config.image_hint = WEBP_HINT_PHOTO;
|
||||||
} else if (!strcmp(argv[c], "picture")) {
|
} else if (!strcmp(argv[c], "picture")) {
|
||||||
config.image_hint = WEBP_HINT_PICTURE;
|
config.image_hint = WEBP_HINT_PICTURE;
|
||||||
|
} else if (!strcmp(argv[c], "graph")) {
|
||||||
|
config.image_hint = WEBP_HINT_GRAPH;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
|
fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
|
||||||
goto Error;
|
goto Error;
|
||||||
@ -1100,8 +1125,10 @@ int main(int argc, const char *argv[]) {
|
|||||||
fprintf(stderr, "be performed, but its results discarded.\n\n");
|
fprintf(stderr, "be performed, but its results discarded.\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
picture.stats = &stats;
|
if (!quiet) {
|
||||||
stats.user_data = (void*)in_file;
|
picture.stats = &stats;
|
||||||
|
picture.user_data = (void*)in_file;
|
||||||
|
}
|
||||||
|
|
||||||
// Compress
|
// Compress
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
@ -59,6 +59,7 @@ extern void* VP8GetCPUInfo; // opaque forward declaration.
|
|||||||
// Output types
|
// Output types
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PNG = 0,
|
PNG = 0,
|
||||||
|
PAM,
|
||||||
PPM,
|
PPM,
|
||||||
PGM,
|
PGM,
|
||||||
ALPHA_PLANE_ONLY // this is for experimenting only
|
ALPHA_PLANE_ONLY // this is for experimenting only
|
||||||
@ -201,15 +202,22 @@ static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer) {
|
static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) {
|
||||||
const uint32_t width = buffer->width;
|
const uint32_t width = buffer->width;
|
||||||
const uint32_t height = buffer->height;
|
const uint32_t height = buffer->height;
|
||||||
const unsigned char* const rgb = buffer->u.RGBA.rgba;
|
const unsigned char* const rgb = buffer->u.RGBA.rgba;
|
||||||
const int stride = buffer->u.RGBA.stride;
|
const int stride = buffer->u.RGBA.stride;
|
||||||
|
const size_t bytes_per_px = alpha ? 4 : 3;
|
||||||
uint32_t y;
|
uint32_t y;
|
||||||
fprintf(fout, "P6\n%d %d\n255\n", width, height);
|
|
||||||
|
if (alpha) {
|
||||||
|
fprintf(fout, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n"
|
||||||
|
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
|
||||||
|
} else {
|
||||||
|
fprintf(fout, "P6\n%d %d\n255\n", width, height);
|
||||||
|
}
|
||||||
for (y = 0; y < height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
if (fwrite(rgb + y * stride, width, 3, fout) != 3) {
|
if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,8 +297,10 @@ static void SaveOutput(const WebPDecBuffer* const buffer,
|
|||||||
#else
|
#else
|
||||||
ok &= WritePNG(fout, buffer);
|
ok &= WritePNG(fout, buffer);
|
||||||
#endif
|
#endif
|
||||||
|
} else if (format == PAM) {
|
||||||
|
ok &= WritePPM(fout, buffer, 1);
|
||||||
} else if (format == PPM) {
|
} else if (format == PPM) {
|
||||||
ok &= WritePPM(fout, buffer);
|
ok &= WritePPM(fout, buffer, 0);
|
||||||
} else if (format == PGM) {
|
} else if (format == PGM) {
|
||||||
ok &= WritePGM(fout, buffer);
|
ok &= WritePGM(fout, buffer);
|
||||||
} else if (format == ALPHA_PLANE_ONLY) {
|
} else if (format == ALPHA_PLANE_ONLY) {
|
||||||
@ -314,7 +324,8 @@ static void Help(void) {
|
|||||||
printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
|
printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
|
||||||
"Decodes the WebP image file to PNG format [Default]\n"
|
"Decodes the WebP image file to PNG format [Default]\n"
|
||||||
"Use following options to convert into alternate image formats:\n"
|
"Use following options to convert into alternate image formats:\n"
|
||||||
" -ppm ......... save the raw RGB samples as color PPM\n"
|
" -pam ......... save the raw RGBA samples as a color PAM\n"
|
||||||
|
" -ppm ......... save the raw RGB samples as a color PPM\n"
|
||||||
" -pgm ......... save the raw YUV samples as a grayscale PGM\n"
|
" -pgm ......... save the raw YUV samples as a grayscale PGM\n"
|
||||||
" file with IMC4 layout.\n"
|
" file with IMC4 layout.\n"
|
||||||
" Other options are:\n"
|
" Other options are:\n"
|
||||||
@ -324,9 +335,7 @@ static void Help(void) {
|
|||||||
" -mt .......... use multi-threading\n"
|
" -mt .......... use multi-threading\n"
|
||||||
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
|
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
|
||||||
" -scale <w> <h> .......... scale the output (*after* any cropping)\n"
|
" -scale <w> <h> .......... scale the output (*after* any cropping)\n"
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
" -alpha ....... only save the alpha plane.\n"
|
" -alpha ....... only save the alpha plane.\n"
|
||||||
#endif
|
|
||||||
" -h ....... this help message.\n"
|
" -h ....... this help message.\n"
|
||||||
" -v ....... verbose (e.g. print encoding/decoding times)\n"
|
" -v ....... verbose (e.g. print encoding/decoding times)\n"
|
||||||
#ifndef WEBP_DLL
|
#ifndef WEBP_DLL
|
||||||
@ -367,6 +376,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
config.options.no_fancy_upsampling = 1;
|
config.options.no_fancy_upsampling = 1;
|
||||||
} else if (!strcmp(argv[c], "-nofilter")) {
|
} else if (!strcmp(argv[c], "-nofilter")) {
|
||||||
config.options.bypass_filtering = 1;
|
config.options.bypass_filtering = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-pam")) {
|
||||||
|
format = PAM;
|
||||||
} else if (!strcmp(argv[c], "-ppm")) {
|
} else if (!strcmp(argv[c], "-ppm")) {
|
||||||
format = PPM;
|
format = PPM;
|
||||||
} else if (!strcmp(argv[c], "-version")) {
|
} else if (!strcmp(argv[c], "-version")) {
|
||||||
@ -434,6 +445,9 @@ int main(int argc, const char *argv[]) {
|
|||||||
output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
|
output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
case PAM:
|
||||||
|
output_buffer->colorspace = MODE_RGBA;
|
||||||
|
break;
|
||||||
case PPM:
|
case PPM:
|
||||||
output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
|
output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
|
||||||
break;
|
break;
|
||||||
|
@ -130,6 +130,7 @@ UTILS_OBJS = \
|
|||||||
src/utils/quant_levels.o \
|
src/utils/quant_levels.o \
|
||||||
src/utils/rescaler.o \
|
src/utils/rescaler.o \
|
||||||
src/utils/thread.o \
|
src/utils/thread.o \
|
||||||
|
src/utils/utils.o \
|
||||||
|
|
||||||
LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS)
|
LIBWEBP_OBJS = $(DEC_OBJS) $(DSP_OBJS) $(ENC_OBJS) $(UTILS_OBJS)
|
||||||
LIBWEBPMUX_OBJS = $(MUX_OBJS)
|
LIBWEBPMUX_OBJS = $(MUX_OBJS)
|
||||||
|
@ -164,7 +164,7 @@ Encode the image without any loss.
|
|||||||
.TP
|
.TP
|
||||||
.B \-hint string
|
.B \-hint string
|
||||||
Specify the hint about input image type. Possible values are:
|
Specify the hint about input image type. Possible values are:
|
||||||
\fBphoto\fP, and \fBpicture\fP.
|
\fBphoto\fP, \fBpicture\fP or \fBgraph\fP.
|
||||||
.TP
|
.TP
|
||||||
.B \-noasm
|
.B \-noasm
|
||||||
Disable all assembly optimizations.
|
Disable all assembly optimizations.
|
||||||
@ -212,4 +212,5 @@ for the Debian project (and may be used by others).
|
|||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.BR dwebp (1).
|
.BR dwebp (1).
|
||||||
.br
|
.br
|
||||||
Please refer to http://code.google.com/speed/webp/ for additional information.
|
Please refer to http://developers.google.com/speed/webp/ for additional
|
||||||
|
information.
|
||||||
|
20
man/dwebp.1
20
man/dwebp.1
@ -1,5 +1,5 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH DWEBP 1 "January 24, 2012"
|
.TH DWEBP 1 "August 2, 2012"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
dwebp \- decompress a WebP file to an image file
|
dwebp \- decompress a WebP file to an image file
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -11,7 +11,7 @@ This manual page documents the
|
|||||||
.B dwebp
|
.B dwebp
|
||||||
command.
|
command.
|
||||||
.PP
|
.PP
|
||||||
\fBdwebp\fP decompresses WebP files into PNG, PPM or PGM images.
|
\fBdwebp\fP decompresses WebP files into PNG, PAM, PPM or PGM images.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
The basic options are:
|
The basic options are:
|
||||||
.TP
|
.TP
|
||||||
@ -24,8 +24,11 @@ Print the version number (as major.minor.revision) and exit.
|
|||||||
.B \-o string
|
.B \-o string
|
||||||
Specify the name of the output file (as PNG format by default).
|
Specify the name of the output file (as PNG format by default).
|
||||||
.TP
|
.TP
|
||||||
|
.B \-pam
|
||||||
|
Change the output format to PAM (retains alpha).
|
||||||
|
.TP
|
||||||
.B \-ppm
|
.B \-ppm
|
||||||
Change the output format to PPM.
|
Change the output format to PPM (discards alpha).
|
||||||
.TP
|
.TP
|
||||||
.B \-pgm
|
.B \-pgm
|
||||||
Change the output format to PGM. The output consist of luma/chroma
|
Change the output format to PGM. The output consist of luma/chroma
|
||||||
@ -87,4 +90,13 @@ for the Debian project (and may be used by others).
|
|||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.BR cwebp (1).
|
.BR cwebp (1).
|
||||||
.br
|
.br
|
||||||
Please refer to http://code.google.com/speed/webp/ for additional information.
|
Please refer to http://developers.google.com/speed/webp/ for additional
|
||||||
|
information.
|
||||||
|
.SS Output file format details
|
||||||
|
PAM: http://netpbm.sourceforge.net/doc/pam.html
|
||||||
|
.br
|
||||||
|
PGM: http://netpbm.sourceforge.net/doc/pgm.html
|
||||||
|
.br
|
||||||
|
PPM: http://netpbm.sourceforge.net/doc/ppm.html
|
||||||
|
.br
|
||||||
|
PNG: http://www.libpng.org/pub/png/png-sitemap.html#info
|
||||||
|
@ -131,4 +131,5 @@ for the Debian project (and may be used by others).
|
|||||||
.BR dwebp (1),
|
.BR dwebp (1),
|
||||||
.BR cwebp (1).
|
.BR cwebp (1).
|
||||||
.br
|
.br
|
||||||
Please refer to http://code.google.com/speed/webp/ for additional information.
|
Please refer to http://developers.google.com/speed/webp/ for additional
|
||||||
|
information.
|
||||||
|
@ -20,7 +20,7 @@ libwebp_la_LIBADD += dsp/libwebpdsp.la
|
|||||||
libwebp_la_LIBADD += enc/libwebpencode.la
|
libwebp_la_LIBADD += enc/libwebpencode.la
|
||||||
libwebp_la_LIBADD += utils/libwebputils.la
|
libwebp_la_LIBADD += utils/libwebputils.la
|
||||||
|
|
||||||
libwebp_la_LDFLAGS = -version-info 3:0:0
|
libwebp_la_LDFLAGS = -version-info 4:0:0
|
||||||
libwebpincludedir = $(includedir)/webp
|
libwebpincludedir = $(includedir)/webp
|
||||||
|
|
||||||
pkgconfig_DATA = libwebp.pc
|
pkgconfig_DATA = libwebp.pc
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "./vp8i.h"
|
#include "./vp8i.h"
|
||||||
#include "./webpi.h"
|
#include "./webpi.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -50,18 +51,23 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
|||||||
ok &= (y_size <= buf->y_size);
|
ok &= (y_size <= buf->y_size);
|
||||||
ok &= (u_size <= buf->u_size);
|
ok &= (u_size <= buf->u_size);
|
||||||
ok &= (v_size <= buf->v_size);
|
ok &= (v_size <= buf->v_size);
|
||||||
ok &= (a_size <= buf->a_size);
|
|
||||||
ok &= (buf->y_stride >= width);
|
ok &= (buf->y_stride >= width);
|
||||||
ok &= (buf->u_stride >= (width + 1) / 2);
|
ok &= (buf->u_stride >= (width + 1) / 2);
|
||||||
ok &= (buf->v_stride >= (width + 1) / 2);
|
ok &= (buf->v_stride >= (width + 1) / 2);
|
||||||
if (buf->a) {
|
ok &= (buf->y != NULL);
|
||||||
|
ok &= (buf->u != NULL);
|
||||||
|
ok &= (buf->v != NULL);
|
||||||
|
if (mode == MODE_YUVA) {
|
||||||
ok &= (buf->a_stride >= width);
|
ok &= (buf->a_stride >= width);
|
||||||
|
ok &= (a_size <= buf->a_size);
|
||||||
|
ok &= (buf->a != NULL);
|
||||||
}
|
}
|
||||||
} else { // RGB checks
|
} else { // RGB checks
|
||||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||||
const uint64_t size = (uint64_t)buf->stride * height;
|
const uint64_t size = (uint64_t)buf->stride * height;
|
||||||
ok &= (size <= buf->size);
|
ok &= (size <= buf->size);
|
||||||
ok &= (buf->stride >= width * kModeBpp[mode]);
|
ok &= (buf->stride >= width * kModeBpp[mode]);
|
||||||
|
ok &= (buf->rgba != NULL);
|
||||||
}
|
}
|
||||||
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
|
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
@ -95,14 +101,11 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
|||||||
total_size = size + 2 * uv_size + a_size;
|
total_size = size + 2 * uv_size + a_size;
|
||||||
|
|
||||||
// Security/sanity checks
|
// Security/sanity checks
|
||||||
if (((size_t)total_size != total_size) || (total_size >= (1ULL << 40))) {
|
output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output));
|
||||||
return VP8_STATUS_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer->private_memory = output = (uint8_t*)malloc((size_t)total_size);
|
|
||||||
if (output == NULL) {
|
if (output == NULL) {
|
||||||
return VP8_STATUS_OUT_OF_MEMORY;
|
return VP8_STATUS_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
buffer->private_memory = output;
|
||||||
|
|
||||||
if (!WebPIsRGBMode(mode)) { // YUVA initialization
|
if (!WebPIsRGBMode(mode)) { // YUVA initialization
|
||||||
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "./vp8i.h"
|
#include "./vp8i.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -435,11 +436,12 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
|||||||
if (needed > dec->mem_size_) {
|
if (needed > dec->mem_size_) {
|
||||||
free(dec->mem_);
|
free(dec->mem_);
|
||||||
dec->mem_size_ = 0;
|
dec->mem_size_ = 0;
|
||||||
dec->mem_ = (uint8_t*)malloc((size_t)needed);
|
dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
|
||||||
if (dec->mem_ == NULL) {
|
if (dec->mem_ == NULL) {
|
||||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||||
"no memory during frame initialization.");
|
"no memory during frame initialization.");
|
||||||
}
|
}
|
||||||
|
// down-cast is ok, thanks to WebPSafeAlloc() above.
|
||||||
dec->mem_size_ = (size_t)needed;
|
dec->mem_size_ = (size_t)needed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "./webpi.h"
|
#include "./webpi.h"
|
||||||
#include "./vp8i.h"
|
#include "./vp8i.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -143,14 +144,15 @@ static int AppendToMemBuffer(WebPIDecoder* const idec,
|
|||||||
|
|
||||||
if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
|
if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
|
||||||
const size_t current_size = MemDataSize(mem);
|
const size_t current_size = MemDataSize(mem);
|
||||||
const size_t new_size = current_size + data_size;
|
const uint64_t new_size = (uint64_t)current_size + data_size;
|
||||||
const size_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
|
const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
|
||||||
uint8_t* const new_buf = (uint8_t*)malloc(extra_size);
|
uint8_t* const new_buf =
|
||||||
|
(uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
|
||||||
if (new_buf == NULL) return 0;
|
if (new_buf == NULL) return 0;
|
||||||
memcpy(new_buf, old_base, current_size);
|
memcpy(new_buf, old_base, current_size);
|
||||||
free(mem->buf_);
|
free(mem->buf_);
|
||||||
mem->buf_ = new_buf;
|
mem->buf_ = new_buf;
|
||||||
mem->buf_size_ = extra_size;
|
mem->buf_size_ = (size_t)extra_size;
|
||||||
mem->start_ = 0;
|
mem->start_ = 0;
|
||||||
mem->end_ = current_size;
|
mem->end_ = current_size;
|
||||||
}
|
}
|
||||||
@ -534,7 +536,7 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
|||||||
// Public functions
|
// Public functions
|
||||||
|
|
||||||
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
||||||
WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(WebPIDecoder));
|
WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(*idec));
|
||||||
if (idec == NULL) {
|
if (idec == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -565,7 +567,7 @@ WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
|
|||||||
}
|
}
|
||||||
// Create an instance of the incremental decoder
|
// Create an instance of the incremental decoder
|
||||||
idec = WebPINewDecoder(config ? &config->output : NULL);
|
idec = WebPINewDecoder(config ? &config->output : NULL);
|
||||||
if (!idec) {
|
if (idec == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
// Finish initialization
|
// Finish initialization
|
||||||
@ -597,7 +599,7 @@ WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
|
|||||||
WebPIDecoder* idec;
|
WebPIDecoder* idec;
|
||||||
if (mode >= MODE_YUV) return NULL;
|
if (mode >= MODE_YUV) return NULL;
|
||||||
idec = WebPINewDecoder(NULL);
|
idec = WebPINewDecoder(NULL);
|
||||||
if (!idec) return NULL;
|
if (idec == NULL) return NULL;
|
||||||
idec->output_.colorspace = mode;
|
idec->output_.colorspace = mode;
|
||||||
idec->output_.is_external_memory = 1;
|
idec->output_.is_external_memory = 1;
|
||||||
idec->output_.u.RGBA.rgba = output_buffer;
|
idec->output_.u.RGBA.rgba = output_buffer;
|
||||||
@ -606,12 +608,13 @@ WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
|
|||||||
return idec;
|
return idec;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
|
WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
|
||||||
uint8_t* u, size_t u_size, int u_stride,
|
uint8_t* u, size_t u_size, int u_stride,
|
||||||
uint8_t* v, size_t v_size, int v_stride) {
|
uint8_t* v, size_t v_size, int v_stride,
|
||||||
|
uint8_t* a, size_t a_size, int a_stride) {
|
||||||
WebPIDecoder* const idec = WebPINewDecoder(NULL);
|
WebPIDecoder* const idec = WebPINewDecoder(NULL);
|
||||||
if (!idec) return NULL;
|
if (idec == NULL) return NULL;
|
||||||
idec->output_.colorspace = MODE_YUV;
|
idec->output_.colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;
|
||||||
idec->output_.is_external_memory = 1;
|
idec->output_.is_external_memory = 1;
|
||||||
idec->output_.u.YUVA.y = luma;
|
idec->output_.u.YUVA.y = luma;
|
||||||
idec->output_.u.YUVA.y_stride = luma_stride;
|
idec->output_.u.YUVA.y_stride = luma_stride;
|
||||||
@ -622,9 +625,21 @@ WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
|
|||||||
idec->output_.u.YUVA.v = v;
|
idec->output_.u.YUVA.v = v;
|
||||||
idec->output_.u.YUVA.v_stride = v_stride;
|
idec->output_.u.YUVA.v_stride = v_stride;
|
||||||
idec->output_.u.YUVA.v_size = v_size;
|
idec->output_.u.YUVA.v_size = v_size;
|
||||||
|
idec->output_.u.YUVA.a = a;
|
||||||
|
idec->output_.u.YUVA.a_stride = a_stride;
|
||||||
|
idec->output_.u.YUVA.a_size = a_size;
|
||||||
return idec;
|
return idec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
|
||||||
|
uint8_t* u, size_t u_size, int u_stride,
|
||||||
|
uint8_t* v, size_t v_size, int v_stride) {
|
||||||
|
return WebPINewYUVA(luma, luma_size, luma_stride,
|
||||||
|
u, u_size, u_stride,
|
||||||
|
v, v_size, v_stride,
|
||||||
|
NULL, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
|
static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
|
||||||
@ -696,15 +711,15 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
|
|||||||
int* left, int* top,
|
int* left, int* top,
|
||||||
int* width, int* height) {
|
int* width, int* height) {
|
||||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||||
if (left) *left = 0;
|
if (left != NULL) *left = 0;
|
||||||
if (top) *top = 0;
|
if (top != NULL) *top = 0;
|
||||||
// TODO(skal): later include handling of rotations.
|
// TODO(skal): later include handling of rotations.
|
||||||
if (src) {
|
if (src) {
|
||||||
if (width) *width = src->width;
|
if (width != NULL) *width = src->width;
|
||||||
if (height) *height = idec->params_.last_y;
|
if (height != NULL) *height = idec->params_.last_y;
|
||||||
} else {
|
} else {
|
||||||
if (width) *width = 0;
|
if (width != NULL) *width = 0;
|
||||||
if (height) *height = 0;
|
if (height != NULL) *height = 0;
|
||||||
}
|
}
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
@ -712,35 +727,38 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
|
|||||||
uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
|
uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
|
||||||
int* width, int* height, int* stride) {
|
int* width, int* height, int* stride) {
|
||||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||||
if (!src) return NULL;
|
if (src == NULL) return NULL;
|
||||||
if (src->colorspace >= MODE_YUV) {
|
if (src->colorspace >= MODE_YUV) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_y) *last_y = idec->params_.last_y;
|
if (last_y != NULL) *last_y = idec->params_.last_y;
|
||||||
if (width) *width = src->width;
|
if (width != NULL) *width = src->width;
|
||||||
if (height) *height = src->height;
|
if (height != NULL) *height = src->height;
|
||||||
if (stride) *stride = src->u.RGBA.stride;
|
if (stride != NULL) *stride = src->u.RGBA.stride;
|
||||||
|
|
||||||
return src->u.RGBA.rgba;
|
return src->u.RGBA.rgba;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* WebPIDecGetYUV(const WebPIDecoder* idec, int* last_y,
|
uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
|
||||||
uint8_t** u, uint8_t** v,
|
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||||
int* width, int* height, int *stride, int* uv_stride) {
|
int* width, int* height,
|
||||||
|
int* stride, int* uv_stride, int* a_stride) {
|
||||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||||
if (!src) return NULL;
|
if (src == NULL) return NULL;
|
||||||
if (src->colorspace < MODE_YUV) {
|
if (src->colorspace < MODE_YUV) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_y) *last_y = idec->params_.last_y;
|
if (last_y != NULL) *last_y = idec->params_.last_y;
|
||||||
if (u) *u = src->u.YUVA.u;
|
if (u != NULL) *u = src->u.YUVA.u;
|
||||||
if (v) *v = src->u.YUVA.v;
|
if (v != NULL) *v = src->u.YUVA.v;
|
||||||
if (width) *width = src->width;
|
if (a != NULL) *a = src->u.YUVA.a;
|
||||||
if (height) *height = src->height;
|
if (width != NULL) *width = src->width;
|
||||||
if (stride) *stride = src->u.YUVA.y_stride;
|
if (height != NULL) *height = src->height;
|
||||||
if (uv_stride) *uv_stride = src->u.YUVA.u_stride;
|
if (stride != NULL) *stride = src->u.YUVA.y_stride;
|
||||||
|
if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride;
|
||||||
|
if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride;
|
||||||
|
|
||||||
return src->u.YUVA.y;
|
return src->u.YUVA.y;
|
||||||
}
|
}
|
||||||
|
139
src/dec/io.c
139
src/dec/io.c
@ -111,7 +111,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
const uint8_t* top_u = p->tmp_u;
|
const uint8_t* top_u = p->tmp_u;
|
||||||
const uint8_t* top_v = p->tmp_v;
|
const uint8_t* top_v = p->tmp_v;
|
||||||
int y = io->mb_y;
|
int y = io->mb_y;
|
||||||
int y_end = io->mb_y + io->mb_h;
|
const int y_end = io->mb_y + io->mb_h;
|
||||||
const int mb_w = io->mb_w;
|
const int mb_w = io->mb_w;
|
||||||
const int uv_w = (mb_w + 1) / 2;
|
const int uv_w = (mb_w + 1) / 2;
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
// Process the very last row of even-sized picture
|
// Process the very last row of even-sized picture
|
||||||
if (!(y_end & 1)) {
|
if (!(y_end & 1)) {
|
||||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
|
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
|
||||||
dst + buf->stride, NULL, mb_w);
|
dst + buf->stride, NULL, mb_w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return num_lines_out;
|
return num_lines_out;
|
||||||
@ -162,64 +162,82 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
|
|
||||||
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||||
const uint8_t* alpha = io->a;
|
const uint8_t* alpha = io->a;
|
||||||
|
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||||
|
const int mb_w = io->mb_w;
|
||||||
|
const int mb_h = io->mb_h;
|
||||||
|
uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
|
||||||
|
int j;
|
||||||
|
|
||||||
if (alpha != NULL) {
|
if (alpha != NULL) {
|
||||||
int j;
|
|
||||||
const int mb_w = io->mb_w;
|
|
||||||
const int mb_h = io->mb_h;
|
|
||||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
|
||||||
uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
|
|
||||||
for (j = 0; j < mb_h; ++j) {
|
for (j = 0; j < mb_h; ++j) {
|
||||||
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
||||||
alpha += io->width;
|
alpha += io->width;
|
||||||
dst += buf->a_stride;
|
dst += buf->a_stride;
|
||||||
}
|
}
|
||||||
|
} else if (buf->a != NULL) {
|
||||||
|
// the user requested alpha, but there is none, set it to opaque.
|
||||||
|
for (j = 0; j < mb_h; ++j) {
|
||||||
|
memset(dst, 0xff, mb_w * sizeof(*dst));
|
||||||
|
dst += buf->a_stride;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int GetAlphaSourceRow(const VP8Io* const io,
|
||||||
|
const uint8_t** alpha, int* const num_rows) {
|
||||||
|
int start_y = io->mb_y;
|
||||||
|
*num_rows = io->mb_h;
|
||||||
|
|
||||||
|
// Compensate for the 1-line delay of the fancy upscaler.
|
||||||
|
// This is similar to EmitFancyRGB().
|
||||||
|
if (io->fancy_upsampling) {
|
||||||
|
if (start_y == 0) {
|
||||||
|
// We don't process the last row yet. It'll be done during the next call.
|
||||||
|
--*num_rows;
|
||||||
|
} else {
|
||||||
|
--start_y;
|
||||||
|
// Fortunately, *alpha data is persistent, so we can go back
|
||||||
|
// one row and finish alpha blending, now that the fancy upscaler
|
||||||
|
// completed the YUV->RGB interpolation.
|
||||||
|
*alpha -= io->width;
|
||||||
|
}
|
||||||
|
if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
|
||||||
|
// If it's the very last call, we process all the remaining rows!
|
||||||
|
*num_rows = io->crop_bottom - io->crop_top - start_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return start_y;
|
||||||
|
}
|
||||||
|
|
||||||
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||||
const uint8_t* alpha = io->a;
|
const uint8_t* alpha = io->a;
|
||||||
if (alpha != NULL) {
|
if (alpha != NULL) {
|
||||||
const int mb_w = io->mb_w;
|
const int mb_w = io->mb_w;
|
||||||
const int mb_h = io->mb_h;
|
|
||||||
int i, j;
|
|
||||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||||
const int alpha_first =
|
const int alpha_first =
|
||||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||||
int start_y = io->mb_y;
|
int num_rows;
|
||||||
int num_rows = mb_h;
|
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||||
|
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||||
|
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||||
|
uint32_t alpha_mask = 0xff;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
// We compensate for the 1-line delay of fancy upscaler.
|
for (j = 0; j < num_rows; ++j) {
|
||||||
// This is similar to EmitFancyRGB().
|
for (i = 0; i < mb_w; ++i) {
|
||||||
if (io->fancy_upsampling) {
|
const uint32_t alpha_value = alpha[i];
|
||||||
if (start_y == 0) {
|
dst[4 * i] = alpha_value;
|
||||||
// We don't process the last row yet. It'll be done during next call.
|
alpha_mask &= alpha_value;
|
||||||
--num_rows;
|
|
||||||
} else {
|
|
||||||
--start_y;
|
|
||||||
// Fortunately, *alpha data is persistent, so we can go back
|
|
||||||
// one row and finish alpha blending, now that the fancy upscaler
|
|
||||||
// completed the YUV->RGB interpolation.
|
|
||||||
alpha -= io->width;
|
|
||||||
}
|
|
||||||
if (io->crop_top + io->mb_y + mb_h == io->crop_bottom) {
|
|
||||||
// If it's the very last call, we process all the remaing rows!
|
|
||||||
num_rows = io->crop_bottom - io->crop_top - start_y;
|
|
||||||
}
|
}
|
||||||
|
alpha += io->width;
|
||||||
|
dst += buf->stride;
|
||||||
}
|
}
|
||||||
{
|
// alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
|
||||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
|
||||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||||
for (j = 0; j < num_rows; ++j) {
|
mb_w, num_rows, buf->stride);
|
||||||
for (i = 0; i < mb_w; ++i) dst[4 * i] = alpha[i];
|
|
||||||
alpha += io->width;
|
|
||||||
dst += buf->stride;
|
|
||||||
}
|
|
||||||
if (WebPIsPremultipliedMode(colorspace)) {
|
|
||||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
|
||||||
mb_w, num_rows, buf->stride);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -229,22 +247,27 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
const uint8_t* alpha = io->a;
|
const uint8_t* alpha = io->a;
|
||||||
if (alpha != NULL) {
|
if (alpha != NULL) {
|
||||||
const int mb_w = io->mb_w;
|
const int mb_w = io->mb_w;
|
||||||
const int mb_h = io->mb_h;
|
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||||
int i, j;
|
|
||||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||||
uint8_t* const base_rgba = buf->rgba + io->mb_y * buf->stride;
|
int num_rows;
|
||||||
|
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||||
|
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||||
uint8_t* alpha_dst = base_rgba + 1;
|
uint8_t* alpha_dst = base_rgba + 1;
|
||||||
for (j = 0; j < mb_h; ++j) {
|
uint32_t alpha_mask = 0x0f;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (j = 0; j < num_rows; ++j) {
|
||||||
for (i = 0; i < mb_w; ++i) {
|
for (i = 0; i < mb_w; ++i) {
|
||||||
// Fill in the alpha value (converted to 4 bits).
|
// Fill in the alpha value (converted to 4 bits).
|
||||||
const uint32_t alpha_val = VP8Clip4Bits(alpha[i]);
|
const uint32_t alpha_value = alpha[i] >> 4;
|
||||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_val;
|
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||||
|
alpha_mask &= alpha_value;
|
||||||
}
|
}
|
||||||
alpha += io->width;
|
alpha += io->width;
|
||||||
alpha_dst += buf->stride;
|
alpha_dst += buf->stride;
|
||||||
}
|
}
|
||||||
if (p->output->colorspace == MODE_rgbA_4444) {
|
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
|
||||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, mb_h, buf->stride);
|
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -389,17 +412,22 @@ static int ExportAlpha(WebPDecParams* const p, int y_pos) {
|
|||||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||||
int num_lines_out = 0;
|
int num_lines_out = 0;
|
||||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||||
|
uint32_t alpha_mask = 0xff;
|
||||||
const int width = p->scaler_a.dst_width;
|
const int width = p->scaler_a.dst_width;
|
||||||
|
|
||||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||||
int i;
|
int i;
|
||||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||||
WebPRescalerExportRow(&p->scaler_a);
|
WebPRescalerExportRow(&p->scaler_a);
|
||||||
for (i = 0; i < width; ++i) dst[4 * i] = p->scaler_a.dst[i];
|
for (i = 0; i < width; ++i) {
|
||||||
|
const uint32_t alpha_value = p->scaler_a.dst[i];
|
||||||
|
dst[4 * i] = alpha_value;
|
||||||
|
alpha_mask &= alpha_value;
|
||||||
|
}
|
||||||
dst += buf->stride;
|
dst += buf->stride;
|
||||||
++num_lines_out;
|
++num_lines_out;
|
||||||
}
|
}
|
||||||
if (is_premult_alpha) {
|
if (is_premult_alpha && alpha_mask != 0xff) {
|
||||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||||
width, num_lines_out, buf->stride);
|
width, num_lines_out, buf->stride);
|
||||||
}
|
}
|
||||||
@ -414,6 +442,7 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
|||||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||||
const int width = p->scaler_a.dst_width;
|
const int width = p->scaler_a.dst_width;
|
||||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||||
|
uint32_t alpha_mask = 0x0f;
|
||||||
|
|
||||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||||
int i;
|
int i;
|
||||||
@ -421,13 +450,14 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
|||||||
WebPRescalerExportRow(&p->scaler_a);
|
WebPRescalerExportRow(&p->scaler_a);
|
||||||
for (i = 0; i < width; ++i) {
|
for (i = 0; i < width; ++i) {
|
||||||
// Fill in the alpha value (converted to 4 bits).
|
// Fill in the alpha value (converted to 4 bits).
|
||||||
const uint32_t alpha_val = VP8Clip4Bits(p->scaler_a.dst[i]);
|
const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
|
||||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_val;
|
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||||
|
alpha_mask &= alpha_value;
|
||||||
}
|
}
|
||||||
alpha_dst += buf->stride;
|
alpha_dst += buf->stride;
|
||||||
++num_lines_out;
|
++num_lines_out;
|
||||||
}
|
}
|
||||||
if (is_premult_alpha) {
|
if (is_premult_alpha && alpha_mask != 0x0f) {
|
||||||
WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
|
WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
|
||||||
}
|
}
|
||||||
return num_lines_out;
|
return num_lines_out;
|
||||||
@ -464,8 +494,7 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
tmp_size1 += work_size;
|
tmp_size1 += work_size;
|
||||||
tmp_size2 += out_width;
|
tmp_size2 += out_width;
|
||||||
}
|
}
|
||||||
p->memory =
|
p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
|
||||||
calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
|
|
||||||
if (p->memory == NULL) {
|
if (p->memory == NULL) {
|
||||||
return 0; // memory error
|
return 0; // memory error
|
||||||
}
|
}
|
||||||
@ -562,7 +591,7 @@ static int CustomSetup(VP8Io* io) {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
static int CustomPut(const VP8Io* io) {
|
static int CustomPut(const VP8Io* io) {
|
||||||
WebPDecParams* p = (WebPDecParams*)io->opaque;
|
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||||
const int mb_w = io->mb_w;
|
const int mb_w = io->mb_w;
|
||||||
const int mb_h = io->mb_h;
|
const int mb_h = io->mb_h;
|
||||||
int num_lines_out;
|
int num_lines_out;
|
||||||
|
@ -45,7 +45,7 @@ int VP8InitIoInternal(VP8Io* const io, int version) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VP8Decoder* VP8New(void) {
|
VP8Decoder* VP8New(void) {
|
||||||
VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(VP8Decoder));
|
VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(*dec));
|
||||||
if (dec != NULL) {
|
if (dec != NULL) {
|
||||||
SetOk(dec);
|
SetOk(dec);
|
||||||
WebPWorkerInit(&dec->worker_);
|
WebPWorkerInit(&dec->worker_);
|
||||||
|
@ -27,8 +27,8 @@ extern "C" {
|
|||||||
|
|
||||||
// version numbers
|
// version numbers
|
||||||
#define DEC_MAJ_VERSION 0
|
#define DEC_MAJ_VERSION 0
|
||||||
#define DEC_MIN_VERSION 1
|
#define DEC_MIN_VERSION 2
|
||||||
#define DEC_REV_VERSION 99
|
#define DEC_REV_VERSION 0
|
||||||
|
|
||||||
#define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames
|
#define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames
|
||||||
|
|
||||||
|
228
src/dec/vp8l.c
228
src/dec/vp8l.c
@ -14,7 +14,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "./vp8li.h"
|
#include "./vp8li.h"
|
||||||
#include "../dsp/lossless.h"
|
#include "../dsp/lossless.h"
|
||||||
|
#include "../dsp/yuv.h"
|
||||||
#include "../utils/huffman.h"
|
#include "../utils/huffman.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -264,7 +266,8 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
code_lengths = (int*)calloc(alphabet_size, sizeof(*code_lengths));
|
code_lengths =
|
||||||
|
(int*)WebPSafeCalloc((uint64_t)alphabet_size, sizeof(*code_lengths));
|
||||||
if (code_lengths == NULL) {
|
if (code_lengths == NULL) {
|
||||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||||
return 0;
|
return 0;
|
||||||
@ -335,7 +338,9 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
|||||||
if (br->error_) goto Error;
|
if (br->error_) goto Error;
|
||||||
|
|
||||||
assert(num_htree_groups <= 0x10000);
|
assert(num_htree_groups <= 0x10000);
|
||||||
htree_groups = (HTreeGroup*)calloc(num_htree_groups, sizeof(*htree_groups));
|
htree_groups =
|
||||||
|
(HTreeGroup*)WebPSafeCalloc((uint64_t)num_htree_groups,
|
||||||
|
sizeof(*htree_groups));
|
||||||
if (htree_groups == NULL) {
|
if (htree_groups == NULL) {
|
||||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
@ -380,10 +385,7 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
|
|||||||
const uint64_t memory_size = sizeof(*dec->rescaler) +
|
const uint64_t memory_size = sizeof(*dec->rescaler) +
|
||||||
work_size * sizeof(*work) +
|
work_size * sizeof(*work) +
|
||||||
scaled_data_size * sizeof(*scaled_data);
|
scaled_data_size * sizeof(*scaled_data);
|
||||||
uint8_t* memory;
|
uint8_t* memory = (uint8_t*)WebPSafeCalloc(memory_size, sizeof(*memory));
|
||||||
|
|
||||||
if (memory_size != (size_t)memory_size) return 0; // overflow check
|
|
||||||
memory = (uint8_t*)calloc(1, (size_t)memory_size);
|
|
||||||
if (memory == NULL) {
|
if (memory == NULL) {
|
||||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||||
return 0;
|
return 0;
|
||||||
@ -403,10 +405,12 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Export to ARGB
|
||||||
|
|
||||||
// We have special "export" function since we need to convert from BGRA
|
// We have special "export" function since we need to convert from BGRA
|
||||||
static int Export(VP8LDecoder* const dec, WEBP_CSP_MODE colorspace,
|
static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace,
|
||||||
int rgba_stride, uint8_t* const rgba) {
|
int rgba_stride, uint8_t* const rgba) {
|
||||||
WebPRescaler* const rescaler = dec->rescaler;
|
|
||||||
const uint32_t* const src = (const uint32_t*)rescaler->dst;
|
const uint32_t* const src = (const uint32_t*)rescaler->dst;
|
||||||
const int dst_width = rescaler->dst_width;
|
const int dst_width = rescaler->dst_width;
|
||||||
int num_lines_out = 0;
|
int num_lines_out = 0;
|
||||||
@ -420,18 +424,19 @@ static int Export(VP8LDecoder* const dec, WEBP_CSP_MODE colorspace,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Emit scaled rows.
|
// Emit scaled rows.
|
||||||
static int EmitRescaledRows(VP8LDecoder* const dec, WEBP_CSP_MODE colorspace,
|
static int EmitRescaledRows(const VP8LDecoder* const dec,
|
||||||
const uint32_t* const data, int in_stride, int mb_h,
|
const uint32_t* const data, int in_stride, int mb_h,
|
||||||
uint8_t* const out, int out_stride) {
|
uint8_t* const out, int out_stride) {
|
||||||
|
const WEBP_CSP_MODE colorspace = dec->output_->colorspace;
|
||||||
const uint8_t* const in = (const uint8_t*)data;
|
const uint8_t* const in = (const uint8_t*)data;
|
||||||
int num_lines_in = 0;
|
int num_lines_in = 0;
|
||||||
int num_lines_out = 0;
|
int num_lines_out = 0;
|
||||||
while (num_lines_in < mb_h) {
|
while (num_lines_in < mb_h) {
|
||||||
const uint8_t* row_in = in + num_lines_in * in_stride;
|
const uint8_t* const row_in = in + num_lines_in * in_stride;
|
||||||
uint8_t* const row_out = out + num_lines_out * out_stride;
|
uint8_t* const row_out = out + num_lines_out * out_stride;
|
||||||
num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in,
|
num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in,
|
||||||
row_in, in_stride);
|
row_in, in_stride);
|
||||||
num_lines_out += Export(dec, colorspace, out_stride, row_out);
|
num_lines_out += Export(dec->rescaler, colorspace, out_stride, row_out);
|
||||||
}
|
}
|
||||||
return num_lines_out;
|
return num_lines_out;
|
||||||
}
|
}
|
||||||
@ -452,6 +457,113 @@ static int EmitRows(WEBP_CSP_MODE colorspace,
|
|||||||
return mb_h; // Num rows out == num rows in.
|
return mb_h; // Num rows out == num rows in.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Export to YUVA
|
||||||
|
|
||||||
|
static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos,
|
||||||
|
const WebPDecBuffer* const output) {
|
||||||
|
const WebPYUVABuffer* const buf = &output->u.YUVA;
|
||||||
|
// first, the luma plane
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint8_t* const y = buf->y + y_pos * buf->y_stride;
|
||||||
|
for (i = 0; i < width; ++i) {
|
||||||
|
const uint32_t p = src[i];
|
||||||
|
y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// then U/V planes
|
||||||
|
{
|
||||||
|
uint8_t* const u = buf->u + (y_pos >> 1) * buf->u_stride;
|
||||||
|
uint8_t* const v = buf->v + (y_pos >> 1) * buf->v_stride;
|
||||||
|
const int uv_width = width >> 1;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < uv_width; ++i) {
|
||||||
|
const uint32_t v0 = src[2 * i + 0];
|
||||||
|
const uint32_t v1 = src[2 * i + 1];
|
||||||
|
// VP8RGBToU/V expects four accumulated pixels. Hence we need to
|
||||||
|
// scale r/g/b value by a factor 2. We just shift v0/v1 one bit less.
|
||||||
|
const int r = ((v0 >> 15) & 0x1fe) + ((v1 >> 15) & 0x1fe);
|
||||||
|
const int g = ((v0 >> 7) & 0x1fe) + ((v1 >> 7) & 0x1fe);
|
||||||
|
const int b = ((v0 << 1) & 0x1fe) + ((v1 << 1) & 0x1fe);
|
||||||
|
if (!(y_pos & 1)) { // even lines: store values
|
||||||
|
u[i] = VP8RGBToU(r, g, b);
|
||||||
|
v[i] = VP8RGBToV(r, g, b);
|
||||||
|
} else { // odd lines: average with previous values
|
||||||
|
const int tmp_u = VP8RGBToU(r, g, b);
|
||||||
|
const int tmp_v = VP8RGBToV(r, g, b);
|
||||||
|
// Approximated average-of-four. But it's an acceptable diff.
|
||||||
|
u[i] = (u[i] + tmp_u + 1) >> 1;
|
||||||
|
v[i] = (v[i] + tmp_v + 1) >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (width & 1) { // last pixel
|
||||||
|
const uint32_t v0 = src[2 * i + 0];
|
||||||
|
const int r = (v0 >> 14) & 0x3fc;
|
||||||
|
const int g = (v0 >> 6) & 0x3fc;
|
||||||
|
const int b = (v0 << 2) & 0x3fc;
|
||||||
|
if (!(y_pos & 1)) { // even lines
|
||||||
|
u[i] = VP8RGBToU(r, g, b);
|
||||||
|
v[i] = VP8RGBToV(r, g, b);
|
||||||
|
} else { // odd lines (note: we could just skip this)
|
||||||
|
const int tmp_u = VP8RGBToU(r, g, b);
|
||||||
|
const int tmp_v = VP8RGBToV(r, g, b);
|
||||||
|
u[i] = (u[i] + tmp_u + 1) >> 1;
|
||||||
|
v[i] = (v[i] + tmp_v + 1) >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Lastly, store alpha if needed.
|
||||||
|
if (buf->a != NULL) {
|
||||||
|
int i;
|
||||||
|
uint8_t* const a = buf->a + y_pos * buf->a_stride;
|
||||||
|
for (i = 0; i < width; ++i) a[i] = (src[i] >> 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) {
|
||||||
|
WebPRescaler* const rescaler = dec->rescaler;
|
||||||
|
const uint32_t* const src = (const uint32_t*)rescaler->dst;
|
||||||
|
const int dst_width = rescaler->dst_width;
|
||||||
|
int num_lines_out = 0;
|
||||||
|
while (WebPRescalerHasPendingOutput(rescaler)) {
|
||||||
|
WebPRescalerExportRow(rescaler);
|
||||||
|
ConvertToYUVA(src, dst_width, y_pos, dec->output_);
|
||||||
|
++y_pos;
|
||||||
|
++num_lines_out;
|
||||||
|
}
|
||||||
|
return num_lines_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec,
|
||||||
|
const uint32_t* const data,
|
||||||
|
int in_stride, int mb_h) {
|
||||||
|
const uint8_t* const in = (const uint8_t*)data;
|
||||||
|
int num_lines_in = 0;
|
||||||
|
int y_pos = dec->last_out_row_;
|
||||||
|
while (num_lines_in < mb_h) {
|
||||||
|
const uint8_t* const row_in = in + num_lines_in * in_stride;
|
||||||
|
num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in,
|
||||||
|
row_in, in_stride);
|
||||||
|
y_pos += ExportYUVA(dec, y_pos);
|
||||||
|
}
|
||||||
|
return y_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int EmitRowsYUVA(const VP8LDecoder* const dec,
|
||||||
|
const uint32_t* const data, int in_stride,
|
||||||
|
int mb_w, int num_rows) {
|
||||||
|
int y_pos = dec->last_out_row_;
|
||||||
|
const uint8_t* row_in = (const uint8_t*)data;
|
||||||
|
while (num_rows-- > 0) {
|
||||||
|
ConvertToYUVA((const uint32_t*)row_in, mb_w, y_pos, dec->output_);
|
||||||
|
row_in += in_stride;
|
||||||
|
++y_pos;
|
||||||
|
}
|
||||||
|
return y_pos;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Cropping.
|
// Cropping.
|
||||||
|
|
||||||
@ -503,20 +615,22 @@ static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr,
|
|||||||
|
|
||||||
typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row);
|
typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row);
|
||||||
|
|
||||||
static void ApplyTransforms(VP8LDecoder* const dec, int num_rows,
|
static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows,
|
||||||
const uint32_t* const rows) {
|
const uint32_t* const rows) {
|
||||||
int n = dec->next_transform_;
|
int n = dec->next_transform_;
|
||||||
const int cache_pixs = dec->width_ * num_rows;
|
const int cache_pixs = dec->width_ * num_rows;
|
||||||
uint32_t* rows_data = dec->argb_cache_;
|
|
||||||
const int start_row = dec->last_row_;
|
const int start_row = dec->last_row_;
|
||||||
const int end_row = start_row + num_rows;
|
const int end_row = start_row + num_rows;
|
||||||
|
const uint32_t* rows_in = rows;
|
||||||
|
uint32_t* const rows_out = dec->argb_cache_;
|
||||||
|
|
||||||
// Inverse transforms.
|
// Inverse transforms.
|
||||||
// TODO: most transforms only need to operate on the cropped region only.
|
// TODO: most transforms only need to operate on the cropped region only.
|
||||||
memcpy(rows_data, rows, cache_pixs * sizeof(*rows_data));
|
memcpy(rows_out, rows_in, cache_pixs * sizeof(*rows_out));
|
||||||
while (n-- > 0) {
|
while (n-- > 0) {
|
||||||
VP8LTransform* const transform = &dec->transforms_[n];
|
VP8LTransform* const transform = &dec->transforms_[n];
|
||||||
VP8LInverseTransform(transform, start_row, end_row, rows, rows_data);
|
VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out);
|
||||||
|
rows_in = rows_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,7 +641,7 @@ static void ProcessRows(VP8LDecoder* const dec, int row) {
|
|||||||
const int num_rows = row - dec->last_row_;
|
const int num_rows = row - dec->last_row_;
|
||||||
|
|
||||||
if (num_rows <= 0) return; // Nothing to be done.
|
if (num_rows <= 0) return; // Nothing to be done.
|
||||||
ApplyTransforms(dec, num_rows, rows);
|
ApplyInverseTransforms(dec, num_rows, rows);
|
||||||
|
|
||||||
// Emit output.
|
// Emit output.
|
||||||
{
|
{
|
||||||
@ -536,19 +650,23 @@ static void ProcessRows(VP8LDecoder* const dec, int row) {
|
|||||||
if (!SetCropWindow(io, dec->last_row_, row, &rows_data, io->width)) {
|
if (!SetCropWindow(io, dec->last_row_, row, &rows_data, io->width)) {
|
||||||
// Nothing to output (this time).
|
// Nothing to output (this time).
|
||||||
} else {
|
} else {
|
||||||
WebPDecParams* const params = (WebPDecParams*)io->opaque;
|
const WebPDecBuffer* const output = dec->output_;
|
||||||
const WebPDecBuffer* const output = params->output;
|
|
||||||
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
|
||||||
uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
|
|
||||||
const int in_stride = io->width * sizeof(*rows_data);
|
const int in_stride = io->width * sizeof(*rows_data);
|
||||||
const WEBP_CSP_MODE colorspace = output->colorspace;
|
if (output->colorspace < MODE_YUV) { // convert to RGBA
|
||||||
const int num_rows_out = io->use_scaling ?
|
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||||
EmitRescaledRows(dec, colorspace, rows_data, in_stride, io->mb_h,
|
uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
|
||||||
rgba, buf->stride) :
|
const int num_rows_out = io->use_scaling ?
|
||||||
EmitRows(colorspace, rows_data, in_stride, io->mb_w, io->mb_h,
|
EmitRescaledRows(dec, rows_data, in_stride, io->mb_h,
|
||||||
rgba, buf->stride);
|
rgba, buf->stride) :
|
||||||
// Update 'last_out_row_'.
|
EmitRows(output->colorspace, rows_data, in_stride,
|
||||||
dec->last_out_row_ += num_rows_out;
|
io->mb_w, io->mb_h, rgba, buf->stride);
|
||||||
|
// Update 'last_out_row_'.
|
||||||
|
dec->last_out_row_ += num_rows_out;
|
||||||
|
} else { // convert to YUVA
|
||||||
|
dec->last_out_row_ = io->use_scaling ?
|
||||||
|
EmitRescaledRowsYUVA(dec, rows_data, in_stride, io->mb_h) :
|
||||||
|
EmitRowsYUVA(dec, rows_data, in_stride, io->mb_w, io->mb_h);
|
||||||
|
}
|
||||||
assert(dec->last_out_row_ <= output->height);
|
assert(dec->last_out_row_ <= output->height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,26 +799,14 @@ static void ClearTransform(VP8LTransform* const transform) {
|
|||||||
transform->data_ = NULL;
|
transform->data_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyInverseTransforms(VP8LDecoder* const dec, int start_idx,
|
|
||||||
uint32_t* const decoded_data) {
|
|
||||||
int n = dec->next_transform_;
|
|
||||||
assert(start_idx >= 0);
|
|
||||||
while (n-- > start_idx) {
|
|
||||||
VP8LTransform* const transform = &dec->transforms_[n];
|
|
||||||
VP8LInverseTransform(transform, 0, transform->ysize_,
|
|
||||||
decoded_data, decoded_data);
|
|
||||||
ClearTransform(transform);
|
|
||||||
}
|
|
||||||
dec->next_transform_ = start_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For security reason, we need to remap the color map to span
|
// For security reason, we need to remap the color map to span
|
||||||
// the total possible bundled values, and not just the num_colors.
|
// the total possible bundled values, and not just the num_colors.
|
||||||
static int ExpandColorMap(int num_colors, VP8LTransform* const transform) {
|
static int ExpandColorMap(int num_colors, VP8LTransform* const transform) {
|
||||||
int i;
|
int i;
|
||||||
const int final_num_colors = 1 << (8 >> transform->bits_);
|
const int final_num_colors = 1 << (8 >> transform->bits_);
|
||||||
uint32_t* const new_color_map =
|
uint32_t* const new_color_map =
|
||||||
(uint32_t*)malloc(final_num_colors * sizeof(*new_color_map));
|
(uint32_t*)WebPSafeMalloc((uint64_t)final_num_colors,
|
||||||
|
sizeof(*new_color_map));
|
||||||
if (new_color_map == NULL) {
|
if (new_color_map == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@ -816,6 +922,8 @@ void VP8LClear(VP8LDecoder* const dec) {
|
|||||||
|
|
||||||
free(dec->rescaler_memory);
|
free(dec->rescaler_memory);
|
||||||
dec->rescaler_memory = NULL;
|
dec->rescaler_memory = NULL;
|
||||||
|
|
||||||
|
dec->output_ = NULL; // leave no trace behind
|
||||||
}
|
}
|
||||||
|
|
||||||
void VP8LDelete(VP8LDecoder* const dec) {
|
void VP8LDelete(VP8LDecoder* const dec) {
|
||||||
@ -845,7 +953,6 @@ static int DecodeImageStream(int xsize, int ysize,
|
|||||||
VP8LBitReader* const br = &dec->br_;
|
VP8LBitReader* const br = &dec->br_;
|
||||||
VP8LMetadata* const hdr = &dec->hdr_;
|
VP8LMetadata* const hdr = &dec->hdr_;
|
||||||
uint32_t* data = NULL;
|
uint32_t* data = NULL;
|
||||||
const int transform_start_idx = dec->next_transform_;
|
|
||||||
int color_cache_bits = 0;
|
int color_cache_bits = 0;
|
||||||
|
|
||||||
// Read the transforms (may recurse).
|
// Read the transforms (may recurse).
|
||||||
@ -892,15 +999,8 @@ static int DecodeImageStream(int xsize, int ysize,
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const uint64_t total_size =
|
const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize;
|
||||||
transform_xsize * transform_ysize * sizeof(*data);
|
data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data));
|
||||||
if (total_size != (size_t)total_size) {
|
|
||||||
// This shouldn't happen, because of transform_bits limit, but...
|
|
||||||
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
|
||||||
ok = 0;
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
data = (uint32_t*)malloc((size_t)total_size);
|
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||||
ok = 0;
|
ok = 0;
|
||||||
@ -912,9 +1012,6 @@ static int DecodeImageStream(int xsize, int ysize,
|
|||||||
ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, NULL);
|
ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, NULL);
|
||||||
ok = ok && !br->error_;
|
ok = ok && !br->error_;
|
||||||
|
|
||||||
// Apply transforms on the decoded data.
|
|
||||||
if (ok) ApplyInverseTransforms(dec, transform_start_idx, data);
|
|
||||||
|
|
||||||
End:
|
End:
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
@ -951,14 +1048,11 @@ static int AllocateARGBBuffers(VP8LDecoder* const dec, int final_width) {
|
|||||||
const uint64_t cache_pixels = (uint64_t)final_width * NUM_ARGB_CACHE_ROWS;
|
const uint64_t cache_pixels = (uint64_t)final_width * NUM_ARGB_CACHE_ROWS;
|
||||||
const uint64_t total_num_pixels =
|
const uint64_t total_num_pixels =
|
||||||
num_pixels + cache_top_pixels + cache_pixels;
|
num_pixels + cache_top_pixels + cache_pixels;
|
||||||
const uint64_t total_size = total_num_pixels * sizeof(*dec->argb_);
|
|
||||||
|
|
||||||
assert(dec->width_ <= final_width);
|
assert(dec->width_ <= final_width);
|
||||||
// Check for overflow
|
dec->argb_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(*dec->argb_));
|
||||||
if ((size_t)total_size != total_size) return 0;
|
|
||||||
dec->argb_ = (uint32_t*)malloc((size_t)total_size);
|
|
||||||
if (dec->argb_ == NULL) {
|
if (dec->argb_ == NULL) {
|
||||||
dec->argb_cache_ = NULL;
|
dec->argb_cache_ = NULL; // for sanity check
|
||||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -974,7 +1068,7 @@ static void ExtractAlphaRows(VP8LDecoder* const dec, int row) {
|
|||||||
const uint32_t* const in = dec->argb_ + dec->width_ * dec->last_row_;
|
const uint32_t* const in = dec->argb_ + dec->width_ * dec->last_row_;
|
||||||
|
|
||||||
if (num_rows <= 0) return; // Nothing to be done.
|
if (num_rows <= 0) return; // Nothing to be done.
|
||||||
ApplyTransforms(dec, num_rows, in);
|
ApplyInverseTransforms(dec, num_rows, in);
|
||||||
|
|
||||||
// Extract alpha (which is stored in the green plane).
|
// Extract alpha (which is stored in the green plane).
|
||||||
{
|
{
|
||||||
@ -1060,20 +1154,16 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) {
|
|||||||
int VP8LDecodeImage(VP8LDecoder* const dec) {
|
int VP8LDecodeImage(VP8LDecoder* const dec) {
|
||||||
VP8Io* io = NULL;
|
VP8Io* io = NULL;
|
||||||
WebPDecParams* params = NULL;
|
WebPDecParams* params = NULL;
|
||||||
WebPDecBuffer* output = NULL;
|
|
||||||
|
|
||||||
// Sanity checks.
|
// Sanity checks.
|
||||||
if (dec == NULL) return 0;
|
if (dec == NULL) return 0;
|
||||||
|
|
||||||
io = dec->io_;
|
io = dec->io_;
|
||||||
|
assert(io != NULL);
|
||||||
params = (WebPDecParams*)io->opaque;
|
params = (WebPDecParams*)io->opaque;
|
||||||
assert(params != NULL);
|
assert(params != NULL);
|
||||||
output = params->output;
|
dec->output_ = params->output;
|
||||||
// YUV modes are invalid.
|
assert(dec->output_ != NULL);
|
||||||
if (output->colorspace >= MODE_YUV) {
|
|
||||||
dec->status_ = VP8_STATUS_INVALID_PARAM;
|
|
||||||
goto Err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialization.
|
// Initialization.
|
||||||
if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) {
|
if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) {
|
||||||
|
@ -61,6 +61,8 @@ typedef struct {
|
|||||||
VP8LDecodeState state_;
|
VP8LDecodeState state_;
|
||||||
VP8Io *io_;
|
VP8Io *io_;
|
||||||
|
|
||||||
|
const WebPDecBuffer *output_; // shortcut to io->opaque->output
|
||||||
|
|
||||||
uint32_t *argb_; // Internal data: always in BGRA color mode.
|
uint32_t *argb_; // Internal data: always in BGRA color mode.
|
||||||
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
|
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
|
||||||
|
|
||||||
|
@ -23,113 +23,156 @@ extern "C" {
|
|||||||
#include "../dsp/dsp.h"
|
#include "../dsp/dsp.h"
|
||||||
#include "../enc/histogram.h"
|
#include "../enc/histogram.h"
|
||||||
|
|
||||||
// A lookup table for small values of log(int) to be used in entropy
|
#define MAX_DIFF_COST (1e30f)
|
||||||
// computation.
|
|
||||||
//
|
// lookup table for small values of log2(int)
|
||||||
// ", ".join(["%.16ff" % x for x in [0.0]+[log(x) for x in range(1, 256)]])
|
#define APPROX_LOG_MAX 4096
|
||||||
|
#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086
|
||||||
#define LOG_LOOKUP_IDX_MAX 256
|
#define LOG_LOOKUP_IDX_MAX 256
|
||||||
static const float kLogTable[LOG_LOOKUP_IDX_MAX] = {
|
static const float kLog2Table[LOG_LOOKUP_IDX_MAX] = {
|
||||||
0.0000000000000000f, 0.0000000000000000f, 0.6931471805599453f,
|
0.0000000000000000f, 0.0000000000000000f,
|
||||||
1.0986122886681098f, 1.3862943611198906f, 1.6094379124341003f,
|
1.0000000000000000f, 1.5849625007211560f,
|
||||||
1.7917594692280550f, 1.9459101490553132f, 2.0794415416798357f,
|
2.0000000000000000f, 2.3219280948873621f,
|
||||||
2.1972245773362196f, 2.3025850929940459f, 2.3978952727983707f,
|
2.5849625007211560f, 2.8073549220576041f,
|
||||||
2.4849066497880004f, 2.5649493574615367f, 2.6390573296152584f,
|
3.0000000000000000f, 3.1699250014423121f,
|
||||||
2.7080502011022101f, 2.7725887222397811f, 2.8332133440562162f,
|
3.3219280948873621f, 3.4594316186372973f,
|
||||||
2.8903717578961645f, 2.9444389791664403f, 2.9957322735539909f,
|
3.5849625007211560f, 3.7004397181410921f,
|
||||||
3.0445224377234230f, 3.0910424533583161f, 3.1354942159291497f,
|
3.8073549220576041f, 3.9068905956085187f,
|
||||||
3.1780538303479458f, 3.2188758248682006f, 3.2580965380214821f,
|
4.0000000000000000f, 4.0874628412503390f,
|
||||||
3.2958368660043291f, 3.3322045101752038f, 3.3672958299864741f,
|
4.1699250014423121f, 4.2479275134435852f,
|
||||||
3.4011973816621555f, 3.4339872044851463f, 3.4657359027997265f,
|
4.3219280948873626f, 4.3923174227787606f,
|
||||||
3.4965075614664802f, 3.5263605246161616f, 3.5553480614894135f,
|
4.4594316186372973f, 4.5235619560570130f,
|
||||||
3.5835189384561099f, 3.6109179126442243f, 3.6375861597263857f,
|
4.5849625007211560f, 4.6438561897747243f,
|
||||||
3.6635616461296463f, 3.6888794541139363f, 3.7135720667043080f,
|
4.7004397181410917f, 4.7548875021634682f,
|
||||||
3.7376696182833684f, 3.7612001156935624f, 3.7841896339182610f,
|
4.8073549220576037f, 4.8579809951275718f,
|
||||||
3.8066624897703196f, 3.8286413964890951f, 3.8501476017100584f,
|
4.9068905956085187f, 4.9541963103868749f,
|
||||||
3.8712010109078911f, 3.8918202981106265f, 3.9120230054281460f,
|
5.0000000000000000f, 5.0443941193584533f,
|
||||||
3.9318256327243257f, 3.9512437185814275f, 3.9702919135521220f,
|
5.0874628412503390f, 5.1292830169449663f,
|
||||||
3.9889840465642745f, 4.0073331852324712f, 4.0253516907351496f,
|
5.1699250014423121f, 5.2094533656289501f,
|
||||||
4.0430512678345503f, 4.0604430105464191f, 4.0775374439057197f,
|
5.2479275134435852f, 5.2854022188622487f,
|
||||||
4.0943445622221004f, 4.1108738641733114f, 4.1271343850450917f,
|
5.3219280948873626f, 5.3575520046180837f,
|
||||||
4.1431347263915326f, 4.1588830833596715f, 4.1743872698956368f,
|
5.3923174227787606f, 5.4262647547020979f,
|
||||||
4.1896547420264252f, 4.2046926193909657f, 4.2195077051761070f,
|
5.4594316186372973f, 5.4918530963296747f,
|
||||||
4.2341065045972597f, 4.2484952420493594f, 4.2626798770413155f,
|
5.5235619560570130f, 5.5545888516776376f,
|
||||||
4.2766661190160553f, 4.2904594411483910f, 4.3040650932041702f,
|
5.5849625007211560f, 5.6147098441152083f,
|
||||||
4.3174881135363101f, 4.3307333402863311f, 4.3438054218536841f,
|
5.6438561897747243f, 5.6724253419714951f,
|
||||||
4.3567088266895917f, 4.3694478524670215f, 4.3820266346738812f,
|
5.7004397181410917f, 5.7279204545631987f,
|
||||||
4.3944491546724391f, 4.4067192472642533f, 4.4188406077965983f,
|
5.7548875021634682f, 5.7813597135246599f,
|
||||||
4.4308167988433134f, 4.4426512564903167f, 4.4543472962535073f,
|
5.8073549220576037f, 5.8328900141647412f,
|
||||||
4.4659081186545837f, 4.4773368144782069f, 4.4886363697321396f,
|
5.8579809951275718f, 5.8826430493618415f,
|
||||||
4.4998096703302650f, 4.5108595065168497f, 4.5217885770490405f,
|
5.9068905956085187f, 5.9307373375628866f,
|
||||||
4.5325994931532563f, 4.5432947822700038f, 4.5538768916005408f,
|
5.9541963103868749f, 5.9772799234999167f,
|
||||||
4.5643481914678361f, 4.5747109785033828f, 4.5849674786705723f,
|
6.0000000000000000f, 6.0223678130284543f,
|
||||||
4.5951198501345898f, 4.6051701859880918f, 4.6151205168412597f,
|
6.0443941193584533f, 6.0660891904577720f,
|
||||||
4.6249728132842707f, 4.6347289882296359f, 4.6443908991413725f,
|
6.0874628412503390f, 6.1085244567781691f,
|
||||||
4.6539603501575231f, 4.6634390941120669f, 4.6728288344619058f,
|
6.1292830169449663f, 6.1497471195046822f,
|
||||||
4.6821312271242199f, 4.6913478822291435f, 4.7004803657924166f,
|
6.1699250014423121f, 6.1898245588800175f,
|
||||||
4.7095302013123339f, 4.7184988712950942f, 4.7273878187123408f,
|
6.2094533656289501f, 6.2288186904958804f,
|
||||||
4.7361984483944957f, 4.7449321283632502f, 4.7535901911063645f,
|
6.2479275134435852f, 6.2667865406949010f,
|
||||||
4.7621739347977563f, 4.7706846244656651f, 4.7791234931115296f,
|
6.2854022188622487f, 6.3037807481771030f,
|
||||||
4.7874917427820458f, 4.7957905455967413f, 4.8040210447332568f,
|
6.3219280948873626f, 6.3398500028846243f,
|
||||||
4.8121843553724171f, 4.8202815656050371f, 4.8283137373023015f,
|
6.3575520046180837f, 6.3750394313469245f,
|
||||||
4.8362819069514780f, 4.8441870864585912f, 4.8520302639196169f,
|
6.3923174227787606f, 6.4093909361377017f,
|
||||||
4.8598124043616719f, 4.8675344504555822f, 4.8751973232011512f,
|
6.4262647547020979f, 6.4429434958487279f,
|
||||||
4.8828019225863706f, 4.8903491282217537f, 4.8978397999509111f,
|
6.4594316186372973f, 6.4757334309663976f,
|
||||||
4.9052747784384296f, 4.9126548857360524f, 4.9199809258281251f,
|
6.4918530963296747f, 6.5077946401986963f,
|
||||||
4.9272536851572051f, 4.9344739331306915f, 4.9416424226093039f,
|
6.5235619560570130f, 6.5391588111080309f,
|
||||||
4.9487598903781684f, 4.9558270576012609f, 4.9628446302599070f,
|
6.5545888516776376f, 6.5698556083309478f,
|
||||||
4.9698132995760007f, 4.9767337424205742f, 4.9836066217083363f,
|
6.5849625007211560f, 6.5999128421871278f,
|
||||||
4.9904325867787360f, 4.9972122737641147f, 5.0039463059454592f,
|
6.6147098441152083f, 6.6293566200796094f,
|
||||||
5.0106352940962555f, 5.0172798368149243f, 5.0238805208462765f,
|
6.6438561897747243f, 6.6582114827517946f,
|
||||||
5.0304379213924353f, 5.0369526024136295f, 5.0434251169192468f,
|
6.6724253419714951f, 6.6865005271832185f,
|
||||||
5.0498560072495371f, 5.0562458053483077f, 5.0625950330269669f,
|
6.7004397181410917f, 6.7142455176661224f,
|
||||||
5.0689042022202315f, 5.0751738152338266f, 5.0814043649844631f,
|
6.7279204545631987f, 6.7414669864011464f,
|
||||||
5.0875963352323836f, 5.0937502008067623f, 5.0998664278241987f,
|
6.7548875021634682f, 6.7681843247769259f,
|
||||||
5.1059454739005803f, 5.1119877883565437f, 5.1179938124167554f,
|
6.7813597135246599f, 6.7944158663501061f,
|
||||||
5.1239639794032588f, 5.1298987149230735f, 5.1357984370502621f,
|
6.8073549220576037f, 6.8201789624151878f,
|
||||||
5.1416635565026603f, 5.1474944768134527f, 5.1532915944977793f,
|
6.8328900141647412f, 6.8454900509443747f,
|
||||||
5.1590552992145291f, 5.1647859739235145f, 5.1704839950381514f,
|
6.8579809951275718f, 6.8703647195834047f,
|
||||||
5.1761497325738288f, 5.1817835502920850f, 5.1873858058407549f,
|
6.8826430493618415f, 6.8948177633079437f,
|
||||||
5.1929568508902104f, 5.1984970312658261f, 5.2040066870767951f,
|
6.9068905956085187f, 6.9188632372745946f,
|
||||||
5.2094861528414214f, 5.2149357576089859f, 5.2203558250783244f,
|
6.9307373375628866f, 6.9425145053392398f,
|
||||||
5.2257466737132017f, 5.2311086168545868f, 5.2364419628299492f,
|
6.9541963103868749f, 6.9657842846620869f,
|
||||||
5.2417470150596426f, 5.2470240721604862f, 5.2522734280466299f,
|
6.9772799234999167f, 6.9886846867721654f,
|
||||||
5.2574953720277815f, 5.2626901889048856f, 5.2678581590633282f,
|
7.0000000000000000f, 7.0112272554232539f,
|
||||||
5.2729995585637468f, 5.2781146592305168f, 5.2832037287379885f,
|
7.0223678130284543f, 7.0334230015374501f,
|
||||||
5.2882670306945352f, 5.2933048247244923f, 5.2983173665480363f,
|
7.0443941193584533f, 7.0552824355011898f,
|
||||||
5.3033049080590757f, 5.3082676974012051f, 5.3132059790417872f,
|
7.0660891904577720f, 7.0768155970508308f,
|
||||||
5.3181199938442161f, 5.3230099791384085f, 5.3278761687895813f,
|
7.0874628412503390f, 7.0980320829605263f,
|
||||||
5.3327187932653688f, 5.3375380797013179f, 5.3423342519648109f,
|
7.1085244567781691f, 7.1189410727235076f,
|
||||||
5.3471075307174685f, 5.3518581334760666f, 5.3565862746720123f,
|
7.1292830169449663f, 7.1395513523987936f,
|
||||||
5.3612921657094255f, 5.3659760150218512f, 5.3706380281276624f,
|
7.1497471195046822f, 7.1598713367783890f,
|
||||||
5.3752784076841653f, 5.3798973535404597f, 5.3844950627890888f,
|
7.1699250014423121f, 7.1799090900149344f,
|
||||||
5.3890717298165010f, 5.3936275463523620f, 5.3981627015177525f,
|
7.1898245588800175f, 7.1996723448363644f,
|
||||||
5.4026773818722793f, 5.4071717714601188f, 5.4116460518550396f,
|
7.2094533656289501f, 7.2191685204621611f,
|
||||||
5.4161004022044201f, 5.4205349992722862f, 5.4249500174814029f,
|
7.2288186904958804f, 7.2384047393250785f,
|
||||||
5.4293456289544411f, 5.4337220035542400f, 5.4380793089231956f,
|
7.2479275134435852f, 7.2573878426926521f,
|
||||||
5.4424177105217932f, 5.4467373716663099f, 5.4510384535657002f,
|
7.2667865406949010f, 7.2761244052742375f,
|
||||||
5.4553211153577017f, 5.4595855141441589f, 5.4638318050256105f,
|
7.2854022188622487f, 7.2946207488916270f,
|
||||||
5.4680601411351315f, 5.4722706736714750f, 5.4764635519315110f,
|
7.3037807481771030f, 7.3128829552843557f,
|
||||||
5.4806389233419912f, 5.4847969334906548f, 5.4889377261566867f,
|
7.3219280948873626f, 7.3309168781146167f,
|
||||||
5.4930614433405482f, 5.4971682252932021f, 5.5012582105447274f,
|
7.3398500028846243f, 7.3487281542310771f,
|
||||||
5.5053315359323625f, 5.5093883366279774f, 5.5134287461649825f,
|
7.3575520046180837f, 7.3663222142458160f,
|
||||||
5.5174528964647074f, 5.5214609178622460f, 5.5254529391317835f,
|
7.3750394313469245f, 7.3837042924740519f,
|
||||||
5.5294290875114234f, 5.5333894887275203f, 5.5373342670185366f,
|
7.3923174227787606f, 7.4008794362821843f,
|
||||||
5.5412635451584258f
|
7.4093909361377017f, 7.4178525148858982f,
|
||||||
|
7.4262647547020979f, 7.4346282276367245f,
|
||||||
|
7.4429434958487279f, 7.4512111118323289f,
|
||||||
|
7.4594316186372973f, 7.4676055500829976f,
|
||||||
|
7.4757334309663976f, 7.4838157772642563f,
|
||||||
|
7.4918530963296747f, 7.4998458870832056f,
|
||||||
|
7.5077946401986963f, 7.5156998382840427f,
|
||||||
|
7.5235619560570130f, 7.5313814605163118f,
|
||||||
|
7.5391588111080309f, 7.5468944598876364f,
|
||||||
|
7.5545888516776376f, 7.5622424242210728f,
|
||||||
|
7.5698556083309478f, 7.5774288280357486f,
|
||||||
|
7.5849625007211560f, 7.5924570372680806f,
|
||||||
|
7.5999128421871278f, 7.6073303137496104f,
|
||||||
|
7.6147098441152083f, 7.6220518194563764f,
|
||||||
|
7.6293566200796094f, 7.6366246205436487f,
|
||||||
|
7.6438561897747243f, 7.6510516911789281f,
|
||||||
|
7.6582114827517946f, 7.6653359171851764f,
|
||||||
|
7.6724253419714951f, 7.6794800995054464f,
|
||||||
|
7.6865005271832185f, 7.6934869574993252f,
|
||||||
|
7.7004397181410917f, 7.7073591320808825f,
|
||||||
|
7.7142455176661224f, 7.7210991887071855f,
|
||||||
|
7.7279204545631987f, 7.7347096202258383f,
|
||||||
|
7.7414669864011464f, 7.7481928495894605f,
|
||||||
|
7.7548875021634682f, 7.7615512324444795f,
|
||||||
|
7.7681843247769259f, 7.7747870596011736f,
|
||||||
|
7.7813597135246599f, 7.7879025593914317f,
|
||||||
|
7.7944158663501061f, 7.8008998999203047f,
|
||||||
|
7.8073549220576037f, 7.8137811912170374f,
|
||||||
|
7.8201789624151878f, 7.8265484872909150f,
|
||||||
|
7.8328900141647412f, 7.8392037880969436f,
|
||||||
|
7.8454900509443747f, 7.8517490414160571f,
|
||||||
|
7.8579809951275718f, 7.8641861446542797f,
|
||||||
|
7.8703647195834047f, 7.8765169465649993f,
|
||||||
|
7.8826430493618415f, 7.8887432488982591f,
|
||||||
|
7.8948177633079437f, 7.9008668079807486f,
|
||||||
|
7.9068905956085187f, 7.9128893362299619f,
|
||||||
|
7.9188632372745946f, 7.9248125036057812f,
|
||||||
|
7.9307373375628866f, 7.9366379390025709f,
|
||||||
|
7.9425145053392398f, 7.9483672315846778f,
|
||||||
|
7.9541963103868749f, 7.9600019320680805f,
|
||||||
|
7.9657842846620869f, 7.9715435539507719f,
|
||||||
|
7.9772799234999167f, 7.9829935746943103f,
|
||||||
|
7.9886846867721654f, 7.9943534368588577f
|
||||||
};
|
};
|
||||||
|
|
||||||
#define APPROX_LOG_MAX 4096
|
float VP8LFastLog2(int v) {
|
||||||
#define LOG_2_BASE_E 0.6931471805599453f
|
if (v < LOG_LOOKUP_IDX_MAX) {
|
||||||
|
return kLog2Table[v];
|
||||||
float VP8LFastLog(int v) {
|
} else if (v < APPROX_LOG_MAX) {
|
||||||
if (v < APPROX_LOG_MAX) {
|
|
||||||
int log_cnt = 0;
|
int log_cnt = 0;
|
||||||
while (v >= LOG_LOOKUP_IDX_MAX) {
|
while (v >= LOG_LOOKUP_IDX_MAX) {
|
||||||
++log_cnt;
|
++log_cnt;
|
||||||
v = v >> 1;
|
v = v >> 1;
|
||||||
}
|
}
|
||||||
return kLogTable[v] + (log_cnt * LOG_2_BASE_E);
|
return kLog2Table[v] + (float)log_cnt;
|
||||||
|
} else {
|
||||||
|
return (float)(LOG_2_RECIPROCAL * log((double)v));
|
||||||
}
|
}
|
||||||
return (float)log(v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@ -284,8 +327,8 @@ static const PredictorFunc kPredictors[16] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO(vikasa): Replace 256 etc with defines.
|
// TODO(vikasa): Replace 256 etc with defines.
|
||||||
static double PredictionCostSpatial(const int* counts,
|
static float PredictionCostSpatial(const int* counts,
|
||||||
int weight_0, double exp_val) {
|
int weight_0, double exp_val) {
|
||||||
const int significant_symbols = 16;
|
const int significant_symbols = 16;
|
||||||
const double exp_decay_factor = 0.6;
|
const double exp_decay_factor = 0.6;
|
||||||
double bits = weight_0 * counts[0];
|
double bits = weight_0 * counts[0];
|
||||||
@ -294,27 +337,26 @@ static double PredictionCostSpatial(const int* counts,
|
|||||||
bits += exp_val * (counts[i] + counts[256 - i]);
|
bits += exp_val * (counts[i] + counts[256 - i]);
|
||||||
exp_val *= exp_decay_factor;
|
exp_val *= exp_decay_factor;
|
||||||
}
|
}
|
||||||
return -0.1 * bits;
|
return (float)(-0.1 * bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the Shanon's entropy: Sum(p*log2(p))
|
// Compute the Shanon's entropy: Sum(p*log2(p))
|
||||||
static double ShannonEntropy(const int* const array, int n) {
|
static float ShannonEntropy(const int* const array, int n) {
|
||||||
int i;
|
int i;
|
||||||
double retval = 0;
|
float retval = 0.f;
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
if (array[i] != 0) {
|
if (array[i] != 0) {
|
||||||
sum += array[i];
|
sum += array[i];
|
||||||
retval += array[i] * VP8LFastLog(array[i]);
|
retval -= VP8LFastSLog2(array[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retval -= sum * VP8LFastLog(sum);
|
retval += VP8LFastSLog2(sum);
|
||||||
retval *= -1.4426950408889634; // 1.0 / -FastLog(2);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double PredictionCostSpatialHistogram(int accumulated[4][256],
|
static float PredictionCostSpatialHistogram(int accumulated[4][256],
|
||||||
int tile[4][256]) {
|
int tile[4][256]) {
|
||||||
int i;
|
int i;
|
||||||
int k;
|
int k;
|
||||||
int combo[256];
|
int combo[256];
|
||||||
@ -328,7 +370,7 @@ static double PredictionCostSpatialHistogram(int accumulated[4][256],
|
|||||||
}
|
}
|
||||||
retval += ShannonEntropy(&combo[0], 256);
|
retval += ShannonEntropy(&combo[0], 256);
|
||||||
}
|
}
|
||||||
return retval;
|
return (float)retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GetBestPredictorForTile(int width, int height,
|
static int GetBestPredictorForTile(int width, int height,
|
||||||
@ -344,14 +386,14 @@ static int GetBestPredictorForTile(int width, int height,
|
|||||||
const int xmax = (tile_size <= width - col_start) ?
|
const int xmax = (tile_size <= width - col_start) ?
|
||||||
tile_size : width - col_start;
|
tile_size : width - col_start;
|
||||||
int histo[4][256];
|
int histo[4][256];
|
||||||
double best_diff = 1e99;
|
float best_diff = MAX_DIFF_COST;
|
||||||
int best_mode = 0;
|
int best_mode = 0;
|
||||||
|
|
||||||
int mode;
|
int mode;
|
||||||
for (mode = 0; mode < kNumPredModes; ++mode) {
|
for (mode = 0; mode < kNumPredModes; ++mode) {
|
||||||
const uint32_t* current_row = argb_scratch;
|
const uint32_t* current_row = argb_scratch;
|
||||||
const PredictorFunc pred_func = kPredictors[mode];
|
const PredictorFunc pred_func = kPredictors[mode];
|
||||||
double cur_diff;
|
float cur_diff;
|
||||||
int y;
|
int y;
|
||||||
memset(&histo[0][0], 0, sizeof(histo));
|
memset(&histo[0][0], 0, sizeof(histo));
|
||||||
for (y = 0; y < ymax; ++y) {
|
for (y = 0; y < ymax; ++y) {
|
||||||
@ -630,8 +672,8 @@ static WEBP_INLINE int SkipRepeatedPixels(const uint32_t* const argb,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double PredictionCostCrossColor(const int accumulated[256],
|
static float PredictionCostCrossColor(const int accumulated[256],
|
||||||
const int counts[256]) {
|
const int counts[256]) {
|
||||||
// Favor low entropy, locally and globally.
|
// Favor low entropy, locally and globally.
|
||||||
int i;
|
int i;
|
||||||
int combo[256];
|
int combo[256];
|
||||||
@ -651,8 +693,8 @@ static Multipliers GetBestColorTransformForTile(
|
|||||||
int* accumulated_red_histo,
|
int* accumulated_red_histo,
|
||||||
int* accumulated_blue_histo,
|
int* accumulated_blue_histo,
|
||||||
const uint32_t* const argb) {
|
const uint32_t* const argb) {
|
||||||
double best_diff = 1e99;
|
float best_diff = MAX_DIFF_COST;
|
||||||
double cur_diff;
|
float cur_diff;
|
||||||
const int halfstep = step / 2;
|
const int halfstep = step / 2;
|
||||||
const int max_tile_size = 1 << bits;
|
const int max_tile_size = 1 << bits;
|
||||||
const int tile_y_offset = tile_y * max_tile_size;
|
const int tile_y_offset = tile_y * max_tile_size;
|
||||||
@ -704,7 +746,7 @@ static Multipliers GetBestColorTransformForTile(
|
|||||||
best_tx = tx;
|
best_tx = tx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
best_diff = 1e99;
|
best_diff = MAX_DIFF_COST;
|
||||||
green_to_red = best_tx.green_to_red_;
|
green_to_red = best_tx.green_to_red_;
|
||||||
for (green_to_blue = -32; green_to_blue <= 32; green_to_blue += step) {
|
for (green_to_blue = -32; green_to_blue <= 32; green_to_blue += step) {
|
||||||
for (red_to_blue = -32; red_to_blue <= 32; red_to_blue += step) {
|
for (red_to_blue = -32; red_to_blue <= 32; red_to_blue += step) {
|
||||||
@ -893,7 +935,7 @@ static void ColorIndexInverseTransform(
|
|||||||
uint32_t packed_pixels = 0;
|
uint32_t packed_pixels = 0;
|
||||||
int x;
|
int x;
|
||||||
for (x = 0; x < width; ++x) {
|
for (x = 0; x < width; ++x) {
|
||||||
// We need to load fresh 'packed_pixels' once every 'bytes_per_pixels'
|
// We need to load fresh 'packed_pixels' once every 'pixels_per_byte'
|
||||||
// increments of x. Fortunately, pixels_per_byte is a power of 2, so
|
// increments of x. Fortunately, pixels_per_byte is a power of 2, so
|
||||||
// can just use a mask for that, instead of decrementing a counter.
|
// can just use a mask for that, instead of decrementing a counter.
|
||||||
if ((x & count_mask) == 0) packed_pixels = ((*src++) >> 8) & 0xff;
|
if ((x & count_mask) == 0) packed_pixels = ((*src++) >> 8) & 0xff;
|
||||||
@ -934,7 +976,21 @@ void VP8LInverseTransform(const VP8LTransform* const transform,
|
|||||||
ColorSpaceInverseTransform(transform, row_start, row_end, out);
|
ColorSpaceInverseTransform(transform, row_start, row_end, out);
|
||||||
break;
|
break;
|
||||||
case COLOR_INDEXING_TRANSFORM:
|
case COLOR_INDEXING_TRANSFORM:
|
||||||
ColorIndexInverseTransform(transform, row_start, row_end, in, out);
|
if (in == out && transform->bits_ > 0) {
|
||||||
|
// Move packed pixels to the end of unpacked region, so that unpacking
|
||||||
|
// can occur seamlessly.
|
||||||
|
// Also, note that this is the only transform that applies on
|
||||||
|
// the effective width of VP8LSubSampleSize(xsize_, bits_). All other
|
||||||
|
// transforms work on effective width of xsize_.
|
||||||
|
const int out_stride = (row_end - row_start) * transform->xsize_;
|
||||||
|
const int in_stride = (row_end - row_start) *
|
||||||
|
VP8LSubSampleSize(transform->xsize_, transform->bits_);
|
||||||
|
uint32_t* const src = out + out_stride - in_stride;
|
||||||
|
memmove(src, out, in_stride * sizeof(*src));
|
||||||
|
ColorIndexInverseTransform(transform, row_start, row_end, src, out);
|
||||||
|
} else {
|
||||||
|
ColorIndexInverseTransform(transform, row_start, row_end, in, out);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,10 @@ static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size,
|
|||||||
return (size + (1 << sampling_bits) - 1) >> sampling_bits;
|
return (size + (1 << sampling_bits) - 1) >> sampling_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Faster logarithm for small integers, with the property of log(0) == 0.
|
// Faster logarithm for integers, with the property of log2(0) == 0.
|
||||||
float VP8LFastLog(int v);
|
float VP8LFastLog2(int v);
|
||||||
|
// Fast calculation of v * log2(v) for integer input.
|
||||||
|
static WEBP_INLINE float VP8LFastSLog2(int v) { return VP8LFastLog2(v) * v; }
|
||||||
|
|
||||||
// In-place difference of each component with mod 256.
|
// In-place difference of each component with mod 256.
|
||||||
static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
|
static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
|
||||||
|
@ -271,8 +271,7 @@ static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first,
|
|||||||
|
|
||||||
// rgbA4444
|
// rgbA4444
|
||||||
|
|
||||||
#define MULTIPLIER(a) ((a) * 0x11)
|
#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
|
||||||
#define PREMULTIPLY(x, m) (((x) * (m)) >> 12)
|
|
||||||
|
|
||||||
static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
|
static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
|
||||||
return (x & 0xf0) | (x >> 4);
|
return (x & 0xf0) | (x >> 4);
|
||||||
@ -282,24 +281,27 @@ static WEBP_INLINE uint8_t dither_lo(uint8_t x) {
|
|||||||
return (x & 0x0f) | (x << 4);
|
return (x & 0x0f) | (x << 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) {
|
||||||
|
return (x * m) >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
static void ApplyAlphaMultiply4444(uint8_t* rgba4444,
|
static void ApplyAlphaMultiply4444(uint8_t* rgba4444,
|
||||||
int w, int h, int stride) {
|
int w, int h, int stride) {
|
||||||
while (h-- > 0) {
|
while (h-- > 0) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < w; ++i) {
|
for (i = 0; i < w; ++i) {
|
||||||
const uint8_t a = dither_lo(rgba4444[2 * i + 1]);
|
const uint8_t a = (rgba4444[2 * i + 1] & 0x0f);
|
||||||
const uint32_t mult = MULTIPLIER(a);
|
const uint32_t mult = MULTIPLIER(a);
|
||||||
const uint8_t r = PREMULTIPLY(dither_hi(rgba4444[2 * i + 0]), mult);
|
const uint8_t r = multiply(dither_hi(rgba4444[2 * i + 0]), mult);
|
||||||
const uint8_t g = PREMULTIPLY(dither_lo(rgba4444[2 * i + 0]), mult);
|
const uint8_t g = multiply(dither_lo(rgba4444[2 * i + 0]), mult);
|
||||||
const uint8_t b = PREMULTIPLY(dither_hi(rgba4444[2 * i + 1]), mult);
|
const uint8_t b = multiply(dither_hi(rgba4444[2 * i + 1]), mult);
|
||||||
rgba4444[2 * i + 0] = (r & 0xf0) | (g & 0x0f);
|
rgba4444[2 * i + 0] = (r & 0xf0) | ((g >> 4) & 0x0f);
|
||||||
rgba4444[2 * i + 1] = (b & 0xf0) | a;
|
rgba4444[2 * i + 1] = (b & 0xf0) | a;
|
||||||
}
|
}
|
||||||
rgba4444 += stride;
|
rgba4444 += stride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#undef MULTIPLIER
|
#undef MULTIPLIER
|
||||||
#undef PREMULTIPLY
|
|
||||||
|
|
||||||
void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int)
|
void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int)
|
||||||
= ApplyAlphaMultiply;
|
= ApplyAlphaMultiply;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// inline YUV->RGB conversion function
|
// inline YUV<->RGB conversion function
|
||||||
//
|
//
|
||||||
// Author: Skal (pascal.massimino@gmail.com)
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
@ -14,6 +14,9 @@
|
|||||||
|
|
||||||
#include "../dec/decode_vp8.h"
|
#include "../dec/decode_vp8.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// YUV -> RGB conversion
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@ -87,14 +90,37 @@ static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v,
|
|||||||
rgba[3] = 0xff;
|
rgba[3] = 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WEBP_INLINE uint32_t VP8Clip4Bits(uint8_t c) {
|
|
||||||
const uint32_t v = (c + 8) >> 4;
|
|
||||||
return (v > 15) ? 15 : v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be called before everything, to initialize the tables.
|
// Must be called before everything, to initialize the tables.
|
||||||
void VP8YUVInit(void);
|
void VP8YUVInit(void);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// RGB -> YUV conversion
|
||||||
|
// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
|
||||||
|
// More information at: http://en.wikipedia.org/wiki/YCbCr
|
||||||
|
// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
|
||||||
|
// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
|
||||||
|
// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
|
||||||
|
// We use 16bit fixed point operations.
|
||||||
|
|
||||||
|
static WEBP_INLINE int VP8ClipUV(int v) {
|
||||||
|
v = (v + (257 << (YUV_FIX + 2 - 1))) >> (YUV_FIX + 2);
|
||||||
|
return ((v & ~0xff) == 0) ? v : (v < 0) ? 0 : 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE int VP8RGBToY(int r, int g, int b) {
|
||||||
|
const int kRound = (1 << (YUV_FIX - 1)) + (16 << YUV_FIX);
|
||||||
|
const int luma = 16839 * r + 33059 * g + 6420 * b;
|
||||||
|
return (luma + kRound) >> YUV_FIX; // no need to clip
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE int VP8RGBToU(int r, int g, int b) {
|
||||||
|
return VP8ClipUV(-9719 * r - 19081 * g + 28800 * b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE int VP8RGBToV(int r, int g, int b) {
|
||||||
|
return VP8ClipUV(+28800 * r - 24116 * g - 4684 * b);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
123
src/enc/alpha.c
123
src/enc/alpha.c
@ -22,19 +22,15 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
// Encodes the given alpha data via specified compression method 'method'.
|
||||||
// int quality, int method, int filter, int effort_level,
|
// The pre-processing (quantization) is performed if 'quality' is less than 100.
|
||||||
// uint8_t** output, size_t* output_size)
|
// For such cases, the encoding is lossy. The valid range is [0, 100] for
|
||||||
//
|
// 'quality' and [0, 1] for 'method':
|
||||||
// Encodes the given alpha data 'data' of size 'stride'x'height' via specified
|
|
||||||
// compression method 'method'. The pre-processing (Quantization) is
|
|
||||||
// performed if 'quality' is less than 100. For such cases, the encoding is
|
|
||||||
// lossy. Valid ranges for 'quality' is [0, 100] and 'method' is [0, 1]:
|
|
||||||
// 'method = 0' - No compression;
|
// 'method = 0' - No compression;
|
||||||
// 'method = 1' - Use lossless coder on the alpha plane only
|
// 'method = 1' - Use lossless coder on the alpha plane only
|
||||||
// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
|
// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
|
||||||
// vertical & gradient filters. The prediction mode 4 will try all the
|
// vertical & gradient filters. The prediction mode 4 will try all the
|
||||||
// prediction modes (0 to 3) and pick the best prediction mode.
|
// prediction modes 0 to 3 and pick the best one.
|
||||||
// 'effort_level': specifies how much effort must be spent to try and reduce
|
// 'effort_level': specifies how much effort must be spent to try and reduce
|
||||||
// the compressed output size. In range 0 (quick) to 6 (slow).
|
// the compressed output size. In range 0 (quick) to 6 (slow).
|
||||||
//
|
//
|
||||||
@ -50,10 +46,10 @@ extern "C" {
|
|||||||
|
|
||||||
#include "../enc/vp8li.h"
|
#include "../enc/vp8li.h"
|
||||||
|
|
||||||
static int EncodeLossless(const uint8_t* data, int width, int height,
|
static int EncodeLossless(const uint8_t* const data, int width, int height,
|
||||||
int effort_level, // in [0..6] range
|
int effort_level, // in [0..6] range
|
||||||
VP8BitWriter* const bw) {
|
VP8BitWriter* const bw,
|
||||||
|
WebPAuxStats* const stats) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
WebPConfig config;
|
WebPConfig config;
|
||||||
WebPPicture picture;
|
WebPPicture picture;
|
||||||
@ -63,6 +59,7 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
|||||||
picture.width = width;
|
picture.width = width;
|
||||||
picture.height = height;
|
picture.height = height;
|
||||||
picture.use_argb = 1;
|
picture.use_argb = 1;
|
||||||
|
picture.stats = stats;
|
||||||
if (!WebPPictureAlloc(&picture)) return 0;
|
if (!WebPPictureAlloc(&picture)) return 0;
|
||||||
|
|
||||||
// Transfer the alpha values to the green channel.
|
// Transfer the alpha values to the green channel.
|
||||||
@ -87,8 +84,8 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
|||||||
config.quality = 10.f + 15.f * effort_level;
|
config.quality = 10.f + 15.f * effort_level;
|
||||||
if (config.quality > 100.f) config.quality = 100.f;
|
if (config.quality > 100.f) config.quality = 100.f;
|
||||||
|
|
||||||
VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
|
ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
|
||||||
ok = (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
|
ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
|
||||||
WebPPictureFree(&picture);
|
WebPPictureFree(&picture);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
const uint8_t* const data = VP8LBitWriterFinish(&tmp_bw);
|
const uint8_t* const data = VP8LBitWriterFinish(&tmp_bw);
|
||||||
@ -101,10 +98,12 @@ static int EncodeLossless(const uint8_t* data, int width, int height,
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
|
||||||
int method, int filter, int reduce_levels,
|
int method, int filter, int reduce_levels,
|
||||||
int effort_level, // in [0..6] range
|
int effort_level, // in [0..6] range
|
||||||
uint8_t* tmp_alpha, VP8BitWriter* const bw) {
|
uint8_t* const tmp_alpha,
|
||||||
|
VP8BitWriter* const bw,
|
||||||
|
WebPAuxStats* const stats) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
const uint8_t* alpha_src;
|
const uint8_t* alpha_src;
|
||||||
WebPFilterFunc filter_func;
|
WebPFilterFunc filter_func;
|
||||||
@ -112,6 +111,7 @@ static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
|||||||
size_t expected_size;
|
size_t expected_size;
|
||||||
const size_t data_size = width * height;
|
const size_t data_size = width * height;
|
||||||
|
|
||||||
|
assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
|
||||||
assert(filter >= 0 && filter < WEBP_FILTER_LAST);
|
assert(filter >= 0 && filter < WEBP_FILTER_LAST);
|
||||||
assert(method >= ALPHA_NO_COMPRESSION);
|
assert(method >= ALPHA_NO_COMPRESSION);
|
||||||
assert(method <= ALPHA_LOSSLESS_COMPRESSION);
|
assert(method <= ALPHA_LOSSLESS_COMPRESSION);
|
||||||
@ -139,7 +139,7 @@ static int EncodeAlphaInternal(const uint8_t* data, int width, int height,
|
|||||||
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
||||||
ok = ok && !bw->error_;
|
ok = ok && !bw->error_;
|
||||||
} else {
|
} else {
|
||||||
ok = EncodeLossless(alpha_src, width, height, effort_level, bw);
|
ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats);
|
||||||
VP8BitWriterFinish(bw);
|
VP8BitWriterFinish(bw);
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
@ -157,19 +157,26 @@ static void CopyPlane(const uint8_t* src, int src_stride,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
static int EncodeAlpha(VP8Encoder* const enc,
|
||||||
int quality, int method, int filter,
|
int quality, int method, int filter,
|
||||||
int effort_level,
|
int effort_level,
|
||||||
uint8_t** output, size_t* output_size) {
|
uint8_t** const output, size_t* const output_size) {
|
||||||
|
const WebPPicture* const pic = enc->pic_;
|
||||||
|
const int width = pic->width;
|
||||||
|
const int height = pic->height;
|
||||||
|
|
||||||
uint8_t* quant_alpha = NULL;
|
uint8_t* quant_alpha = NULL;
|
||||||
const size_t data_size = width * height;
|
const size_t data_size = width * height;
|
||||||
|
uint64_t sse = 0;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
const int reduce_levels = (quality < 100);
|
const int reduce_levels = (quality < 100);
|
||||||
|
|
||||||
// quick sanity checks
|
// quick sanity checks
|
||||||
assert(data != NULL && output != NULL && output_size != NULL);
|
assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
|
||||||
|
assert(enc != NULL && pic != NULL && pic->a != NULL);
|
||||||
|
assert(output != NULL && output_size != NULL);
|
||||||
assert(width > 0 && height > 0);
|
assert(width > 0 && height > 0);
|
||||||
assert(stride >= width);
|
assert(pic->a_stride >= width);
|
||||||
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
|
assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
|
||||||
|
|
||||||
if (quality < 0 || quality > 100) {
|
if (quality < 0 || quality > 100) {
|
||||||
@ -186,7 +193,7 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract alpha data (width x height) from raw_data (stride x height).
|
// Extract alpha data (width x height) from raw_data (stride x height).
|
||||||
CopyPlane(data, stride, quant_alpha, width, width, height);
|
CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
|
||||||
|
|
||||||
if (reduce_levels) { // No Quantization required for 'quality = 100'.
|
if (reduce_levels) { // No Quantization required for 'quality = 100'.
|
||||||
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
|
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
|
||||||
@ -194,24 +201,22 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
// and Quality:]70, 100] -> Levels:]16, 256].
|
// and Quality:]70, 100] -> Levels:]16, 256].
|
||||||
const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
|
const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
|
||||||
: (16 + (quality - 70) * 8);
|
: (16 + (quality - 70) * 8);
|
||||||
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, NULL);
|
ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
VP8BitWriter bw;
|
VP8BitWriter bw;
|
||||||
size_t best_score;
|
|
||||||
int test_filter;
|
int test_filter;
|
||||||
uint8_t* filtered_alpha = NULL;
|
uint8_t* filtered_alpha = NULL;
|
||||||
|
|
||||||
// We always test WEBP_FILTER_NONE first.
|
// We always test WEBP_FILTER_NONE first.
|
||||||
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
||||||
method, WEBP_FILTER_NONE, reduce_levels,
|
method, WEBP_FILTER_NONE, reduce_levels,
|
||||||
effort_level, NULL, &bw);
|
effort_level, NULL, &bw, pic->stats);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
VP8BitWriterWipeOut(&bw);
|
VP8BitWriterWipeOut(&bw);
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
best_score = VP8BitWriterSize(&bw);
|
|
||||||
|
|
||||||
if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate?
|
if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate?
|
||||||
filter = EstimateBestFilter(quant_alpha, width, height, width);
|
filter = EstimateBestFilter(quant_alpha, width, height, width);
|
||||||
@ -228,35 +233,48 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try the other mode(s).
|
// Try the other mode(s).
|
||||||
for (test_filter = WEBP_FILTER_HORIZONTAL;
|
{
|
||||||
ok && (test_filter <= WEBP_FILTER_GRADIENT);
|
WebPAuxStats best_stats;
|
||||||
++test_filter) {
|
size_t best_score = VP8BitWriterSize(&bw);
|
||||||
VP8BitWriter tmp_bw;
|
|
||||||
if (filter != WEBP_FILTER_BEST && test_filter != filter) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
memset(&best_stats, 0, sizeof(best_stats)); // prevent spurious warning
|
||||||
method, test_filter, reduce_levels,
|
if (pic->stats != NULL) best_stats = *pic->stats;
|
||||||
effort_level, filtered_alpha, &tmp_bw);
|
for (test_filter = WEBP_FILTER_HORIZONTAL;
|
||||||
if (ok) {
|
ok && (test_filter <= WEBP_FILTER_GRADIENT);
|
||||||
const size_t score = VP8BitWriterSize(&tmp_bw);
|
++test_filter) {
|
||||||
if (score < best_score) {
|
VP8BitWriter tmp_bw;
|
||||||
// swap bitwriter objects.
|
if (filter != WEBP_FILTER_BEST && test_filter != filter) {
|
||||||
VP8BitWriter tmp = tmp_bw;
|
continue;
|
||||||
tmp_bw = bw;
|
|
||||||
bw = tmp;
|
|
||||||
best_score = score;
|
|
||||||
}
|
}
|
||||||
} else {
|
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
||||||
VP8BitWriterWipeOut(&bw);
|
method, test_filter, reduce_levels,
|
||||||
|
effort_level, filtered_alpha, &tmp_bw,
|
||||||
|
pic->stats);
|
||||||
|
if (ok) {
|
||||||
|
const size_t score = VP8BitWriterSize(&tmp_bw);
|
||||||
|
if (score < best_score) {
|
||||||
|
// swap bitwriter objects.
|
||||||
|
VP8BitWriter tmp = tmp_bw;
|
||||||
|
tmp_bw = bw;
|
||||||
|
bw = tmp;
|
||||||
|
best_score = score;
|
||||||
|
if (pic->stats != NULL) best_stats = *pic->stats;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VP8BitWriterWipeOut(&bw);
|
||||||
|
}
|
||||||
|
VP8BitWriterWipeOut(&tmp_bw);
|
||||||
}
|
}
|
||||||
VP8BitWriterWipeOut(&tmp_bw);
|
if (pic->stats != NULL) *pic->stats = best_stats;
|
||||||
}
|
}
|
||||||
Ok:
|
Ok:
|
||||||
if (ok) {
|
if (ok) {
|
||||||
*output_size = VP8BitWriterSize(&bw);
|
*output_size = VP8BitWriterSize(&bw);
|
||||||
*output = VP8BitWriterBuf(&bw);
|
*output = VP8BitWriterBuf(&bw);
|
||||||
|
if (pic->stats != NULL) { // need stats?
|
||||||
|
pic->stats->coded_size += (int)(*output_size);
|
||||||
|
enc->sse_[3] = sse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(filtered_alpha);
|
free(filtered_alpha);
|
||||||
}
|
}
|
||||||
@ -269,16 +287,15 @@ static int EncodeAlpha(const uint8_t* data, int width, int height, int stride,
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Main calls
|
// Main calls
|
||||||
|
|
||||||
void VP8EncInitAlpha(VP8Encoder* enc) {
|
void VP8EncInitAlpha(VP8Encoder* const enc) {
|
||||||
enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
|
enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
|
||||||
enc->alpha_data_ = NULL;
|
enc->alpha_data_ = NULL;
|
||||||
enc->alpha_data_size_ = 0;
|
enc->alpha_data_size_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VP8EncFinishAlpha(VP8Encoder* enc) {
|
int VP8EncFinishAlpha(VP8Encoder* const enc) {
|
||||||
if (enc->has_alpha_) {
|
if (enc->has_alpha_) {
|
||||||
const WebPConfig* config = enc->config_;
|
const WebPConfig* config = enc->config_;
|
||||||
const WebPPicture* pic = enc->pic_;
|
|
||||||
uint8_t* tmp_data = NULL;
|
uint8_t* tmp_data = NULL;
|
||||||
size_t tmp_size = 0;
|
size_t tmp_size = 0;
|
||||||
const int effort_level = config->method; // maps to [0..6]
|
const int effort_level = config->method; // maps to [0..6]
|
||||||
@ -287,9 +304,7 @@ int VP8EncFinishAlpha(VP8Encoder* enc) {
|
|||||||
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
|
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
|
||||||
WEBP_FILTER_BEST;
|
WEBP_FILTER_BEST;
|
||||||
|
|
||||||
assert(pic->a);
|
if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
|
||||||
if (!EncodeAlpha(pic->a, pic->width, pic->height, pic->a_stride,
|
|
||||||
config->alpha_quality, config->alpha_compression,
|
|
||||||
filter, effort_level, &tmp_data, &tmp_size)) {
|
filter, effort_level, &tmp_data, &tmp_size)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -303,7 +318,7 @@ int VP8EncFinishAlpha(VP8Encoder* enc) {
|
|||||||
return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VP8EncDeleteAlpha(VP8Encoder* enc) {
|
void VP8EncDeleteAlpha(VP8Encoder* const enc) {
|
||||||
free(enc->alpha_data_);
|
free(enc->alpha_data_);
|
||||||
enc->alpha_data_ = NULL;
|
enc->alpha_data_ = NULL;
|
||||||
enc->alpha_data_size_ = 0;
|
enc->alpha_data_size_ = 0;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "./vp8enci.h"
|
#include "./vp8enci.h"
|
||||||
#include "./cost.h"
|
#include "./cost.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -35,7 +36,8 @@ static void SmoothSegmentMap(VP8Encoder* const enc) {
|
|||||||
const int w = enc->mb_w_;
|
const int w = enc->mb_w_;
|
||||||
const int h = enc->mb_h_;
|
const int h = enc->mb_h_;
|
||||||
const int majority_cnt_3_x_3_grid = 5;
|
const int majority_cnt_3_x_3_grid = 5;
|
||||||
uint8_t* const tmp = (uint8_t*)malloc(w * h * sizeof(uint8_t));
|
uint8_t* const tmp = (uint8_t*)WebPSafeMalloc((uint64_t)w * h, sizeof(*tmp));
|
||||||
|
assert((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec
|
||||||
|
|
||||||
if (tmp == NULL) return;
|
if (tmp == NULL) return;
|
||||||
for (y = 1; y < h - 1; ++y) {
|
for (y = 1; y < h - 1; ++y) {
|
||||||
@ -145,7 +147,7 @@ static void SetSegmentAlphas(VP8Encoder* const enc,
|
|||||||
static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
|
static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
|
||||||
const int nb = enc->segment_hdr_.num_segments_;
|
const int nb = enc->segment_hdr_.num_segments_;
|
||||||
int centers[NUM_MB_SEGMENTS];
|
int centers[NUM_MB_SEGMENTS];
|
||||||
int weighted_average;
|
int weighted_average = 0;
|
||||||
int map[256];
|
int map[256];
|
||||||
int a, n, k;
|
int a, n, k;
|
||||||
int min_a = 0, max_a = 255, range_a;
|
int min_a = 0, max_a = 255, range_a;
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
|
|
||||||
#include "./backward_references.h"
|
#include "./backward_references.h"
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
|
#include "../dsp/lossless.h"
|
||||||
#include "../utils/color_cache.h"
|
#include "../utils/color_cache.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
#define VALUES_IN_BYTE 256
|
#define VALUES_IN_BYTE 256
|
||||||
|
|
||||||
@ -93,7 +95,8 @@ int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size) {
|
|||||||
assert(refs != NULL);
|
assert(refs != NULL);
|
||||||
refs->size = 0;
|
refs->size = 0;
|
||||||
refs->max_size = 0;
|
refs->max_size = 0;
|
||||||
refs->refs = (PixOrCopy*)malloc(max_size * sizeof(*refs->refs));
|
refs->refs = (PixOrCopy*)WebPSafeMalloc((uint64_t)max_size,
|
||||||
|
sizeof(*refs->refs));
|
||||||
if (refs->refs == NULL) return 0;
|
if (refs->refs == NULL) return 0;
|
||||||
refs->max_size = max_size;
|
refs->max_size = max_size;
|
||||||
return 1;
|
return 1;
|
||||||
@ -110,7 +113,7 @@ static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) {
|
|||||||
|
|
||||||
static int HashChainInit(HashChain* const p, int size) {
|
static int HashChainInit(HashChain* const p, int size) {
|
||||||
int i;
|
int i;
|
||||||
p->chain_ = (int*)malloc(size * sizeof(*p->chain_));
|
p->chain_ = (int*)WebPSafeMalloc((uint64_t)size, sizeof(*p->chain_));
|
||||||
if (p->chain_ == NULL) {
|
if (p->chain_ == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -256,8 +259,10 @@ static int BackwardReferencesHashChain(int xsize, int ysize,
|
|||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
|
|
||||||
if (hash_chain == NULL) return 0;
|
if (hash_chain == NULL) return 0;
|
||||||
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
if (use_color_cache) {
|
||||||
if (!cc_init) goto Error;
|
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
||||||
|
if (!cc_init) goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
if (!HashChainInit(hash_chain, pix_count)) goto Error;
|
if (!HashChainInit(hash_chain, pix_count)) goto Error;
|
||||||
|
|
||||||
@ -289,15 +294,16 @@ static int BackwardReferencesHashChain(int xsize, int ysize,
|
|||||||
HashChainFindCopy(hash_chain, quality,
|
HashChainFindCopy(hash_chain, quality,
|
||||||
i + 1, xsize, argb, maxlen, &offset2, &len2);
|
i + 1, xsize, argb, maxlen, &offset2, &len2);
|
||||||
if (len2 > len + 1) {
|
if (len2 > len + 1) {
|
||||||
|
const uint32_t pixel = argb[i];
|
||||||
// Alternative#2 is a better match. So push pixel at 'i' as literal.
|
// Alternative#2 is a better match. So push pixel at 'i' as literal.
|
||||||
if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
|
if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
|
||||||
const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]);
|
const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
|
||||||
refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
|
refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
|
||||||
} else {
|
} else {
|
||||||
refs->refs[refs->size] = PixOrCopyCreateLiteral(argb[i]);
|
refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
|
||||||
}
|
}
|
||||||
++refs->size;
|
++refs->size;
|
||||||
VP8LColorCacheInsert(&hashers, argb[i]);
|
if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
|
||||||
i++; // Backward reference to be done for next pixel.
|
i++; // Backward reference to be done for next pixel.
|
||||||
len = len2;
|
len = len2;
|
||||||
offset = offset2;
|
offset = offset2;
|
||||||
@ -307,24 +313,30 @@ static int BackwardReferencesHashChain(int xsize, int ysize,
|
|||||||
len = MAX_LENGTH - 1;
|
len = MAX_LENGTH - 1;
|
||||||
}
|
}
|
||||||
refs->refs[refs->size++] = PixOrCopyCreateCopy(offset, len);
|
refs->refs[refs->size++] = PixOrCopyCreateCopy(offset, len);
|
||||||
for (k = 0; k < len; ++k) {
|
if (use_color_cache) {
|
||||||
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
for (k = 0; k < len; ++k) {
|
||||||
if (k != 0 && i + k + 1 < pix_count) {
|
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
||||||
// Add to the hash_chain (but cannot add the last pixel).
|
}
|
||||||
|
}
|
||||||
|
// Add to the hash_chain (but cannot add the last pixel).
|
||||||
|
{
|
||||||
|
const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
|
||||||
|
for (k = 1; k < last; ++k) {
|
||||||
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i += len;
|
i += len;
|
||||||
} else {
|
} else {
|
||||||
if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
|
const uint32_t pixel = argb[i];
|
||||||
|
if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
|
||||||
// push pixel as a PixOrCopyCreateCacheIdx pixel
|
// push pixel as a PixOrCopyCreateCacheIdx pixel
|
||||||
int ix = VP8LColorCacheGetIndex(&hashers, argb[i]);
|
const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
|
||||||
refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
|
refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
|
||||||
} else {
|
} else {
|
||||||
refs->refs[refs->size] = PixOrCopyCreateLiteral(argb[i]);
|
refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
|
||||||
}
|
}
|
||||||
++refs->size;
|
++refs->size;
|
||||||
VP8LColorCacheInsert(&hashers, argb[i]);
|
if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
|
||||||
if (i + 1 < pix_count) {
|
if (i + 1 < pix_count) {
|
||||||
HashChainInsert(hash_chain, &argb[i], i);
|
HashChainInsert(hash_chain, &argb[i], i);
|
||||||
}
|
}
|
||||||
@ -346,46 +358,65 @@ typedef struct {
|
|||||||
double literal_[PIX_OR_COPY_CODES_MAX];
|
double literal_[PIX_OR_COPY_CODES_MAX];
|
||||||
double blue_[VALUES_IN_BYTE];
|
double blue_[VALUES_IN_BYTE];
|
||||||
double distance_[NUM_DISTANCE_CODES];
|
double distance_[NUM_DISTANCE_CODES];
|
||||||
int cache_bits_;
|
|
||||||
} CostModel;
|
} CostModel;
|
||||||
|
|
||||||
static int BackwardReferencesTraceBackwards(
|
static int BackwardReferencesTraceBackwards(
|
||||||
int xsize, int ysize, int recursive_cost_model,
|
int xsize, int ysize, int recursive_cost_model,
|
||||||
const uint32_t* const argb, int cache_bits, VP8LBackwardRefs* const refs);
|
const uint32_t* const argb, int cache_bits, VP8LBackwardRefs* const refs);
|
||||||
|
|
||||||
static int CostModelBuild(CostModel* const p, int xsize, int ysize,
|
static void ConvertPopulationCountTableToBitEstimates(
|
||||||
|
int num_symbols, const int population_counts[], double output[]) {
|
||||||
|
int sum = 0;
|
||||||
|
int nonzeros = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_symbols; ++i) {
|
||||||
|
sum += population_counts[i];
|
||||||
|
if (population_counts[i] > 0) {
|
||||||
|
++nonzeros;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nonzeros <= 1) {
|
||||||
|
memset(output, 0, num_symbols * sizeof(*output));
|
||||||
|
} else {
|
||||||
|
const double logsum = VP8LFastLog2(sum);
|
||||||
|
for (i = 0; i < num_symbols; ++i) {
|
||||||
|
output[i] = logsum - VP8LFastLog2(population_counts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CostModelBuild(CostModel* const m, int xsize, int ysize,
|
||||||
int recursion_level, const uint32_t* const argb,
|
int recursion_level, const uint32_t* const argb,
|
||||||
int cache_bits) {
|
int cache_bits) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
VP8LHistogram histo;
|
VP8LHistogram histo;
|
||||||
VP8LBackwardRefs refs;
|
VP8LBackwardRefs refs;
|
||||||
|
const int quality = 100;
|
||||||
|
|
||||||
if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error;
|
if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error;
|
||||||
|
|
||||||
p->cache_bits_ = cache_bits;
|
|
||||||
if (recursion_level > 0) {
|
if (recursion_level > 0) {
|
||||||
if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
|
if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
|
||||||
argb, cache_bits, &refs)) {
|
argb, cache_bits, &refs)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const int quality = 100;
|
|
||||||
if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
|
if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
|
||||||
&refs)) {
|
&refs)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VP8LHistogramCreate(&histo, &refs, cache_bits);
|
VP8LHistogramCreate(&histo, &refs, cache_bits);
|
||||||
VP8LConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
VP8LHistogramNumCodes(&histo), histo.literal_, p->literal_);
|
VP8LHistogramNumCodes(&histo), histo.literal_, m->literal_);
|
||||||
VP8LConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
VALUES_IN_BYTE, histo.red_, p->red_);
|
VALUES_IN_BYTE, histo.red_, m->red_);
|
||||||
VP8LConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
VALUES_IN_BYTE, histo.blue_, p->blue_);
|
VALUES_IN_BYTE, histo.blue_, m->blue_);
|
||||||
VP8LConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
VALUES_IN_BYTE, histo.alpha_, p->alpha_);
|
VALUES_IN_BYTE, histo.alpha_, m->alpha_);
|
||||||
VP8LConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
NUM_DISTANCE_CODES, histo.distance_, p->distance_);
|
NUM_DISTANCE_CODES, histo.distance_, m->distance_);
|
||||||
ok = 1;
|
ok = 1;
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
@ -393,30 +424,30 @@ static int CostModelBuild(CostModel* const p, int xsize, int ysize,
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WEBP_INLINE double GetLiteralCost(const CostModel* const p, uint32_t v) {
|
static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
|
||||||
return p->alpha_[v >> 24] +
|
return m->alpha_[v >> 24] +
|
||||||
p->red_[(v >> 16) & 0xff] +
|
m->red_[(v >> 16) & 0xff] +
|
||||||
p->literal_[(v >> 8) & 0xff] +
|
m->literal_[(v >> 8) & 0xff] +
|
||||||
p->blue_[v & 0xff];
|
m->blue_[v & 0xff];
|
||||||
}
|
}
|
||||||
|
|
||||||
static WEBP_INLINE double GetCacheCost(const CostModel* const p, uint32_t idx) {
|
static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
|
||||||
const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
|
const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
|
||||||
return p->literal_[literal_idx];
|
return m->literal_[literal_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
static WEBP_INLINE double GetLengthCost(const CostModel* const p,
|
static WEBP_INLINE double GetLengthCost(const CostModel* const m,
|
||||||
uint32_t length) {
|
uint32_t length) {
|
||||||
int code, extra_bits_count, extra_bits_value;
|
int code, extra_bits_count, extra_bits_value;
|
||||||
PrefixEncode(length, &code, &extra_bits_count, &extra_bits_value);
|
PrefixEncode(length, &code, &extra_bits_count, &extra_bits_value);
|
||||||
return p->literal_[VALUES_IN_BYTE + code] + extra_bits_count;
|
return m->literal_[VALUES_IN_BYTE + code] + extra_bits_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WEBP_INLINE double GetDistanceCost(const CostModel* const p,
|
static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
|
||||||
uint32_t distance) {
|
uint32_t distance) {
|
||||||
int code, extra_bits_count, extra_bits_value;
|
int code, extra_bits_count, extra_bits_value;
|
||||||
PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value);
|
PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value);
|
||||||
return p->distance_[code] + extra_bits_count;
|
return m->distance_[code] + extra_bits_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int BackwardReferencesHashChainDistanceOnly(
|
static int BackwardReferencesHashChainDistanceOnly(
|
||||||
@ -428,7 +459,8 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
const int quality = 100;
|
const int quality = 100;
|
||||||
const int pix_count = xsize * ysize;
|
const int pix_count = xsize * ysize;
|
||||||
const int use_color_cache = (cache_bits > 0);
|
const int use_color_cache = (cache_bits > 0);
|
||||||
double* const cost = (double*)malloc(pix_count * sizeof(*cost));
|
double* const cost =
|
||||||
|
(double*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost));
|
||||||
CostModel* cost_model = (CostModel*)malloc(sizeof(*cost_model));
|
CostModel* cost_model = (CostModel*)malloc(sizeof(*cost_model));
|
||||||
HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
|
HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
|
||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
@ -437,8 +469,12 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
|
|
||||||
if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error;
|
if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error;
|
||||||
|
|
||||||
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
if (!HashChainInit(hash_chain, pix_count)) goto Error;
|
||||||
if (!cc_init || !HashChainInit(hash_chain, pix_count)) goto Error;
|
|
||||||
|
if (use_color_cache) {
|
||||||
|
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
||||||
|
if (!cc_init) goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
|
if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
|
||||||
cache_bits)) {
|
cache_bits)) {
|
||||||
@ -486,14 +522,20 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
// Long copy for short distances, let's skip the middle
|
// Long copy for short distances, let's skip the middle
|
||||||
// lookups for better copies.
|
// lookups for better copies.
|
||||||
// 1) insert the hashes.
|
// 1) insert the hashes.
|
||||||
for (k = 0; k < len; ++k) {
|
if (use_color_cache) {
|
||||||
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
for (k = 0; k < len; ++k) {
|
||||||
if (i + k + 1 < pix_count) {
|
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
||||||
// Add to the hash_chain (but cannot add the last pixel).
|
}
|
||||||
|
}
|
||||||
|
// 2) Add to the hash_chain (but cannot add the last pixel)
|
||||||
|
{
|
||||||
|
const int last = (len < pix_count - 1 - i) ? len
|
||||||
|
: pix_count - 1 - i;
|
||||||
|
for (k = 0; k < last; ++k) {
|
||||||
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2) jump.
|
// 3) jump.
|
||||||
i += len - 1; // for loop does ++i, thus -1 here.
|
i += len - 1; // for loop does ++i, thus -1 here.
|
||||||
goto next_symbol;
|
goto next_symbol;
|
||||||
}
|
}
|
||||||
@ -515,7 +557,7 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
cost[i] = cost_val;
|
cost[i] = cost_val;
|
||||||
dist_array[i] = 1; // only one is inserted.
|
dist_array[i] = 1; // only one is inserted.
|
||||||
}
|
}
|
||||||
VP8LColorCacheInsert(&hashers, argb[i]);
|
if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
|
||||||
}
|
}
|
||||||
next_symbol: ;
|
next_symbol: ;
|
||||||
}
|
}
|
||||||
@ -545,7 +587,8 @@ static int TraceBackwards(const uint32_t* const dist_array,
|
|||||||
}
|
}
|
||||||
// Allocate.
|
// Allocate.
|
||||||
*chosen_path_size = count;
|
*chosen_path_size = count;
|
||||||
*chosen_path = (uint32_t*)malloc(count * sizeof(*chosen_path));
|
*chosen_path =
|
||||||
|
(uint32_t*)WebPSafeMalloc((uint64_t)count, sizeof(**chosen_path));
|
||||||
if (*chosen_path == NULL) return 0;
|
if (*chosen_path == NULL) return 0;
|
||||||
|
|
||||||
// Write in reverse order.
|
// Write in reverse order.
|
||||||
@ -574,11 +617,13 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
|||||||
HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
|
HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
|
||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
|
|
||||||
if (hash_chain == NULL ||
|
if (hash_chain == NULL || !HashChainInit(hash_chain, pix_count)) {
|
||||||
!(cc_init = VP8LColorCacheInit(&hashers, cache_bits)) ||
|
|
||||||
!HashChainInit(hash_chain, pix_count)) {
|
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
if (use_color_cache) {
|
||||||
|
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
||||||
|
if (!cc_init) goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
refs->size = 0;
|
refs->size = 0;
|
||||||
for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
|
for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
|
||||||
@ -590,10 +635,14 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
|||||||
i, xsize, argb, maxlen, &offset, &len);
|
i, xsize, argb, maxlen, &offset, &len);
|
||||||
assert(len == maxlen);
|
assert(len == maxlen);
|
||||||
refs->refs[size] = PixOrCopyCreateCopy(offset, len);
|
refs->refs[size] = PixOrCopyCreateCopy(offset, len);
|
||||||
for (k = 0; k < len; ++k) {
|
if (use_color_cache) {
|
||||||
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
for (k = 0; k < len; ++k) {
|
||||||
if (i + k + 1 < pix_count) {
|
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
||||||
// Add to the hash_chain (but cannot add the last pixel).
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
|
||||||
|
for (k = 0; k < last; ++k) {
|
||||||
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -606,7 +655,7 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
|||||||
} else {
|
} else {
|
||||||
refs->refs[size] = PixOrCopyCreateLiteral(argb[i]);
|
refs->refs[size] = PixOrCopyCreateLiteral(argb[i]);
|
||||||
}
|
}
|
||||||
VP8LColorCacheInsert(&hashers, argb[i]);
|
if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
|
||||||
if (i + 1 < pix_count) {
|
if (i + 1 < pix_count) {
|
||||||
HashChainInsert(hash_chain, &argb[i], i);
|
HashChainInsert(hash_chain, &argb[i], i);
|
||||||
}
|
}
|
||||||
@ -633,7 +682,7 @@ static int BackwardReferencesTraceBackwards(int xsize, int ysize,
|
|||||||
uint32_t* chosen_path = NULL;
|
uint32_t* chosen_path = NULL;
|
||||||
int chosen_path_size = 0;
|
int chosen_path_size = 0;
|
||||||
uint32_t* dist_array =
|
uint32_t* dist_array =
|
||||||
(uint32_t*)malloc(dist_array_size * sizeof(*dist_array));
|
(uint32_t*)WebPSafeMalloc((uint64_t)dist_array_size, sizeof(*dist_array));
|
||||||
|
|
||||||
if (dist_array == NULL) goto Error;
|
if (dist_array == NULL) goto Error;
|
||||||
|
|
||||||
@ -755,13 +804,18 @@ static int ComputeCacheHistogram(const uint32_t* const argb,
|
|||||||
int i;
|
int i;
|
||||||
uint32_t k;
|
uint32_t k;
|
||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
|
const int use_color_cache = (cache_bits > 0);
|
||||||
|
int cc_init = 0;
|
||||||
|
|
||||||
if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0;
|
if (use_color_cache) {
|
||||||
|
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
||||||
|
if (!cc_init) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < refs->size; ++i) {
|
for (i = 0; i < refs->size; ++i) {
|
||||||
const PixOrCopy* const v = &refs->refs[i];
|
const PixOrCopy* const v = &refs->refs[i];
|
||||||
if (PixOrCopyIsLiteral(v)) {
|
if (PixOrCopyIsLiteral(v)) {
|
||||||
if (cache_bits != 0 &&
|
if (use_color_cache &&
|
||||||
VP8LColorCacheContains(&hashers, argb[pixel_index])) {
|
VP8LColorCacheContains(&hashers, argb[pixel_index])) {
|
||||||
// push pixel as a cache index
|
// push pixel as a cache index
|
||||||
const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
|
const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
|
||||||
@ -773,15 +827,17 @@ static int ComputeCacheHistogram(const uint32_t* const argb,
|
|||||||
} else {
|
} else {
|
||||||
VP8LHistogramAddSinglePixOrCopy(histo, v);
|
VP8LHistogramAddSinglePixOrCopy(histo, v);
|
||||||
}
|
}
|
||||||
for (k = 0; k < PixOrCopyLength(v); ++k) {
|
if (use_color_cache) {
|
||||||
VP8LColorCacheInsert(&hashers, argb[pixel_index]);
|
for (k = 0; k < PixOrCopyLength(v); ++k) {
|
||||||
++pixel_index;
|
VP8LColorCacheInsert(&hashers, argb[pixel_index + k]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pixel_index += PixOrCopyLength(v);
|
||||||
}
|
}
|
||||||
assert(pixel_index == xsize * ysize);
|
assert(pixel_index == xsize * ysize);
|
||||||
(void)xsize; // xsize is not used in non-debug compilations otherwise.
|
(void)xsize; // xsize is not used in non-debug compilations otherwise.
|
||||||
(void)ysize; // ysize is not used in non-debug compilations otherwise.
|
(void)ysize; // ysize is not used in non-debug compilations otherwise.
|
||||||
VP8LColorCacheClear(&hashers);
|
if (cc_init) VP8LColorCacheClear(&hashers);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ int WebPValidateConfig(const WebPConfig* config) {
|
|||||||
return 0;
|
return 0;
|
||||||
if (config->lossless < 0 || config->lossless > 1)
|
if (config->lossless < 0 || config->lossless > 1)
|
||||||
return 0;
|
return 0;
|
||||||
if (config->image_hint > WEBP_HINT_PHOTO)
|
if (config->image_hint >= WEBP_HINT_LAST)
|
||||||
return 0;
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,7 @@
|
|||||||
#include "./backward_references.h"
|
#include "./backward_references.h"
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
#include "../dsp/lossless.h"
|
#include "../dsp/lossless.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
#if defined(_MSC_VER) && !defined(NOT_HAVE_LOG2)
|
|
||||||
# define NOT_HAVE_LOG2 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef NOT_HAVE_LOG2
|
|
||||||
static WEBP_INLINE double log2(double d) {
|
|
||||||
const double kLog2Reciprocal = 1.442695040888963;
|
|
||||||
return log(d) * kLog2Reciprocal;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void HistogramClear(VP8LHistogram* const p) {
|
static void HistogramClear(VP8LHistogram* const p) {
|
||||||
memset(p->literal_, 0, sizeof(p->literal_));
|
memset(p->literal_, 0, sizeof(p->literal_));
|
||||||
@ -65,10 +55,10 @@ VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
|
|||||||
int i;
|
int i;
|
||||||
VP8LHistogramSet* set;
|
VP8LHistogramSet* set;
|
||||||
VP8LHistogram* bulk;
|
VP8LHistogram* bulk;
|
||||||
const size_t total_size = sizeof(*set)
|
const uint64_t total_size = (uint64_t)sizeof(*set)
|
||||||
+ size * sizeof(*set->histograms)
|
+ size * sizeof(*set->histograms)
|
||||||
+ size * sizeof(**set->histograms);
|
+ size * sizeof(**set->histograms);
|
||||||
uint8_t* memory = (uint8_t*)malloc(total_size);
|
uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
|
||||||
if (memory == NULL) return NULL;
|
if (memory == NULL) return NULL;
|
||||||
|
|
||||||
set = (VP8LHistogramSet*)memory;
|
set = (VP8LHistogramSet*)memory;
|
||||||
@ -87,33 +77,6 @@ VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
void VP8LConvertPopulationCountTableToBitEstimates(
|
|
||||||
int num_symbols, const int population_counts[], double output[]) {
|
|
||||||
int sum = 0;
|
|
||||||
int nonzeros = 0;
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < num_symbols; ++i) {
|
|
||||||
sum += population_counts[i];
|
|
||||||
if (population_counts[i] > 0) {
|
|
||||||
++nonzeros;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nonzeros <= 1) {
|
|
||||||
memset(output, 0, num_symbols * sizeof(*output));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const double log2sum = log2(sum);
|
|
||||||
for (i = 0; i < num_symbols; ++i) {
|
|
||||||
if (population_counts[i] == 0) {
|
|
||||||
output[i] = log2sum;
|
|
||||||
} else {
|
|
||||||
output[i] = log2sum - log2(population_counts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
|
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
|
||||||
const PixOrCopy* const v) {
|
const PixOrCopy* const v) {
|
||||||
if (PixOrCopyIsLiteral(v)) {
|
if (PixOrCopyIsLiteral(v)) {
|
||||||
@ -138,7 +101,7 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
|
|||||||
|
|
||||||
|
|
||||||
static double BitsEntropy(const int* const array, int n) {
|
static double BitsEntropy(const int* const array, int n) {
|
||||||
double retval = 0;
|
double retval = 0.;
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
int nonzeros = 0;
|
int nonzeros = 0;
|
||||||
int max_val = 0;
|
int max_val = 0;
|
||||||
@ -148,15 +111,14 @@ static double BitsEntropy(const int* const array, int n) {
|
|||||||
if (array[i] != 0) {
|
if (array[i] != 0) {
|
||||||
sum += array[i];
|
sum += array[i];
|
||||||
++nonzeros;
|
++nonzeros;
|
||||||
retval += array[i] * VP8LFastLog(array[i]);
|
retval -= VP8LFastSLog2(array[i]);
|
||||||
if (max_val < array[i]) {
|
if (max_val < array[i]) {
|
||||||
max_val = array[i];
|
max_val = array[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retval -= sum * VP8LFastLog(sum);
|
retval += VP8LFastSLog2(sum);
|
||||||
retval *= -1.4426950408889634; // 1.0 / -Log(2);
|
|
||||||
mix = 0.627;
|
|
||||||
if (nonzeros < 5) {
|
if (nonzeros < 5) {
|
||||||
if (nonzeros <= 1) {
|
if (nonzeros <= 1) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -176,15 +138,15 @@ static double BitsEntropy(const int* const array, int n) {
|
|||||||
} else {
|
} else {
|
||||||
mix = 0.7; // nonzeros == 4.
|
mix = 0.7; // nonzeros == 4.
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mix = 0.627;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
double min_limit = 2 * sum - max_val;
|
double min_limit = 2 * sum - max_val;
|
||||||
min_limit = mix * min_limit + (1.0 - mix) * retval;
|
min_limit = mix * min_limit + (1.0 - mix) * retval;
|
||||||
if (retval < min_limit) {
|
return (retval < min_limit) ? min_limit : retval;
|
||||||
return min_limit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
|
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
|
||||||
|
@ -101,9 +101,6 @@ static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) {
|
|||||||
((p->palette_code_bits_ > 0) ? (1 << p->palette_code_bits_) : 0);
|
((p->palette_code_bits_ > 0) ? (1 << p->palette_code_bits_) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VP8LConvertPopulationCountTableToBitEstimates(
|
|
||||||
int num_symbols, const int population_counts[], double output[]);
|
|
||||||
|
|
||||||
// Builds the histogram image.
|
// Builds the histogram image.
|
||||||
int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
||||||
const VP8LBackwardRefs* const refs,
|
const VP8LBackwardRefs* const refs,
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
|
|
||||||
#include "./vp8enci.h"
|
#include "./vp8enci.h"
|
||||||
#include "../utils/rescaler.h"
|
#include "../utils/rescaler.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
#include "../dsp/dsp.h"
|
#include "../dsp/dsp.h"
|
||||||
|
#include "../dsp/yuv.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -81,14 +83,12 @@ int WebPPictureAlloc(WebPPicture* picture) {
|
|||||||
|
|
||||||
// Security and validation checks
|
// Security and validation checks
|
||||||
if (width <= 0 || height <= 0 || // luma/alpha param error
|
if (width <= 0 || height <= 0 || // luma/alpha param error
|
||||||
uv_width < 0 || uv_height < 0 || // u/v param error
|
uv_width < 0 || uv_height < 0) { // u/v param error
|
||||||
y_size >= (1ULL << 40) || // reasonable global size
|
|
||||||
(size_t)total_size != total_size) { // overflow on 32bit
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Clear previous buffer and allocate a new one.
|
// Clear previous buffer and allocate a new one.
|
||||||
WebPPictureFree(picture); // erase previous buffer
|
WebPPictureFree(picture); // erase previous buffer
|
||||||
mem = (uint8_t*)malloc((size_t)total_size);
|
mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem));
|
||||||
if (mem == NULL) return 0;
|
if (mem == NULL) return 0;
|
||||||
|
|
||||||
// From now on, we're in the clear, we can no longer fail...
|
// From now on, we're in the clear, we can no longer fail...
|
||||||
@ -119,15 +119,12 @@ int WebPPictureAlloc(WebPPicture* picture) {
|
|||||||
} else {
|
} else {
|
||||||
void* memory;
|
void* memory;
|
||||||
const uint64_t argb_size = (uint64_t)width * height;
|
const uint64_t argb_size = (uint64_t)width * height;
|
||||||
const uint64_t total_size = argb_size * sizeof(*picture->argb);
|
if (width <= 0 || height <= 0) {
|
||||||
if (width <= 0 || height <= 0 ||
|
|
||||||
argb_size >= (1ULL << 40) ||
|
|
||||||
(size_t)total_size != total_size) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Clear previous buffer and allocate a new one.
|
// Clear previous buffer and allocate a new one.
|
||||||
WebPPictureFree(picture); // erase previous buffer
|
WebPPictureFree(picture); // erase previous buffer
|
||||||
memory = malloc((size_t)total_size);
|
memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb));
|
||||||
if (memory == NULL) return 0;
|
if (memory == NULL) return 0;
|
||||||
|
|
||||||
// TODO(skal): align plane to cache line?
|
// TODO(skal): align plane to cache line?
|
||||||
@ -416,7 +413,7 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) {
|
|||||||
if (!WebPPictureAlloc(&tmp)) return 0;
|
if (!WebPPictureAlloc(&tmp)) return 0;
|
||||||
|
|
||||||
if (!pic->use_argb) {
|
if (!pic->use_argb) {
|
||||||
work = (int32_t*)malloc(2 * width * sizeof(*work));
|
work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
|
||||||
if (work == NULL) {
|
if (work == NULL) {
|
||||||
WebPPictureFree(&tmp);
|
WebPPictureFree(&tmp);
|
||||||
return 0;
|
return 0;
|
||||||
@ -449,7 +446,7 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
work = (int32_t*)malloc(2 * width * 4 * sizeof(*work));
|
work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
|
||||||
if (work == NULL) {
|
if (work == NULL) {
|
||||||
WebPPictureFree(&tmp);
|
WebPPictureFree(&tmp);
|
||||||
return 0;
|
return 0;
|
||||||
@ -480,17 +477,17 @@ void WebPMemoryWriterInit(WebPMemoryWriter* writer) {
|
|||||||
int WebPMemoryWrite(const uint8_t* data, size_t data_size,
|
int WebPMemoryWrite(const uint8_t* data, size_t data_size,
|
||||||
const WebPPicture* picture) {
|
const WebPPicture* picture) {
|
||||||
WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
|
WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
|
||||||
size_t next_size;
|
uint64_t next_size;
|
||||||
if (w == NULL) {
|
if (w == NULL) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
next_size = w->size + data_size;
|
next_size = (uint64_t)w->size + data_size;
|
||||||
if (next_size > w->max_size) {
|
if (next_size > w->max_size) {
|
||||||
uint8_t* new_mem;
|
uint8_t* new_mem;
|
||||||
size_t next_max_size = w->max_size * 2;
|
uint64_t next_max_size = 2ULL * w->max_size;
|
||||||
if (next_max_size < next_size) next_max_size = next_size;
|
if (next_max_size < next_size) next_max_size = next_size;
|
||||||
if (next_max_size < 8192) next_max_size = 8192;
|
if (next_max_size < 8192ULL) next_max_size = 8192ULL;
|
||||||
new_mem = (uint8_t*)malloc(next_max_size);
|
new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1);
|
||||||
if (new_mem == NULL) {
|
if (new_mem == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -499,7 +496,8 @@ int WebPMemoryWrite(const uint8_t* data, size_t data_size,
|
|||||||
}
|
}
|
||||||
free(w->mem);
|
free(w->mem);
|
||||||
w->mem = new_mem;
|
w->mem = new_mem;
|
||||||
w->max_size = next_max_size;
|
// down-cast is ok, thanks to WebPSafeMalloc
|
||||||
|
w->max_size = (size_t)next_max_size;
|
||||||
}
|
}
|
||||||
if (data_size > 0) {
|
if (data_size > 0) {
|
||||||
memcpy(w->mem + w->size, data, data_size);
|
memcpy(w->mem + w->size, data, data_size);
|
||||||
@ -547,33 +545,6 @@ int WebPPictureHasTransparency(const WebPPicture* picture) {
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// RGB -> YUV conversion
|
// RGB -> YUV conversion
|
||||||
// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
|
|
||||||
// More information at: http://en.wikipedia.org/wiki/YCbCr
|
|
||||||
// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
|
|
||||||
// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
|
|
||||||
// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
|
|
||||||
// We use 16bit fixed point operations.
|
|
||||||
|
|
||||||
enum { YUV_FRAC = 16 };
|
|
||||||
|
|
||||||
static WEBP_INLINE int clip_uv(int v) {
|
|
||||||
v = (v + (257 << (YUV_FRAC + 2 - 1))) >> (YUV_FRAC + 2);
|
|
||||||
return ((v & ~0xff) == 0) ? v : (v < 0) ? 0 : 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE int rgb_to_y(int r, int g, int b) {
|
|
||||||
const int kRound = (1 << (YUV_FRAC - 1)) + (16 << YUV_FRAC);
|
|
||||||
const int luma = 16839 * r + 33059 * g + 6420 * b;
|
|
||||||
return (luma + kRound) >> YUV_FRAC; // no need to clip
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE int rgb_to_u(int r, int g, int b) {
|
|
||||||
return clip_uv(-9719 * r - 19081 * g + 28800 * b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE int rgb_to_v(int r, int g, int b) {
|
|
||||||
return clip_uv(+28800 * r - 24116 * g - 4684 * b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we can do better than simply 2x2 averaging on U/V samples.
|
// TODO: we can do better than simply 2x2 averaging on U/V samples.
|
||||||
#define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \
|
#define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \
|
||||||
@ -587,8 +558,8 @@ static WEBP_INLINE int rgb_to_v(int r, int g, int b) {
|
|||||||
const int r = SUM(r_ptr + src); \
|
const int r = SUM(r_ptr + src); \
|
||||||
const int g = SUM(g_ptr + src); \
|
const int g = SUM(g_ptr + src); \
|
||||||
const int b = SUM(b_ptr + src); \
|
const int b = SUM(b_ptr + src); \
|
||||||
picture->u[dst] = rgb_to_u(r, g, b); \
|
picture->u[dst] = VP8RGBToU(r, g, b); \
|
||||||
picture->v[dst] = rgb_to_v(r, g, b); \
|
picture->v[dst] = VP8RGBToV(r, g, b); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RGB_TO_UV0(x_in, x_out, y, SUM) { \
|
#define RGB_TO_UV0(x_in, x_out, y, SUM) { \
|
||||||
@ -597,8 +568,8 @@ static WEBP_INLINE int rgb_to_v(int r, int g, int b) {
|
|||||||
const int r = SUM(r_ptr + src); \
|
const int r = SUM(r_ptr + src); \
|
||||||
const int g = SUM(g_ptr + src); \
|
const int g = SUM(g_ptr + src); \
|
||||||
const int b = SUM(b_ptr + src); \
|
const int b = SUM(b_ptr + src); \
|
||||||
picture->u0[dst] = rgb_to_u(r, g, b); \
|
picture->u0[dst] = VP8RGBToU(r, g, b); \
|
||||||
picture->v0[dst] = rgb_to_v(r, g, b); \
|
picture->v0[dst] = VP8RGBToV(r, g, b); \
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MakeGray(WebPPicture* const picture) {
|
static void MakeGray(WebPPicture* const picture) {
|
||||||
@ -636,7 +607,7 @@ static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
|
|||||||
for (x = 0; x < width; ++x) {
|
for (x = 0; x < width; ++x) {
|
||||||
const int offset = step * x + y * rgb_stride;
|
const int offset = step * x + y * rgb_stride;
|
||||||
picture->y[x + y * picture->y_stride] =
|
picture->y[x + y * picture->y_stride] =
|
||||||
rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
|
VP8RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -646,7 +617,7 @@ static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
|
|||||||
for (x = 0; x < (width >> 1); ++x) {
|
for (x = 0; x < (width >> 1); ++x) {
|
||||||
RGB_TO_UV(x, y, SUM4);
|
RGB_TO_UV(x, y, SUM4);
|
||||||
}
|
}
|
||||||
if (picture->width & 1) {
|
if (width & 1) {
|
||||||
RGB_TO_UV(x, y, SUM2V);
|
RGB_TO_UV(x, y, SUM2V);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ extern "C" {
|
|||||||
|
|
||||||
// version numbers
|
// version numbers
|
||||||
#define ENC_MAJ_VERSION 0
|
#define ENC_MAJ_VERSION 0
|
||||||
#define ENC_MIN_VERSION 1
|
#define ENC_MIN_VERSION 2
|
||||||
#define ENC_REV_VERSION 99
|
#define ENC_REV_VERSION 0
|
||||||
|
|
||||||
// size of histogram used by CollectHistogram.
|
// size of histogram used by CollectHistogram.
|
||||||
#define MAX_COEFF_THRESH 64
|
#define MAX_COEFF_THRESH 64
|
||||||
@ -402,7 +402,7 @@ struct VP8Encoder {
|
|||||||
|
|
||||||
// probabilities and statistics
|
// probabilities and statistics
|
||||||
VP8Proba proba_;
|
VP8Proba proba_;
|
||||||
uint64_t sse_[3]; // sum of Y/U/V squared errors for all macroblocks
|
uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks
|
||||||
uint64_t sse_count_; // pixel count for the sse_[] stats
|
uint64_t sse_count_; // pixel count for the sse_[] stats
|
||||||
int coded_size_;
|
int coded_size_;
|
||||||
int residual_bytes_[3][4];
|
int residual_bytes_[3][4];
|
||||||
@ -488,9 +488,9 @@ void VP8SetSegmentParams(VP8Encoder* const enc, float quality);
|
|||||||
int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt);
|
int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt);
|
||||||
|
|
||||||
// in alpha.c
|
// in alpha.c
|
||||||
void VP8EncInitAlpha(VP8Encoder* enc); // initialize alpha compression
|
void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression
|
||||||
int VP8EncFinishAlpha(VP8Encoder* enc); // finalize compressed data
|
int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data
|
||||||
void VP8EncDeleteAlpha(VP8Encoder* enc); // delete compressed data
|
void VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data
|
||||||
|
|
||||||
// in layer.c
|
// in layer.c
|
||||||
void VP8EncInitLayer(VP8Encoder* const enc); // init everything
|
void VP8EncInitLayer(VP8Encoder* const enc); // init everything
|
||||||
|
157
src/enc/vp8l.c
157
src/enc/vp8l.c
@ -20,6 +20,7 @@
|
|||||||
#include "../dsp/lossless.h"
|
#include "../dsp/lossless.h"
|
||||||
#include "../utils/bit_writer.h"
|
#include "../utils/bit_writer.h"
|
||||||
#include "../utils/huffman_encode.h"
|
#include "../utils/huffman_encode.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
#include "../webp/format_constants.h"
|
#include "../webp/format_constants.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
@ -28,6 +29,7 @@ extern "C" {
|
|||||||
|
|
||||||
#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
|
#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
|
||||||
#define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024)
|
#define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024)
|
||||||
|
#define MAX_COLORS_FOR_GRAPH 64
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Palette
|
// Palette
|
||||||
@ -97,23 +99,24 @@ static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int AnalyzeEntropy(const WebPPicture* const pic,
|
static int AnalyzeEntropy(const uint32_t* argb,
|
||||||
|
int width, int height, int argb_stride,
|
||||||
double* const nonpredicted_bits,
|
double* const nonpredicted_bits,
|
||||||
double* const predicted_bits) {
|
double* const predicted_bits) {
|
||||||
int x, y;
|
int x, y;
|
||||||
const uint32_t* argb = pic->argb;
|
|
||||||
const uint32_t* last_line = NULL;
|
const uint32_t* last_line = NULL;
|
||||||
uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
|
uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
|
||||||
|
|
||||||
VP8LHistogram* nonpredicted = NULL;
|
VP8LHistogram* nonpredicted = NULL;
|
||||||
VP8LHistogram* predicted = (VP8LHistogram*)malloc(2 * sizeof(*predicted));
|
VP8LHistogram* predicted =
|
||||||
|
(VP8LHistogram*)malloc(2 * sizeof(*predicted));
|
||||||
if (predicted == NULL) return 0;
|
if (predicted == NULL) return 0;
|
||||||
nonpredicted = predicted + 1;
|
nonpredicted = predicted + 1;
|
||||||
|
|
||||||
VP8LHistogramInit(predicted, 0);
|
VP8LHistogramInit(predicted, 0);
|
||||||
VP8LHistogramInit(nonpredicted, 0);
|
VP8LHistogramInit(nonpredicted, 0);
|
||||||
for (y = 0; y < pic->height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
for (x = 0; x < pic->width; ++x) {
|
for (x = 0; x < width; ++x) {
|
||||||
const uint32_t pix = argb[x];
|
const uint32_t pix = argb[x];
|
||||||
const uint32_t pix_diff = VP8LSubPixels(pix, last_pix);
|
const uint32_t pix_diff = VP8LSubPixels(pix, last_pix);
|
||||||
if (pix_diff == 0) continue;
|
if (pix_diff == 0) continue;
|
||||||
@ -129,7 +132,7 @@ static int AnalyzeEntropy(const WebPPicture* const pic,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_line = argb;
|
last_line = argb;
|
||||||
argb += pic->argb_stride;
|
argb += argb_stride;
|
||||||
}
|
}
|
||||||
*nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted);
|
*nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted);
|
||||||
*predicted_bits = VP8LHistogramEstimateBitsBulk(predicted);
|
*predicted_bits = VP8LHistogramEstimateBitsBulk(predicted);
|
||||||
@ -143,32 +146,42 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) {
|
|||||||
|
|
||||||
enc->use_palette_ =
|
enc->use_palette_ =
|
||||||
AnalyzeAndCreatePalette(pic, enc->palette_, &enc->palette_size_);
|
AnalyzeAndCreatePalette(pic, enc->palette_, &enc->palette_size_);
|
||||||
if (!enc->use_palette_) {
|
|
||||||
if (image_hint == WEBP_HINT_DEFAULT) {
|
|
||||||
double non_pred_entropy, pred_entropy;
|
|
||||||
if (!AnalyzeEntropy(pic, &non_pred_entropy, &pred_entropy)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pred_entropy < 0.95 * non_pred_entropy) {
|
if (image_hint == WEBP_HINT_GRAPH) {
|
||||||
enc->use_predict_ = 1;
|
if (enc->use_palette_ && enc->palette_size_ < MAX_COLORS_FOR_GRAPH) {
|
||||||
enc->use_cross_color_ = 1;
|
enc->use_palette_ = 0;
|
||||||
}
|
|
||||||
} else if (image_hint == WEBP_HINT_PHOTO) {
|
|
||||||
enc->use_predict_ = 1;
|
|
||||||
enc->use_cross_color_ = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!enc->use_palette_) {
|
||||||
|
if (image_hint == WEBP_HINT_PHOTO) {
|
||||||
|
enc->use_predict_ = 1;
|
||||||
|
enc->use_cross_color_ = 1;
|
||||||
|
} else {
|
||||||
|
double non_pred_entropy, pred_entropy;
|
||||||
|
if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, pic->argb_stride,
|
||||||
|
&non_pred_entropy, &pred_entropy)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (pred_entropy < 0.95 * non_pred_entropy) {
|
||||||
|
enc->use_predict_ = 1;
|
||||||
|
// TODO(vikasa): Observed some correlation of cross_color transform with
|
||||||
|
// predict. Need to investigate this further and add separate heuristic
|
||||||
|
// for setting use_cross_color flag.
|
||||||
|
enc->use_cross_color_ = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int GetHuffBitLengthsAndCodes(
|
static int GetHuffBitLengthsAndCodes(
|
||||||
const VP8LHistogramSet* const histogram_image,
|
const VP8LHistogramSet* const histogram_image,
|
||||||
HuffmanTreeCode* const huffman_codes) {
|
HuffmanTreeCode* const huffman_codes) {
|
||||||
int i, k;
|
int i, k;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
int total_length_size = 0;
|
uint64_t total_length_size = 0;
|
||||||
uint8_t* mem_buf = NULL;
|
uint8_t* mem_buf = NULL;
|
||||||
const int histogram_image_size = histogram_image->size;
|
const int histogram_image_size = histogram_image->size;
|
||||||
|
|
||||||
@ -189,9 +202,8 @@ static int GetHuffBitLengthsAndCodes(
|
|||||||
{
|
{
|
||||||
uint16_t* codes;
|
uint16_t* codes;
|
||||||
uint8_t* lengths;
|
uint8_t* lengths;
|
||||||
const size_t total_buf_size = total_length_size * sizeof(*lengths)
|
mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
|
||||||
+ total_length_size * sizeof(*codes);
|
sizeof(*lengths) + sizeof(*codes));
|
||||||
mem_buf = (uint8_t*)calloc(total_buf_size, 1);
|
|
||||||
if (mem_buf == NULL) {
|
if (mem_buf == NULL) {
|
||||||
ok = 0;
|
ok = 0;
|
||||||
goto End;
|
goto End;
|
||||||
@ -293,7 +305,7 @@ static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
int num_tokens;
|
int num_tokens;
|
||||||
HuffmanTreeCode huffman_code;
|
HuffmanTreeCode huffman_code;
|
||||||
HuffmanTreeToken* const tokens =
|
HuffmanTreeToken* const tokens =
|
||||||
(HuffmanTreeToken*)malloc(max_tokens * sizeof(*tokens));
|
(HuffmanTreeToken*)WebPSafeMalloc((uint64_t)max_tokens, sizeof(*tokens));
|
||||||
if (tokens == NULL) return 0;
|
if (tokens == NULL) return 0;
|
||||||
|
|
||||||
huffman_code.num_symbols = CODE_LENGTH_CODES;
|
huffman_code.num_symbols = CODE_LENGTH_CODES;
|
||||||
@ -500,21 +512,21 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
const uint32_t* const argb,
|
const uint32_t* const argb,
|
||||||
int width, int height, int quality,
|
int width, int height, int quality,
|
||||||
int cache_bits, int histogram_bits) {
|
int cache_bits, int histogram_bits) {
|
||||||
int i;
|
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
const int use_2d_locality = 1;
|
const int use_2d_locality = 1;
|
||||||
const int use_color_cache = (cache_bits > 0);
|
const int use_color_cache = (cache_bits > 0);
|
||||||
const int histogram_image_xysize =
|
const uint32_t histogram_image_xysize =
|
||||||
VP8LSubSampleSize(width, histogram_bits) *
|
VP8LSubSampleSize(width, histogram_bits) *
|
||||||
VP8LSubSampleSize(height, histogram_bits);
|
VP8LSubSampleSize(height, histogram_bits);
|
||||||
VP8LHistogramSet* histogram_image =
|
VP8LHistogramSet* histogram_image =
|
||||||
VP8LAllocateHistogramSet(histogram_image_xysize, 0);
|
VP8LAllocateHistogramSet(histogram_image_xysize, 0);
|
||||||
int histogram_image_size = 0;
|
int histogram_image_size = 0;
|
||||||
int bit_array_size = 0;
|
size_t bit_array_size = 0;
|
||||||
HuffmanTreeCode* huffman_codes = NULL;
|
HuffmanTreeCode* huffman_codes = NULL;
|
||||||
VP8LBackwardRefs refs;
|
VP8LBackwardRefs refs;
|
||||||
uint16_t* const histogram_symbols =
|
uint16_t* const histogram_symbols =
|
||||||
(uint16_t*)malloc(histogram_image_xysize * sizeof(*histogram_symbols));
|
(uint16_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
|
||||||
|
sizeof(*histogram_symbols));
|
||||||
assert(histogram_bits >= MIN_HUFFMAN_BITS);
|
assert(histogram_bits >= MIN_HUFFMAN_BITS);
|
||||||
assert(histogram_bits <= MAX_HUFFMAN_BITS);
|
assert(histogram_bits <= MAX_HUFFMAN_BITS);
|
||||||
if (histogram_image == NULL || histogram_symbols == NULL) goto Error;
|
if (histogram_image == NULL || histogram_symbols == NULL) goto Error;
|
||||||
@ -534,8 +546,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
// Create Huffman bit lengths and codes for each histogram image.
|
// Create Huffman bit lengths and codes for each histogram image.
|
||||||
histogram_image_size = histogram_image->size;
|
histogram_image_size = histogram_image->size;
|
||||||
bit_array_size = 5 * histogram_image_size;
|
bit_array_size = 5 * histogram_image_size;
|
||||||
huffman_codes = (HuffmanTreeCode*)calloc(bit_array_size,
|
huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
|
||||||
sizeof(*huffman_codes));
|
sizeof(*huffman_codes));
|
||||||
if (huffman_codes == NULL ||
|
if (huffman_codes == NULL ||
|
||||||
!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
|
!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
@ -553,8 +565,10 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
VP8LWriteBits(bw, 1, write_histogram_image);
|
VP8LWriteBits(bw, 1, write_histogram_image);
|
||||||
if (write_histogram_image) {
|
if (write_histogram_image) {
|
||||||
uint32_t* const histogram_argb =
|
uint32_t* const histogram_argb =
|
||||||
(uint32_t*)malloc(histogram_image_xysize * sizeof(*histogram_argb));
|
(uint32_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
|
||||||
|
sizeof(*histogram_argb));
|
||||||
int max_index = 0;
|
int max_index = 0;
|
||||||
|
uint32_t i;
|
||||||
if (histogram_argb == NULL) goto Error;
|
if (histogram_argb == NULL) goto Error;
|
||||||
for (i = 0; i < histogram_image_xysize; ++i) {
|
for (i = 0; i < histogram_image_xysize; ++i) {
|
||||||
const int index = histogram_symbols[i] & 0xffff;
|
const int index = histogram_symbols[i] & 0xffff;
|
||||||
@ -576,14 +590,14 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store Huffman codes.
|
// Store Huffman codes.
|
||||||
for (i = 0; i < 5 * histogram_image_size; ++i) {
|
{
|
||||||
HuffmanTreeCode* const codes = &huffman_codes[i];
|
int i;
|
||||||
if (!StoreHuffmanCode(bw, codes)) {
|
for (i = 0; i < 5 * histogram_image_size; ++i) {
|
||||||
goto Error;
|
HuffmanTreeCode* const codes = &huffman_codes[i];
|
||||||
|
if (!StoreHuffmanCode(bw, codes)) goto Error;
|
||||||
|
ClearHuffmanTreeIfOnlyOneSymbol(codes);
|
||||||
}
|
}
|
||||||
ClearHuffmanTreeIfOnlyOneSymbol(codes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free combined histograms.
|
// Free combined histograms.
|
||||||
free(histogram_image);
|
free(histogram_image);
|
||||||
histogram_image = NULL;
|
histogram_image = NULL;
|
||||||
@ -610,7 +624,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
|
|
||||||
// Check if it would be a good idea to subtract green from red and blue. We
|
// Check if it would be a good idea to subtract green from red and blue. We
|
||||||
// only impact entropy in red/blue components, don't bother to look at others.
|
// only impact entropy in red/blue components, don't bother to look at others.
|
||||||
static int EvalAndApplySubtractGreen(const VP8LEncoder* const enc,
|
static int EvalAndApplySubtractGreen(VP8LEncoder* const enc,
|
||||||
int width, int height,
|
int width, int height,
|
||||||
VP8LBitWriter* const bw) {
|
VP8LBitWriter* const bw) {
|
||||||
if (!enc->use_palette_) {
|
if (!enc->use_palette_) {
|
||||||
@ -639,7 +653,8 @@ static int EvalAndApplySubtractGreen(const VP8LEncoder* const enc,
|
|||||||
free(histo);
|
free(histo);
|
||||||
|
|
||||||
// Check if subtracting green yields low entropy.
|
// Check if subtracting green yields low entropy.
|
||||||
if (bit_cost_after < bit_cost_before) {
|
enc->use_subtract_green_ = (bit_cost_after < bit_cost_before);
|
||||||
|
if (enc->use_subtract_green_) {
|
||||||
VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
|
VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
|
||||||
VP8LWriteBits(bw, 2, SUBTRACT_GREEN);
|
VP8LWriteBits(bw, 2, SUBTRACT_GREEN);
|
||||||
VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
|
VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
|
||||||
@ -754,7 +769,7 @@ static WebPEncodingError WriteImage(const WebPPicture* const pic,
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*coded_size = vp8l_size;
|
*coded_size = CHUNK_HEADER_SIZE + riff_size;
|
||||||
return VP8_ENC_OK;
|
return VP8_ENC_OK;
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
@ -769,14 +784,14 @@ static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
|
|||||||
int width, int height) {
|
int width, int height) {
|
||||||
WebPEncodingError err = VP8_ENC_OK;
|
WebPEncodingError err = VP8_ENC_OK;
|
||||||
const int tile_size = 1 << enc->transform_bits_;
|
const int tile_size = 1 << enc->transform_bits_;
|
||||||
const size_t image_size = width * height;
|
const uint64_t image_size = width * height;
|
||||||
const size_t argb_scratch_size = tile_size * width + width;
|
const uint64_t argb_scratch_size = tile_size * width + width;
|
||||||
const size_t transform_data_size =
|
const uint64_t transform_data_size =
|
||||||
VP8LSubSampleSize(width, enc->transform_bits_) *
|
(uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) *
|
||||||
VP8LSubSampleSize(height, enc->transform_bits_);
|
(uint64_t)VP8LSubSampleSize(height, enc->transform_bits_);
|
||||||
const size_t total_size =
|
const uint64_t total_size =
|
||||||
image_size + argb_scratch_size + transform_data_size;
|
image_size + argb_scratch_size + transform_data_size;
|
||||||
uint32_t* mem = (uint32_t*)malloc(total_size * sizeof(*mem));
|
uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem));
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
@ -938,6 +953,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
const int width = picture->width;
|
const int width = picture->width;
|
||||||
const int height = picture->height;
|
const int height = picture->height;
|
||||||
VP8LEncoder* const enc = VP8LEncoderNew(config, picture);
|
VP8LEncoder* const enc = VP8LEncoderNew(config, picture);
|
||||||
|
const size_t byte_position = VP8LBitWriterNumBytes(bw);
|
||||||
|
|
||||||
if (enc == NULL) {
|
if (enc == NULL) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
@ -957,6 +973,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
if (enc->use_palette_) {
|
if (enc->use_palette_) {
|
||||||
err = ApplyPalette(bw, enc, quality);
|
err = ApplyPalette(bw, enc, quality);
|
||||||
if (err != VP8_ENC_OK) goto Error;
|
if (err != VP8_ENC_OK) goto Error;
|
||||||
|
// Color cache is disabled for palette.
|
||||||
enc->cache_bits_ = 0;
|
enc->cache_bits_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1017,6 +1034,20 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (picture->stats != NULL) {
|
||||||
|
WebPAuxStats* const stats = picture->stats;
|
||||||
|
stats->lossless_features = 0;
|
||||||
|
if (enc->use_predict_) stats->lossless_features |= 1;
|
||||||
|
if (enc->use_cross_color_) stats->lossless_features |= 2;
|
||||||
|
if (enc->use_subtract_green_) stats->lossless_features |= 4;
|
||||||
|
if (enc->use_palette_) stats->lossless_features |= 8;
|
||||||
|
stats->histogram_bits = enc->histo_bits_;
|
||||||
|
stats->transform_bits = enc->transform_bits_;
|
||||||
|
stats->cache_bits = enc->cache_bits_;
|
||||||
|
stats->palette_size = enc->palette_size_;
|
||||||
|
stats->lossless_size = (int)(VP8LBitWriterNumBytes(bw) - byte_position);
|
||||||
|
}
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
VP8LEncoderDelete(enc);
|
VP8LEncoderDelete(enc);
|
||||||
return err;
|
return err;
|
||||||
@ -1035,19 +1066,34 @@ int VP8LEncodeImage(const WebPConfig* const config,
|
|||||||
|
|
||||||
if (config == NULL || picture->argb == NULL) {
|
if (config == NULL || picture->argb == NULL) {
|
||||||
err = VP8_ENC_ERROR_NULL_PARAMETER;
|
err = VP8_ENC_ERROR_NULL_PARAMETER;
|
||||||
goto Error;
|
WebPEncodingSetError(picture, err);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
width = picture->width;
|
width = picture->width;
|
||||||
height = picture->height;
|
height = picture->height;
|
||||||
|
if (!VP8LBitWriterInit(&bw, (width * height) >> 1)) {
|
||||||
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
if (!WebPReportProgress(picture, 1, &percent)) {
|
if (!WebPReportProgress(picture, 1, &percent)) {
|
||||||
UserAbort:
|
UserAbort:
|
||||||
err = VP8_ENC_ERROR_USER_ABORT;
|
err = VP8_ENC_ERROR_USER_ABORT;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
// Reset stats (for pure lossless coding)
|
||||||
|
if (picture->stats != NULL) {
|
||||||
|
WebPAuxStats* const stats = picture->stats;
|
||||||
|
memset(stats, 0, sizeof(*stats));
|
||||||
|
stats->PSNR[0] = 99.f;
|
||||||
|
stats->PSNR[1] = 99.f;
|
||||||
|
stats->PSNR[2] = 99.f;
|
||||||
|
stats->PSNR[3] = 99.f;
|
||||||
|
stats->PSNR[4] = 99.f;
|
||||||
|
}
|
||||||
|
|
||||||
// Write image size.
|
// Write image size.
|
||||||
VP8LBitWriterInit(&bw, (width * height) >> 1);
|
|
||||||
if (!WriteImageSize(picture, &bw)) {
|
if (!WriteImageSize(picture, &bw)) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
@ -1075,15 +1121,10 @@ int VP8LEncodeImage(const WebPConfig* const config,
|
|||||||
|
|
||||||
if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
|
if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
|
||||||
|
|
||||||
// Collect some stats if needed.
|
// Save size.
|
||||||
if (picture->stats != NULL) {
|
if (picture->stats != NULL) {
|
||||||
WebPAuxStats* const stats = picture->stats;
|
picture->stats->coded_size += (int)coded_size;
|
||||||
memset(stats, 0, sizeof(*stats));
|
picture->stats->lossless_size = (int)coded_size;
|
||||||
stats->PSNR[0] = 99.;
|
|
||||||
stats->PSNR[1] = 99.;
|
|
||||||
stats->PSNR[2] = 99.;
|
|
||||||
stats->PSNR[3] = 99.;
|
|
||||||
stats->coded_size = (int)coded_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (picture->extra_info != NULL) {
|
if (picture->extra_info != NULL) {
|
||||||
|
@ -38,6 +38,7 @@ typedef struct {
|
|||||||
|
|
||||||
// Encoding parameters derived from image characteristics.
|
// Encoding parameters derived from image characteristics.
|
||||||
int use_cross_color_;
|
int use_cross_color_;
|
||||||
|
int use_subtract_green_;
|
||||||
int use_predict_;
|
int use_predict_;
|
||||||
int use_palette_;
|
int use_palette_;
|
||||||
int palette_size_;
|
int palette_size_;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "./vp8enci.h"
|
#include "./vp8enci.h"
|
||||||
#include "./vp8li.h"
|
#include "./vp8li.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
// #define PRINT_MEMORY_INFO
|
// #define PRINT_MEMORY_INFO
|
||||||
|
|
||||||
@ -164,13 +165,14 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
|||||||
config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
|
config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
|
||||||
VP8Encoder* enc;
|
VP8Encoder* enc;
|
||||||
uint8_t* mem;
|
uint8_t* mem;
|
||||||
size_t size = sizeof(VP8Encoder) + ALIGN_CST // main struct
|
const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct
|
||||||
+ cache_size // working caches
|
+ ALIGN_CST // cache alignment
|
||||||
+ info_size // modes info
|
+ cache_size // working caches
|
||||||
+ preds_size // prediction modes
|
+ info_size // modes info
|
||||||
+ samples_size // top/left samples
|
+ preds_size // prediction modes
|
||||||
+ nz_size // coeff context bits
|
+ samples_size // top/left samples
|
||||||
+ lf_stats_size; // autofilter stats
|
+ nz_size // coeff context bits
|
||||||
|
+ lf_stats_size; // autofilter stats
|
||||||
|
|
||||||
#ifdef PRINT_MEMORY_INFO
|
#ifdef PRINT_MEMORY_INFO
|
||||||
printf("===================================\n");
|
printf("===================================\n");
|
||||||
@ -198,7 +200,7 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
|||||||
mb_w * mb_h * 384 * sizeof(uint8_t));
|
mb_w * mb_h * 384 * sizeof(uint8_t));
|
||||||
printf("===================================\n");
|
printf("===================================\n");
|
||||||
#endif
|
#endif
|
||||||
mem = (uint8_t*)malloc(size);
|
mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -284,6 +286,7 @@ static void FinalizePSNR(const VP8Encoder* const enc) {
|
|||||||
stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
|
stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
|
||||||
stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
|
stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
|
||||||
stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
|
stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
|
||||||
|
stats->PSNR[4] = (float)GetPSNR(sse[3], size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StoreStats(VP8Encoder* const enc) {
|
static void StoreStats(VP8Encoder* const enc) {
|
||||||
@ -343,6 +346,8 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
|
|||||||
if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
|
if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
|
||||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
|
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
|
||||||
|
|
||||||
|
if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));
|
||||||
|
|
||||||
if (!config->lossless) {
|
if (!config->lossless) {
|
||||||
VP8Encoder* enc = NULL;
|
VP8Encoder* enc = NULL;
|
||||||
if (pic->y == NULL || pic->u == NULL || pic->v == NULL) {
|
if (pic->y == NULL || pic->u == NULL || pic->v == NULL) {
|
||||||
|
@ -20,6 +20,8 @@ libwebputils_la_SOURCES += rescaler.c
|
|||||||
libwebputils_la_SOURCES += rescaler.h
|
libwebputils_la_SOURCES += rescaler.h
|
||||||
libwebputils_la_SOURCES += thread.c
|
libwebputils_la_SOURCES += thread.c
|
||||||
libwebputils_la_SOURCES += thread.h
|
libwebputils_la_SOURCES += thread.h
|
||||||
|
libwebputils_la_SOURCES += utils.c
|
||||||
|
libwebputils_la_SOURCES += utils.h
|
||||||
|
|
||||||
libwebputilsinclude_HEADERS = ../webp/types.h
|
libwebputilsinclude_HEADERS = ../webp/types.h
|
||||||
libwebputilsincludedir = $(includedir)/webp
|
libwebputilsincludedir = $(includedir)/webp
|
||||||
|
@ -25,18 +25,23 @@ extern "C" {
|
|||||||
static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) {
|
static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) {
|
||||||
uint8_t* new_buf;
|
uint8_t* new_buf;
|
||||||
size_t new_size;
|
size_t new_size;
|
||||||
const size_t needed_size = bw->pos_ + extra_size;
|
const uint64_t needed_size_64b = (uint64_t)bw->pos_ + extra_size;
|
||||||
|
const size_t needed_size = (size_t)needed_size_64b;
|
||||||
|
if (needed_size_64b != needed_size) {
|
||||||
|
bw->error_ = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (needed_size <= bw->max_pos_) return 1;
|
if (needed_size <= bw->max_pos_) return 1;
|
||||||
|
// If the following line wraps over 32bit, the test just after will catch it.
|
||||||
new_size = 2 * bw->max_pos_;
|
new_size = 2 * bw->max_pos_;
|
||||||
if (new_size < needed_size)
|
if (new_size < needed_size) new_size = needed_size;
|
||||||
new_size = needed_size;
|
|
||||||
if (new_size < 1024) new_size = 1024;
|
if (new_size < 1024) new_size = 1024;
|
||||||
new_buf = (uint8_t*)malloc(new_size);
|
new_buf = (uint8_t*)malloc(new_size);
|
||||||
if (new_buf == NULL) {
|
if (new_buf == NULL) {
|
||||||
bw->error_ = 1;
|
bw->error_ = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (bw->pos_ > 0) memcpy(new_buf, bw->buf_, bw->pos_);
|
memcpy(new_buf, bw->buf_, bw->pos_);
|
||||||
free(bw->buf_);
|
free(bw->buf_);
|
||||||
bw->buf_ = new_buf;
|
bw->buf_ = new_buf;
|
||||||
bw->max_pos_ = new_size;
|
bw->max_pos_ = new_size;
|
||||||
@ -51,10 +56,8 @@ static void kFlush(VP8BitWriter* const bw) {
|
|||||||
bw->nb_bits_ -= 8;
|
bw->nb_bits_ -= 8;
|
||||||
if ((bits & 0xff) != 0xff) {
|
if ((bits & 0xff) != 0xff) {
|
||||||
size_t pos = bw->pos_;
|
size_t pos = bw->pos_;
|
||||||
if (pos + bw->run_ >= bw->max_pos_) { // reallocate
|
if (!BitWriterResize(bw, bw->run_ + 1)) {
|
||||||
if (!BitWriterResize(bw, bw->run_ + 1)) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's
|
if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's
|
||||||
if (pos > 0) bw->buf_[pos - 1]++;
|
if (pos > 0) bw->buf_[pos - 1]++;
|
||||||
@ -194,23 +197,28 @@ void VP8BitWriterWipeOut(VP8BitWriter* const bw) {
|
|||||||
static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) {
|
static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) {
|
||||||
uint8_t* allocated_buf;
|
uint8_t* allocated_buf;
|
||||||
size_t allocated_size;
|
size_t allocated_size;
|
||||||
const size_t size_required = VP8LBitWriterNumBytes(bw) + extra_size;
|
const size_t current_size = VP8LBitWriterNumBytes(bw);
|
||||||
if ((bw->max_bytes_ > 0) && (size_required <= bw->max_bytes_)) return 1;
|
const uint64_t size_required_64b = (uint64_t)current_size + extra_size;
|
||||||
allocated_size = (3 * bw->max_bytes_) >> 1;
|
const size_t size_required = (size_t)size_required_64b;
|
||||||
if (allocated_size < size_required) {
|
if (size_required != size_required_64b) {
|
||||||
allocated_size = size_required;
|
bw->error_ = 1;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
// Make Allocated size multiple of KBs
|
if (bw->max_bytes_ > 0 && size_required <= bw->max_bytes_) return 1;
|
||||||
|
allocated_size = (3 * bw->max_bytes_) >> 1;
|
||||||
|
if (allocated_size < size_required) allocated_size = size_required;
|
||||||
|
// make allocated size multiple of 1k
|
||||||
allocated_size = (((allocated_size >> 10) + 1) << 10);
|
allocated_size = (((allocated_size >> 10) + 1) << 10);
|
||||||
allocated_buf = (uint8_t*)malloc(allocated_size);
|
allocated_buf = (uint8_t*)malloc(allocated_size);
|
||||||
if (allocated_buf == NULL) return 0;
|
if (allocated_buf == NULL) {
|
||||||
memset(allocated_buf, 0, allocated_size);
|
bw->error_ = 1;
|
||||||
if (bw->bit_pos_ > 0) {
|
return 0;
|
||||||
memcpy(allocated_buf, bw->buf_, VP8LBitWriterNumBytes(bw));
|
|
||||||
}
|
}
|
||||||
|
memcpy(allocated_buf, bw->buf_, current_size);
|
||||||
free(bw->buf_);
|
free(bw->buf_);
|
||||||
bw->buf_ = allocated_buf;
|
bw->buf_ = allocated_buf;
|
||||||
bw->max_bytes_ = allocated_size;
|
bw->max_bytes_ = allocated_size;
|
||||||
|
memset(allocated_buf + current_size, 0, allocated_size - current_size);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,33 +240,37 @@ void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits) {
|
|||||||
// Technically, this branch of the code can write up to 25 bits at a time,
|
// Technically, this branch of the code can write up to 25 bits at a time,
|
||||||
// but in prefix encoding, the maximum number of bits written is 18 at a time.
|
// but in prefix encoding, the maximum number of bits written is 18 at a time.
|
||||||
{
|
{
|
||||||
uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3];
|
uint8_t* const p = &bw->buf_[bw->bit_pos_ >> 3];
|
||||||
uint32_t v = *(const uint32_t*)(p);
|
uint32_t v = *(const uint32_t*)p;
|
||||||
v |= bits << (bw->bit_pos_ & 7);
|
v |= bits << (bw->bit_pos_ & 7);
|
||||||
*(uint32_t*)(p) = v;
|
*(uint32_t*)p = v;
|
||||||
bw->bit_pos_ += n_bits;
|
bw->bit_pos_ += n_bits;
|
||||||
}
|
}
|
||||||
#else // LITTLE_ENDIAN
|
#else // BIG_ENDIAN
|
||||||
// implicit & 0xff is assumed for uint8_t arithmetics
|
|
||||||
{
|
{
|
||||||
uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3];
|
uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3];
|
||||||
const int bits_reserved_in_first_byte = (bw->bit_pos_ & 7);
|
const int bits_reserved_in_first_byte = bw->bit_pos_ & 7;
|
||||||
*p++ |= (bits << bits_reserved_in_first_byte);
|
|
||||||
const int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte;
|
const int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte;
|
||||||
|
// implicit & 0xff is assumed for uint8_t arithmetics
|
||||||
|
*p++ |= bits << bits_reserved_in_first_byte;
|
||||||
|
bits >>= 8 - bits_reserved_in_first_byte;
|
||||||
if (bits_left_to_write >= 1) {
|
if (bits_left_to_write >= 1) {
|
||||||
*p++ = bits >> (8 - bits_reserved_in_first_byte);
|
*p++ = bits;
|
||||||
|
bits >>= 8;
|
||||||
if (bits_left_to_write >= 9) {
|
if (bits_left_to_write >= 9) {
|
||||||
*p++ = bits >> (16 - bits_reserved_in_first_byte);
|
*p++ = bits;
|
||||||
|
bits >>= 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(n_bits <= 25);
|
assert(n_bits <= 25);
|
||||||
*p = bits >> (24 - bits_reserved_in_first_byte);
|
*p = bits;
|
||||||
bw->bit_pos_ += n_bits;
|
bw->bit_pos_ += n_bits;
|
||||||
}
|
}
|
||||||
#endif // BIG_ENDIAN
|
#endif
|
||||||
if ((bw->bit_pos_ >> 3) > (bw->max_bytes_ - 8)) {
|
if ((bw->bit_pos_ >> 3) > (bw->max_bytes_ - 8)) {
|
||||||
const size_t kAdditionalBuffer = 32768 + bw->max_bytes_;
|
const uint64_t extra_size = 32768ULL + bw->max_bytes_;
|
||||||
if (!VP8LBitWriterResize(bw, kAdditionalBuffer)) {
|
if (extra_size != (size_t)extra_size ||
|
||||||
|
!VP8LBitWriterResize(bw, (size_t)extra_size)) {
|
||||||
bw->bit_pos_ = 0;
|
bw->bit_pos_ = 0;
|
||||||
bw->error_ = 1;
|
bw->error_ = 1;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "./color_cache.h"
|
#include "./color_cache.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -21,12 +22,11 @@ extern "C" {
|
|||||||
// VP8LColorCache.
|
// VP8LColorCache.
|
||||||
|
|
||||||
int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) {
|
int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) {
|
||||||
int hash_size;
|
const int hash_size = 1 << hash_bits;
|
||||||
assert(cc != NULL);
|
assert(cc != NULL);
|
||||||
|
assert(hash_bits > 0);
|
||||||
if (hash_bits == 0) hash_bits = 1;
|
cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size,
|
||||||
hash_size = 1 << hash_bits;
|
sizeof(*cc->colors_));
|
||||||
cc->colors_ = (uint32_t*)calloc(hash_size, sizeof(*cc->colors_));
|
|
||||||
if (cc->colors_ == NULL) return 0;
|
if (cc->colors_ == NULL) return 0;
|
||||||
cc->hash_shift_ = 32 - hash_bits;
|
cc->hash_shift_ = 32 - hash_bits;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "./huffman.h"
|
#include "./huffman.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
#include "../webp/format_constants.h"
|
#include "../webp/format_constants.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
@ -49,8 +50,8 @@ static int TreeInit(HuffmanTree* const tree, int num_leaves) {
|
|||||||
// Note that a Huffman tree is a full binary tree; and in a full binary tree
|
// Note that a Huffman tree is a full binary tree; and in a full binary tree
|
||||||
// with L leaves, the total number of nodes N = 2 * L - 1.
|
// with L leaves, the total number of nodes N = 2 * L - 1.
|
||||||
tree->max_nodes_ = 2 * num_leaves - 1;
|
tree->max_nodes_ = 2 * num_leaves - 1;
|
||||||
tree->root_ =
|
tree->root_ = (HuffmanTreeNode*)WebPSafeMalloc((uint64_t)tree->max_nodes_,
|
||||||
(HuffmanTreeNode*)malloc(tree->max_nodes_ * sizeof(*tree->root_));
|
sizeof(*tree->root_));
|
||||||
if (tree->root_ == NULL) return 0;
|
if (tree->root_ == NULL) return 0;
|
||||||
TreeNodeInit(tree->root_); // Initialize root.
|
TreeNodeInit(tree->root_); // Initialize root.
|
||||||
tree->num_nodes_ = 1;
|
tree->num_nodes_ = 1;
|
||||||
@ -173,7 +174,8 @@ int HuffmanTreeBuildImplicit(HuffmanTree* const tree,
|
|||||||
int ok = 0;
|
int ok = 0;
|
||||||
|
|
||||||
// Get Huffman codes from the code lengths.
|
// Get Huffman codes from the code lengths.
|
||||||
int* const codes = (int*)malloc(code_lengths_size * sizeof(*codes));
|
int* const codes =
|
||||||
|
(int*)WebPSafeMalloc((uint64_t)code_lengths_size, sizeof(*codes));
|
||||||
if (codes == NULL) goto End;
|
if (codes == NULL) goto End;
|
||||||
|
|
||||||
if (!HuffmanCodeLengthsToCodes(code_lengths, code_lengths_size, codes)) {
|
if (!HuffmanCodeLengthsToCodes(code_lengths, code_lengths_size, codes)) {
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "./huffman_encode.h"
|
#include "./huffman_encode.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
#include "../webp/format_constants.h"
|
#include "../webp/format_constants.h"
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -196,7 +197,7 @@ static int GenerateOptimalTree(const int* const histogram, int histogram_size,
|
|||||||
// population and all the inserted nodes combining two existing nodes.
|
// population and all the inserted nodes combining two existing nodes.
|
||||||
// The tree pool needs 2 * (tree_size_orig - 1) entities, and the
|
// The tree pool needs 2 * (tree_size_orig - 1) entities, and the
|
||||||
// tree needs exactly tree_size_orig entities.
|
// tree needs exactly tree_size_orig entities.
|
||||||
tree = (HuffmanTree*)malloc(3 * tree_size_orig * sizeof(*tree));
|
tree = (HuffmanTree*)WebPSafeMalloc(3ULL * tree_size_orig, sizeof(*tree));
|
||||||
if (tree == NULL) return 0;
|
if (tree == NULL) return 0;
|
||||||
tree_pool = tree + tree_size_orig;
|
tree_pool = tree + tree_size_orig;
|
||||||
|
|
||||||
|
44
src/utils/utils.c
Normal file
44
src/utils/utils.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// This code is licensed under the same terms as WebM:
|
||||||
|
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||||
|
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Misc. common utility functions
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "./utils.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Checked memory allocation
|
||||||
|
|
||||||
|
static int CheckSizeArguments(uint64_t nmemb, size_t size) {
|
||||||
|
const uint64_t total_size = nmemb * size;
|
||||||
|
if (nmemb == 0) return 1;
|
||||||
|
if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
|
||||||
|
if (total_size != (size_t)total_size) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
|
||||||
|
if (!CheckSizeArguments(nmemb, size)) return NULL;
|
||||||
|
return malloc((size_t)(nmemb * size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* WebPSafeCalloc(uint64_t nmemb, size_t size) {
|
||||||
|
if (!CheckSizeArguments(nmemb, size)) return NULL;
|
||||||
|
return calloc((size_t)nmemb, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
44
src/utils/utils.h
Normal file
44
src/utils/utils.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// This code is licensed under the same terms as WebM:
|
||||||
|
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||||
|
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Misc. common utility functions
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#ifndef WEBP_UTILS_UTILS_H_
|
||||||
|
#define WEBP_UTILS_UTILS_H_
|
||||||
|
|
||||||
|
#include "../webp/types.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Memory allocation
|
||||||
|
|
||||||
|
// This is the maximum memory amount that libwebp will ever try to allocate.
|
||||||
|
#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 40)
|
||||||
|
|
||||||
|
// size-checking safe malloc/calloc: verify that the requested size is not too
|
||||||
|
// large, or return NULL. You don't need to call these for constructs like
|
||||||
|
// malloc(sizeof(foo)), but only if there's picture-dependent size involved
|
||||||
|
// somewhere (like: malloc(num_pixels * sizeof(*something))). That's why this
|
||||||
|
// safe malloc() borrows the signature from calloc(), pointing at the dangerous
|
||||||
|
// underlying multiply involved.
|
||||||
|
void* WebPSafeMalloc(uint64_t nmemb, size_t size);
|
||||||
|
// Note that WebPSafeCalloc() expects the second argument type to be 'size_t'
|
||||||
|
// in order to favor the "calloc(num_foo, sizeof(foo))" pattern.
|
||||||
|
void* WebPSafeCalloc(uint64_t nmemb, size_t size);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* WEBP_UTILS_UTILS_H_ */
|
@ -18,7 +18,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define WEBP_DECODER_ABI_VERSION 0x0100 // MAJOR(8b) + MINOR(8b)
|
#define WEBP_DECODER_ABI_VERSION 0x0200 // MAJOR(8b) + MINOR(8b)
|
||||||
|
|
||||||
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
||||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||||
@ -233,7 +233,7 @@ typedef enum {
|
|||||||
//
|
//
|
||||||
// // The above call decodes the current available buffer.
|
// // The above call decodes the current available buffer.
|
||||||
// // Part of the image can now be refreshed by calling to
|
// // Part of the image can now be refreshed by calling to
|
||||||
// // WebPIDecGetRGB()/WebPIDecGetYUV() etc.
|
// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
|
||||||
// }
|
// }
|
||||||
// WebPIDelete(idec);
|
// WebPIDelete(idec);
|
||||||
|
|
||||||
@ -260,9 +260,18 @@ WEBP_EXTERN(WebPIDecoder*) WebPINewRGB(
|
|||||||
// will output the raw luma/chroma samples into a preallocated planes. The luma
|
// will output the raw luma/chroma samples into a preallocated planes. The luma
|
||||||
// plane is specified by its pointer 'luma', its size 'luma_size' and its stride
|
// plane is specified by its pointer 'luma', its size 'luma_size' and its stride
|
||||||
// 'luma_stride'. Similarly, the chroma-u plane is specified by the 'u',
|
// 'luma_stride'. Similarly, the chroma-u plane is specified by the 'u',
|
||||||
// 'u_size' and 'u_stride' parameters, and the chroma-v plane by 'v', 'v_size'
|
// 'u_size' and 'u_stride' parameters, and the chroma-v plane by 'v'
|
||||||
// and 'v_size'.
|
// and 'v_size'. And same for the alpha-plane. The 'a' pointer can be pass
|
||||||
|
// NULL in case one is not interested in the transparency plane.
|
||||||
// Returns NULL if the allocation failed.
|
// Returns NULL if the allocation failed.
|
||||||
|
WEBP_EXTERN(WebPIDecoder*) WebPINewYUVA(
|
||||||
|
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||||
|
uint8_t* u, size_t u_size, int u_stride,
|
||||||
|
uint8_t* v, size_t v_size, int v_stride,
|
||||||
|
uint8_t* a, size_t a_size, int a_stride);
|
||||||
|
|
||||||
|
// Deprecated version of the above, without the alpha plane.
|
||||||
|
// Kept for backward compatibility.
|
||||||
WEBP_EXTERN(WebPIDecoder*) WebPINewYUV(
|
WEBP_EXTERN(WebPIDecoder*) WebPINewYUV(
|
||||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||||
uint8_t* u, size_t u_size, int u_stride,
|
uint8_t* u, size_t u_size, int u_stride,
|
||||||
@ -296,12 +305,22 @@ WEBP_EXTERN(uint8_t*) WebPIDecGetRGB(
|
|||||||
const WebPIDecoder* idec, int* last_y,
|
const WebPIDecoder* idec, int* last_y,
|
||||||
int* width, int* height, int* stride);
|
int* width, int* height, int* stride);
|
||||||
|
|
||||||
// Same as above function to get YUV image. Returns pointer to the luma plane
|
// Same as above function to get a YUVA image. Returns pointer to the luma
|
||||||
// or NULL in case of error.
|
// plane or NULL in case of error. If there is no alpha information
|
||||||
WEBP_EXTERN(uint8_t*) WebPIDecGetYUV(
|
// the alpha pointer '*a' will be returned NULL.
|
||||||
|
WEBP_EXTERN(uint8_t*) WebPIDecGetYUVA(
|
||||||
const WebPIDecoder* idec, int* last_y,
|
const WebPIDecoder* idec, int* last_y,
|
||||||
uint8_t** u, uint8_t** v,
|
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||||
int* width, int* height, int* stride, int* uv_stride);
|
int* width, int* height, int* stride, int* uv_stride, int* a_stride);
|
||||||
|
|
||||||
|
// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the
|
||||||
|
// alpha information (if present). Kept for backward compatibility.
|
||||||
|
static WEBP_INLINE uint8_t* WebPIDecGetYUV(
|
||||||
|
const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,
|
||||||
|
int* width, int* height, int* stride, int* uv_stride) {
|
||||||
|
return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,
|
||||||
|
stride, uv_stride, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Generic call to retrieve information about the displayable area.
|
// Generic call to retrieve information about the displayable area.
|
||||||
// If non NULL, the left/right/width/height pointers are filled with the visible
|
// If non NULL, the left/right/width/height pointers are filled with the visible
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define WEBP_ENCODER_ABI_VERSION 0x0100 // MAJOR(8b) + MINOR(8b)
|
#define WEBP_ENCODER_ABI_VERSION 0x0200 // MAJOR(8b) + MINOR(8b)
|
||||||
|
|
||||||
// Return the encoder's version number, packed in hexadecimal using 8bits for
|
// Return the encoder's version number, packed in hexadecimal using 8bits for
|
||||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||||
@ -69,7 +69,9 @@ WEBP_EXTERN(size_t) WebPEncodeLosslessBGRA(const uint8_t* bgra,
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
WEBP_HINT_DEFAULT = 0, // default preset.
|
WEBP_HINT_DEFAULT = 0, // default preset.
|
||||||
WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot
|
WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot
|
||||||
WEBP_HINT_PHOTO // outdoor photograph, with natural lighting
|
WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting
|
||||||
|
WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc).
|
||||||
|
WEBP_HINT_LAST
|
||||||
} WebPImageHint;
|
} WebPImageHint;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -157,7 +159,7 @@ typedef struct WebPPicture WebPPicture; // main structure for I/O
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int coded_size; // final size
|
int coded_size; // final size
|
||||||
|
|
||||||
float PSNR[4]; // peak-signal-to-noise ratio for Y/U/V/All
|
float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha
|
||||||
int block_count[3]; // number of intra4/intra16/skipped macroblocks
|
int block_count[3]; // number of intra4/intra16/skipped macroblocks
|
||||||
int header_bytes[2]; // approximate number of bytes spent for header
|
int header_bytes[2]; // approximate number of bytes spent for header
|
||||||
// and mode-partition #0
|
// and mode-partition #0
|
||||||
@ -170,10 +172,16 @@ typedef struct {
|
|||||||
int alpha_data_size; // size of the transparency data
|
int alpha_data_size; // size of the transparency data
|
||||||
int layer_data_size; // size of the enhancement layer data
|
int layer_data_size; // size of the enhancement layer data
|
||||||
|
|
||||||
void* user_data; // this field is free to be set to any value and
|
// lossless encoder statistics
|
||||||
// used during callbacks (like progress-report e.g.).
|
uint32_t lossless_features; // bit0:predictor bit1:cross-color transform
|
||||||
|
// bit2:subtract-green bit3:color indexing
|
||||||
|
int histogram_bits; // number of precision bits of histogram
|
||||||
|
int transform_bits; // precision bits for transform
|
||||||
|
int cache_bits; // number of bits for color cache lookup
|
||||||
|
int palette_size; // number of color in palette, if used
|
||||||
|
int lossless_size; // final lossless size
|
||||||
|
|
||||||
uint32_t pad[6]; // padding for later use
|
uint32_t pad[4]; // padding for later use
|
||||||
} WebPAuxStats;
|
} WebPAuxStats;
|
||||||
|
|
||||||
// Signature for output function. Should return true if writing was successful.
|
// Signature for output function. Should return true if writing was successful.
|
||||||
@ -290,6 +298,9 @@ struct WebPPicture {
|
|||||||
// If not NULL, report progress during encoding.
|
// If not NULL, report progress during encoding.
|
||||||
WebPProgressHook progress_hook;
|
WebPProgressHook progress_hook;
|
||||||
|
|
||||||
|
void* user_data; // this field is free to be set to any value and
|
||||||
|
// used during callbacks (like progress-report e.g.).
|
||||||
|
|
||||||
uint32_t pad3[3]; // padding for later use
|
uint32_t pad3[3]; // padding for later use
|
||||||
|
|
||||||
// Unused for now: original samples (for non-YUV420 modes)
|
// Unused for now: original samples (for non-YUV420 modes)
|
||||||
|
Loading…
Reference in New Issue
Block a user