Annotation of embedaddon/php/ext/gd/libgd/webpimg.c, revision 1.1.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>