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>