1// Copyright 2011 Google Inc. All Rights Reserved.
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// Everything about WebPDecBuffer
9//
10// Author: Skal (pascal.massimino@gmail.com)
11
12#include <stdlib.h>
13
14#include "./vp8i.h"
15#include "./webpi.h"
16#include "../utils/utils.h"
17
18#if defined(__cplusplus) || defined(c_plusplus)
19extern "C" {
20#endif
21
22//------------------------------------------------------------------------------
23// WebPDecBuffer
24
25// Number of bytes per pixel for the different color-spaces.
26static const int kModeBpp[MODE_LAST] = {
27  3, 4, 3, 4, 4, 2, 2,
28  4, 4, 4, 2,    // pre-multiplied modes
29  1, 1 };
30
31// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
32// Convert to an integer to handle both the unsigned/signed enum cases
33// without the need for casting to remove type limit warnings.
34static int IsValidColorspace(int webp_csp_mode) {
35  return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
36}
37
38static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
39  int ok = 1;
40  const WEBP_CSP_MODE mode = buffer->colorspace;
41  const int width = buffer->width;
42  const int height = buffer->height;
43  if (!IsValidColorspace(mode)) {
44    ok = 0;
45  } else if (!WebPIsRGBMode(mode)) {   // YUV checks
46    const WebPYUVABuffer* const buf = &buffer->u.YUVA;
47    const uint64_t y_size = (uint64_t)buf->y_stride * height;
48    const uint64_t u_size = (uint64_t)buf->u_stride * ((height + 1) / 2);
49    const uint64_t v_size = (uint64_t)buf->v_stride * ((height + 1) / 2);
50    const uint64_t a_size = (uint64_t)buf->a_stride * height;
51    ok &= (y_size <= buf->y_size);
52    ok &= (u_size <= buf->u_size);
53    ok &= (v_size <= buf->v_size);
54    ok &= (buf->y_stride >= width);
55    ok &= (buf->u_stride >= (width + 1) / 2);
56    ok &= (buf->v_stride >= (width + 1) / 2);
57    ok &= (buf->y != NULL);
58    ok &= (buf->u != NULL);
59    ok &= (buf->v != NULL);
60    if (mode == MODE_YUVA) {
61      ok &= (buf->a_stride >= width);
62      ok &= (a_size <= buf->a_size);
63      ok &= (buf->a != NULL);
64    }
65  } else {    // RGB checks
66    const WebPRGBABuffer* const buf = &buffer->u.RGBA;
67    const uint64_t size = (uint64_t)buf->stride * height;
68    ok &= (size <= buf->size);
69    ok &= (buf->stride >= width * kModeBpp[mode]);
70    ok &= (buf->rgba != NULL);
71  }
72  return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
73}
74
75static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
76  const int w = buffer->width;
77  const int h = buffer->height;
78  const WEBP_CSP_MODE mode = buffer->colorspace;
79
80  if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) {
81    return VP8_STATUS_INVALID_PARAM;
82  }
83
84  if (!buffer->is_external_memory && buffer->private_memory == NULL) {
85    uint8_t* output;
86    int uv_stride = 0, a_stride = 0;
87    uint64_t uv_size = 0, a_size = 0, total_size;
88    // We need memory and it hasn't been allocated yet.
89    // => initialize output buffer, now that dimensions are known.
90    const int stride = w * kModeBpp[mode];
91    const uint64_t size = (uint64_t)stride * h;
92
93    if (!WebPIsRGBMode(mode)) {
94      uv_stride = (w + 1) / 2;
95      uv_size = (uint64_t)uv_stride * ((h + 1) / 2);
96      if (mode == MODE_YUVA) {
97        a_stride = w;
98        a_size = (uint64_t)a_stride * h;
99      }
100    }
101    total_size = size + 2 * uv_size + a_size;
102
103    // Security/sanity checks
104    output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output));
105    if (output == NULL) {
106      return VP8_STATUS_OUT_OF_MEMORY;
107    }
108    buffer->private_memory = output;
109
110    if (!WebPIsRGBMode(mode)) {   // YUVA initialization
111      WebPYUVABuffer* const buf = &buffer->u.YUVA;
112      buf->y = output;
113      buf->y_stride = stride;
114      buf->y_size = (size_t)size;
115      buf->u = output + size;
116      buf->u_stride = uv_stride;
117      buf->u_size = (size_t)uv_size;
118      buf->v = output + size + uv_size;
119      buf->v_stride = uv_stride;
120      buf->v_size = (size_t)uv_size;
121      if (mode == MODE_YUVA) {
122        buf->a = output + size + 2 * uv_size;
123      }
124      buf->a_size = (size_t)a_size;
125      buf->a_stride = a_stride;
126    } else {  // RGBA initialization
127      WebPRGBABuffer* const buf = &buffer->u.RGBA;
128      buf->rgba = output;
129      buf->stride = stride;
130      buf->size = (size_t)size;
131    }
132  }
133  return CheckDecBuffer(buffer);
134}
135
136VP8StatusCode WebPAllocateDecBuffer(int w, int h,
137                                    const WebPDecoderOptions* const options,
138                                    WebPDecBuffer* const out) {
139  if (out == NULL || w <= 0 || h <= 0) {
140    return VP8_STATUS_INVALID_PARAM;
141  }
142  if (options != NULL) {    // First, apply options if there is any.
143    if (options->use_cropping) {
144      const int cw = options->crop_width;
145      const int ch = options->crop_height;
146      const int x = options->crop_left & ~1;
147      const int y = options->crop_top & ~1;
148      if (x < 0 || y < 0 || cw <= 0 || ch <= 0 || x + cw > w || y + ch > h) {
149        return VP8_STATUS_INVALID_PARAM;   // out of frame boundary.
150      }
151      w = cw;
152      h = ch;
153    }
154    if (options->use_scaling) {
155      if (options->scaled_width <= 0 || options->scaled_height <= 0) {
156        return VP8_STATUS_INVALID_PARAM;
157      }
158      w = options->scaled_width;
159      h = options->scaled_height;
160    }
161  }
162  out->width = w;
163  out->height = h;
164
165  // Then, allocate buffer for real
166  return AllocateBuffer(out);
167}
168
169//------------------------------------------------------------------------------
170// constructors / destructors
171
172int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
173  if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
174    return 0;  // version mismatch
175  }
176  if (buffer == NULL) return 0;
177  memset(buffer, 0, sizeof(*buffer));
178  return 1;
179}
180
181void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
182  if (buffer != NULL) {
183    if (!buffer->is_external_memory)
184      free(buffer->private_memory);
185    buffer->private_memory = NULL;
186  }
187}
188
189void WebPCopyDecBuffer(const WebPDecBuffer* const src,
190                       WebPDecBuffer* const dst) {
191  if (src != NULL && dst != NULL) {
192    *dst = *src;
193    if (src->private_memory != NULL) {
194      dst->is_external_memory = 1;   // dst buffer doesn't own the memory.
195      dst->private_memory = NULL;
196    }
197  }
198}
199
200// Copy and transfer ownership from src to dst (beware of parameter order!)
201void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
202  if (src != NULL && dst != NULL) {
203    *dst = *src;
204    if (src->private_memory != NULL) {
205      src->is_external_memory = 1;   // src relinquishes ownership
206      src->private_memory = NULL;
207    }
208  }
209}
210
211//------------------------------------------------------------------------------
212
213#if defined(__cplusplus) || defined(c_plusplus)
214}    // extern "C"
215#endif
216