mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 04:18:26 +01:00
anim_diff: Add an experimental option for max inter-frame diff.
After the introduction of lossy frame rectangles we need equivalent option in anim_diff for merging similar frames. Change-Id: I1d03acace396ec4cb0212586c6e8b8ec5b0b0bfc
This commit is contained in:
parent
31f2b8d8e1
commit
47dd07080f
@ -21,7 +21,7 @@ if BUILD_ANIMDIFF
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h
|
anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h
|
||||||
anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES)
|
anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GIF_INCLUDES)
|
||||||
anim_diff_LDADD = ../src/demux/libwebpdemux.la
|
anim_diff_LDADD = ../src/demux/libwebpdemux.la
|
||||||
anim_diff_LDADD += libexampleutil.la
|
anim_diff_LDADD += libexampleutil.la
|
||||||
anim_diff_LDADD += $(GIF_LIBS) -lm
|
anim_diff_LDADD += $(GIF_LIBS) -lm
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
//
|
//
|
||||||
// example: anim_diff foo.gif bar.webp
|
// example: anim_diff foo.gif bar.webp
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h> // for 'strtod'.
|
#include <stdlib.h> // for 'strtod'.
|
||||||
@ -29,20 +30,67 @@ static int AdditionWillOverflow(int a, int b) {
|
|||||||
return (b > 0) && (a > INT_MAX - b);
|
return (b > 0) && (a > INT_MAX - b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimize number of frames by combining successive frames that have exact same
|
static int FramesAreEqual(const uint8_t* const rgba1,
|
||||||
// ARGB data into a single longer duration frame.
|
const uint8_t* const rgba2, int width, int height) {
|
||||||
static void MinimizeAnimationFrames(AnimatedImage* const img) {
|
const int stride = width * 4; // Always true for 'DecodedFrame.rgba'.
|
||||||
|
return !memcmp(rgba1, rgba2, stride * height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
|
||||||
|
int max_allowed_diff) {
|
||||||
|
const int src_a = (src >> 24) & 0xff;
|
||||||
|
const int src_r = (src >> 16) & 0xff;
|
||||||
|
const int src_g = (src >> 8) & 0xff;
|
||||||
|
const int src_b = (src >> 0) & 0xff;
|
||||||
|
const int dst_a = (dst >> 24) & 0xff;
|
||||||
|
const int dst_r = (dst >> 16) & 0xff;
|
||||||
|
const int dst_g = (dst >> 8) & 0xff;
|
||||||
|
const int dst_b = (dst >> 0) & 0xff;
|
||||||
|
|
||||||
|
return (abs(src_r * src_a - dst_r * dst_a) <= (max_allowed_diff * 255)) &&
|
||||||
|
(abs(src_g * src_a - dst_g * dst_a) <= (max_allowed_diff * 255)) &&
|
||||||
|
(abs(src_b * src_a - dst_b * dst_a) <= (max_allowed_diff * 255)) &&
|
||||||
|
(abs(src_a - dst_a) <= max_allowed_diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int FramesAreSimilar(const uint8_t* const rgba1,
|
||||||
|
const uint8_t* const rgba2,
|
||||||
|
int width, int height, int max_allowed_diff) {
|
||||||
|
int i, j;
|
||||||
|
assert(max_allowed_diff > 0);
|
||||||
|
for (j = 0; j < height; ++j) {
|
||||||
|
for (i = 0; i < width; ++i) {
|
||||||
|
const int stride = width * 4;
|
||||||
|
const size_t offset = j * stride + i;
|
||||||
|
if (!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimize number of frames by combining successive frames that have at max
|
||||||
|
// 'max_diff' difference per channel between corresponding pixels.
|
||||||
|
static void MinimizeAnimationFrames(AnimatedImage* const img, int max_diff) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
for (i = 1; i < img->num_frames; ++i) {
|
for (i = 1; i < img->num_frames; ++i) {
|
||||||
DecodedFrame* const frame1 = &img->frames[i - 1];
|
DecodedFrame* const frame1 = &img->frames[i - 1];
|
||||||
DecodedFrame* const frame2 = &img->frames[i];
|
DecodedFrame* const frame2 = &img->frames[i];
|
||||||
const uint8_t* const rgba1 = frame1->rgba;
|
const uint8_t* const rgba1 = frame1->rgba;
|
||||||
const uint8_t* const rgba2 = frame2->rgba;
|
const uint8_t* const rgba2 = frame2->rgba;
|
||||||
|
int should_merge_frames = 0;
|
||||||
// If merging frames will result in integer overflow for 'duration',
|
// If merging frames will result in integer overflow for 'duration',
|
||||||
// skip merging.
|
// skip merging.
|
||||||
if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue;
|
if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue;
|
||||||
if (!memcmp(rgba1, rgba2, img->canvas_width * 4 * img->canvas_height)) {
|
if (max_diff > 0) {
|
||||||
// Merge 'i+1'th frame into 'i'th frame.
|
should_merge_frames = FramesAreSimilar(rgba1, rgba2, img->canvas_width,
|
||||||
|
img->canvas_height, max_diff);
|
||||||
|
} else {
|
||||||
|
should_merge_frames =
|
||||||
|
FramesAreEqual(rgba1, rgba2, img->canvas_width, img->canvas_height);
|
||||||
|
}
|
||||||
|
if (should_merge_frames) { // Merge 'i+1'th frame into 'i'th frame.
|
||||||
frame1->duration += frame2->duration;
|
frame1->duration += frame2->duration;
|
||||||
if (i + 1 < img->num_frames) {
|
if (i + 1 < img->num_frames) {
|
||||||
memmove(&img->frames[i], &img->frames[i + 1],
|
memmove(&img->frames[i], &img->frames[i + 1],
|
||||||
@ -125,6 +173,11 @@ static void Help(void) {
|
|||||||
printf(" -min_psnr <float> ... minimum per-frame PSNR\n");
|
printf(" -min_psnr <float> ... minimum per-frame PSNR\n");
|
||||||
printf(" -raw_comparison ..... if this flag is not used, RGB is\n");
|
printf(" -raw_comparison ..... if this flag is not used, RGB is\n");
|
||||||
printf(" premultiplied before comparison\n");
|
printf(" premultiplied before comparison\n");
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
|
printf(" -max_diff <int> ..... maximum allowed difference per channel "
|
||||||
|
" between corresponding pixels in subsequent"
|
||||||
|
" frames\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
@ -135,6 +188,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
int got_input1 = 0;
|
int got_input1 = 0;
|
||||||
int got_input2 = 0;
|
int got_input2 = 0;
|
||||||
int premultiply = 1;
|
int premultiply = 1;
|
||||||
|
int max_diff = 0;
|
||||||
int i, c;
|
int i, c;
|
||||||
const char* files[2] = { NULL, NULL };
|
const char* files[2] = { NULL, NULL };
|
||||||
AnimatedImage images[2];
|
AnimatedImage images[2];
|
||||||
@ -168,6 +222,21 @@ int main(int argc, const char* argv[]) {
|
|||||||
}
|
}
|
||||||
} else if (!strcmp(argv[c], "-raw_comparison")) {
|
} else if (!strcmp(argv[c], "-raw_comparison")) {
|
||||||
premultiply = 0;
|
premultiply = 0;
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
|
} else if (!strcmp(argv[c], "-max_diff")) {
|
||||||
|
if (c < argc - 1) {
|
||||||
|
const char* const v = argv[++c];
|
||||||
|
char* end = NULL;
|
||||||
|
const int n = (int)strtol(v, &end, 10);
|
||||||
|
if (end == v) {
|
||||||
|
parse_error = 1;
|
||||||
|
fprintf(stderr, "Error! '%s' is not an integer.\n", v);
|
||||||
|
}
|
||||||
|
max_diff = n;
|
||||||
|
} else {
|
||||||
|
parse_error = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
if (!got_input1) {
|
if (!got_input1) {
|
||||||
files[0] = argv[c];
|
files[0] = argv[c];
|
||||||
@ -201,7 +270,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
return_code = -2;
|
return_code = -2;
|
||||||
goto End;
|
goto End;
|
||||||
} else {
|
} else {
|
||||||
MinimizeAnimationFrames(&images[i]);
|
MinimizeAnimationFrames(&images[i], max_diff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user