1// Copyright 2010 Google Inc.
2//
3// This code is licensed under the same terms as WebM:
4//  Software License Agreement:  http://www.webmproject.org/license/software/
5//  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Main decoding functions for WEBP images.
9//
10// Author: Skal (pascal.massimino@gmail.com)
11
12#include <stdlib.h>
13#include "vp8i.h"
14#include "yuv.h"
15
16#if defined(__cplusplus) || defined(c_plusplus)
17extern "C" {
18#endif
19
20#define FANCY_UPSCALING   // undefined to remove fancy upscaling support
21
22//-----------------------------------------------------------------------------
23// RIFF layout is:
24//   0ffset  tag
25//   0...3   "RIFF" 4-byte tag
26//   4...7   size of image data (including metadata) starting at offset 8
27//   8...11  "WEBP"   our form-type signature
28//   12..15  "VP8 ": 4-bytes tags, describing the raw video format used
29//   16..19  size of the raw VP8 image data, starting at offset 20
30//   20....  the VP8 bytes
31// There can be extra chunks after the "VP8 " chunk (ICMT, ICOP, ...)
32// All 32-bits sizes are in little-endian order.
33// Note: chunk data must be padded to multiple of 2 in size
34
35static inline uint32_t get_le32(const uint8_t* const data) {
36  return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
37}
38
39// If a RIFF container is detected, validate it and skip over it.
40static uint32_t CheckRIFFHeader(const uint8_t** data_ptr,
41                                uint32_t *data_size_ptr) {
42  uint32_t chunk_size = 0xffffffffu;
43  if (*data_size_ptr >= 10 + 20 && !memcmp(*data_ptr, "RIFF", 4)) {
44    if (memcmp(*data_ptr + 8, "WEBP", 4)) {
45      return 0;  // wrong image file signature
46    } else {
47      const uint32_t riff_size = get_le32(*data_ptr + 4);
48      if (riff_size < 12) {
49        return 0;   // we should have at least one chunk
50      }
51      if (memcmp(*data_ptr + 12, "VP8 ", 4)) {
52        return 0;   // invalid compression format
53      }
54      chunk_size = get_le32(*data_ptr + 16);
55      if (chunk_size > riff_size - 12) {
56        return 0;  // inconsistent size information.
57      }
58      // We have a RIFF container. Skip it.
59      *data_ptr += 20;
60      *data_size_ptr -= 20;
61      // Note: we don't report error for odd-sized chunks.
62    }
63    return chunk_size;
64  }
65  return *data_size_ptr;
66}
67
68//-----------------------------------------------------------------------------
69// Fancy upscaling
70
71typedef enum { MODE_RGB = 0, MODE_RGBA = 1,
72               MODE_BGR = 2, MODE_BGRA = 3,
73               MODE_YUV = 4 } CSP_MODE;
74
75#ifdef FANCY_UPSCALING
76
77// Given samples laid out in a square as:
78//  [a b]
79//  [c d]
80// we interpolate u/v as:
81//  ([9*a + 3*b + 3*c +   d    3*a + 9*b + 3*c +   d] + [8 8]) / 16
82//  ([3*a +   b + 9*c + 3*d      a + 3*b + 3*c + 9*d]   [8 8]) / 16
83
84// We process u and v together stashed into 32bit (16bit each).
85#define LOAD_UV(u,v) ((u) | ((v) << 16))
86
87#define UPSCALE_FUNC(FUNC_NAME, FUNC, XSTEP)                                   \
88static inline void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y,    \
89                             const uint8_t* top_u, const uint8_t* top_v,       \
90                             const uint8_t* cur_u, const uint8_t* cur_v,       \
91                             uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
92  int x;                                                                       \
93  const int last_pixel_pair = (len - 1) >> 1;                                  \
94  uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]);   /* top-left sample */        \
95  uint32_t l_uv  = LOAD_UV(cur_u[0], cur_v[0]);   /* left-sample */            \
96  if (top_y) {                                                                 \
97    const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2;                \
98    FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst);                          \
99  }                                                                            \
100  if (bottom_y) {                                                              \
101    const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2;                \
102    FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst);                    \
103  }                                                                            \
104  for (x = 1; x <= last_pixel_pair; ++x) {                                     \
105    const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]);  /* top sample */       \
106    const uint32_t uv   = LOAD_UV(cur_u[x], cur_v[x]);  /* sample */           \
107    /* precompute invariant values associated with first and second diagonals*/\
108    const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u;               \
109    const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3;                   \
110    const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3;                    \
111    if (top_y) {                                                               \
112      const uint32_t uv0 = (diag_12 + tl_uv) >> 1;                             \
113      const uint32_t uv1 = (diag_03 + t_uv) >> 1;                              \
114      FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16),                          \
115           top_dst + (2 * x - 1) * XSTEP);                                     \
116      FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16),                          \
117           top_dst + (2 * x - 0) * XSTEP);                                     \
118    }                                                                          \
119    if (bottom_y) {                                                            \
120      const uint32_t uv0 = (diag_03 + l_uv) >> 1;                              \
121      const uint32_t uv1 = (diag_12 + uv) >> 1;                                \
122      FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16),                       \
123           bottom_dst + (2 * x - 1) * XSTEP);                                  \
124      FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16),                       \
125           bottom_dst + (2 * x + 0) * XSTEP);                                  \
126    }                                                                          \
127    tl_uv = t_uv;                                                              \
128    l_uv = uv;                                                                 \
129  }                                                                            \
130  if (!(len & 1)) {                                                            \
131    if (top_y) {                                                               \
132      const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2;              \
133      FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16),                            \
134           top_dst + (len - 1) * XSTEP);                                       \
135    }                                                                          \
136    if (bottom_y) {                                                            \
137      const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2;              \
138      FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16),                         \
139           bottom_dst + (len - 1) * XSTEP);                                    \
140    }                                                                          \
141  }                                                                            \
142}
143
144// All variants implemented.
145UPSCALE_FUNC(UpscaleRgbLinePair,  VP8YuvToRgb,  3)
146UPSCALE_FUNC(UpscaleBgrLinePair,  VP8YuvToBgr,  3)
147UPSCALE_FUNC(UpscaleRgbaLinePair, VP8YuvToRgba, 4)
148UPSCALE_FUNC(UpscaleBgraLinePair, VP8YuvToBgra, 4)
149
150// Main driver function.
151static inline
152void UpscaleLinePair(const uint8_t* top_y, const uint8_t* bottom_y,
153                     const uint8_t* top_u, const uint8_t* top_v,
154                     const uint8_t* cur_u, const uint8_t* cur_v,
155                     uint8_t* top_dst, uint8_t* bottom_dst, int len,
156                     CSP_MODE mode) {
157  if (mode == MODE_RGB) {
158    UpscaleRgbLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
159                       top_dst, bottom_dst, len);
160  } else if (mode == MODE_BGR) {
161    UpscaleBgrLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
162                       top_dst, bottom_dst, len);
163  } else if (mode == MODE_RGBA) {
164    UpscaleRgbaLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
165                        top_dst, bottom_dst, len);
166  } else {
167    assert(mode == MODE_BGRA);
168    UpscaleBgraLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
169                        top_dst, bottom_dst, len);
170  }
171}
172
173#undef LOAD_UV
174#undef UPSCALE_FUNC
175
176#endif  // FANCY_UPSCALING
177
178//-----------------------------------------------------------------------------
179// Main conversion driver.
180
181typedef struct {
182  uint8_t* output;      // rgb(a) or luma
183  uint8_t *u, *v;
184  uint8_t *top_y, *top_u, *top_v;
185  int stride;           // rgb(a) stride or luma stride
186  int u_stride;
187  int v_stride;
188  CSP_MODE mode;
189} Params;
190
191static int CustomPut(const VP8Io* io) {
192  Params *p = (Params*)io->opaque;
193  const int w = io->width;
194  const int mb_h = io->mb_h;
195  const int uv_w = (w + 1) / 2;
196  assert(!(io->mb_y & 1));
197
198  if (w <= 0 || mb_h <= 0) {
199    return 0;
200  }
201
202  if (p->mode == MODE_YUV) {
203    uint8_t* const y_dst = p->output + io->mb_y * p->stride;
204    uint8_t* const u_dst = p->u + (io->mb_y >> 1) * p->u_stride;
205    uint8_t* const v_dst = p->v + (io->mb_y >> 1) * p->v_stride;
206    int j;
207    for (j = 0; j < mb_h; ++j) {
208      memcpy(y_dst + j * p->stride, io->y + j * io->y_stride, w);
209    }
210    for (j = 0; j < (mb_h + 1) / 2; ++j) {
211      memcpy(u_dst + j * p->u_stride, io->u + j * io->uv_stride, uv_w);
212      memcpy(v_dst + j * p->v_stride, io->v + j * io->uv_stride, uv_w);
213    }
214  } else {
215    uint8_t* dst = p->output + io->mb_y * p->stride;
216    if (io->fancy_upscaling) {
217#ifdef FANCY_UPSCALING
218      const uint8_t* cur_y = io->y;
219      const uint8_t* cur_u = io->u;
220      const uint8_t* cur_v = io->v;
221      const uint8_t* top_u = p->top_u;
222      const uint8_t* top_v = p->top_v;
223      int y = io->mb_y;
224      int y_end = io->mb_y + io->mb_h;
225      if (y == 0) {
226        // First line is special cased. We mirror the u/v samples at boundary.
227        UpscaleLinePair(NULL, cur_y, cur_u, cur_v, cur_u, cur_v,
228                        NULL, dst, w, p->mode);
229      } else {
230        // We can finish the left-over line from previous call
231        UpscaleLinePair(p->top_y, cur_y, top_u, top_v, cur_u, cur_v,
232                        dst - p->stride, dst, w, p->mode);
233      }
234      // Loop over each output pairs of row.
235      for (; y + 2 < y_end; y += 2) {
236        top_u = cur_u;
237        top_v = cur_v;
238        cur_u += io->uv_stride;
239        cur_v += io->uv_stride;
240        dst += 2 * p->stride;
241        cur_y += 2 * io->y_stride;
242        UpscaleLinePair(cur_y - io->y_stride, cur_y,
243                        top_u, top_v, cur_u, cur_v,
244                        dst - p->stride, dst, w, p->mode);
245      }
246      // move to last row
247      cur_y += io->y_stride;
248      if (y_end != io->height) {
249        // Save the unfinished samples for next call (as we're not done yet).
250        memcpy(p->top_y, cur_y, w * sizeof(*p->top_y));
251        memcpy(p->top_u, cur_u, uv_w * sizeof(*p->top_u));
252        memcpy(p->top_v, cur_v, uv_w * sizeof(*p->top_v));
253      } else {
254        // Process the very last row of even-sized picture
255        if (!(y_end & 1)) {
256          UpscaleLinePair(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
257                          dst + p->stride, NULL, w, p->mode);
258        }
259      }
260#else
261      assert(0);  // shouldn't happen.
262#endif
263    } else {
264      // Point-sampling U/V upscaler.
265      int j;
266      for (j = 0; j < mb_h; ++j) {
267        const uint8_t* y_src = io->y + j * io->y_stride;
268        int i;
269        for (i = 0; i < w; ++i) {
270          const int y = y_src[i];
271          const int u = io->u[(j / 2) * io->uv_stride + (i / 2)];
272          const int v = io->v[(j / 2) * io->uv_stride + (i / 2)];
273          if (p->mode == MODE_RGB) {
274            VP8YuvToRgb(y, u, v, dst + i * 3);
275          } else if (p->mode == MODE_BGR) {
276            VP8YuvToBgr(y, u, v, dst + i * 3);
277          } else if (p->mode == MODE_RGBA) {
278            VP8YuvToRgba(y, u, v, dst + i * 4);
279          } else {
280            VP8YuvToBgra(y, u, v, dst + i * 4);
281          }
282        }
283        dst += p->stride;
284      }
285    }
286  }
287  return 1;
288}
289
290//-----------------------------------------------------------------------------
291
292static int CustomSetup(VP8Io* io) {
293#ifdef FANCY_UPSCALING
294  Params *p = (Params*)io->opaque;
295  p->top_y = p->top_u = p->top_v = NULL;
296  if (p->mode != MODE_YUV) {
297    const int uv_width = (io->width + 1) >> 1;
298    p->top_y = (uint8_t*)malloc(io->width + 2 * uv_width);
299    if (p->top_y == NULL) {
300      return 0;   // memory error.
301    }
302    p->top_u = p->top_y + io->width;
303    p->top_v = p->top_u + uv_width;
304    io->fancy_upscaling = 1;  // activate fancy upscaling
305  }
306#endif
307  return 1;
308}
309
310static void CustomTeardown(const VP8Io* io) {
311#ifdef FANCY_UPSCALING
312  Params *p = (Params*)io->opaque;
313  if (p->top_y) {
314    free(p->top_y);
315    p->top_y = p->top_u = p->top_v = NULL;
316  }
317#endif
318}
319
320//-----------------------------------------------------------------------------
321// "Into" variants
322
323static uint8_t* DecodeInto(CSP_MODE mode,
324                           const uint8_t* data, uint32_t data_size,
325                           Params* params, int output_size,
326                           int output_u_size, int output_v_size) {
327  VP8Decoder* dec = VP8New();
328  VP8Io io;
329  int ok = 1;
330
331  if (dec == NULL) {
332    return NULL;
333  }
334
335  VP8InitIo(&io);
336  io.data = data;
337  io.data_size = data_size;
338
339  params->mode = mode;
340  io.opaque = params;
341  io.put = CustomPut;
342  io.setup = CustomSetup;
343  io.teardown = CustomTeardown;
344
345  if (!VP8GetHeaders(dec, &io)) {
346    VP8Delete(dec);
347    return NULL;
348  }
349  // check output buffers
350
351  ok &= (params->stride * io.height <= output_size);
352  if (mode == MODE_RGB || mode == MODE_BGR) {
353    ok &= (params->stride >= io.width * 3);
354  } else if (mode == MODE_RGBA || mode == MODE_BGRA) {
355    ok &= (params->stride >= io.width * 4);
356  } else {
357    // some extra checks for U/V
358    const int u_size = params->u_stride * ((io.height + 1) / 2);
359    const int v_size = params->v_stride * ((io.height + 1) / 2);
360    ok &= (params->stride >= io.width);
361    ok &= (params->u_stride >= (io.width + 1) / 2) &&
362          (params->v_stride >= (io.width + 1) / 2);
363    ok &= (u_size <= output_u_size && v_size <= output_v_size);
364  }
365  if (!ok) {
366    VP8Delete(dec);
367    return NULL;
368  }
369
370  if (mode != MODE_YUV) {
371    VP8YUVInit();
372  }
373
374  ok = VP8Decode(dec, &io);
375  VP8Delete(dec);
376  return ok ? params->output : NULL;
377}
378
379uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size,
380                           uint8_t* output, int output_size,
381                           int output_stride) {
382  Params params;
383
384  if (output == NULL) {
385    return NULL;
386  }
387
388  params.output = output;
389  params.stride = output_stride;
390  return DecodeInto(MODE_RGB, data, data_size, &params, output_size, 0, 0);
391}
392
393uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size,
394                            uint8_t* output, int output_size,
395                            int output_stride) {
396  Params params;
397
398  if (output == NULL) {
399    return NULL;
400  }
401
402  params.output = output;
403  params.stride = output_stride;
404  return DecodeInto(MODE_RGBA, data, data_size, &params, output_size, 0, 0);
405}
406
407uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size,
408                           uint8_t* output, int output_size,
409                           int output_stride) {
410  Params params;
411
412  if (output == NULL) {
413    return NULL;
414  }
415
416  params.output = output;
417  params.stride = output_stride;
418  return DecodeInto(MODE_BGR, data, data_size, &params, output_size, 0, 0);
419}
420
421uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size,
422                            uint8_t* output, int output_size,
423                            int output_stride) {
424  Params params;
425
426  if (output == NULL) {
427    return NULL;
428  }
429
430  params.output = output;
431  params.stride = output_stride;
432  return DecodeInto(MODE_BGRA, data, data_size, &params, output_size, 0, 0);
433}
434
435uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
436                           uint8_t* luma, int luma_size, int luma_stride,
437                           uint8_t* u, int u_size, int u_stride,
438                           uint8_t* v, int v_size, int v_stride) {
439  Params params;
440
441  if (luma == NULL) {
442    return NULL;
443  }
444
445  params.output = luma;
446  params.stride = luma_stride;
447  params.u = u;
448  params.u_stride = u_stride;
449  params.v = v;
450  params.v_stride = v_stride;
451  return DecodeInto(MODE_YUV, data, data_size, &params,
452                    luma_size, u_size, v_size);
453}
454
455//-----------------------------------------------------------------------------
456
457static uint8_t* Decode(CSP_MODE mode, const uint8_t* data, uint32_t data_size,
458                       int* width, int* height, Params* params_out) {
459  int w, h, stride;
460  int uv_size = 0;
461  int uv_stride = 0;
462  int size;
463  uint8_t* output;
464  Params params = { 0 };
465
466  if (!WebPGetInfo(data, data_size, &w, &h)) {
467    return NULL;
468  }
469  if (width) *width = w;
470  if (height) *height = h;
471
472  // initialize output buffer, now that dimensions are known.
473  stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w
474             : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w
475             : w;
476  size = stride * h;
477
478  if (mode == MODE_YUV) {
479    uv_stride = (w + 1) / 2;
480    uv_size = uv_stride * ((h + 1) / 2);
481  }
482
483  output = (uint8_t*)malloc(size + 2 * uv_size);
484  if (!output) {
485    return NULL;
486  }
487
488  params.output = output;
489  params.stride = stride;
490  if (mode == MODE_YUV) {
491    params.u = output + size;
492    params.u_stride = uv_stride;
493    params.v = output + size + uv_size;
494    params.v_stride = uv_stride;
495  }
496  if (params_out) *params_out = params;
497  return DecodeInto(mode, data, data_size, &params, size, uv_size, uv_size);
498}
499
500uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size,
501                       int *width, int *height) {
502  return Decode(MODE_RGB, data, data_size, width, height, NULL);
503}
504
505uint8_t* WebPDecodeRGBA(const uint8_t* data, uint32_t data_size,
506                        int *width, int *height) {
507  return Decode(MODE_RGBA, data, data_size, width, height, NULL);
508}
509
510uint8_t* WebPDecodeBGR(const uint8_t* data, uint32_t data_size,
511                       int *width, int *height) {
512  return Decode(MODE_BGR, data, data_size, width, height, NULL);
513}
514
515uint8_t* WebPDecodeBGRA(const uint8_t* data, uint32_t data_size,
516                        int *width, int *height) {
517  return Decode(MODE_BGRA, data, data_size, width, height, NULL);
518}
519
520uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size,
521                       int *width, int *height, uint8_t** u, uint8_t** v,
522                       int *stride, int* uv_stride) {
523  Params params;
524  uint8_t* const out = Decode(MODE_YUV, data, data_size,
525                              width, height, &params);
526
527  if (out) {
528    *u = params.u;
529    *v = params.v;
530    *stride = params.stride;
531    *uv_stride = params.u_stride;
532    assert(params.u_stride == params.v_stride);
533  }
534  return out;
535}
536
537//-----------------------------------------------------------------------------
538// WebPGetInfo()
539
540int WebPGetInfo(const uint8_t* data, uint32_t data_size,
541                int *width, int *height) {
542  const uint32_t chunk_size = CheckRIFFHeader(&data, &data_size);
543  if (!chunk_size) {
544    return 0;         // unsupported RIFF header
545  }
546  // Validate raw video data
547  if (data_size < 10) {
548    return 0;         // not enough data
549  }
550  // check signature
551  if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) {
552    return 0;         // Wrong signature.
553  } else {
554    const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
555    const int key_frame = !(bits & 1);
556    const int w = ((data[7] << 8) | data[6]) & 0x3fff;
557    const int h = ((data[9] << 8) | data[8]) & 0x3fff;
558
559    if (!key_frame) {   // Not a keyframe.
560      return 0;
561    }
562
563    if (((bits >> 1) & 7) > 3) {
564      return 0;         // unknown profile
565    }
566    if (!((bits >> 4) & 1)) {
567      return 0;         // first frame is invisible!
568    }
569    if (((bits >> 5)) >= chunk_size) {  // partition_length
570      return 0;         // inconsistent size information.
571    }
572
573    if (width) {
574      *width = w;
575    }
576    if (height) {
577      *height = h;
578    }
579
580    return 1;
581  }
582}
583
584#if defined(__cplusplus) || defined(c_plusplus)
585}    // extern "C"
586#endif
587