blob: 2631969bc31391d0c6380d867f13b074fd133b72 [file] [log] [blame]
Urvang Joshid538cea2013-09-12 13:41:09 -07001// Copyright 2013 Google Inc. All Rights Reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the COPYING file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
8// -----------------------------------------------------------------------------
9//
10// Helper structs and methods for gif2webp tool.
11//
12
13#include <assert.h>
14#include <stdio.h>
15
16#include "webp/encode.h"
17#include "./gif2webp_util.h"
18
19#define DELTA_INFINITY 1ULL << 32
20#define KEYFRAME_NONE -1
21
22//------------------------------------------------------------------------------
23// Encoded frame.
24
25// Used to store two candidates of encoded data for an animation frame. One of
26// the two will be chosen later.
27typedef struct {
28 WebPMuxFrameInfo sub_frame; // Encoded frame rectangle.
29 WebPMuxFrameInfo key_frame; // Encoded frame if it was converted to keyframe.
30} EncodedFrame;
31
32// Release the data contained by 'encoded_frame'.
33static void FrameRelease(EncodedFrame* const encoded_frame) {
34 WebPDataClear(&encoded_frame->sub_frame.bitstream);
35 WebPDataClear(&encoded_frame->key_frame.bitstream);
36 memset(encoded_frame, 0, sizeof(*encoded_frame));
37}
38
39//------------------------------------------------------------------------------
40// Frame cache.
41
42// Used to store encoded frames that haven't been output yet.
43struct WebPFrameCache {
44 EncodedFrame* encoded_frames; // Array of encoded frames.
45 size_t size; // Number of allocated data elements.
46 size_t start; // Start index.
47 size_t count; // Number of valid data elements.
48 int flush_count; // If >0, ‘flush_count’ frames starting from
49 // 'start' are ready to be added to mux.
50 int64_t best_delta; // min(canvas size - frame size) over the frames.
51 // Can be negative in certain cases due to
52 // transparent pixels in a frame.
53 int keyframe; // Index of selected keyframe relative to 'start'.
54
55 size_t kmin; // Min distance between key frames.
56 size_t kmax; // Max distance between key frames.
57 size_t count_since_key_frame; // Frames seen since the last key frame.
58};
59
60// Reset the counters in the cache struct. Doesn't touch 'cache->encoded_frames'
61// and 'cache->size'.
62static void CacheReset(WebPFrameCache* const cache) {
63 cache->start = 0;
64 cache->count = 0;
65 cache->flush_count = 0;
66 cache->best_delta = DELTA_INFINITY;
67 cache->keyframe = KEYFRAME_NONE;
68}
69
70WebPFrameCache* WebPFrameCacheNew(size_t kmin, size_t kmax) {
71 WebPFrameCache* cache = (WebPFrameCache*)malloc(sizeof(*cache));
72 if (cache == NULL) return NULL;
73 CacheReset(cache);
74 cache->kmin = kmin;
75 cache->kmax = kmax;
76 cache->count_since_key_frame = 0;
77 cache->size = kmax - kmin;
78 cache->encoded_frames =
79 (EncodedFrame*)calloc(cache->size, sizeof(*cache->encoded_frames));
Urvang Joshie89c6fc2013-09-16 13:12:33 -070080 if (cache->encoded_frames == NULL) {
81 free(cache);
82 return NULL;
83 }
Urvang Joshid538cea2013-09-12 13:41:09 -070084 return cache;
85}
86
87void WebPFrameCacheDelete(WebPFrameCache* const cache) {
88 if (cache != NULL) {
89 size_t i;
90 for (i = 0; i < cache->size; ++i) {
91 FrameRelease(&cache->encoded_frames[i]);
92 }
93 free(cache->encoded_frames);
94 free(cache);
95 }
96}
97
98static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic,
99 WebPData* const encoded_data) {
100 WebPMemoryWriter memory;
101 pic->use_argb = 1;
102 pic->writer = WebPMemoryWrite;
103 pic->custom_ptr = &memory;
104 WebPMemoryWriterInit(&memory);
105 if (!WebPEncode(config, pic)) {
106 return 0;
107 }
108 encoded_data->bytes = memory.mem;
109 encoded_data->size = memory.size;
110 return 1;
111}
112
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700113// Returns cached frame at given 'position' index.
Urvang Joshid538cea2013-09-12 13:41:09 -0700114static EncodedFrame* CacheGetFrame(const WebPFrameCache* const cache,
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700115 size_t position) {
116 assert(cache->start + position < cache->size);
117 return &cache->encoded_frames[cache->start + position];
Urvang Joshid538cea2013-09-12 13:41:09 -0700118}
119
120// Calculate the penalty incurred if we encode given frame as a key frame
121// instead of a sub-frame.
122static int64_t KeyFramePenalty(const EncodedFrame* const encoded_frame) {
123 return ((int64_t)encoded_frame->key_frame.bitstream.size -
124 encoded_frame->sub_frame.bitstream.size);
125}
126
127static int SetFrame(const WebPConfig* const config,
128 const WebPMuxFrameInfo* const info, WebPPicture* const pic,
129 WebPMuxFrameInfo* const dst) {
130 *dst = *info;
131 if (!EncodeFrame(config, pic, &dst->bitstream)) {
132 return 0;
133 }
134 return 1;
135}
136
137int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
138 const WebPConfig* const config,
139 const WebPMuxFrameInfo* const sub_frame_info,
140 WebPPicture* const sub_frame_pic,
141 const WebPMuxFrameInfo* const key_frame_info,
142 WebPPicture* const key_frame_pic) {
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700143 const size_t position = cache->count;
144 EncodedFrame* const encoded_frame = CacheGetFrame(cache, position);
145 assert(position < cache->size);
Urvang Joshid538cea2013-09-12 13:41:09 -0700146 assert(sub_frame_pic != NULL || key_frame_pic != NULL);
147 if (sub_frame_pic != NULL && !SetFrame(config, sub_frame_info, sub_frame_pic,
148 &encoded_frame->sub_frame)) {
149 return 0;
150 }
151 if (key_frame_pic != NULL && !SetFrame(config, key_frame_info, key_frame_pic,
152 &encoded_frame->key_frame)) {
153 return 0;
154 }
155
156 ++cache->count;
157
158 if (sub_frame_pic == NULL && key_frame_pic != NULL) { // Keyframe.
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700159 cache->keyframe = position;
Urvang Joshid538cea2013-09-12 13:41:09 -0700160 cache->flush_count = cache->count;
161 cache->count_since_key_frame = 0;
162 } else {
163 ++cache->count_since_key_frame;
164 if (sub_frame_pic != NULL && key_frame_pic == NULL) { // Non-keyframe.
165 assert(cache->count_since_key_frame < cache->kmax);
166 cache->flush_count = cache->count;
167 } else { // Analyze size difference of the two variants.
168 const int64_t curr_delta = KeyFramePenalty(encoded_frame);
169 if (curr_delta <= cache->best_delta) { // Pick this as keyframe.
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700170 cache->keyframe = position;
Urvang Joshid538cea2013-09-12 13:41:09 -0700171 cache->best_delta = curr_delta;
172 cache->flush_count = cache->count - 1; // We can flush previous frames.
173 }
174 if (cache->count_since_key_frame == cache->kmax) {
175 cache->flush_count = cache->count;
176 cache->count_since_key_frame = 0;
177 }
178 }
179 }
180
181 return 1;
182}
183
184WebPMuxError WebPFrameCacheFlush(WebPFrameCache* const cache, int verbose,
185 WebPMux* const mux) {
186 while (cache->flush_count > 0) {
187 WebPMuxFrameInfo* info;
188 WebPMuxError err;
189 EncodedFrame* const curr = CacheGetFrame(cache, 0);
190 // Pick frame or full canvas.
191 if (cache->keyframe == 0) {
192 info = &curr->key_frame;
193 info->blend_method = WEBP_MUX_NO_BLEND;
194 cache->keyframe = KEYFRAME_NONE;
195 cache->best_delta = DELTA_INFINITY;
196 } else {
197 info = &curr->sub_frame;
198 info->blend_method = WEBP_MUX_BLEND;
199 }
200 // Add to mux.
201 err = WebPMuxPushFrame(mux, info, 1);
202 if (err != WEBP_MUX_OK) return err;
203 if (verbose) {
204 printf("Added frame. offset:%d,%d duration:%d dispose:%d blend:%d\n",
205 info->x_offset, info->y_offset, info->duration,
206 info->dispose_method, info->blend_method);
207 }
208 FrameRelease(curr);
209 ++cache->start;
210 --cache->flush_count;
211 --cache->count;
212 if (cache->keyframe != KEYFRAME_NONE) --cache->keyframe;
213 }
214
215 if (cache->count == 0) CacheReset(cache);
216 return WEBP_MUX_OK;
217}
218
219WebPMuxError WebPFrameCacheFlushAll(WebPFrameCache* const cache, int verbose,
220 WebPMux* const mux) {
221 cache->flush_count = cache->count; // Force flushing of all frames.
222 return WebPFrameCacheFlush(cache, verbose, mux);
223}
224
225int WebPFrameCacheShouldTryKeyFrame(const WebPFrameCache* const cache) {
226 return cache->count_since_key_frame >= cache->kmin;
227}
228
229//------------------------------------------------------------------------------
230// Frame rectangle and related utilities.
231
232static void ClearRectangle(WebPPicture* const picture,
233 int left, int top, int width, int height) {
234 int j;
235 for (j = top; j < top + height; ++j) {
236 uint32_t* const dst = picture->argb + j * picture->argb_stride;
237 int i;
238 for (i = left; i < left + width; ++i) {
239 dst[i] = TRANSPARENT_COLOR;
240 }
241 }
242}
243
244// Clear pixels in 'picture' within given 'rect' to transparent color.
245void WebPUtilClearPic(WebPPicture* const picture,
246 const WebPFrameRect* const rect) {
247 if (rect != NULL) {
248 ClearRectangle(picture, rect->x_offset, rect->y_offset,
249 rect->width, rect->height);
250 } else {
251 ClearRectangle(picture, 0, 0, picture->width, picture->height);
252 }
253}
254
255// TODO: Also used in picture.c. Move to a common location?
256// Copy width x height pixels from 'src' to 'dst' honoring the strides.
257static void CopyPlane(const uint8_t* src, int src_stride,
258 uint8_t* dst, int dst_stride, int width, int height) {
259 while (height-- > 0) {
260 memcpy(dst, src, width);
261 src += src_stride;
262 dst += dst_stride;
263 }
264}
265
266void WebPUtilCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
267 assert(src->width == dst->width && src->height == dst->height);
268 CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
269 4 * dst->argb_stride, 4 * src->width, src->height);
270}
271
272void WebPUtilBlendPixels(const WebPPicture* const src,
273 const WebPFrameRect* const rect,
274 WebPPicture* const dst) {
275 int j;
276 assert(src->width == dst->width && src->height == dst->height);
277 for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
278 int i;
279 for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
280 const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
281 const int src_alpha = src_pixel >> 24;
282 if (src_alpha != 0) {
283 dst->argb[j * dst->argb_stride + i] = src_pixel;
284 }
285 }
286 }
287}
288
289//------------------------------------------------------------------------------
290// Key frame related utilities.
291
292int WebPUtilIsKeyFrame(const WebPPicture* const curr,
293 const WebPFrameRect* const curr_rect,
294 const WebPPicture* const prev) {
295 int i, j;
296 int is_key_frame = 1;
297
298 // If previous canvas (with previous frame disposed) is all transparent,
299 // current frame is a key frame.
300 for (i = 0; i < prev->width; ++i) {
301 for (j = 0; j < prev->height; ++j) {
302 const uint32_t prev_alpha = (prev->argb[j * prev->argb_stride + i]) >> 24;
303 if (prev_alpha != 0) {
304 is_key_frame = 0;
305 break;
306 }
307 }
308 if (!is_key_frame) break;
309 }
310 if (is_key_frame) return 1;
311
312 // If current frame covers the whole canvas and does not contain any
313 // transparent pixels that depend on previous canvas, then current frame is
314 // a key frame.
315 if (curr_rect->width == curr->width && curr_rect->height == curr->height) {
316 assert(curr_rect->x_offset == 0 && curr_rect->y_offset == 0);
317 is_key_frame = 1;
318 for (j = 0; j < prev->height; ++j) {
319 for (i = 0; i < prev->width; ++i) {
320 const uint32_t prev_alpha =
321 (prev->argb[j * prev->argb_stride + i]) >> 24;
322 const uint32_t curr_alpha =
323 (curr->argb[j * curr->argb_stride + i]) >> 24;
324 if (curr_alpha != 0xff && prev_alpha != 0) {
325 is_key_frame = 0;
326 break;
327 }
328 }
329 if (!is_key_frame) break;
330 }
331 if (is_key_frame) return 1;
332 }
333
334 return 0;
335}
336
337void WebPUtilConvertToKeyFrame(const WebPPicture* const prev,
338 WebPFrameRect* const rect,
339 WebPPicture* const curr) {
340 int j;
341 assert(curr->width == prev->width && curr->height == prev->height);
342
343 // Replace transparent pixels of current canvas with those from previous
344 // canvas (with previous frame disposed).
345 for (j = 0; j < curr->height; ++j) {
346 int i;
347 for (i = 0; i < curr->width; ++i) {
348 uint32_t* const curr_pixel = curr->argb + j * curr->argb_stride + i;
349 const int curr_alpha = *curr_pixel >> 24;
350 if (curr_alpha == 0) {
351 *curr_pixel = prev->argb[j * prev->argb_stride + i];
352 }
353 }
354 }
355
356 // Frame rectangle now covers the whole canvas.
357 rect->x_offset = 0;
358 rect->y_offset = 0;
359 rect->width = curr->width;
360 rect->height = curr->height;
361}
362
363//------------------------------------------------------------------------------