From 48f66b6687c195c78992b45d78e2117d648c4796 Mon Sep 17 00:00:00 2001 From: Mislav Bradac Date: Fri, 11 Sep 2015 12:29:07 +0000 Subject: [PATCH] Add delta_palettization feature to WebP Change-Id: Ibaf4e49aa67d63d0eb11848cca4fd0c60815864a --- Android.mk | 1 + Makefile.vc | 1 + examples/cwebp.c | 8 + makefile.unix | 2 + src/dsp/lossless.c | 100 +++++++++++++ src/dsp/lossless.h | 4 + src/enc/Makefile.am | 2 + src/enc/config.c | 7 + src/enc/delta_palettization.c | 273 ++++++++++++++++++++++++++++++++++ src/enc/delta_palettization.h | 22 +++ src/enc/vp8enci.h | 4 + src/enc/vp8l.c | 189 ++++++++++++++++++----- src/webp/encode.h | 5 + 13 files changed, 584 insertions(+), 34 deletions(-) create mode 100644 src/enc/delta_palettization.c create mode 100644 src/enc/delta_palettization.h diff --git a/Android.mk b/Android.mk index 99510133..ee4033d1 100644 --- a/Android.mk +++ b/Android.mk @@ -97,6 +97,7 @@ enc_srcs := \ src/enc/backward_references.c \ src/enc/config.c \ src/enc/cost.c \ + src/enc/delta_palettization.c \ src/enc/filter.c \ src/enc/frame.c \ src/enc/histogram.c \ diff --git a/Makefile.vc b/Makefile.vc index fdeaf1c7..e57f8143 100644 --- a/Makefile.vc +++ b/Makefile.vc @@ -262,6 +262,7 @@ ENC_OBJS = \ $(DIROBJ)\enc\backward_references.obj \ $(DIROBJ)\enc\config.obj \ $(DIROBJ)\enc\cost.obj \ + $(DIROBJ)\enc\delta_palettization.obj \ $(DIROBJ)\enc\filter.obj \ $(DIROBJ)\enc\frame.obj \ $(DIROBJ)\enc\histogram.obj \ diff --git a/examples/cwebp.c b/examples/cwebp.c index e8cbee3e..183307ac 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -617,6 +617,9 @@ static void HelpLong(void) { printf(" -lossless .............. encode image losslessly\n"); printf(" -near_lossless ... use near-lossless image\n" " preprocessing (0..100=off)\n"); +#ifdef WEBP_EXPERIMENTAL_FEATURES + printf(" -delta_palettization ... use delta palettization\n"); +#endif // WEBP_EXPERIMENTAL_FEATURES printf(" -hint ......... specify image characteristics hint,\n"); printf(" one of: photo, picture or graph\n"); @@ -775,6 +778,11 @@ int main(int argc, const char *argv[]) { } else if (!strcmp(argv[c], "-near_lossless") && c < argc - 1) { config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error); config.lossless = 1; // use near-lossless only with lossless +#ifdef WEBP_EXPERIMENTAL_FEATURES + } else if (!strcmp(argv[c], "-delta_palettization")) { + config.delta_palettization = 1; + config.lossless = 1; // use delta-palettization only with lossless +#endif // WEBP_EXPERIMENTAL_FEATURES } else if (!strcmp(argv[c], "-hint") && c < argc - 1) { ++c; if (!strcmp(argv[c], "photo")) { diff --git a/makefile.unix b/makefile.unix index 48df98dd..c90c0749 100644 --- a/makefile.unix +++ b/makefile.unix @@ -187,6 +187,7 @@ ENC_OBJS = \ src/enc/backward_references.o \ src/enc/config.o \ src/enc/cost.o \ + src/enc/delta_palettization.o \ src/enc/filter.o \ src/enc/frame.o \ src/enc/histogram.o \ @@ -272,6 +273,7 @@ HDRS = \ src/dsp/yuv_tables_sse2.h \ src/enc/backward_references.h \ src/enc/cost.h \ + src/enc/delta_palettization.h \ src/enc/histogram.h \ src/enc/vp8enci.h \ src/enc/vp8li.h \ diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index 5ca77594..f634b8dc 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -634,3 +634,103 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInit(void) { } //------------------------------------------------------------------------------ + +#ifdef WEBP_EXPERIMENTAL_FEATURES +// Delta palettization functions. +static WEBP_INLINE int Square(int x) { + return x * x; +} + +static WEBP_INLINE uint32_t Intensity(uint32_t a) { + return + 30 * ((a >> 16) & 0xff) + + 59 * ((a >> 8) & 0xff) + + 11 * ((a >> 0) & 0xff); +} + +static uint32_t CalcDist(uint32_t predicted_value, uint32_t actual_value, + uint32_t palette_entry) { + int i; + uint32_t distance = 0; + AddPixelsEq(&predicted_value, palette_entry); + for (i = 0; i < 32; i += 8) { + const int32_t av = (actual_value >> i) & 0xff; + const int32_t pv = (predicted_value >> i) & 0xff; + distance += Square(pv - av); + } + // We sum square of intensity difference with factor 10, but because Intensity + // returns 100 times real intensity we need to multiply differences of colors + // by 1000. + distance *= 1000u; + distance += Square(Intensity(predicted_value) + - Intensity(actual_value)); + return distance; +} + +static uint32_t Predict(int x, int y, uint32_t* image) { + const uint32_t t = y == 0 ? ARGB_BLACK : image[x]; + const uint32_t l = x == 0 ? ARGB_BLACK : image[x - 1]; + const uint32_t p = + (((((t >> 24) & 0xff) + ((l >> 24) & 0xff)) / 2) << 24) + + (((((t >> 16) & 0xff) + ((l >> 16) & 0xff)) / 2) << 16) + + (((((t >> 8) & 0xff) + ((l >> 8) & 0xff)) / 2) << 8) + + (((((t >> 0) & 0xff) + ((l >> 0) & 0xff)) / 2) << 0); + if (x == 0 && y == 0) return ARGB_BLACK; + if (x == 0) return t; + if (y == 0) return l; + return p; +} + +static WEBP_INLINE int AddSubtractComponentFullWithCoefficient( + int a, int b, int c) { + return Clip255(a + ((b - c) >> 2)); +} + +static WEBP_INLINE uint32_t ClampedAddSubtractFullWithCoefficient( + uint32_t c0, uint32_t c1, uint32_t c2) { + const int a = AddSubtractComponentFullWithCoefficient( + c0 >> 24, c1 >> 24, c2 >> 24); + const int r = AddSubtractComponentFullWithCoefficient((c0 >> 16) & 0xff, + (c1 >> 16) & 0xff, + (c2 >> 16) & 0xff); + const int g = AddSubtractComponentFullWithCoefficient((c0 >> 8) & 0xff, + (c1 >> 8) & 0xff, + (c2 >> 8) & 0xff); + const int b = AddSubtractComponentFullWithCoefficient( + c0 & 0xff, c1 & 0xff, c2 & 0xff); + return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; +} + +int FindBestPaletteEntry(int x, int y, const uint32_t* palette, + int palette_size, uint32_t* src, + int src_stride, uint32_t* new_image) { + int i; + const uint32_t predicted_value = Predict(x, y, new_image); + int idx = 0; + uint32_t best_distance = CalcDist(predicted_value, src[x], palette[0]); + for (i = 1; i < palette_size; ++i) { + const uint32_t distance = CalcDist(predicted_value, src[x], palette[i]); + if (distance < best_distance) { + best_distance = distance; + idx = i; + } + } + + { + uint32_t new_value = predicted_value; + AddPixelsEq(&new_value, palette[idx]); + if (x > 0) { + src[x - 1] = ClampedAddSubtractFullWithCoefficient( + src[x - 1], new_value, src[x]); + } + if (y > 0) { + src[x - src_stride] = + ClampedAddSubtractFullWithCoefficient( + src[x - src_stride], new_value, src[x]); + } + new_image[x] = new_value; + } + return idx; +} + +#endif // WEBP_EXPERIMENTAL_FEATURES diff --git a/src/dsp/lossless.h b/src/dsp/lossless.h index a7430c49..ee677133 100644 --- a/src/dsp/lossless.h +++ b/src/dsp/lossless.h @@ -25,6 +25,10 @@ extern "C" { #endif +#ifdef WEBP_EXPERIMENTAL_FEATURES +#include "../enc/delta_palettization.h" +#endif // WEBP_EXPERIMENTAL_FEATURES + // Not a trivial literal symbol. #define VP8L_NON_TRIVIAL_SYM (0xffffffff) diff --git a/src/enc/Makefile.am b/src/enc/Makefile.am index 91714fc4..20ccb4c8 100644 --- a/src/enc/Makefile.am +++ b/src/enc/Makefile.am @@ -7,6 +7,8 @@ libwebpencode_la_SOURCES += backward_references.c libwebpencode_la_SOURCES += config.c libwebpencode_la_SOURCES += cost.c libwebpencode_la_SOURCES += cost.h +libwebpencode_la_SOURCES += delta_palettization.c +libwebpencode_la_SOURCES += delta_palettization.h libwebpencode_la_SOURCES += filter.c libwebpencode_la_SOURCES += frame.c libwebpencode_la_SOURCES += histogram.c diff --git a/src/enc/config.c b/src/enc/config.c index fbbdecf8..3c313e4d 100644 --- a/src/enc/config.c +++ b/src/enc/config.c @@ -48,6 +48,9 @@ int WebPConfigInitInternal(WebPConfig* config, config->thread_level = 0; config->low_memory = 0; config->near_lossless = 100; +#ifdef WEBP_EXPERIMENTAL_FEATURES + config->delta_palettization = 0; +#endif // WEBP_EXPERIMENTAL_FEATURES // TODO(skal): tune. switch (preset) { @@ -136,6 +139,10 @@ int WebPValidateConfig(const WebPConfig* config) { return 0; if (config->low_memory < 0 || config->low_memory > 1) return 0; +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (config->delta_palettization < 0 || config->delta_palettization > 1) + return 0; +#endif // WEBP_EXPERIMENTAL_FEATURES return 1; } diff --git a/src/enc/delta_palettization.c b/src/enc/delta_palettization.c new file mode 100644 index 00000000..a41baf9a --- /dev/null +++ b/src/enc/delta_palettization.c @@ -0,0 +1,273 @@ +#ifdef WEBP_EXPERIMENTAL_FEATURES + +#include "./delta_palettization.h" +#include "../webp/types.h" + +#define MK_COL(r, g, b) (((r) << 16) + ((g) << 8) + (b)) + +// Palette used for delta_palettization. +const uint32_t kDeltaPalette[DELTA_PALETTE_SIZE] = { + MK_COL(128u, 128u, 128u), + MK_COL(128u, 128u, 64u), + MK_COL(128u, 128u, 192u), + MK_COL(128u, 128u, 0u), + MK_COL(128u, 64u, 128u), + MK_COL(128u, 64u, 64u), + MK_COL(128u, 64u, 192u), + MK_COL(128u, 64u, 0u), + MK_COL(128u, 192u, 128u), + MK_COL(128u, 192u, 64u), + MK_COL(128u, 192u, 192u), + MK_COL(128u, 192u, 0u), + MK_COL(128u, 0u, 128u), + MK_COL(128u, 0u, 64u), + MK_COL(128u, 0u, 192u), + MK_COL(128u, 0u, 0u), + MK_COL(64u, 128u, 128u), + MK_COL(64u, 128u, 64u), + MK_COL(64u, 128u, 192u), + MK_COL(64u, 128u, 0u), + MK_COL(64u, 64u, 128u), + MK_COL(64u, 64u, 64u), + MK_COL(64u, 64u, 192u), + MK_COL(64u, 64u, 0u), + MK_COL(64u, 192u, 128u), + MK_COL(64u, 192u, 64u), + MK_COL(64u, 192u, 192u), + MK_COL(64u, 192u, 0u), + MK_COL(64u, 0u, 128u), + MK_COL(64u, 0u, 64u), + MK_COL(64u, 0u, 192u), + MK_COL(64u, 0u, 0u), + MK_COL(192u, 128u, 128u), + MK_COL(192u, 128u, 64u), + MK_COL(192u, 128u, 192u), + MK_COL(192u, 128u, 0u), + MK_COL(192u, 64u, 128u), + MK_COL(192u, 64u, 64u), + MK_COL(192u, 64u, 192u), + MK_COL(192u, 64u, 0u), + MK_COL(192u, 192u, 128u), + MK_COL(192u, 192u, 64u), + MK_COL(192u, 192u, 192u), + MK_COL(192u, 192u, 0u), + MK_COL(192u, 0u, 128u), + MK_COL(192u, 0u, 64u), + MK_COL(192u, 0u, 192u), + MK_COL(192u, 0u, 0u), + MK_COL(176u, 80u, 80u), + MK_COL(176u, 80u, 176u), + MK_COL(176u, 80u, 40u), + MK_COL(176u, 80u, 216u), + MK_COL(176u, 176u, 80u), + MK_COL(176u, 176u, 176u), + MK_COL(176u, 176u, 40u), + MK_COL(176u, 176u, 216u), + MK_COL(176u, 40u, 80u), + MK_COL(176u, 40u, 176u), + MK_COL(176u, 40u, 40u), + MK_COL(176u, 40u, 216u), + MK_COL(176u, 216u, 80u), + MK_COL(176u, 216u, 176u), + MK_COL(176u, 216u, 40u), + MK_COL(176u, 216u, 216u), + MK_COL(0u, 128u, 128u), + MK_COL(0u, 128u, 64u), + MK_COL(0u, 128u, 192u), + MK_COL(0u, 128u, 0u), + MK_COL(0u, 64u, 128u), + MK_COL(0u, 64u, 64u), + MK_COL(0u, 64u, 192u), + MK_COL(0u, 64u, 0u), + MK_COL(0u, 192u, 128u), + MK_COL(0u, 192u, 64u), + MK_COL(0u, 192u, 192u), + MK_COL(0u, 192u, 0u), + MK_COL(0u, 0u, 128u), + MK_COL(0u, 0u, 64u), + MK_COL(0u, 0u, 192u), + MK_COL(80u, 80u, 80u), + MK_COL(80u, 80u, 176u), + MK_COL(80u, 80u, 40u), + MK_COL(80u, 80u, 216u), + MK_COL(80u, 176u, 80u), + MK_COL(80u, 176u, 176u), + MK_COL(80u, 176u, 40u), + MK_COL(80u, 176u, 216u), + MK_COL(80u, 40u, 80u), + MK_COL(80u, 40u, 176u), + MK_COL(80u, 40u, 40u), + MK_COL(80u, 40u, 216u), + MK_COL(80u, 216u, 80u), + MK_COL(80u, 216u, 176u), + MK_COL(80u, 216u, 40u), + MK_COL(80u, 216u, 216u), + MK_COL(40u, 80u, 80u), + MK_COL(40u, 80u, 176u), + MK_COL(40u, 80u, 40u), + MK_COL(40u, 80u, 216u), + MK_COL(40u, 176u, 80u), + MK_COL(40u, 176u, 176u), + MK_COL(40u, 176u, 40u), + MK_COL(40u, 176u, 216u), + MK_COL(40u, 40u, 80u), + MK_COL(40u, 40u, 176u), + MK_COL(40u, 40u, 40u), + MK_COL(40u, 40u, 216u), + MK_COL(40u, 216u, 80u), + MK_COL(40u, 216u, 176u), + MK_COL(40u, 216u, 40u), + MK_COL(40u, 216u, 216u), + MK_COL(216u, 80u, 80u), + MK_COL(216u, 80u, 176u), + MK_COL(216u, 80u, 40u), + MK_COL(216u, 80u, 216u), + MK_COL(216u, 176u, 80u), + MK_COL(216u, 176u, 176u), + MK_COL(216u, 176u, 40u), + MK_COL(216u, 176u, 216u), + MK_COL(216u, 40u, 80u), + MK_COL(216u, 40u, 176u), + MK_COL(216u, 40u, 40u), + MK_COL(216u, 40u, 216u), + MK_COL(216u, 216u, 80u), + MK_COL(216u, 216u, 176u), + MK_COL(216u, 216u, 40u), + MK_COL(216u, 216u, 216u), + MK_COL(104u, 104u, 0u), + MK_COL(104u, 152u, 0u), + MK_COL(104u, 0u, 104u), + MK_COL(104u, 0u, 152u), + MK_COL(104u, 0u, 0u), + MK_COL(152u, 104u, 0u), + MK_COL(152u, 152u, 0u), + MK_COL(152u, 0u, 104u), + MK_COL(152u, 0u, 152u), + MK_COL(152u, 0u, 0u), + MK_COL(0u, 104u, 104u), + MK_COL(0u, 104u, 152u), + MK_COL(0u, 104u, 0u), + MK_COL(0u, 152u, 104u), + MK_COL(0u, 152u, 152u), + MK_COL(0u, 152u, 0u), + MK_COL(0u, 0u, 104u), + MK_COL(0u, 0u, 152u), + MK_COL(80u, 80u, 0u), + MK_COL(80u, 176u, 0u), + MK_COL(80u, 0u, 80u), + MK_COL(80u, 0u, 176u), + MK_COL(80u, 0u, 0u), + MK_COL(176u, 80u, 0u), + MK_COL(176u, 176u, 0u), + MK_COL(176u, 0u, 80u), + MK_COL(176u, 0u, 176u), + MK_COL(176u, 0u, 0u), + MK_COL(0u, 80u, 80u), + MK_COL(0u, 80u, 176u), + MK_COL(0u, 80u, 0u), + MK_COL(0u, 176u, 80u), + MK_COL(0u, 176u, 176u), + MK_COL(0u, 176u, 0u), + MK_COL(0u, 0u, 80u), + MK_COL(0u, 0u, 176u), + MK_COL(224u, 224u, 224u), + MK_COL(32u, 224u, 224u), + MK_COL(224u, 32u, 224u), + MK_COL(32u, 32u, 224u), + MK_COL(224u, 224u, 32u), + MK_COL(32u, 224u, 32u), + MK_COL(224u, 32u, 32u), + MK_COL(32u, 32u, 32u), + MK_COL(24u, 0u, 0u), + MK_COL(0u, 24u, 0u), + MK_COL(0u, 0u, 24u), + MK_COL(232u, 0u, 0u), + MK_COL(0u, 232u, 0u), + MK_COL(0u, 0u, 232u), + MK_COL(240u, 240u, 240u), + MK_COL(16u, 240u, 240u), + MK_COL(240u, 16u, 240u), + MK_COL(16u, 16u, 240u), + MK_COL(240u, 240u, 16u), + MK_COL(16u, 240u, 16u), + MK_COL(240u, 16u, 16u), + MK_COL(16u, 16u, 16u), + MK_COL(8u, 8u, 0u), + MK_COL(8u, 248u, 0u), + MK_COL(8u, 0u, 8u), + MK_COL(8u, 0u, 248u), + MK_COL(8u, 0u, 0u), + MK_COL(248u, 8u, 0u), + MK_COL(248u, 248u, 0u), + MK_COL(248u, 0u, 8u), + MK_COL(248u, 0u, 248u), + MK_COL(248u, 0u, 0u), + MK_COL(0u, 8u, 8u), + MK_COL(0u, 8u, 248u), + MK_COL(0u, 8u, 0u), + MK_COL(0u, 248u, 8u), + MK_COL(0u, 248u, 248u), + MK_COL(0u, 248u, 0u), + MK_COL(0u, 0u, 8u), + MK_COL(0u, 0u, 248u), + MK_COL(0u, 0u, 0u), + MK_COL(255u, 255u, 255u), + MK_COL(1u, 1u, 1u), + MK_COL(254u, 254u, 2u), + MK_COL(248u, 249u, 248u), + MK_COL(255u, 0u, 255u), +// MK_COL(0u, 255u, 255u), + MK_COL(1u, 2u, 255u), + MK_COL(50u, 50u, 50u), + MK_COL(1u, 254u, 1u), + MK_COL(5u, 5u, 5u), + MK_COL(255u, 2u, 1u), + MK_COL(9u, 9u, 9u), + MK_COL(3u, 3u, 3u), + MK_COL(253u, 253u, 253u), +// MK_COL(0u, 0u, 1u), +// MK_COL(0u, 0u, 255u), + MK_COL(2u, 2u, 2u), + MK_COL(254u, 254u, 254u), + MK_COL(244u, 244u, 244u), + MK_COL(255u, 0u, 0u), + MK_COL(232u, 232u, 232u), +// MK_COL(1u, 1u, 2u), +// MK_COL(1u, 0u, 0u), +// MK_COL(0u, 0u, 2u), + MK_COL(13u, 13u, 13u), +// MK_COL(255u, 255u, 0u), + MK_COL(23u, 23u, 23u), + MK_COL(0u, 255u, 0u), + MK_COL(250u, 249u, 253u), + MK_COL(2u, 1u, 1u), + MK_COL(254u, 254u, 255u), + MK_COL(255u, 254u, 254u), + MK_COL(254u, 249u, 249u), + MK_COL(1u, 2u, 1u), +// MK_COL(0u, 1u, 0u), + MK_COL(254u, 255u, 254u), + MK_COL(2u, 2u, 3u), + MK_COL(3u, 2u, 2u), + MK_COL(249u, 254u, 249u), + MK_COL(251u, 251u, 251u), + MK_COL(4u, 4u, 4u), + MK_COL(2u, 3u, 2u), +// MK_COL(0u, 1u, 255u), + MK_COL(0u, 0u, 253u), + MK_COL(252u, 252u, 252u), + MK_COL(236u, 236u, 236u), + MK_COL(255u, 1u, 0u), + MK_COL(204u, 204u, 204u), + MK_COL(245u, 245u, 250u), +// MK_COL(0u, 255u, 1u) +}; + +#undef MK_COL + +#else // !WEBP_EXPERIMENTAL_FEATURES + +void delta_palettization_stub(void); +void delta_palettization_stub(void) {} + +#endif // WEBP_EXPERIMENTAL_FEATURES diff --git a/src/enc/delta_palettization.h b/src/enc/delta_palettization.h new file mode 100644 index 00000000..e8f2e6de --- /dev/null +++ b/src/enc/delta_palettization.h @@ -0,0 +1,22 @@ +#ifndef WEBP_ENC_DELTA_PALETTIZATION_H_ +#define WEBP_ENC_DELTA_PALETTIZATION_H_ + +#ifdef WEBP_EXPERIMENTAL_FEATURES + +#include "../webp/types.h" + +#define DELTA_PALETTE_SIZE (256 - 10) + +extern const uint32_t kDeltaPalette[DELTA_PALETTE_SIZE]; + +// Find palette entry with minimum error from difference of actual pixel value +// and predicted pixel value. Propagate error of pixel to its top and left pixel +// in src array. Write predicted_value + palette_entry to new_image. Return +// index of best palette entry. +int FindBestPaletteEntry(int x, int y, const uint32_t* palette, + int palette_size, uint32_t* src, + int src_stride, uint32_t* new_image); + +#endif // WEBP_EXPERIMENTAL_FEATURES + +#endif // WEBP_ENC_DELTA_PALETTIZATION_H_ diff --git a/src/enc/vp8enci.h b/src/enc/vp8enci.h index 24d022c9..25cc6a5b 100644 --- a/src/enc/vp8enci.h +++ b/src/enc/vp8enci.h @@ -21,6 +21,10 @@ #include "../utils/thread.h" #include "../webp/encode.h" +#ifdef WEBP_EXPERIMENTAL_FEATURES +#include "./vp8li.h" +#endif // WEBP_EXPERIMENTAL_FEATURES + #ifdef __cplusplus extern "C" { #endif diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index afb55853..784c39ff 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -24,6 +24,8 @@ #include "../utils/utils.h" #include "../webp/format_constants.h" +#include "./delta_palettization.h" + #define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer. // Maximum number of histogram images (sub-blocks). #define MAX_HUFF_IMAGE_SIZE 2600 @@ -1044,6 +1046,42 @@ static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc, quality); } + +#ifdef WEBP_EXPERIMENTAL_FEATURES + +static WebPEncodingError ApplyDeltaPalette(uint32_t* src, uint32_t* dst, + uint32_t src_stride, + uint32_t dst_stride, + const uint32_t* palette, + int palette_size, + int width, int height, + int xbits, uint8_t* row, + int num_passes) { + int x, y; + WebPEncodingError err = VP8_ENC_OK; + uint32_t* new_image = (uint32_t*)WebPSafeMalloc(width, sizeof(*new_image)); + if (new_image == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; + + while (num_passes--) { + uint32_t* cur_src = src; + uint32_t* cur_dst = dst; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + row[x] = FindBestPaletteEntry(x, y, palette, palette_size, cur_src, + src_stride, new_image); + } + VP8LBundleColorMap(row, width, xbits, cur_dst); + cur_src += src_stride; + cur_dst += dst_stride; + } + } + + WebPSafeFree(new_image); + return err; +} + +#endif // WEBP_EXPERIMENTAL_FEATURES + // ----------------------------------------------------------------------------- static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic, @@ -1210,7 +1248,8 @@ static void ApplyPalette(uint32_t* src, uint32_t* dst, // Also, "enc->palette_" will be modified after this call and should not be used // later. static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, - VP8LEncoder* const enc) { + VP8LEncoder* const enc, + int use_delta_palette) { WebPEncodingError err = VP8_ENC_OK; int i; const WebPPicture* const pic = enc->pic_; @@ -1223,6 +1262,10 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, uint8_t* row = NULL; int xbits; +#ifndef WEBP_EXPERIMENTAL_FEATURES + (void)use_delta_palette; +#endif // WEBP_EXPERIMENTAL_FEATURES + // Replace each input pixel by corresponding palette index. // This is done line by line. if (palette_size <= 4) { @@ -1238,8 +1281,17 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, row = (uint8_t*)WebPSafeMalloc(width, sizeof(*row)); if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; - ApplyPalette(src, dst, pic->argb_stride, enc->current_width_, - palette, palette_size, width, height, xbits, row); +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (use_delta_palette) { + ApplyDeltaPalette(src, dst, pic->argb_stride, enc->current_width_, + palette, palette_size, width, height, xbits, row, 2); + } else { +#endif // WEBP_EXPERIMENTAL_FEATURES + ApplyPalette(src, dst, pic->argb_stride, enc->current_width_, + palette, palette_size, width, height, xbits, row); +#ifdef WEBP_EXPERIMENTAL_FEATURES + } +#endif // WEBP_EXPERIMENTAL_FEATURES // Save palette to bitstream. VP8LPutBits(bw, TRANSFORM_PRESENT, 1); @@ -1256,6 +1308,62 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, return err; } +#ifdef WEBP_EXPERIMENTAL_FEATURES + +static WebPEncodingError EncodeDeltaPalettizedImage(VP8LBitWriter* const bw, + VP8LEncoder* const enc, + int quality) { + const WebPPicture* const pic = enc->pic_; + uint32_t* src = pic->argb; + const int width = pic->width; + const int height = pic->height; + uint8_t* row = NULL; + + const int pred_bits = 5; + const int transform_width = VP8LSubSampleSize(width, pred_bits); + const int transform_height = VP8LSubSampleSize(height, pred_bits); + const int pred = 7; + const int tiles_per_row = VP8LSubSampleSize(width, pred_bits); + const int tiles_per_col = VP8LSubSampleSize(height, pred_bits); + uint32_t* predictors; + int tile_x, tile_y; + WebPEncodingError err = VP8_ENC_OK; + + predictors = (uint32_t*)WebPSafeMalloc(tiles_per_col * tiles_per_row, + sizeof(*predictors)); + if (predictors == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { + for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { + predictors[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8); + } + } + + VP8LPutBits(bw, TRANSFORM_PRESENT, 1); + VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); + VP8LPutBits(bw, pred_bits - 2, 3); + err = EncodeImageNoHuffman(bw, predictors, &enc->hash_chain_, + (VP8LBackwardRefs*)enc->refs_, // cast const away + transform_width, transform_height, + quality); + if (err != VP8_ENC_OK) goto Error; + + memcpy(enc->palette_, kDeltaPalette, sizeof(kDeltaPalette)); + enc->palette_[DELTA_PALETTE_SIZE - 1] = src[0] - 0xff000000u; + enc->palette_size_ = DELTA_PALETTE_SIZE; + EncodePalette(bw, enc, 1); + + Error: + WebPSafeFree(row); + WebPSafeFree(predictors); + return err; +} + +#endif // WEBP_EXPERIMENTAL_FEATURES + // ----------------------------------------------------------------------------- // VP8LEncoder @@ -1324,42 +1432,55 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, } } - if (enc->use_palette_) { - err = EncodePalette(bw, enc); - if (err != VP8_ENC_OK) goto Error; - } - - // In case image is not packed. - if (enc->argb_ == NULL) { - int y; - err = AllocateTransformBuffer(enc, width, height); - if (err != VP8_ENC_OK) goto Error; - assert(enc->argb_ != NULL); - for (y = 0; y < height; ++y) { - memcpy(enc->argb_ + y * width, - picture->argb + y * picture->argb_stride, - width * sizeof(*enc->argb_)); +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (config->delta_palettization) { + enc->use_predict_ = 1; + enc->use_cross_color_ = 0; + enc->use_subtract_green_ = 0; + enc->use_palette_ = 1; + EncodeDeltaPalettizedImage(bw, enc, quality); + } else { +#endif // WEBP_EXPERIMENTAL_FEATURES + if (enc->use_palette_) { + err = EncodePalette(bw, enc, 0); + if (err != VP8_ENC_OK) goto Error; } - enc->current_width_ = width; - } - // --------------------------------------------------------------------------- - // Apply transforms and write transform data. + // In case image is not packed. + if (enc->argb_ == NULL) { + int y; + err = AllocateTransformBuffer(enc, width, height); + if (err != VP8_ENC_OK) goto Error; + assert(enc->argb_ != NULL); + for (y = 0; y < height; ++y) { + memcpy(enc->argb_ + y * width, + picture->argb + y * picture->argb_stride, + width * sizeof(*enc->argb_)); + } + enc->current_width_ = width; + } - if (enc->use_subtract_green_) { - ApplySubtractGreen(enc, enc->current_width_, height, bw); - } + // ------------------------------------------------------------------------- + // Apply transforms and write transform data. - if (enc->use_predict_) { - err = ApplyPredictFilter(enc, enc->current_width_, height, quality, - low_effort, bw); - if (err != VP8_ENC_OK) goto Error; - } + if (enc->use_subtract_green_) { + ApplySubtractGreen(enc, enc->current_width_, height, bw); + } - if (enc->use_cross_color_) { - err = ApplyCrossColorFilter(enc, enc->current_width_, height, quality, bw); - if (err != VP8_ENC_OK) goto Error; + if (enc->use_predict_) { + err = ApplyPredictFilter(enc, enc->current_width_, height, quality, + low_effort, bw); + if (err != VP8_ENC_OK) goto Error; + } + + if (enc->use_cross_color_) { + err = ApplyCrossColorFilter(enc, enc->current_width_, + height, quality, bw); + if (err != VP8_ENC_OK) goto Error; + } +#ifdef WEBP_EXPERIMENTAL_FEATURES } +#endif // WEBP_EXPERIMENTAL_FEATURES VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms. @@ -1415,7 +1536,7 @@ int VP8LEncodeImage(const WebPConfig* const config, // Initialize BitWriter with size corresponding to 16 bpp to photo images and // 8 bpp for graphical images. initial_size = (config->image_hint == WEBP_HINT_GRAPH) ? - width * height : width * height * 2; + width * height : width * height * 2; if (!VP8LBitWriterInit(&bw, initial_size)) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; diff --git a/src/webp/encode.h b/src/webp/encode.h index f61a275a..020795fb 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -137,7 +137,12 @@ struct WebPConfig { int near_lossless; // Near lossless encoding [0 = off(default) .. 100]. // This feature is experimental. +#ifdef WEBP_EXPERIMENTAL_FEATURES + int delta_palettization; + uint32_t pad[3]; // padding for later use +#else uint32_t pad[4]; // padding for later use +#endif // WEBP_EXPERIMENTAL_FEATURES }; // Enumerate some predefined settings for WebPConfig, depending on the type