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