Annotation of embedaddon/php/ext/gd/libgd/webpimg.c, revision 1.1
1.1 ! misho 1: /*===========================================================================*
! 2: - Copyright 2010 Google Inc.
! 3: -
! 4: - This code is licensed under the same terms as WebM:
! 5: - Software License Agreement: http://www.webmproject.org/license/software/
! 6: - Additional IP Rights Grant: http://www.webmproject.org/license/additional/
! 7: *===========================================================================*/
! 8:
! 9: /*
! 10: * Encoding/Decoding of WebP still image compression format.
! 11: *
! 12: * 1. WebPDecode: Takes an array of bytes (string) corresponding to the WebP
! 13: * encoded image and generates output in the YUV format with
! 14: * the color components U, V subsampled to 1/2 resolution along
! 15: * each dimension.
! 16: *
! 17: * 2. YUV420toRGBA: Converts from YUV (with color subsampling) such as produced
! 18: * by the WebPDecode routine into 32 bits per pixel RGBA data
! 19: * array. This data array can be directly used by the Leptonica
! 20: * Pix in-memory image format.
! 21: *
! 22: * 3. WebPEncode: Takes a Y, U, V data buffers (with color components U and V
! 23: * subsampled to 1/2 resolution) and generates the WebP string
! 24: *
! 25: * 4. RGBAToYUV420: Generates Y, U, V data (with color subsampling) from 32 bits
! 26: * per pixel RGBA data buffer. The resulting YUV data can be
! 27: * directly fed into the WebPEncode routine.
! 28: *
! 29: * 5. AdjustColorspace:
! 30: *
! 31: * 6. AdjustColorspaceBack:
! 32: */
! 33: #include "gd.h"
! 34: #ifdef HAVE_LIBVPX
! 35: #include "webpimg.h"
! 36:
! 37: #include <math.h>
! 38: #include <stdio.h>
! 39: #include <stdlib.h>
! 40: #include <string.h>
! 41: #include <sys/stat.h>
! 42:
! 43: #include "vpx/vpx_decoder.h"
! 44: #include "vpx/vp8dx.h"
! 45: #include "vpx/vpx_encoder.h"
! 46: #include "vpx/vp8cx.h"
! 47: #include "gd.h"
! 48:
! 49: /*---------------------------------------------------------------------*
! 50: * color conversions *
! 51: *---------------------------------------------------------------------*/
! 52:
! 53: #ifndef inline
! 54: # define inline __inline
! 55: #endif
! 56: static inline int clip(float v, int a, int b) {
! 57: return (v > b) ? b : (v < 0) ? 0 : (int)(v);
! 58: }
! 59: enum {
! 60: COLOR_RED = 0,
! 61: COLOR_GREEN = 1,
! 62: COLOR_BLUE = 2,
! 63: ALPHA_CHANNEL = 3
! 64: };
! 65:
! 66: /* endian neutral extractions of RGBA from a 32 bit pixel */
! 67: static const uint32 RED_SHIFT =
! 68: 8 * (sizeof(uint32) - 1 - COLOR_RED); /* 24 */
! 69: static const uint32 GREEN_SHIFT =
! 70: 8 * (sizeof(uint32) - 1 - COLOR_GREEN); /* 16 */
! 71: static const uint32 BLUE_SHIFT =
! 72: 8 * (sizeof(uint32) - 1 - COLOR_BLUE); /* 8 */
! 73: static const uint32 ALPHA_SHIFT =
! 74: 8 * (sizeof(uint32) - 1 - ALPHA_CHANNEL); /* 0 */
! 75:
! 76: static inline int GetRed(const uint32* rgba) {
! 77: return gdTrueColorGetRed(*rgba);
! 78: }
! 79:
! 80: static inline int GetGreen(const uint32* rgba) {
! 81: return gdTrueColorGetGreen(*rgba);
! 82: }
! 83:
! 84: static inline int GetBlue(const uint32* rgba) {
! 85: return gdTrueColorGetBlue(*rgba);
! 86: }
! 87:
! 88: enum { YUV_FRAC = 16 };
! 89:
! 90: static inline int clip_uv(int v) {
! 91: v = (v + (257 << (YUV_FRAC + 2 - 1))) >> (YUV_FRAC + 2);
! 92: return ((v & ~0xff) == 0) ? v : v < 0 ? 0u : 255u;
! 93: }
! 94:
! 95:
! 96: /* YUV <-----> RGB conversions */
! 97: /* The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
! 98: * More information at: http://en.wikipedia.org/wiki/YCbCr
! 99: */
! 100: static inline int GetLumaY(int r, int g, int b) {
! 101: const int kRound = (1 << (YUV_FRAC - 1)) + (16 << YUV_FRAC);
! 102: // Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
! 103: const int luma = 16839 * r + 33059 * g + 6420 * b;
! 104: return (luma + kRound) >> YUV_FRAC;
! 105: }
! 106:
! 107: static inline int GetLumaYfromPtr(uint32* rgba) {
! 108: const int r = GetRed(rgba);
! 109: const int g = GetGreen(rgba);
! 110: const int b = GetBlue(rgba);
! 111: return GetLumaY(r, g, b);
! 112: }
! 113:
! 114: static inline int GetChromaU(int r, int g, int b) {
! 115: // U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
! 116: return clip_uv(-9719 * r - 19081 * g + 28800 * b);
! 117: }
! 118:
! 119: static inline int GetChromaV(int r, int g, int b) {
! 120: // V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
! 121: return clip_uv(+28800 * r - 24116 * g - 4684 * b);
! 122: }
! 123:
! 124: /* Converts YUV to RGB and writes into a 32 bit pixel in endian
! 125: * neutral fashion
! 126: */
! 127: enum { RGB_FRAC = 16, RGB_HALF = (1 << RGB_FRAC) / 2,
! 128: RGB_RANGE_MIN = -227, RGB_RANGE_MAX = 256 + 226 };
! 129:
! 130: static int init_done = 0;
! 131: static int16_t kVToR[256], kUToB[256];
! 132: static int32_t kVToG[256], kUToG[256];
! 133: static uint8_t kClip[RGB_RANGE_MAX - RGB_RANGE_MIN];
! 134:
! 135: static void InitTables() {
! 136: int i;
! 137: for (i = 0; i < 256; ++i) {
! 138: kVToR[i] = (89858 * (i - 128) + RGB_HALF) >> RGB_FRAC;
! 139: kUToG[i] = -22014 * (i - 128) + RGB_HALF;
! 140: kVToG[i] = -45773 * (i - 128);
! 141: kUToB[i] = (113618 * (i - 128) + RGB_HALF) >> RGB_FRAC;
! 142: }
! 143: for (i = RGB_RANGE_MIN; i < RGB_RANGE_MAX; ++i) {
! 144: const int j = ((i - 16) * 76283 + RGB_HALF) >> RGB_FRAC;
! 145: kClip[i - RGB_RANGE_MIN] = (j < 0) ? 0 : (j > 255) ? 255 : j;
! 146: }
! 147:
! 148: init_done = 1;
! 149: }
! 150:
! 151: static void ToRGB(int y, int u, int v, uint32* const dst) {
! 152: const int r_off = kVToR[v];
! 153: const int g_off = (kVToG[v] + kUToG[u]) >> RGB_FRAC;
! 154: const int b_off = kUToB[u];
! 155: const int r = kClip[y + r_off - RGB_RANGE_MIN];
! 156: const int g = kClip[y + g_off - RGB_RANGE_MIN];
! 157: const int b = kClip[y + b_off - RGB_RANGE_MIN];
! 158: *dst = (r << RED_SHIFT) | (g << GREEN_SHIFT) | (b << BLUE_SHIFT);
! 159: }
! 160:
! 161: static inline uint32 get_le32(const uint8* const data) {
! 162: return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
! 163: }
! 164:
! 165: /* Returns the difference (in dB) between two images represented in YUV format
! 166: *
! 167: * Input:
! 168: * Y1/U1/V1: The Y/U/V data of the first image
! 169: * Y2/U2/V2: The Y/U/V data of the second image
! 170: *
! 171: * Returns the PSNR (http://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio)
! 172: * value computed bewteen the two images
! 173: */
! 174: double GetPSNRYuv(const uint8* Y1,
! 175: const uint8* U1,
! 176: const uint8* V1,
! 177: const uint8* Y2,
! 178: const uint8* U2,
! 179: const uint8* V2,
! 180: int y_width,
! 181: int y_height) {
! 182: int x, y, row_idx;
! 183: const int uv_width = ((y_width + 1) >> 1);
! 184: const int uv_height = ((y_height + 1) >> 1);
! 185: double sse = 0., count = 0.;
! 186: for (y = 0; y < y_height; ++y) {
! 187: count += y_width;
! 188: row_idx = y * y_width;
! 189: for (x = 0; x < y_width; ++x) {
! 190: double diff = Y1[row_idx + x] - Y2[row_idx + x];
! 191: sse += diff * diff;
! 192: }
! 193: }
! 194: for (y = 0; y < uv_height; ++y) {
! 195: count += 2 * uv_width;
! 196: row_idx = y * uv_width;
! 197: for (x = 0; x < uv_width; ++x) {
! 198: const double diff_U = U1[row_idx + x] - U2[row_idx + x];
! 199: const double diff_V = V1[row_idx + x] - V2[row_idx + x];
! 200: sse += diff_U * diff_U + diff_V * diff_V;
! 201: }
! 202: }
! 203: return -4.3429448 * log(sse / (255. * 255. * count));
! 204: }
! 205:
! 206: /* Returns the difference (in dB) between two images. One represented
! 207: * using Y,U,V vectors and the other is webp image data.
! 208: * Input:
! 209: * Y1/U1/V1: The Y/U/V data of the first image
! 210: * imgdata: data buffer containing webp image
! 211: * imgdata_size: size of the imgdata buffer
! 212: *
! 213: * Returns the PSNR value computed bewteen the two images
! 214: */
! 215: double WebPGetPSNR(const uint8* Y1,
! 216: const uint8* U1,
! 217: const uint8* V1,
! 218: uint8* imgdata,
! 219: int imgdata_size) {
! 220: uint8* Y2 = NULL;
! 221: uint8* U2 = NULL;
! 222: uint8* V2 = NULL;
! 223: int w = 0, h = 0;
! 224: double psnr = 0;
! 225:
! 226: WebPDecode(imgdata,
! 227: imgdata_size,
! 228: &Y2,
! 229: &U2,
! 230: &V2,
! 231: &w,
! 232: &h);
! 233:
! 234: psnr = GetPSNRYuv(Y1, U1, V1, Y2, U2, V2, w, h);
! 235: free(Y2);
! 236:
! 237: return psnr;
! 238: }
! 239:
! 240: /*---------------------------------------------------------------------*
! 241: * Reading WebP *
! 242: *---------------------------------------------------------------------*/
! 243:
! 244: /* RIFF layout is:
! 245: * 0ffset tag
! 246: * 0...3 "RIFF" 4-byte tag
! 247: * 4...7 size of image data (including metadata) starting at offset 8
! 248: * 8...11 "WEBP" our form-type signature
! 249: * 12..15 "VP8 " 4-byte tags, describing the raw video format used
! 250: * 16..19 size of the raw WebP image data, starting at offset 20
! 251: * 20.... the WebP bytes
! 252: * There can be extra chunks after the "VP8 " chunk (ICMT, ICOP, ...)
! 253: * All 32-bits sizes are in little-endian order.
! 254: * Note: chunk data must be padded to multiple of 2 in size
! 255: */
! 256:
! 257: int SkipRiffHeader(const uint8** data_ptr, int *data_size_ptr) {
! 258: /* 20 bytes RIFF header 10 bytes VP8 header */
! 259: const int kHeaderSize = (20 + 10);
! 260: uint32 chunk_size = 0xffffffffu;
! 261:
! 262: if (*data_size_ptr >= kHeaderSize && !memcmp(*data_ptr, "RIFF", 4)) {
! 263: if (memcmp(*data_ptr + 8, "WEBP", 4)) {
! 264: return 0; /* wrong image file signature */
! 265: } else {
! 266: const uint32 riff_size = get_le32(*data_ptr + 4);
! 267: if (memcmp(*data_ptr + 12, "VP8 ", 4)) {
! 268: return 0; /* invalid compression format */
! 269: }
! 270: chunk_size = get_le32(*data_ptr + 16);
! 271: if ((chunk_size > riff_size + 8) || (chunk_size & 1)) {
! 272: return 0; /* inconsistent size information. */
! 273: }
! 274: /* We have a RIFF container. Skip it. */
! 275: *data_ptr += 20;
! 276: *data_size_ptr -= 20;
! 277: }
! 278: }
! 279: return chunk_size;
! 280: }
! 281:
! 282: /* Generate RGBA row from an YUV row (with width upsampling of chrome data)
! 283: * Input:
! 284: * 1, 2, 3. y_src, u_src, v_src - Pointers to input Y, U, V row data
! 285: * respectively. We reuse these variables, they iterate over all pixels in
! 286: * the row.
! 287: * 4. y_width: width of the Y image plane (aka image width)
! 288: * Output:
! 289: * 5. rgb_sat: pointer to the output rgb row. We reuse this variable, it
! 290: * iterates over all pixels in the row.
! 291: */
! 292: static void YUV420toRGBLine(uint8* y_src,
! 293: uint8* u_src,
! 294: uint8* v_src,
! 295: int y_width,
! 296: uint32* rgb_dst) {
! 297: int x;
! 298: for (x = 0; x < (y_width >> 1); ++x) {
! 299: const int U = u_src[0];
! 300: const int V = v_src[0];
! 301: ToRGB(y_src[0], U, V, rgb_dst);
! 302: ToRGB(y_src[1], U, V, rgb_dst + 1);
! 303: ++u_src;
! 304: ++v_src;
! 305: y_src += 2;
! 306: rgb_dst += 2;
! 307: }
! 308: if (y_width & 1) { /* Rightmost pixel */
! 309: ToRGB(y_src[0], (*u_src), (*v_src), rgb_dst);
! 310: }
! 311: }
! 312:
! 313: /* Converts from YUV (with color subsampling) such as produced by the WebPDecode
! 314: * routine into 32 bits per pixel RGBA data array. This data array can be
! 315: * directly used by the Leptonica Pix in-memory image format.
! 316: * Input:
! 317: * 1, 2, 3. Y, U, V: the input data buffers
! 318: * 4. pixwpl: the desired words per line corresponding to the supplied
! 319: * output pixdata.
! 320: * 5. width, height: the dimensions of the image whose data resides in Y,
! 321: * U, V.
! 322: * Output:
! 323: * 6. pixdata: the output data buffer. Caller should allocate
! 324: * height * pixwpl bytes of memory before calling this routine.
! 325: */
! 326: void YUV420toRGBA(uint8* Y,
! 327: uint8* U,
! 328: uint8* V,
! 329: int words_per_line,
! 330: int width,
! 331: int height,
! 332: uint32* pixdata) {
! 333: int y_width = width;
! 334: int y_stride = y_width;
! 335: int uv_width = ((y_width + 1) >> 1);
! 336: int uv_stride = uv_width;
! 337: int y;
! 338:
! 339: if (!init_done)
! 340: InitTables();
! 341:
! 342: /* note that the U, V upsampling in height is happening here as the U, V
! 343: * buffers sent to successive odd-even pair of lines is same.
! 344: */
! 345: for (y = 0; y < height; ++y) {
! 346: YUV420toRGBLine(Y + y * y_stride,
! 347: U + (y >> 1) * uv_stride,
! 348: V + (y >> 1) * uv_stride,
! 349: width,
! 350: pixdata + y * words_per_line);
! 351: }
! 352: }
! 353:
! 354: void gd_YUV420toRGBA(uint8* Y,
! 355: uint8* U,
! 356: uint8* V,
! 357: gdImagePtr im) {
! 358: int width = im->sx;
! 359: int height = im->sy;
! 360: int y_width = width;
! 361: int y_stride = y_width;
! 362: int uv_width = ((y_width + 1) >> 1);
! 363: int uv_stride = uv_width;
! 364: int y;
! 365:
! 366: /* output im must be truecolor */
! 367: if (!im->trueColor) {
! 368: return;
! 369: }
! 370:
! 371: if (!init_done)
! 372: InitTables();
! 373:
! 374: /* note that the U, V upsampling in height is happening here as the U, V
! 375: * buffers sent to successive odd-even pair of lines is same.
! 376: */
! 377: for (y = 0; y < height; ++y) {
! 378: YUV420toRGBLine(Y + y * y_stride,
! 379: U + (y >> 1) * uv_stride,
! 380: V + (y >> 1) * uv_stride,
! 381: width,
! 382: im->tpixels[y]);
! 383: }
! 384: }
! 385:
! 386: static WebPResult VPXDecode(const uint8* data,
! 387: int data_size,
! 388: uint8** p_Y,
! 389: uint8** p_U,
! 390: uint8** p_V,
! 391: int* p_width,
! 392: int* p_height) {
! 393: vpx_codec_ctx_t dec;
! 394: vp8_postproc_cfg_t ppcfg;
! 395: WebPResult result = webp_failure;
! 396:
! 397: if (!data || data_size <= 10 || !p_Y || !p_U || !p_V
! 398: || *p_Y != NULL || *p_U != NULL || *p_V != NULL) {
! 399: return webp_failure;
! 400: }
! 401:
! 402: if (vpx_codec_dec_init(&dec,
! 403: &vpx_codec_vp8_dx_algo, NULL, 0) != VPX_CODEC_OK) {
! 404: return webp_failure;
! 405: }
! 406:
! 407: ppcfg.post_proc_flag = VP8_NOFILTERING;
! 408: vpx_codec_control(&dec, VP8_SET_POSTPROC, &ppcfg);
! 409:
! 410:
! 411: if (vpx_codec_decode(&dec, data, data_size, NULL, 0) == VPX_CODEC_OK) {
! 412: vpx_codec_iter_t iter = NULL;
! 413: vpx_image_t* const img = vpx_codec_get_frame(&dec, &iter);
! 414: if (img) {
! 415: int y_width = img->d_w;
! 416: int y_height = img->d_h;
! 417: int y_stride = y_width;
! 418: int uv_width = (y_width + 1) >> 1;
! 419: int uv_stride = uv_width;
! 420: int uv_height = ((y_height + 1) >> 1);
! 421: int y;
! 422:
! 423: *p_width = y_width;
! 424: *p_height = y_height;
! 425: if ((*p_Y = (uint8 *)(calloc(y_stride * y_height
! 426: + 2 * uv_stride * uv_height,
! 427: sizeof(uint8)))) != NULL) {
! 428: *p_U = *p_Y + y_height * y_stride;
! 429: *p_V = *p_U + uv_height * uv_stride;
! 430: for (y = 0; y < y_height; ++y) {
! 431: memcpy(*p_Y + y * y_stride,
! 432: img->planes[0] + y * img->stride[0],
! 433: y_width);
! 434: }
! 435: for (y = 0; y < uv_height; ++y) {
! 436: memcpy(*p_U + y * uv_stride,
! 437: img->planes[1] + y * img->stride[1],
! 438: uv_width);
! 439: memcpy(*p_V + y * uv_stride,
! 440: img->planes[2] + y * img->stride[2],
! 441: uv_width);
! 442: }
! 443: result = webp_success;
! 444: }
! 445: }
! 446: }
! 447: vpx_codec_destroy(&dec);
! 448:
! 449: return result;
! 450: }
! 451:
! 452: WebPResult WebPDecode(const uint8* data,
! 453: int data_size,
! 454: uint8** p_Y,
! 455: uint8** p_U,
! 456: uint8** p_V,
! 457: int* p_width,
! 458: int* p_height) {
! 459:
! 460: const uint32 chunk_size = SkipRiffHeader(&data, &data_size);
! 461: if (!chunk_size) {
! 462: return webp_failure; /* unsupported RIFF header */
! 463: }
! 464:
! 465: return VPXDecode(data, data_size, p_Y, p_U, p_V, p_width, p_height);
! 466: }
! 467:
! 468: /*---------------------------------------------------------------------*
! 469: * Writing WebP *
! 470: *---------------------------------------------------------------------*/
! 471:
! 472: /* Takes a pair of RGBA row data as input and generates 2 rows of Y data and one
! 473: * row of subsampled U, V data as output
! 474: * Input:
! 475: * 1, 2. rgb_line1, rgb_line2 - input rgba rows
! 476: * 3. width - image width
! 477: * Outout:
! 478: * 4, 5, 6: Output Y, U, V row
! 479: */
! 480: static void RGBALinepairToYUV420(uint32* rgb_line1,
! 481: uint32* rgb_line2,
! 482: int width,
! 483: uint8* Y_dst1,
! 484: uint8* Y_dst2,
! 485: uint8* u_dst,
! 486: uint8* v_dst) {
! 487: int x;
! 488: for (x = (width >> 1); x > 0; --x) {
! 489: const int sum_r =
! 490: GetRed(rgb_line1 + 0) + GetRed(rgb_line1 + 1) +
! 491: GetRed(rgb_line2 + 0) + GetRed(rgb_line2 + 1);
! 492: const int sum_g =
! 493: GetGreen(rgb_line1 + 0) + GetGreen(rgb_line1 + 1) +
! 494: GetGreen(rgb_line2 + 0) + GetGreen(rgb_line2 + 1);
! 495: const int sum_b =
! 496: GetBlue(rgb_line1 + 0) + GetBlue(rgb_line1 + 1) +
! 497: GetBlue(rgb_line2 + 0) + GetBlue(rgb_line2 + 1);
! 498:
! 499: Y_dst1[0] = GetLumaYfromPtr(rgb_line1 + 0);
! 500: Y_dst1[1] = GetLumaYfromPtr(rgb_line1 + 1);
! 501: Y_dst2[0] = GetLumaYfromPtr(rgb_line2 + 0);
! 502: Y_dst2[1] = GetLumaYfromPtr(rgb_line2 + 1);
! 503:
! 504: *u_dst++ = GetChromaU(sum_r, sum_g, sum_b);
! 505: *v_dst++ = GetChromaV(sum_r, sum_g, sum_b);
! 506:
! 507: rgb_line1 += 2;
! 508: rgb_line2 += 2;
! 509: Y_dst1 += 2;
! 510: Y_dst2 += 2;
! 511: }
! 512:
! 513: if (width & 1) { /* rightmost pixel. */
! 514: const int sum_r = GetRed(rgb_line1) + GetRed(rgb_line2);
! 515: const int sum_g = GetGreen(rgb_line1) + GetGreen(rgb_line2);
! 516: const int sum_b = GetBlue(rgb_line1) + GetBlue(rgb_line2);
! 517:
! 518: Y_dst1[0] = GetLumaYfromPtr(rgb_line1);
! 519: Y_dst2[0] = GetLumaYfromPtr(rgb_line2);
! 520: *u_dst = GetChromaU(2 * sum_r, 2 * sum_g, 2 * sum_b);
! 521: *v_dst = GetChromaV(2 * sum_r, 2 * sum_g, 2 * sum_b);
! 522: }
! 523: }
! 524:
! 525: /* Generates Y, U, V data (with color subsampling) from 32 bits
! 526: * per pixel RGBA data buffer. The resulting YUV data can be directly fed into
! 527: * the WebPEncode routine.
! 528: * Input:
! 529: * 1. pixdatainput rgba data buffer
! 530: * 2. words per line corresponding to pixdata
! 531: * 3, 4. image width and height respectively
! 532: * Output:
! 533: * 5, 6, 7. Output YUV data buffers
! 534: */
! 535: void gd_RGBAToYUV420(gdImagePtr im2,
! 536: uint8* Y,
! 537: uint8* U,
! 538: uint8* V) {
! 539: int y_width = im2->sx;
! 540: int y_height = im2->sy;
! 541: int y_stride = y_width;
! 542: int uv_width = ((y_width + 1) >> 1);
! 543: int uv_stride = uv_width;
! 544: int y;
! 545: gdImagePtr im = NULL;
! 546: int free_im = 0;
! 547:
! 548: if (!im2->trueColor) {
! 549: /* Todo: Replace the color/YUV functions with our own and simplify
! 550: that should boost the conversion a bit as well, not only for
! 551: palette image. */
! 552: im = gdImageCreateTrueColor(im2->sx, im2->sy);
! 553: if (!im) {
! 554: php_gd_error("gd-webp error: cannot convert palette input to truecolor");
! 555: return;
! 556: }
! 557: gdImageCopy(im, im2, 0, 0, 0, 0, im->sx, im->sy);
! 558: free_im = 1;
! 559: } else {
! 560: im = im2;
! 561: }
! 562: for (y = 0; y < (y_height >> 1); ++y) {
! 563: RGBALinepairToYUV420(im->tpixels[2 * y],
! 564: im->tpixels[2 * y + 1],
! 565: y_width,
! 566: Y + 2 * y * y_stride,
! 567: Y + (2 * y + 1) * y_stride,
! 568: U + y * uv_stride,
! 569: V + y * uv_stride);
! 570: }
! 571: if (y_height & 1) {
! 572: RGBALinepairToYUV420(im->tpixels[y_height - 1],
! 573: im->tpixels[y_height - 1],
! 574: y_width,
! 575: Y + (y_height - 1) * y_stride,
! 576: Y + (y_height - 1) * y_stride,
! 577: U + (y_height >> 1) * uv_stride,
! 578: V + (y_height >> 1) * uv_stride);
! 579: }
! 580: if (free_im) {
! 581: gdImageDestroy(im);
! 582: }
! 583: }
! 584:
! 585: /* Generates Y, U, V data (with color subsampling) from 32 bits
! 586: * per pixel RGBA data buffer. The resulting YUV data can be directly fed into
! 587: * the WebPEncode routine.
! 588: * Input:
! 589: * 1. pixdatainput rgba data buffer
! 590: * 2. words per line corresponding to pixdata
! 591: * 3, 4. image width and height respectively
! 592: * Output:
! 593: * 5, 6, 7. Output YUV data buffers
! 594: */
! 595: void RGBAToYUV420(uint32* pixdata,
! 596: int words_per_line,
! 597: int width,
! 598: int height,
! 599: uint8* Y,
! 600: uint8* U,
! 601: uint8* V) {
! 602: int y_width = width;
! 603: int y_height = height;
! 604: int y_stride = y_width;
! 605: int uv_width = ((y_width + 1) >> 1);
! 606: int uv_stride = uv_width;
! 607: int y;
! 608:
! 609: for (y = 0; y < (y_height >> 1); ++y) {
! 610: RGBALinepairToYUV420(pixdata + 2 * y * words_per_line,
! 611: pixdata + (2 * y + 1) * words_per_line,
! 612: y_width,
! 613: Y + 2 * y * y_stride,
! 614: Y + (2 * y + 1) * y_stride,
! 615: U + y * uv_stride,
! 616: V + y * uv_stride);
! 617: }
! 618: if (y_height & 1) {
! 619: RGBALinepairToYUV420(pixdata + (y_height - 1) * words_per_line,
! 620: pixdata + (y_height - 1) * words_per_line,
! 621: y_width,
! 622: Y + (y_height - 1) * y_stride,
! 623: Y + (y_height - 1) * y_stride,
! 624: U + (y_height >> 1) * uv_stride,
! 625: V + (y_height >> 1) * uv_stride);
! 626: }
! 627: }
! 628:
! 629: static int codec_ctl(vpx_codec_ctx_t *enc,
! 630: enum vp8e_enc_control_id id,
! 631: int value) {
! 632: const vpx_codec_err_t res = vpx_codec_control_(enc, id, value);
! 633: if (res != VPX_CODEC_OK) {
! 634: return webp_failure;
! 635: }
! 636: return webp_success;
! 637: }
! 638:
! 639: static void SetupParams(vpx_codec_enc_cfg_t* cfg,
! 640: int QP) {
! 641: cfg->g_threads = 2;
! 642: cfg->rc_min_quantizer = QP;
! 643: cfg->rc_max_quantizer = QP;
! 644: cfg->kf_mode = VPX_KF_FIXED;
! 645: }
! 646:
! 647: /* VPXEncode: Takes a Y, U, V data buffers (with color components U and V
! 648: * subsampled to 1/2 resolution) and generates the VPX string.
! 649: * Output VPX string is placed in the *p_out buffer. container_size
! 650: * indicates number of bytes to be left blank at the beginning of
! 651: * *p_out buffer to accommodate for a container header.
! 652: *
! 653: * Return: success/failure
! 654: */
! 655: static WebPResult VPXEncode(const uint8* Y,
! 656: const uint8* U,
! 657: const uint8* V,
! 658: int y_width,
! 659: int y_height,
! 660: int y_stride,
! 661: int uv_width,
! 662: int uv_height,
! 663: int uv_stride,
! 664: int QP,
! 665: int container_size,
! 666: unsigned char** p_out,
! 667: int* p_out_size_bytes) {
! 668: vpx_codec_iface_t* iface = &vpx_codec_vp8_cx_algo;
! 669: vpx_codec_err_t res;
! 670: vpx_codec_enc_cfg_t cfg;
! 671: vpx_codec_ctx_t enc;
! 672: WebPResult result = webp_failure;
! 673: vpx_image_t img;
! 674:
! 675: *p_out = NULL;
! 676: *p_out_size_bytes = 0;
! 677:
! 678:
! 679: /* validate input parameters. */
! 680: if (!p_out || !Y || !U || !V
! 681: || y_width <= 0 || y_height <= 0 || uv_width <= 0 || uv_height <= 0
! 682: || y_stride < y_width || uv_stride < uv_width
! 683: || QP < 0 || QP > 63) {
! 684: return webp_failure;
! 685: }
! 686:
! 687: res = vpx_codec_enc_config_default(iface, &cfg, 0);
! 688: if (res != VPX_CODEC_OK) {
! 689: return webp_failure;
! 690: }
! 691:
! 692: SetupParams(&cfg, QP);
! 693: cfg.g_w = y_width;
! 694: cfg.g_h = y_height;
! 695:
! 696: res = vpx_codec_enc_init(&enc, iface, &cfg, 0);
! 697:
! 698: if (res == VPX_CODEC_OK) {
! 699: codec_ctl(&enc, VP8E_SET_CPUUSED, 3);
! 700: codec_ctl(&enc, VP8E_SET_NOISE_SENSITIVITY, 0);
! 701: codec_ctl(&enc, VP8E_SET_SHARPNESS, 0);
! 702: codec_ctl(&enc, VP8E_SET_ENABLEAUTOALTREF, 0);
! 703: codec_ctl(&enc, VP8E_SET_ARNR_MAXFRAMES, 0);
! 704: codec_ctl(&enc, VP8E_SET_ARNR_TYPE, 0);
! 705: codec_ctl(&enc, VP8E_SET_ARNR_STRENGTH, 0);
! 706: codec_ctl(&enc, VP8E_SET_STATIC_THRESHOLD, 0);
! 707: codec_ctl(&enc, VP8E_SET_TOKEN_PARTITIONS, 2);
! 708:
! 709: vpx_img_wrap(&img, IMG_FMT_I420,
! 710: y_width, y_height, 16, (uint8*)(Y));
! 711: img.planes[PLANE_Y] = (uint8*)(Y);
! 712: img.planes[PLANE_U] = (uint8*)(U);
! 713: img.planes[PLANE_V] = (uint8*)(V);
! 714: img.stride[PLANE_Y] = y_stride;
! 715: img.stride[PLANE_U] = uv_stride;
! 716: img.stride[PLANE_V] = uv_stride;
! 717:
! 718: res = vpx_codec_encode(&enc, &img, 0, 1, 0, VPX_DL_BEST_QUALITY);
! 719:
! 720: if (res == VPX_CODEC_OK) {
! 721: vpx_codec_iter_t iter = NULL;
! 722: const vpx_codec_cx_pkt_t* pkt = vpx_codec_get_cx_data(&enc, &iter);
! 723: if (pkt != NULL) {
! 724: *p_out = (unsigned char*)(calloc(container_size + pkt->data.frame.sz,
! 725: 1));
! 726:
! 727: memcpy(*p_out + container_size,
! 728: (const void*)(pkt->data.frame.buf),
! 729: pkt->data.frame.sz);
! 730: *p_out_size_bytes = container_size + pkt->data.frame.sz;
! 731:
! 732: result = webp_success;
! 733: }
! 734: }
! 735: }
! 736:
! 737: vpx_codec_destroy(&enc);
! 738:
! 739: return result;
! 740: }
! 741:
! 742: WebPResult WebPEncode(const uint8* Y,
! 743: const uint8* U,
! 744: const uint8* V,
! 745: int y_width,
! 746: int y_height,
! 747: int y_stride,
! 748: int uv_width,
! 749: int uv_height,
! 750: int uv_stride,
! 751: int QP,
! 752: unsigned char** p_out,
! 753: int* p_out_size_bytes,
! 754: double *psnr) {
! 755:
! 756: const int kRiffHeaderSize = 20;
! 757:
! 758: if (VPXEncode(Y, U, V,
! 759: y_width, y_height, y_stride,
! 760: uv_width, uv_height, uv_stride,
! 761: QP, kRiffHeaderSize,
! 762: p_out, p_out_size_bytes) != webp_success) {
! 763: return webp_failure;
! 764: } else {
! 765: /* Write RIFF header */
! 766: const int img_size_bytes = *p_out_size_bytes - kRiffHeaderSize;
! 767: const int chunk_size = (img_size_bytes + 1) & ~1; /* make size even */
! 768: const int riff_size = chunk_size + 12;
! 769: const uint8_t kRiffHeader[20] = { 'R', 'I', 'F', 'F',
! 770: (riff_size >> 0) & 255,
! 771: (riff_size >> 8) & 255,
! 772: (riff_size >> 16) & 255,
! 773: (riff_size >> 24) & 255,
! 774: 'W', 'E', 'B', 'P',
! 775: 'V', 'P', '8', ' ',
! 776: (chunk_size >> 0) & 255,
! 777: (chunk_size >> 8) & 255,
! 778: (chunk_size >> 16) & 255,
! 779: (chunk_size >> 24) & 255 };
! 780: memcpy(*p_out, kRiffHeader, kRiffHeaderSize);
! 781:
! 782: if (psnr) {
! 783: *psnr = WebPGetPSNR(Y, U, V, *p_out, *p_out_size_bytes);
! 784: }
! 785:
! 786: return webp_success;
! 787: }
! 788: }
! 789:
! 790: void AdjustColorspace(uint8* Y, uint8* U, uint8* V, int width, int height) {
! 791: int y_width = width;
! 792: int y_height = height;
! 793: int y_stride = y_width;
! 794: int uv_width = ((y_width + 1) >> 1);
! 795: int uv_height = ((y_height + 1) >> 1);
! 796: int uv_stride = uv_width;
! 797: int x, y;
! 798: /* convert luma */
! 799: for (y = 0; y < y_height; ++y) {
! 800: uint8* const Yrow = Y + y * y_stride;
! 801: for (x = 0; x < y_width; ++x) {
! 802: /* maps [0..255] to [16..235] */
! 803: Yrow[x] = ((Yrow[x] * 55 + 32) >> 6) + 16;
! 804: }
! 805: }
! 806: /* convert chroma */
! 807: for (y = 0; y < uv_height; ++y) {
! 808: uint8* const Urow = U + y * uv_stride;
! 809: uint8* const Vrow = V + y * uv_stride;
! 810: for (x = 0; x < uv_width; ++x) {
! 811: /* maps [0..255] to [16..240] */
! 812: Urow[x] = (((Urow[x] - 127) * 7) >> 3) + 128;
! 813: Vrow[x] = (((Vrow[x] - 127) * 7) >> 3) + 128;
! 814: }
! 815: }
! 816: }
! 817:
! 818: void AdjustColorspaceBack(uint8* Y, uint8* U, uint8* V, int width, int height) {
! 819: int y_width = width;
! 820: int y_height = height;
! 821: int y_stride = y_width;
! 822: int uv_width = ((y_width + 1) >> 1);
! 823: int uv_height = ((y_height + 1) >> 1);
! 824: int uv_stride = uv_width;
! 825: int x, y;
! 826: /* convert luma */
! 827: for (y = 0; y < y_height; ++y) {
! 828: uint8* const Yrow = Y + y * y_stride;
! 829: for (x = 0; x < y_width; ++x) {
! 830: /* maps [16..235] to [0..255] */
! 831: const int v = ((Yrow[x] - 16) * 149 + 64) >> 7;
! 832: Yrow[x] = (v < 0) ? 0 : (v > 255) ? 255u : v;
! 833: }
! 834: }
! 835: /* convert chroma */
! 836: for (y = 0; y < uv_height; ++y) {
! 837: uint8* const Urow = U + y * uv_stride;
! 838: uint8* const Vrow = V + y * uv_stride;
! 839: for (x = 0; x < uv_width; ++x) {
! 840: /* maps [0..255] to [16..240] */
! 841: const int ru = (((Urow[x] - 128) * 73) >> 6) + 128;
! 842: const int rv = (((Vrow[x] - 128) * 73) >> 6) + 128;
! 843: Urow[x] = (ru < 0) ? 0 : (ru > 255) ? 255u : ru;
! 844: Vrow[x] = (rv < 0) ? 0 : (rv > 255) ? 255u : rv;
! 845: }
! 846: }
! 847: }
! 848:
! 849: WebPResult WebPGetInfo(const uint8* data,
! 850: int data_size,
! 851: int *width,
! 852: int *height) {
! 853: const uint32 chunk_size = SkipRiffHeader(&data, &data_size);
! 854:
! 855: if (width) *width = 0;
! 856: if (height) *height = 0;
! 857:
! 858: if (!chunk_size) {
! 859: return webp_failure; /* unsupported RIFF header */
! 860: }
! 861:
! 862: /* Validate raw video data */
! 863: if (data_size < 10) {
! 864: return webp_failure; /* not enough data */
! 865: }
! 866:
! 867: /* check signature */
! 868: if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) {
! 869: return webp_failure; /* Wrong signature. */
! 870: } else {
! 871: const uint32 bits = data[0] | (data[1] << 8) | (data[2] << 16);
! 872:
! 873: if ((bits & 1)) { /* Not a keyframe. */
! 874: return webp_failure;
! 875: } else {
! 876: const int profile = (bits >> 1) & 7;
! 877: const int show_frame = (bits >> 4) & 1;
! 878: const uint32 partition_length = (bits >> 5);
! 879:
! 880: if (profile > 3) {
! 881: return webp_failure; /* unknown profile */
! 882: }
! 883: if (!show_frame) {
! 884: return webp_failure; /* first frame is invisible! */
! 885: }
! 886: if (partition_length >= chunk_size) {
! 887: return webp_failure; /* inconsistent size information. */
! 888: } else {
! 889: const int w = ((data[7] << 8) | data[6]) & 0x3fff;
! 890: const int h = ((data[9] << 8) | data[8]) & 0x3fff;
! 891: if (width) *width = w;
! 892: if (height) *height = h;
! 893:
! 894: return webp_success;
! 895: }
! 896: }
! 897: }
! 898: return webp_failure;
! 899: }
! 900: #endif /* HAVE_LIBVPX */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>