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