buffer_dec.c (10765B)
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 <assert.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include "src/dec/vp8i_dec.h" 19 #include "src/dec/webpi_dec.h" 20 #include "src/utils/rescaler_utils.h" 21 #include "src/utils/utils.h" 22 #include "src/webp/decode.h" 23 #include "src/webp/types.h" 24 25 //------------------------------------------------------------------------------ 26 // WebPDecBuffer 27 28 // Number of bytes per pixel for the different color-spaces. 29 static const uint8_t kModeBpp[MODE_LAST] = { 30 3, 4, 3, 4, 4, 2, 2, 31 4, 4, 4, 2, // pre-multiplied modes 32 1, 1 }; 33 34 // Convert to an integer to handle both the unsigned/signed enum cases 35 // without the need for casting to remove type limit warnings. 36 int IsValidColorspace(int webp_csp_mode) { 37 return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST); 38 } 39 40 // strictly speaking, the very last (or first, if flipped) row 41 // doesn't require padding. 42 #define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \ 43 ((uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH)) 44 45 static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) { 46 int ok = 1; 47 const WEBP_CSP_MODE mode = buffer->colorspace; 48 const int width = buffer->width; 49 const int height = buffer->height; 50 if (!IsValidColorspace(mode)) { 51 ok = 0; 52 } else if (!WebPIsRGBMode(mode)) { // YUV checks 53 const WebPYUVABuffer* const buf = &buffer->u.YUVA; 54 const int uv_width = (width + 1) / 2; 55 const int uv_height = (height + 1) / 2; 56 const int y_stride = abs(buf->y_stride); 57 const int u_stride = abs(buf->u_stride); 58 const int v_stride = abs(buf->v_stride); 59 const int a_stride = abs(buf->a_stride); 60 const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride); 61 const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride); 62 const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride); 63 const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride); 64 ok &= (y_size <= buf->y_size); 65 ok &= (u_size <= buf->u_size); 66 ok &= (v_size <= buf->v_size); 67 ok &= (y_stride >= width); 68 ok &= (u_stride >= uv_width); 69 ok &= (v_stride >= uv_width); 70 ok &= (buf->y != NULL); 71 ok &= (buf->u != NULL); 72 ok &= (buf->v != NULL); 73 if (mode == MODE_YUVA) { 74 ok &= (a_stride >= width); 75 ok &= (a_size <= buf->a_size); 76 ok &= (buf->a != NULL); 77 } 78 } else { // RGB checks 79 const WebPRGBABuffer* const buf = &buffer->u.RGBA; 80 const int stride = abs(buf->stride); 81 const uint64_t size = 82 MIN_BUFFER_SIZE((uint64_t)width * kModeBpp[mode], height, stride); 83 ok &= (size <= buf->size); 84 ok &= (stride >= width * kModeBpp[mode]); 85 ok &= (buf->rgba != NULL); 86 } 87 return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM; 88 } 89 #undef MIN_BUFFER_SIZE 90 91 static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { 92 const int w = buffer->width; 93 const int h = buffer->height; 94 const WEBP_CSP_MODE mode = buffer->colorspace; 95 96 if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) { 97 return VP8_STATUS_INVALID_PARAM; 98 } 99 100 if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) { 101 uint8_t* output; 102 int uv_stride = 0, a_stride = 0; 103 uint64_t uv_size = 0, a_size = 0, total_size; 104 // We need memory and it hasn't been allocated yet. 105 // => initialize output buffer, now that dimensions are known. 106 int stride; 107 uint64_t size; 108 109 if ((uint64_t)w * kModeBpp[mode] >= (1ull << 31)) { 110 return VP8_STATUS_INVALID_PARAM; 111 } 112 stride = w * kModeBpp[mode]; 113 size = (uint64_t)stride * h; 114 if (!WebPIsRGBMode(mode)) { 115 uv_stride = (w + 1) / 2; 116 uv_size = (uint64_t)uv_stride * ((h + 1) / 2); 117 if (mode == MODE_YUVA) { 118 a_stride = w; 119 a_size = (uint64_t)a_stride * h; 120 } 121 } 122 total_size = size + 2 * uv_size + a_size; 123 124 output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output)); 125 if (output == NULL) { 126 return VP8_STATUS_OUT_OF_MEMORY; 127 } 128 buffer->private_memory = output; 129 130 if (!WebPIsRGBMode(mode)) { // YUVA initialization 131 WebPYUVABuffer* const buf = &buffer->u.YUVA; 132 buf->y = output; 133 buf->y_stride = stride; 134 buf->y_size = (size_t)size; 135 buf->u = output + size; 136 buf->u_stride = uv_stride; 137 buf->u_size = (size_t)uv_size; 138 buf->v = output + size + uv_size; 139 buf->v_stride = uv_stride; 140 buf->v_size = (size_t)uv_size; 141 if (mode == MODE_YUVA) { 142 buf->a = output + size + 2 * uv_size; 143 } 144 buf->a_size = (size_t)a_size; 145 buf->a_stride = a_stride; 146 } else { // RGBA initialization 147 WebPRGBABuffer* const buf = &buffer->u.RGBA; 148 buf->rgba = output; 149 buf->stride = stride; 150 buf->size = (size_t)size; 151 } 152 } 153 return CheckDecBuffer(buffer); 154 } 155 156 VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) { 157 if (buffer == NULL) { 158 return VP8_STATUS_INVALID_PARAM; 159 } 160 if (WebPIsRGBMode(buffer->colorspace)) { 161 WebPRGBABuffer* const buf = &buffer->u.RGBA; 162 buf->rgba += (int64_t)(buffer->height - 1) * buf->stride; 163 buf->stride = -buf->stride; 164 } else { 165 WebPYUVABuffer* const buf = &buffer->u.YUVA; 166 const int64_t H = buffer->height; 167 buf->y += (H - 1) * buf->y_stride; 168 buf->y_stride = -buf->y_stride; 169 buf->u += ((H - 1) >> 1) * buf->u_stride; 170 buf->u_stride = -buf->u_stride; 171 buf->v += ((H - 1) >> 1) * buf->v_stride; 172 buf->v_stride = -buf->v_stride; 173 if (buf->a != NULL) { 174 buf->a += (H - 1) * buf->a_stride; 175 buf->a_stride = -buf->a_stride; 176 } 177 } 178 return VP8_STATUS_OK; 179 } 180 181 VP8StatusCode WebPAllocateDecBuffer(int width, int height, 182 const WebPDecoderOptions* const options, 183 WebPDecBuffer* const buffer) { 184 VP8StatusCode status; 185 if (buffer == NULL || width <= 0 || height <= 0) { 186 return VP8_STATUS_INVALID_PARAM; 187 } 188 if (options != NULL) { // First, apply options if there is any. 189 if (options->use_cropping) { 190 const int cw = options->crop_width; 191 const int ch = options->crop_height; 192 const int x = options->crop_left & ~1; 193 const int y = options->crop_top & ~1; 194 if (!WebPCheckCropDimensions(width, height, x, y, cw, ch)) { 195 return VP8_STATUS_INVALID_PARAM; // out of frame boundary. 196 } 197 width = cw; 198 height = ch; 199 } 200 201 if (options->use_scaling) { 202 #if !defined(WEBP_REDUCE_SIZE) 203 int scaled_width = options->scaled_width; 204 int scaled_height = options->scaled_height; 205 if (!WebPRescalerGetScaledDimensions( 206 width, height, &scaled_width, &scaled_height)) { 207 return VP8_STATUS_INVALID_PARAM; 208 } 209 width = scaled_width; 210 height = scaled_height; 211 #else 212 return VP8_STATUS_INVALID_PARAM; // rescaling not supported 213 #endif 214 } 215 } 216 buffer->width = width; 217 buffer->height = height; 218 219 // Then, allocate buffer for real. 220 status = AllocateBuffer(buffer); 221 if (status != VP8_STATUS_OK) return status; 222 223 // Use the stride trick if vertical flip is needed. 224 if (options != NULL && options->flip) { 225 status = WebPFlipBuffer(buffer); 226 } 227 return status; 228 } 229 230 //------------------------------------------------------------------------------ 231 // constructors / destructors 232 233 int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) { 234 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { 235 return 0; // version mismatch 236 } 237 if (buffer == NULL) return 0; 238 memset(buffer, 0, sizeof(*buffer)); 239 return 1; 240 } 241 242 void WebPFreeDecBuffer(WebPDecBuffer* buffer) { 243 if (buffer != NULL) { 244 if (buffer->is_external_memory <= 0) { 245 WebPSafeFree(buffer->private_memory); 246 } 247 buffer->private_memory = NULL; 248 } 249 } 250 251 void WebPCopyDecBuffer(const WebPDecBuffer* const src, 252 WebPDecBuffer* const dst) { 253 if (src != NULL && dst != NULL) { 254 *dst = *src; 255 if (src->private_memory != NULL) { 256 dst->is_external_memory = 1; // dst buffer doesn't own the memory. 257 dst->private_memory = NULL; 258 } 259 } 260 } 261 262 // Copy and transfer ownership from src to dst (beware of parameter order!) 263 void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) { 264 if (src != NULL && dst != NULL) { 265 *dst = *src; 266 if (src->private_memory != NULL) { 267 src->is_external_memory = 1; // src relinquishes ownership 268 src->private_memory = NULL; 269 } 270 } 271 } 272 273 VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf, 274 WebPDecBuffer* const dst_buf) { 275 assert(src_buf != NULL && dst_buf != NULL); 276 assert(src_buf->colorspace == dst_buf->colorspace); 277 278 dst_buf->width = src_buf->width; 279 dst_buf->height = src_buf->height; 280 if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) { 281 return VP8_STATUS_INVALID_PARAM; 282 } 283 if (WebPIsRGBMode(src_buf->colorspace)) { 284 const WebPRGBABuffer* const src = &src_buf->u.RGBA; 285 const WebPRGBABuffer* const dst = &dst_buf->u.RGBA; 286 WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride, 287 src_buf->width * kModeBpp[src_buf->colorspace], 288 src_buf->height); 289 } else { 290 const WebPYUVABuffer* const src = &src_buf->u.YUVA; 291 const WebPYUVABuffer* const dst = &dst_buf->u.YUVA; 292 WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride, 293 src_buf->width, src_buf->height); 294 WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride, 295 (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); 296 WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride, 297 (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); 298 if (WebPIsAlphaMode(src_buf->colorspace)) { 299 WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride, 300 src_buf->width, src_buf->height); 301 } 302 } 303 return VP8_STATUS_OK; 304 } 305 306 int WebPAvoidSlowMemory(const WebPDecBuffer* const output, 307 const WebPBitstreamFeatures* const features) { 308 assert(output != NULL); 309 return (output->is_external_memory >= 2) && 310 WebPIsPremultipliedMode(output->colorspace) && 311 (features != NULL && features->has_alpha); 312 } 313 314 //------------------------------------------------------------------------------