1 // Copyright 2011 Google Inc. All Rights Reserved.
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 // -----------------------------------------------------------------------------
10 // Everything about WebPDecBuffer
12 // Author: Skal (pascal.massimino@gmail.com)
18 #include "../utils/utils.h"
20 //------------------------------------------------------------------------------
23 // Number of bytes per pixel for the different color-spaces.
24 static const int kModeBpp
[MODE_LAST
] = {
26 4, 4, 4, 2, // pre-multiplied modes
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.
32 static int IsValidColorspace(int webp_csp_mode
) {
33 return (webp_csp_mode
>= MODE_RGB
&& webp_csp_mode
< MODE_LAST
);
36 static VP8StatusCode
CheckDecBuffer(const WebPDecBuffer
* const buffer
) {
38 const WEBP_CSP_MODE mode
= buffer
->colorspace
;
39 const int width
= buffer
->width
;
40 const int height
= buffer
->height
;
41 if (!IsValidColorspace(mode
)) {
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
);
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
);
75 return ok
? VP8_STATUS_OK
: VP8_STATUS_INVALID_PARAM
;
78 static 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
;
83 if (w
<= 0 || h
<= 0 || !IsValidColorspace(mode
)) {
84 return VP8_STATUS_INVALID_PARAM
;
87 if (!buffer
->is_external_memory
&& buffer
->private_memory
== NULL
) {
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
;
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
) {
101 a_size
= (uint64_t)a_stride
* h
;
104 total_size
= size
+ 2 * uv_size
+ a_size
;
106 // Security/sanity checks
107 output
= (uint8_t*)WebPSafeMalloc(total_size
, sizeof(*output
));
108 if (output
== NULL
) {
109 return VP8_STATUS_OUT_OF_MEMORY
;
111 buffer
->private_memory
= output
;
113 if (!WebPIsRGBMode(mode
)) { // YUVA initialization
114 WebPYUVABuffer
* const buf
= &buffer
->u
.YUVA
;
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
;
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
;
132 buf
->stride
= stride
;
133 buf
->size
= (size_t)size
;
136 return CheckDecBuffer(buffer
);
139 VP8StatusCode
WebPFlipBuffer(WebPDecBuffer
* const buffer
) {
140 if (buffer
== NULL
) {
141 return VP8_STATUS_INVALID_PARAM
;
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
;
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
;
161 return VP8_STATUS_OK
;
164 VP8StatusCode
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
;
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.
183 if (options
->use_scaling
) {
184 if (options
->scaled_width
<= 0 || options
->scaled_height
<= 0) {
185 return VP8_STATUS_INVALID_PARAM
;
187 w
= options
->scaled_width
;
188 h
= options
->scaled_height
;
194 // Then, allocate buffer for real.
195 status
= AllocateBuffer(out
);
196 if (status
!= VP8_STATUS_OK
) return status
;
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
);
207 //------------------------------------------------------------------------------
208 // constructors / destructors
210 int WebPInitDecBufferInternal(WebPDecBuffer
* buffer
, int version
) {
211 if (WEBP_ABI_IS_INCOMPATIBLE(version
, WEBP_DECODER_ABI_VERSION
)) {
212 return 0; // version mismatch
214 if (buffer
== NULL
) return 0;
215 memset(buffer
, 0, sizeof(*buffer
));
219 void WebPFreeDecBuffer(WebPDecBuffer
* buffer
) {
220 if (buffer
!= NULL
) {
221 if (!buffer
->is_external_memory
) {
222 WebPSafeFree(buffer
->private_memory
);
224 buffer
->private_memory
= NULL
;
228 void WebPCopyDecBuffer(const WebPDecBuffer
* const src
,
229 WebPDecBuffer
* const dst
) {
230 if (src
!= NULL
&& dst
!= NULL
) {
232 if (src
->private_memory
!= NULL
) {
233 dst
->is_external_memory
= 1; // dst buffer doesn't own the memory.
234 dst
->private_memory
= NULL
;
239 // Copy and transfer ownership from src to dst (beware of parameter order!)
240 void WebPGrabDecBuffer(WebPDecBuffer
* const src
, WebPDecBuffer
* const dst
) {
241 if (src
!= NULL
&& dst
!= NULL
) {
243 if (src
->private_memory
!= NULL
) {
244 src
->is_external_memory
= 1; // src relinquishes ownership
245 src
->private_memory
= NULL
;
250 //------------------------------------------------------------------------------