1// Copyright 2011 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// WebPPicture utils: colorspace conversion, crop, ... 9// 10// Author: Skal (pascal.massimino@gmail.com) 11 12#include <stdlib.h> 13#include "vp8enci.h" 14 15#if defined(__cplusplus) || defined(c_plusplus) 16extern "C" { 17#endif 18 19//----------------------------------------------------------------------------- 20// WebPPicture 21//----------------------------------------------------------------------------- 22 23int WebPPictureAlloc(WebPPicture* const picture) { 24 if (picture) { 25 const int width = picture->width; 26 const int height = picture->height; 27 const int uv_width = (width + 1) / 2; 28 const int uv_height = (height + 1) / 2; 29 const uint64_t y_size = (uint64_t)width * height; 30 const uint64_t uv_size = (uint64_t)uv_width * uv_height; 31 const uint64_t total_size = y_size + 2 * uv_size; 32 // Security and validation checks 33 if (uv_width <= 0 || uv_height <= 0 || // check param error 34 y_size >= (1ULL << 40) || // check for reasonable global size 35 (size_t)total_size != total_size) { // check for overflow on 32bit 36 return 0; 37 } 38 picture->y_stride = width; 39 picture->uv_stride = uv_width; 40 WebPPictureFree(picture); // erase previous buffer 41 picture->y = (uint8_t*)malloc(total_size); 42 if (picture->y == NULL) return 0; 43 picture->u = picture->y + y_size; 44 picture->v = picture->u + uv_size; 45 } 46 return 1; 47} 48 49void WebPPictureFree(WebPPicture* const picture) { 50 if (picture) { 51 free(picture->y); 52 picture->y = picture->u = picture->v = NULL; 53 } 54} 55 56//----------------------------------------------------------------------------- 57 58int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { 59 int y; 60 if (src == NULL || dst == NULL) return 0; 61 if (src == dst) return 1; 62 *dst = *src; 63 dst->y = NULL; 64 if (!WebPPictureAlloc(dst)) return 0; 65 for (y = 0; y < dst->height; ++y) { 66 memcpy(dst->y + y * dst->y_stride, src->y + y * src->y_stride, src->width); 67 } 68 for (y = 0; y < (dst->height + 1) / 2; ++y) { 69 memcpy(dst->u + y * dst->uv_stride, 70 src->u + y * src->uv_stride, (src->width + 1) / 2); 71 memcpy(dst->v + y * dst->uv_stride, 72 src->v + y * src->uv_stride, (src->width + 1) / 2); 73 } 74 return 1; 75} 76 77int WebPPictureCrop(WebPPicture* const pic, 78 int left, int top, int width, int height) { 79 WebPPicture tmp; 80 int y; 81 82 if (pic == NULL) return 0; 83 if (width <= 0 || height <= 0) return 0; 84 if (left < 0 || ((left + width + 1) & ~1) > pic->width) return 0; 85 if (top < 0 || ((top + height + 1) & ~1) > pic->height) return 0; 86 87 tmp = *pic; 88 tmp.y = NULL; 89 tmp.width = width; 90 tmp.height = height; 91 if (!WebPPictureAlloc(&tmp)) return 0; 92 93 for (y = 0; y < height; ++y) { 94 memcpy(tmp.y + y * tmp.y_stride, 95 pic->y + (top + y) * pic->y_stride + left, width); 96 } 97 for (y = 0; y < (height + 1) / 2; ++y) { 98 const int offset = (y + top / 2) * pic->uv_stride + left / 2; 99 memcpy(tmp.u + y * tmp.uv_stride, pic->u + offset, (width + 1) / 2); 100 memcpy(tmp.v + y * tmp.uv_stride, pic->v + offset, (width + 1) / 2); 101 } 102 WebPPictureFree(pic); 103 *pic = tmp; 104 return 1; 105} 106 107//----------------------------------------------------------------------------- 108// Write-to-memory 109 110typedef struct { 111 uint8_t** mem; 112 size_t max_size; 113 size_t* size; 114} WebPMemoryWriter; 115 116static void InitMemoryWriter(WebPMemoryWriter* const writer) { 117 *writer->mem = NULL; 118 *writer->size = 0; 119 writer->max_size = 0; 120} 121 122static int WebPMemoryWrite(const uint8_t* data, size_t data_size, 123 const WebPPicture* const picture) { 124 WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr; 125 size_t next_size; 126 if (w == NULL) { 127 return 1; 128 } 129 next_size = (*w->size) + data_size; 130 if (next_size > w->max_size) { 131 uint8_t* new_mem; 132 size_t next_max_size = w->max_size * 2; 133 if (next_max_size < next_size) next_max_size = next_size; 134 if (next_max_size < 8192) next_max_size = 8192; 135 new_mem = (uint8_t*)malloc(next_max_size); 136 if (new_mem == NULL) { 137 return 0; 138 } 139 if ((*w->size) > 0) { 140 memcpy(new_mem, *w->mem, *w->size); 141 } 142 free(*w->mem); 143 *w->mem = new_mem; 144 w->max_size = next_max_size; 145 } 146 if (data_size) { 147 memcpy((*w->mem) + (*w->size), data, data_size); 148 *w->size += data_size; 149 } 150 return 1; 151} 152 153//----------------------------------------------------------------------------- 154// RGB -> YUV conversion 155// The exact naming is Y'CbCr, following the ITU-R BT.601 standard. 156// More information at: http://en.wikipedia.org/wiki/YCbCr 157// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16 158// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128 159// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128 160// We use 16bit fixed point operations. 161 162enum { YUV_FRAC = 16 }; 163 164static inline int clip_uv(int v) { 165 v = (v + (257 << (YUV_FRAC + 2 - 1))) >> (YUV_FRAC + 2); 166 return ((v & ~0xff) == 0) ? v : (v < 0) ? 0u : 255u; 167} 168 169static inline int rgb_to_y(int r, int g, int b) { 170 const int kRound = (1 << (YUV_FRAC - 1)) + (16 << YUV_FRAC); 171 const int luma = 16839 * r + 33059 * g + 6420 * b; 172 return (luma + kRound) >> YUV_FRAC; // no need to clip 173} 174 175static inline int rgb_to_u(int r, int g, int b) { 176 return clip_uv(-9719 * r - 19081 * g + 28800 * b); 177} 178 179static inline int rgb_to_v(int r, int g, int b) { 180 return clip_uv(+28800 * r - 24116 * g - 4684 * b); 181} 182 183// TODO: we can do better than simply 2x2 averaging on U/V samples. 184#define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \ 185 (ptr)[rgb_stride] + (ptr)[rgb_stride + step]) 186#define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step]) 187#define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride]) 188#define SUM1(ptr) (4 * (ptr)[0]) 189#define RGB_TO_UV(x, y, SUM) { \ 190 const int src = (2 * (step * (x) + (y) * rgb_stride)); \ 191 const int dst = (x) + (y) * picture->uv_stride; \ 192 const int r = SUM(r_ptr + src); \ 193 const int g = SUM(g_ptr + src); \ 194 const int b = SUM(b_ptr + src); \ 195 picture->u[dst] = rgb_to_u(r, g, b); \ 196 picture->v[dst] = rgb_to_v(r, g, b); \ 197} 198 199static int Import(WebPPicture* const picture, 200 const uint8_t* const rgb, int rgb_stride, 201 int step, int swap) { 202 int x, y; 203 const uint8_t* const r_ptr = rgb + (swap ? 2 : 0); 204 const uint8_t* const g_ptr = rgb + 1; 205 const uint8_t* const b_ptr = rgb + (swap ? 0 : 2); 206 207 for (y = 0; y < picture->height; ++y) { 208 for (x = 0; x < picture->width; ++x) { 209 const int offset = step * x + y * rgb_stride; 210 picture->y[x + y * picture->y_stride] = 211 rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]); 212 } 213 } 214 for (y = 0; y < (picture->height >> 1); ++y) { 215 for (x = 0; x < (picture->width >> 1); ++x) { 216 RGB_TO_UV(x, y, SUM4); 217 } 218 if (picture->width & 1) { 219 RGB_TO_UV(x, y, SUM2V); 220 } 221 } 222 if (picture->height & 1) { 223 for (x = 0; x < (picture->width >> 1); ++x) { 224 RGB_TO_UV(x, y, SUM2H); 225 } 226 if (picture->width & 1) { 227 RGB_TO_UV(x, y, SUM1); 228 } 229 } 230 return 1; 231} 232#undef SUM4 233#undef SUM2V 234#undef SUM2H 235#undef SUM1 236#undef RGB_TO_UV 237 238int WebPPictureImportRGB(WebPPicture* const picture, 239 const uint8_t* const rgb, int rgb_stride) { 240 if (!WebPPictureAlloc(picture)) return 0; 241 return Import(picture, rgb, rgb_stride, 3, 0); 242} 243 244int WebPPictureImportBGR(WebPPicture* const picture, 245 const uint8_t* const rgb, int rgb_stride) { 246 if (!WebPPictureAlloc(picture)) return 0; 247 return Import(picture, rgb, rgb_stride, 3, 1); 248} 249 250int WebPPictureImportRGBA(WebPPicture* const picture, 251 const uint8_t* const rgba, int rgba_stride) { 252 if (!WebPPictureAlloc(picture)) return 0; 253 return Import(picture, rgba, rgba_stride, 4, 0); 254} 255 256int WebPPictureImportBGRA(WebPPicture* const picture, 257 const uint8_t* const rgba, int rgba_stride) { 258 if (!WebPPictureAlloc(picture)) return 0; 259 return Import(picture, rgba, rgba_stride, 4, 1); 260} 261 262//----------------------------------------------------------------------------- 263// Simplest call: 264 265typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int); 266 267static size_t Encode(const uint8_t* rgb, int width, int height, int stride, 268 Importer import, float quality_factor, uint8_t** output) { 269 size_t output_size = 0; 270 WebPPicture pic; 271 WebPConfig config; 272 WebPMemoryWriter wrt; 273 int ok; 274 275 if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) || 276 !WebPPictureInit(&pic)) { 277 return 0; // shouldn't happen, except if system installation is broken 278 } 279 280 pic.width = width; 281 pic.height = height; 282 pic.writer = WebPMemoryWrite; 283 pic.custom_ptr = &wrt; 284 285 wrt.mem = output; 286 wrt.size = &output_size; 287 InitMemoryWriter(&wrt); 288 289 ok = import(&pic, rgb, stride) && WebPEncode(&config, &pic); 290 WebPPictureFree(&pic); 291 if (!ok) { 292 free(*output); 293 *output = NULL; 294 return 0; 295 } 296 return output_size; 297} 298 299#define ENCODE_FUNC(NAME, IMPORTER) \ 300size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \ 301 uint8_t** out) { \ 302 return Encode(in, w, h, bps, IMPORTER, q, out); \ 303} 304 305ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB); 306ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR); 307ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA); 308ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA); 309 310#undef ENCODE_FUNC 311 312//----------------------------------------------------------------------------- 313 314#if defined(__cplusplus) || defined(c_plusplus) 315} // extern "C" 316#endif 317