mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-27 06:08:21 +01:00
gif2webp: Support GIF_DISPOSE_RESTORE_PREVIOUS
Tweaked the gif2webp_util API to support this.
Requested in: https://code.google.com/p/webp/issues/detail?id=144
(cherry picked from commit 65e5eb8a62
)
Change-Id: I0e8c4edc39227355cd8d3acc55795186e25d0c3a
This commit is contained in:
parent
5691bdd9da
commit
b7eb6d55c7
@ -46,18 +46,7 @@
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int transparent_index; // Index of transparent color in the map.
|
||||
|
||||
static void ResetFrameInfo(WebPMuxFrameInfo* const info) {
|
||||
WebPDataInit(&info->bitstream);
|
||||
info->x_offset = 0;
|
||||
info->y_offset = 0;
|
||||
info->duration = 0;
|
||||
info->id = WEBP_CHUNK_ANMF;
|
||||
info->dispose_method = WEBP_MUX_DISPOSE_NONE;
|
||||
info->blend_method = WEBP_MUX_BLEND;
|
||||
transparent_index = -1; // Opaque frame by default.
|
||||
}
|
||||
static int transparent_index = -1; // Opaque frame by default.
|
||||
|
||||
static void SanitizeKeyFrameIntervals(size_t* const kmin_ptr,
|
||||
size_t* const kmax_ptr) {
|
||||
@ -270,7 +259,8 @@ int main(int argc, const char *argv[]) {
|
||||
GifFileType* gif = NULL;
|
||||
WebPConfig config;
|
||||
WebPPicture frame;
|
||||
WebPMuxFrameInfo info;
|
||||
int duration = 0;
|
||||
FrameDisposeMethod orig_dispose = FRAME_DISPOSE_NONE;
|
||||
WebPMuxAnimParams anim = { WHITE_COLOR, 0 };
|
||||
WebPFrameCache* cache = NULL;
|
||||
|
||||
@ -290,8 +280,6 @@ int main(int argc, const char *argv[]) {
|
||||
size_t kmax = 0;
|
||||
int allow_mixed = 0; // If true, each frame can be lossy or lossless.
|
||||
|
||||
ResetFrameInfo(&info);
|
||||
|
||||
if (!WebPConfigInit(&config) || !WebPPictureInit(&frame)) {
|
||||
fprintf(stderr, "Error! Version mismatch!\n");
|
||||
return -1;
|
||||
@ -499,7 +487,8 @@ int main(int argc, const char *argv[]) {
|
||||
goto End;
|
||||
}
|
||||
|
||||
if (!WebPFrameCacheAddFrame(cache, &config, &gif_rect, &frame, &info)) {
|
||||
if (!WebPFrameCacheAddFrame(cache, &config, &gif_rect, orig_dispose,
|
||||
duration, &frame)) {
|
||||
fprintf(stderr, "Error! Cannot encode frame as WebP\n");
|
||||
fprintf(stderr, "Error code: %d\n", frame.error_code);
|
||||
}
|
||||
@ -515,7 +504,9 @@ int main(int argc, const char *argv[]) {
|
||||
// In GIF, graphic control extensions are optional for a frame, so we
|
||||
// may not get one before reading the next frame. To handle this case,
|
||||
// we reset frame properties to reasonable defaults for the next frame.
|
||||
ResetFrameInfo(&info);
|
||||
orig_dispose = FRAME_DISPOSE_NONE;
|
||||
duration = 0;
|
||||
transparent_index = -1; // Opaque frame by default.
|
||||
break;
|
||||
}
|
||||
case EXTENSION_RECORD_TYPE: {
|
||||
@ -533,20 +524,19 @@ int main(int argc, const char *argv[]) {
|
||||
const int dispose = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
|
||||
const int delay = data[2] | (data[3] << 8); // In 10 ms units.
|
||||
if (data[0] != 4) goto End;
|
||||
info.duration = delay * 10; // Duration is in 1 ms units for WebP.
|
||||
if (dispose == 3) {
|
||||
static int warning_printed = 0;
|
||||
if (!warning_printed) {
|
||||
fprintf(stderr, "WARNING: GIF_DISPOSE_RESTORE unsupported.\n");
|
||||
warning_printed = 1;
|
||||
}
|
||||
// failsafe. TODO(urvang): emulate the correct behaviour by
|
||||
// recoding the whole frame.
|
||||
info.dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
|
||||
} else {
|
||||
info.dispose_method =
|
||||
(dispose == 2) ? WEBP_MUX_DISPOSE_BACKGROUND
|
||||
: WEBP_MUX_DISPOSE_NONE;
|
||||
duration = delay * 10; // Duration is in 1 ms units for WebP.
|
||||
switch (dispose) {
|
||||
case 3:
|
||||
orig_dispose = FRAME_DISPOSE_RESTORE_PREVIOUS;
|
||||
break;
|
||||
case 2:
|
||||
orig_dispose = FRAME_DISPOSE_BACKGROUND;
|
||||
break;
|
||||
case 1:
|
||||
case 0:
|
||||
default:
|
||||
orig_dispose = FRAME_DISPOSE_NONE;
|
||||
break;
|
||||
}
|
||||
transparent_index = (flags & GIF_TRANSPARENT_MASK) ? data[4] : -1;
|
||||
break;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utils/utils.h"
|
||||
#include "webp/encode.h"
|
||||
#include "./gif2webp_util.h"
|
||||
|
||||
@ -293,14 +294,17 @@ struct WebPFrameCache {
|
||||
|
||||
WebPFrameRect prev_orig_rect; // Previous input (e.g. GIF) frame rectangle.
|
||||
WebPFrameRect prev_webp_rect; // Previous WebP frame rectangle.
|
||||
WebPMuxAnimDispose prev_orig_dispose; // Previous input dispose method.
|
||||
FrameDisposeMethod prev_orig_dispose; // Previous input dispose method.
|
||||
int prev_candidate_undecided; // True if sub-frame vs keyframe decision
|
||||
// hasn't been made for the previous frame yet.
|
||||
|
||||
WebPPicture prev_canvas; // Previous canvas (NOT disposed).
|
||||
WebPPicture curr_canvas; // Current canvas (NOT disposed).
|
||||
WebPPicture curr_canvas; // Current canvas (NOT disposed).
|
||||
WebPPicture curr_canvas_tmp; // Temporary storage for current canvas.
|
||||
WebPPicture prev_canvas; // Previous canvas (NOT disposed).
|
||||
WebPPicture prev_canvas_disposed; // Previous canvas disposed to background.
|
||||
WebPPicture curr_canvas_tmp; // Temporary storage for current canvas.
|
||||
WebPPicture prev_to_prev_canvas_disposed; // Previous to previous canvas
|
||||
// (disposed as per its original
|
||||
// dispose method).
|
||||
int is_first_frame; // True if no frames have been added to the cache
|
||||
// since WebPFrameCacheNew().
|
||||
};
|
||||
@ -317,7 +321,7 @@ static void CacheReset(WebPFrameCache* const cache) {
|
||||
|
||||
WebPFrameCache* WebPFrameCacheNew(int width, int height,
|
||||
size_t kmin, size_t kmax, int allow_mixed) {
|
||||
WebPFrameCache* cache = (WebPFrameCache*)malloc(sizeof(*cache));
|
||||
WebPFrameCache* cache = (WebPFrameCache*)WebPSafeCalloc(1, sizeof(*cache));
|
||||
if (cache == NULL) return NULL;
|
||||
CacheReset(cache);
|
||||
// sanity init, so we can call WebPFrameCacheDelete():
|
||||
@ -327,22 +331,27 @@ WebPFrameCache* WebPFrameCacheNew(int width, int height,
|
||||
cache->is_first_frame = 1;
|
||||
|
||||
// Picture buffers.
|
||||
if (!WebPPictureInit(&cache->prev_canvas) ||
|
||||
!WebPPictureInit(&cache->curr_canvas) ||
|
||||
if (!WebPPictureInit(&cache->curr_canvas) ||
|
||||
!WebPPictureInit(&cache->curr_canvas_tmp) ||
|
||||
!WebPPictureInit(&cache->prev_canvas) ||
|
||||
!WebPPictureInit(&cache->prev_canvas_disposed) ||
|
||||
!WebPPictureInit(&cache->curr_canvas_tmp)) {
|
||||
!WebPPictureInit(&cache->prev_to_prev_canvas_disposed)) {
|
||||
return NULL;
|
||||
}
|
||||
cache->prev_canvas.width = width;
|
||||
cache->prev_canvas.height = height;
|
||||
cache->prev_canvas.use_argb = 1;
|
||||
if (!WebPPictureAlloc(&cache->prev_canvas) ||
|
||||
!WebPPictureCopy(&cache->prev_canvas, &cache->curr_canvas) ||
|
||||
!WebPPictureCopy(&cache->prev_canvas, &cache->prev_canvas_disposed) ||
|
||||
!WebPPictureCopy(&cache->prev_canvas, &cache->curr_canvas_tmp)) {
|
||||
cache->curr_canvas.width = width;
|
||||
cache->curr_canvas.height = height;
|
||||
cache->curr_canvas.use_argb = 1;
|
||||
if (!WebPPictureAlloc(&cache->curr_canvas) ||
|
||||
!WebPPictureCopy(&cache->curr_canvas, &cache->curr_canvas_tmp) ||
|
||||
!WebPPictureCopy(&cache->curr_canvas, &cache->prev_canvas) ||
|
||||
!WebPPictureCopy(&cache->curr_canvas, &cache->prev_canvas_disposed) ||
|
||||
!WebPPictureCopy(&cache->curr_canvas,
|
||||
&cache->prev_to_prev_canvas_disposed)) {
|
||||
goto Err;
|
||||
}
|
||||
WebPUtilClearPic(&cache->prev_canvas, NULL);
|
||||
WebPUtilClearPic(&cache->prev_canvas_disposed, NULL);
|
||||
WebPUtilClearPic(&cache->prev_to_prev_canvas_disposed, NULL);
|
||||
|
||||
// Cache data.
|
||||
cache->allow_mixed = allow_mixed;
|
||||
@ -351,8 +360,8 @@ WebPFrameCache* WebPFrameCacheNew(int width, int height,
|
||||
cache->count_since_key_frame = 0;
|
||||
assert(kmax > kmin);
|
||||
cache->size = kmax - kmin + 1; // One extra storage for previous frame.
|
||||
cache->encoded_frames =
|
||||
(EncodedFrame*)calloc(cache->size, sizeof(*cache->encoded_frames));
|
||||
cache->encoded_frames = (EncodedFrame*)WebPSafeCalloc(
|
||||
cache->size, sizeof(*cache->encoded_frames));
|
||||
if (cache->encoded_frames == NULL) goto Err;
|
||||
|
||||
return cache; // All OK.
|
||||
@ -369,13 +378,14 @@ void WebPFrameCacheDelete(WebPFrameCache* const cache) {
|
||||
for (i = 0; i < cache->size; ++i) {
|
||||
FrameRelease(&cache->encoded_frames[i]);
|
||||
}
|
||||
free(cache->encoded_frames);
|
||||
WebPSafeFree(cache->encoded_frames);
|
||||
}
|
||||
WebPPictureFree(&cache->prev_canvas);
|
||||
WebPPictureFree(&cache->curr_canvas);
|
||||
WebPPictureFree(&cache->prev_canvas_disposed);
|
||||
WebPPictureFree(&cache->curr_canvas_tmp);
|
||||
free(cache);
|
||||
WebPPictureFree(&cache->prev_canvas);
|
||||
WebPPictureFree(&cache->prev_canvas_disposed);
|
||||
WebPPictureFree(&cache->prev_to_prev_canvas_disposed);
|
||||
WebPSafeFree(cache);
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,18 +461,23 @@ static int GetColorCount(const WebPPicture* const pic) {
|
||||
#undef HASH_SIZE
|
||||
#undef HASH_RIGHT_SHIFT
|
||||
|
||||
static void DisposeFullFrame(WebPMuxAnimDispose dispose_method,
|
||||
WebPPicture* const frame) {
|
||||
if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||
WebPUtilClearPic(frame, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void DisposeFrameRectangle(WebPMuxAnimDispose dispose_method,
|
||||
const WebPFrameRect* const gif_rect,
|
||||
WebPPicture* const frame) {
|
||||
if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||
WebPUtilClearPic(frame, gif_rect);
|
||||
static void DisposeFrameRectangle(int dispose_method,
|
||||
const WebPFrameRect* const rect,
|
||||
const WebPPicture* const prev_canvas,
|
||||
WebPPicture* const curr_canvas) {
|
||||
assert(rect != NULL);
|
||||
if (dispose_method == FRAME_DISPOSE_BACKGROUND) {
|
||||
WebPUtilClearPic(curr_canvas, rect);
|
||||
} else if (dispose_method == FRAME_DISPOSE_RESTORE_PREVIOUS) {
|
||||
const int src_stride = prev_canvas->argb_stride;
|
||||
const uint32_t* const src =
|
||||
prev_canvas->argb + rect->x_offset + rect->y_offset * src_stride;
|
||||
const int dst_stride = curr_canvas->argb_stride;
|
||||
uint32_t* const dst =
|
||||
curr_canvas->argb + rect->x_offset + rect->y_offset * dst_stride;
|
||||
assert(prev_canvas != NULL);
|
||||
CopyPlane((uint8_t*)src, 4 * src_stride, (uint8_t*)dst, 4 * dst_stride,
|
||||
4 * rect->width, rect->height);
|
||||
}
|
||||
}
|
||||
|
||||
@ -536,9 +551,8 @@ typedef struct {
|
||||
// Generates a candidate encoded frame given a picture and metadata.
|
||||
static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame,
|
||||
const WebPFrameRect* const rect,
|
||||
const WebPMuxFrameInfo* const info,
|
||||
const WebPConfig* const config,
|
||||
int use_blending,
|
||||
int use_blending, int duration,
|
||||
Candidate* const candidate) {
|
||||
WebPEncodingError error_code = VP8_ENC_OK;
|
||||
assert(candidate != NULL);
|
||||
@ -546,11 +560,13 @@ static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame,
|
||||
|
||||
// Set frame rect and info.
|
||||
candidate->rect = *rect;
|
||||
candidate->info = *info;
|
||||
candidate->info.id = WEBP_CHUNK_ANMF;
|
||||
candidate->info.x_offset = rect->x_offset;
|
||||
candidate->info.y_offset = rect->y_offset;
|
||||
candidate->info.dispose_method = WEBP_MUX_DISPOSE_NONE; // Set later.
|
||||
candidate->info.blend_method =
|
||||
use_blending ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
|
||||
candidate->info.duration = duration;
|
||||
|
||||
// Encode picture.
|
||||
WebPMemoryWriterInit(&candidate->mem);
|
||||
@ -612,8 +628,7 @@ enum {
|
||||
static WebPEncodingError GenerateCandidates(
|
||||
WebPFrameCache* const cache, Candidate candidates[CANDIDATE_COUNT],
|
||||
WebPMuxAnimDispose dispose_method, int is_lossless, int is_key_frame,
|
||||
const WebPFrameRect* const rect, WebPPicture* sub_frame,
|
||||
const WebPMuxFrameInfo* const info,
|
||||
const WebPFrameRect* const rect, WebPPicture* sub_frame, int duration,
|
||||
const WebPConfig* const config_ll, const WebPConfig* const config_lossy) {
|
||||
WebPEncodingError error_code = VP8_ENC_OK;
|
||||
const int is_dispose_none = (dispose_method == WEBP_MUX_DISPOSE_NONE);
|
||||
@ -622,10 +637,10 @@ static WebPEncodingError GenerateCandidates(
|
||||
Candidate* const candidate_lossy = is_dispose_none
|
||||
? &candidates[LOSSY_DISP_NONE]
|
||||
: &candidates[LOSSY_DISP_BG];
|
||||
const WebPPicture* const prev_canvas =
|
||||
is_dispose_none ? &cache->prev_canvas : &cache->prev_canvas_disposed;
|
||||
WebPPicture* const curr_canvas = &cache->curr_canvas;
|
||||
WebPPicture* const curr_canvas_tmp = &cache->curr_canvas_tmp;
|
||||
const WebPPicture* const prev_canvas =
|
||||
is_dispose_none ? &cache->prev_canvas : &cache->prev_canvas_disposed;
|
||||
const int use_blending =
|
||||
!is_key_frame &&
|
||||
IsBlendingPossible(prev_canvas, curr_canvas, rect);
|
||||
@ -648,8 +663,8 @@ static WebPEncodingError GenerateCandidates(
|
||||
curr_canvas_saved = 1;
|
||||
IncreaseTransparency(prev_canvas, rect, curr_canvas);
|
||||
}
|
||||
error_code = EncodeCandidate(sub_frame, rect, info, config_ll, use_blending,
|
||||
candidate_ll);
|
||||
error_code = EncodeCandidate(sub_frame, rect, config_ll, use_blending,
|
||||
duration, candidate_ll);
|
||||
if (error_code != VP8_ENC_OK) return error_code;
|
||||
if (use_blending) {
|
||||
CopyPixels(curr_canvas_tmp, curr_canvas); // restore
|
||||
@ -666,8 +681,8 @@ static WebPEncodingError GenerateCandidates(
|
||||
}
|
||||
FlattenSimilarBlocks(prev_canvas, rect, curr_canvas);
|
||||
}
|
||||
error_code = EncodeCandidate(sub_frame, rect, info, config_lossy,
|
||||
use_blending, candidate_lossy);
|
||||
error_code = EncodeCandidate(sub_frame, rect, config_lossy, use_blending,
|
||||
duration, candidate_lossy);
|
||||
if (error_code != VP8_ENC_OK) return error_code;
|
||||
if (!is_key_frame) {
|
||||
CopyPixels(curr_canvas_tmp, curr_canvas); // restore
|
||||
@ -732,16 +747,15 @@ static void PickBestCandidate(WebPFrameCache* const cache,
|
||||
// (lossy/lossless), dispose methods, blending methods etc to encode the current
|
||||
// frame and outputs the best one in 'encoded_frame'.
|
||||
static WebPEncodingError SetFrame(WebPFrameCache* const cache,
|
||||
const WebPConfig* const config,
|
||||
const WebPMuxFrameInfo* const info,
|
||||
const WebPConfig* const config, int duration,
|
||||
const WebPFrameRect* const orig_rect,
|
||||
int is_key_frame,
|
||||
EncodedFrame* const encoded_frame) {
|
||||
int i;
|
||||
WebPEncodingError error_code = VP8_ENC_OK;
|
||||
WebPPicture* const curr_canvas = &cache->curr_canvas;
|
||||
const WebPPicture* const prev_canvas = &cache->prev_canvas;
|
||||
WebPPicture* const prev_canvas_disposed = &cache->prev_canvas_disposed;
|
||||
WebPPicture* const curr_canvas = &cache->curr_canvas;
|
||||
Candidate candidates[CANDIDATE_COUNT];
|
||||
const int is_lossless = config->lossless;
|
||||
|
||||
@ -782,7 +796,7 @@ static WebPEncodingError SetFrame(WebPFrameCache* const cache,
|
||||
// Change-rectangle assuming previous frame was DISPOSE_BACKGROUND.
|
||||
CopyPixels(prev_canvas, prev_canvas_disposed);
|
||||
DisposeFrameRectangle(WEBP_MUX_DISPOSE_BACKGROUND, &cache->prev_webp_rect,
|
||||
prev_canvas_disposed);
|
||||
NULL, prev_canvas_disposed);
|
||||
GetSubRect(prev_canvas_disposed, curr_canvas, orig_rect, is_key_frame,
|
||||
&rect_bg, &sub_frame_bg);
|
||||
|
||||
@ -795,16 +809,17 @@ static WebPEncodingError SetFrame(WebPFrameCache* const cache,
|
||||
if (try_dispose_none) {
|
||||
error_code = GenerateCandidates(
|
||||
cache, candidates, WEBP_MUX_DISPOSE_NONE, is_lossless, is_key_frame,
|
||||
&rect_none, &sub_frame_none, info, &config_ll, &config_lossy);
|
||||
&rect_none, &sub_frame_none, duration, &config_ll, &config_lossy);
|
||||
if (error_code != VP8_ENC_OK) goto Err;
|
||||
}
|
||||
|
||||
if (try_dispose_bg) {
|
||||
assert(!cache->is_first_frame);
|
||||
assert(dispose_bg_possible);
|
||||
error_code = GenerateCandidates(
|
||||
cache, candidates, WEBP_MUX_DISPOSE_BACKGROUND, is_lossless,
|
||||
is_key_frame, &rect_bg, &sub_frame_bg, info, &config_ll, &config_lossy);
|
||||
error_code =
|
||||
GenerateCandidates(cache, candidates, WEBP_MUX_DISPOSE_BACKGROUND,
|
||||
is_lossless, is_key_frame, &rect_bg, &sub_frame_bg,
|
||||
duration, &config_ll, &config_lossy);
|
||||
if (error_code != VP8_ENC_OK) goto Err;
|
||||
}
|
||||
|
||||
@ -843,19 +858,22 @@ static int64_t KeyFramePenalty(const EncodedFrame* const encoded_frame) {
|
||||
int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
|
||||
const WebPConfig* const config,
|
||||
const WebPFrameRect* const orig_rect_ptr,
|
||||
WebPPicture* const frame,
|
||||
WebPMuxFrameInfo* const info) {
|
||||
FrameDisposeMethod orig_dispose_method,
|
||||
int duration, WebPPicture* const frame) {
|
||||
// Initialize.
|
||||
int ok = 0;
|
||||
WebPEncodingError error_code = VP8_ENC_OK;
|
||||
WebPPicture* const prev_canvas = &cache->prev_canvas;
|
||||
WebPPicture* const curr_canvas = &cache->curr_canvas;
|
||||
WebPPicture* const prev_canvas = &cache->prev_canvas;
|
||||
WebPPicture* const prev_to_prev_canvas_disposed =
|
||||
&cache->prev_to_prev_canvas_disposed;
|
||||
WebPPicture* const prev_canvas_disposed = &cache->prev_canvas_disposed;
|
||||
const size_t position = cache->count;
|
||||
EncodedFrame* const encoded_frame = CacheGetFrame(cache, position);
|
||||
WebPFrameRect orig_rect;
|
||||
assert(position < cache->size);
|
||||
|
||||
if (frame == NULL || info == NULL) {
|
||||
if (frame == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -882,7 +900,8 @@ int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
|
||||
CopyPixels(frame, curr_canvas);
|
||||
// Add this as a key frame.
|
||||
// Note: we use original rectangle as-is for the first frame.
|
||||
error_code = SetFrame(cache, config, info, &orig_rect, 1, encoded_frame);
|
||||
error_code =
|
||||
SetFrame(cache, config, duration, &orig_rect, 1, encoded_frame);
|
||||
if (error_code != VP8_ENC_OK) {
|
||||
goto End;
|
||||
}
|
||||
@ -892,19 +911,21 @@ int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
|
||||
cache->count_since_key_frame = 0;
|
||||
cache->prev_candidate_undecided = 0;
|
||||
} else {
|
||||
// Store previous canvas.
|
||||
// Store previous to previous and previous canvases.
|
||||
CopyPixels(prev_canvas_disposed, prev_to_prev_canvas_disposed);
|
||||
CopyPixels(curr_canvas, prev_canvas);
|
||||
// Create curr_canvas:
|
||||
// * Start with disposed previous canvas.
|
||||
// * Then blend 'frame' onto it.
|
||||
DisposeFrameRectangle(cache->prev_orig_dispose, &cache->prev_orig_rect,
|
||||
curr_canvas);
|
||||
prev_to_prev_canvas_disposed, curr_canvas);
|
||||
CopyPixels(curr_canvas, prev_canvas_disposed);
|
||||
BlendPixels(frame, &orig_rect, curr_canvas);
|
||||
|
||||
++cache->count_since_key_frame;
|
||||
if (cache->count_since_key_frame <= cache->kmin) {
|
||||
// Add this as a frame rectangle.
|
||||
error_code = SetFrame(cache, config, info, NULL, 0, encoded_frame);
|
||||
error_code = SetFrame(cache, config, duration, NULL, 0, encoded_frame);
|
||||
if (error_code != VP8_ENC_OK) {
|
||||
goto End;
|
||||
}
|
||||
@ -912,21 +933,16 @@ int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
|
||||
cache->flush_count = cache->count - 1;
|
||||
cache->prev_candidate_undecided = 0;
|
||||
} else {
|
||||
WebPMuxFrameInfo full_image_info;
|
||||
int64_t curr_delta;
|
||||
|
||||
// Add frame rectangle to cache.
|
||||
error_code = SetFrame(cache, config, info, NULL, 0, encoded_frame);
|
||||
error_code = SetFrame(cache, config, duration, NULL, 0, encoded_frame);
|
||||
if (error_code != VP8_ENC_OK) {
|
||||
goto End;
|
||||
}
|
||||
|
||||
// Add key frame to cache, too.
|
||||
full_image_info = *info;
|
||||
full_image_info.x_offset = 0;
|
||||
full_image_info.y_offset = 0;
|
||||
error_code =
|
||||
SetFrame(cache, config, &full_image_info, NULL, 1, encoded_frame);
|
||||
error_code = SetFrame(cache, config, duration, NULL, 1, encoded_frame);
|
||||
if (error_code != VP8_ENC_OK) goto End;
|
||||
|
||||
// Analyze size difference of the two variants.
|
||||
@ -951,12 +967,18 @@ int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
|
||||
}
|
||||
cache->prev_candidate_undecided = 1;
|
||||
}
|
||||
// Recalculate prev_canvas_disposed (as it might have been modified).
|
||||
CopyPixels(prev_canvas, prev_canvas_disposed);
|
||||
DisposeFrameRectangle(cache->prev_orig_dispose, &cache->prev_orig_rect,
|
||||
prev_to_prev_canvas_disposed, prev_canvas_disposed);
|
||||
}
|
||||
|
||||
DisposeFullFrame(info->dispose_method, frame);
|
||||
// Dispose the 'frame'.
|
||||
DisposeFrameRectangle(orig_dispose_method, &orig_rect, prev_canvas_disposed,
|
||||
frame);
|
||||
|
||||
cache->is_first_frame = 0;
|
||||
cache->prev_orig_dispose = info->dispose_method;
|
||||
cache->prev_orig_dispose = orig_dispose_method;
|
||||
cache->prev_orig_rect = orig_rect;
|
||||
ok = 1;
|
||||
|
||||
|
@ -29,6 +29,13 @@ extern "C" {
|
||||
|
||||
struct WebPPicture;
|
||||
|
||||
// Includes all disposal methods, even the ones not supported by WebP bitstream.
|
||||
typedef enum FrameDisposeMethod {
|
||||
FRAME_DISPOSE_NONE,
|
||||
FRAME_DISPOSE_BACKGROUND,
|
||||
FRAME_DISPOSE_RESTORE_PREVIOUS
|
||||
} FrameDisposeMethod;
|
||||
|
||||
typedef struct {
|
||||
int x_offset, y_offset, width, height;
|
||||
} WebPFrameRect;
|
||||
@ -53,15 +60,15 @@ WebPFrameCache* WebPFrameCacheNew(int width, int height,
|
||||
// Release all the frame data from 'cache' and free 'cache'.
|
||||
void WebPFrameCacheDelete(WebPFrameCache* const cache);
|
||||
|
||||
// Given an image described by 'frame', 'info' and 'orig_rect', optimize it for
|
||||
// WebP, encode it and add it to 'cache'. 'orig_rect' can be NULL.
|
||||
// This takes care of frame disposal too, according to 'info->dispose_method'.
|
||||
// Given an image described by 'frame', 'rect', 'dispose_method' and 'duration',
|
||||
// optimize it for WebP, encode it and add it to 'cache'. 'rect' can be NULL.
|
||||
// This takes care of frame disposal too, according to 'dispose_method'.
|
||||
// Returns false in case of error (and sets frame->error_code accordingly).
|
||||
int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
|
||||
const WebPConfig* const config,
|
||||
const WebPFrameRect* const orig_rect,
|
||||
WebPPicture* const frame,
|
||||
WebPMuxFrameInfo* const info);
|
||||
const WebPFrameRect* const rect,
|
||||
FrameDisposeMethod dispose_method, int duration,
|
||||
WebPPicture* const frame);
|
||||
|
||||
// Flush the *ready* frames from cache and add them to 'mux'. If 'verbose' is
|
||||
// true, prints the information about these frames.
|
||||
|
Loading…
Reference in New Issue
Block a user