blob: a0664a616ce07b7914adcb66fdd012451108e431 [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;
Urvang Joshid78a82c2013-09-16 13:35:13 -070077 assert(kmax > kmin);
Urvang Joshid538cea2013-09-12 13:41:09 -070078 cache->size = kmax - kmin;
79 cache->encoded_frames =
80 (EncodedFrame*)calloc(cache->size, sizeof(*cache->encoded_frames));
Urvang Joshie89c6fc2013-09-16 13:12:33 -070081 if (cache->encoded_frames == NULL) {
82 free(cache);
83 return NULL;
84 }
Urvang Joshid538cea2013-09-12 13:41:09 -070085 return cache;
86}
87
88void WebPFrameCacheDelete(WebPFrameCache* const cache) {
89 if (cache != NULL) {
90 size_t i;
91 for (i = 0; i < cache->size; ++i) {
92 FrameRelease(&cache->encoded_frames[i]);
93 }
94 free(cache->encoded_frames);
95 free(cache);
96 }
97}
98
99static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic,
100 WebPData* const encoded_data) {
101 WebPMemoryWriter memory;
102 pic->use_argb = 1;
103 pic->writer = WebPMemoryWrite;
104 pic->custom_ptr = &memory;
105 WebPMemoryWriterInit(&memory);
106 if (!WebPEncode(config, pic)) {
107 return 0;
108 }
109 encoded_data->bytes = memory.mem;
110 encoded_data->size = memory.size;
111 return 1;
112}
113
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700114// Returns cached frame at given 'position' index.
Urvang Joshid538cea2013-09-12 13:41:09 -0700115static EncodedFrame* CacheGetFrame(const WebPFrameCache* const cache,
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700116 size_t position) {
117 assert(cache->start + position < cache->size);
118 return &cache->encoded_frames[cache->start + position];
Urvang Joshid538cea2013-09-12 13:41:09 -0700119}
120
121// Calculate the penalty incurred if we encode given frame as a key frame
122// instead of a sub-frame.
123static int64_t KeyFramePenalty(const EncodedFrame* const encoded_frame) {
124 return ((int64_t)encoded_frame->key_frame.bitstream.size -
125 encoded_frame->sub_frame.bitstream.size);
126}
127
128static int SetFrame(const WebPConfig* const config,
129 const WebPMuxFrameInfo* const info, WebPPicture* const pic,
130 WebPMuxFrameInfo* const dst) {
131 *dst = *info;
132 if (!EncodeFrame(config, pic, &dst->bitstream)) {
133 return 0;
134 }
135 return 1;
136}
137
138int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
139 const WebPConfig* const config,
140 const WebPMuxFrameInfo* const sub_frame_info,
141 WebPPicture* const sub_frame_pic,
142 const WebPMuxFrameInfo* const key_frame_info,
143 WebPPicture* const key_frame_pic) {
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700144 const size_t position = cache->count;
145 EncodedFrame* const encoded_frame = CacheGetFrame(cache, position);
146 assert(position < cache->size);
Urvang Joshid538cea2013-09-12 13:41:09 -0700147 assert(sub_frame_pic != NULL || key_frame_pic != NULL);
148 if (sub_frame_pic != NULL && !SetFrame(config, sub_frame_info, sub_frame_pic,
149 &encoded_frame->sub_frame)) {
150 return 0;
151 }
152 if (key_frame_pic != NULL && !SetFrame(config, key_frame_info, key_frame_pic,
153 &encoded_frame->key_frame)) {
154 return 0;
155 }
156
157 ++cache->count;
158
159 if (sub_frame_pic == NULL && key_frame_pic != NULL) { // Keyframe.
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700160 cache->keyframe = position;
Urvang Joshid538cea2013-09-12 13:41:09 -0700161 cache->flush_count = cache->count;
162 cache->count_since_key_frame = 0;
163 } else {
164 ++cache->count_since_key_frame;
165 if (sub_frame_pic != NULL && key_frame_pic == NULL) { // Non-keyframe.
166 assert(cache->count_since_key_frame < cache->kmax);
167 cache->flush_count = cache->count;
168 } else { // Analyze size difference of the two variants.
169 const int64_t curr_delta = KeyFramePenalty(encoded_frame);
170 if (curr_delta <= cache->best_delta) { // Pick this as keyframe.
Pascal Massimino2f5e8932013-09-14 02:02:09 -0700171 cache->keyframe = position;
Urvang Joshid538cea2013-09-12 13:41:09 -0700172 cache->best_delta = curr_delta;
173 cache->flush_count = cache->count - 1; // We can flush previous frames.
174 }
175 if (cache->count_since_key_frame == cache->kmax) {
176 cache->flush_count = cache->count;
177 cache->count_since_key_frame = 0;
178 }
179 }
180 }
181
182 return 1;
183}
184
185WebPMuxError WebPFrameCacheFlush(WebPFrameCache* const cache, int verbose,
186 WebPMux* const mux) {
187 while (cache->flush_count > 0) {
188 WebPMuxFrameInfo* info;
189 WebPMuxError err;
190 EncodedFrame* const curr = CacheGetFrame(cache, 0);
191 // Pick frame or full canvas.
192 if (cache->keyframe == 0) {
193 info = &curr->key_frame;
194 info->blend_method = WEBP_MUX_NO_BLEND;
195 cache->keyframe = KEYFRAME_NONE;
196 cache->best_delta = DELTA_INFINITY;
197 } else {
198 info = &curr->sub_frame;
199 info->blend_method = WEBP_MUX_BLEND;
200 }
201 // Add to mux.
202 err = WebPMuxPushFrame(mux, info, 1);
203 if (err != WEBP_MUX_OK) return err;
204 if (verbose) {
205 printf("Added frame. offset:%d,%d duration:%d dispose:%d blend:%d\n",
206 info->x_offset, info->y_offset, info->duration,
207 info->dispose_method, info->blend_method);
208 }
209 FrameRelease(curr);
210 ++cache->start;
211 --cache->flush_count;
212 --cache->count;
213 if (cache->keyframe != KEYFRAME_NONE) --cache->keyframe;
214 }
215
216 if (cache->count == 0) CacheReset(cache);
217 return WEBP_MUX_OK;
218}
219
220WebPMuxError WebPFrameCacheFlushAll(WebPFrameCache* const cache, int verbose,
221 WebPMux* const mux) {
222 cache->flush_count = cache->count; // Force flushing of all frames.
223 return WebPFrameCacheFlush(cache, verbose, mux);
224}
225
226int WebPFrameCacheShouldTryKeyFrame(const WebPFrameCache* const cache) {
227 return cache->count_since_key_frame >= cache->kmin;
228}
229
230//------------------------------------------------------------------------------
231// Frame rectangle and related utilities.
232
233static void ClearRectangle(WebPPicture* const picture,
234 int left, int top, int width, int height) {
235 int j;
236 for (j = top; j < top + height; ++j) {
237 uint32_t* const dst = picture->argb + j * picture->argb_stride;
238 int i;
239 for (i = left; i < left + width; ++i) {
240 dst[i] = TRANSPARENT_COLOR;
241 }
242 }
243}
244
245// Clear pixels in 'picture' within given 'rect' to transparent color.
246void WebPUtilClearPic(WebPPicture* const picture,
247 const WebPFrameRect* const rect) {
248 if (rect != NULL) {
249 ClearRectangle(picture, rect->x_offset, rect->y_offset,
250 rect->width, rect->height);
251 } else {
252 ClearRectangle(picture, 0, 0, picture->width, picture->height);
253 }
254}
255
256// TODO: Also used in picture.c. Move to a common location?
257// Copy width x height pixels from 'src' to 'dst' honoring the strides.
258static void CopyPlane(const uint8_t* src, int src_stride,
259 uint8_t* dst, int dst_stride, int width, int height) {
260 while (height-- > 0) {
261 memcpy(dst, src, width);
262 src += src_stride;
263 dst += dst_stride;
264 }
265}
266
267void WebPUtilCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
268 assert(src->width == dst->width && src->height == dst->height);
269 CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
270 4 * dst->argb_stride, 4 * src->width, src->height);
271}
272
273void WebPUtilBlendPixels(const WebPPicture* const src,
274 const WebPFrameRect* const rect,
275 WebPPicture* const dst) {
276 int j;
277 assert(src->width == dst->width && src->height == dst->height);
278 for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
279 int i;
280 for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
281 const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
282 const int src_alpha = src_pixel >> 24;
283 if (src_alpha != 0) {
284 dst->argb[j * dst->argb_stride + i] = src_pixel;
285 }
286 }
287 }
288}
289
290//------------------------------------------------------------------------------
291// Key frame related utilities.
292
293int WebPUtilIsKeyFrame(const WebPPicture* const curr,
294 const WebPFrameRect* const curr_rect,
295 const WebPPicture* const prev) {
296 int i, j;
297 int is_key_frame = 1;
298
299 // If previous canvas (with previous frame disposed) is all transparent,
300 // current frame is a key frame.
301 for (i = 0; i < prev->width; ++i) {
302 for (j = 0; j < prev->height; ++j) {
303 const uint32_t prev_alpha = (prev->argb[j * prev->argb_stride + i]) >> 24;
304 if (prev_alpha != 0) {
305 is_key_frame = 0;
306 break;
307 }
308 }
309 if (!is_key_frame) break;
310 }
311 if (is_key_frame) return 1;
312
313 // If current frame covers the whole canvas and does not contain any
314 // transparent pixels that depend on previous canvas, then current frame is
315 // a key frame.
316 if (curr_rect->width == curr->width && curr_rect->height == curr->height) {
317 assert(curr_rect->x_offset == 0 && curr_rect->y_offset == 0);
318 is_key_frame = 1;
319 for (j = 0; j < prev->height; ++j) {
320 for (i = 0; i < prev->width; ++i) {
321 const uint32_t prev_alpha =
322 (prev->argb[j * prev->argb_stride + i]) >> 24;
323 const uint32_t curr_alpha =
324 (curr->argb[j * curr->argb_stride + i]) >> 24;
325 if (curr_alpha != 0xff && prev_alpha != 0) {
326 is_key_frame = 0;
327 break;
328 }
329 }
330 if (!is_key_frame) break;
331 }
332 if (is_key_frame) return 1;
333 }
334
335 return 0;
336}
337
338void WebPUtilConvertToKeyFrame(const WebPPicture* const prev,
339 WebPFrameRect* const rect,
340 WebPPicture* const curr) {
341 int j;
342 assert(curr->width == prev->width && curr->height == prev->height);
343
344 // Replace transparent pixels of current canvas with those from previous
345 // canvas (with previous frame disposed).
346 for (j = 0; j < curr->height; ++j) {
347 int i;
348 for (i = 0; i < curr->width; ++i) {
349 uint32_t* const curr_pixel = curr->argb + j * curr->argb_stride + i;
350 const int curr_alpha = *curr_pixel >> 24;
351 if (curr_alpha == 0) {
352 *curr_pixel = prev->argb[j * prev->argb_stride + i];
353 }
354 }
355 }
356
357 // Frame rectangle now covers the whole canvas.
358 rect->x_offset = 0;
359 rect->y_offset = 0;
360 rect->width = curr->width;
361 rect->height = curr->height;
362}
363
364//------------------------------------------------------------------------------