go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

picture_rescale_enc.c (10488B)


      1 // Copyright 2014 Google Inc. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the COPYING file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS. All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 // -----------------------------------------------------------------------------
      9 //
     10 // WebPPicture tools: copy, crop, rescaling and view.
     11 //
     12 // Author: Skal (pascal.massimino@gmail.com)
     13 
     14 #include "src/webp/encode.h"
     15 
     16 #include <assert.h>
     17 #include <stdlib.h>
     18 
     19 #include "src/webp/types.h"
     20 #include "src/dsp/dsp.h"
     21 #include "src/enc/vp8i_enc.h"
     22 
     23 #if !defined(WEBP_REDUCE_SIZE)
     24 #include "src/utils/rescaler_utils.h"
     25 #include "src/utils/utils.h"
     26 #endif  // !defined(WEBP_REDUCE_SIZE)
     27 
     28 #define HALVE(x) (((x) + 1) >> 1)
     29 
     30 // Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
     31 // into 'dst'. Mark 'dst' as not owning any memory.
     32 static void PictureGrabSpecs(const WebPPicture* const src,
     33                              WebPPicture* const dst) {
     34   assert(src != NULL && dst != NULL);
     35   *dst = *src;
     36   WebPPictureResetBuffers(dst);
     37 }
     38 
     39 //------------------------------------------------------------------------------
     40 
     41 // Adjust top-left corner to chroma sample position.
     42 static void SnapTopLeftPosition(const WebPPicture* const pic,
     43                                 int* const left, int* const top) {
     44   if (!pic->use_argb) {
     45     *left &= ~1;
     46     *top &= ~1;
     47   }
     48 }
     49 
     50 // Adjust top-left corner and verify that the sub-rectangle is valid.
     51 static int AdjustAndCheckRectangle(const WebPPicture* const pic,
     52                                    int* const left, int* const top,
     53                                    int width, int height) {
     54   SnapTopLeftPosition(pic, left, top);
     55   if ((*left) < 0 || (*top) < 0) return 0;
     56   if (width <= 0 || height <= 0) return 0;
     57   if ((*left) + width > pic->width) return 0;
     58   if ((*top) + height > pic->height) return 0;
     59   return 1;
     60 }
     61 
     62 #if !defined(WEBP_REDUCE_SIZE)
     63 int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
     64   if (src == NULL || dst == NULL) return 0;
     65   if (src == dst) return 1;
     66 
     67   PictureGrabSpecs(src, dst);
     68   if (!WebPPictureAlloc(dst)) return 0;
     69 
     70   if (!src->use_argb) {
     71     WebPCopyPlane(src->y, src->y_stride,
     72                   dst->y, dst->y_stride, dst->width, dst->height);
     73     WebPCopyPlane(src->u, src->uv_stride, dst->u, dst->uv_stride,
     74                   HALVE(dst->width), HALVE(dst->height));
     75     WebPCopyPlane(src->v, src->uv_stride, dst->v, dst->uv_stride,
     76                   HALVE(dst->width), HALVE(dst->height));
     77     if (dst->a != NULL)  {
     78       WebPCopyPlane(src->a, src->a_stride,
     79                     dst->a, dst->a_stride, dst->width, dst->height);
     80     }
     81   } else {
     82     WebPCopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
     83                   (uint8_t*)dst->argb, 4 * dst->argb_stride,
     84                   4 * dst->width, dst->height);
     85   }
     86   return 1;
     87 }
     88 #endif  // !defined(WEBP_REDUCE_SIZE)
     89 
     90 int WebPPictureIsView(const WebPPicture* picture) {
     91   if (picture == NULL) return 0;
     92   if (picture->use_argb) {
     93     return (picture->memory_argb_ == NULL);
     94   }
     95   return (picture->memory_ == NULL);
     96 }
     97 
     98 int WebPPictureView(const WebPPicture* src,
     99                     int left, int top, int width, int height,
    100                     WebPPicture* dst) {
    101   if (src == NULL || dst == NULL) return 0;
    102 
    103   // verify rectangle position.
    104   if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0;
    105 
    106   if (src != dst) {  // beware of aliasing! We don't want to leak 'memory_'.
    107     PictureGrabSpecs(src, dst);
    108   }
    109   dst->width = width;
    110   dst->height = height;
    111   if (!src->use_argb) {
    112     dst->y = src->y + top * src->y_stride + left;
    113     dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1);
    114     dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1);
    115     dst->y_stride = src->y_stride;
    116     dst->uv_stride = src->uv_stride;
    117     if (src->a != NULL) {
    118       dst->a = src->a + top * src->a_stride + left;
    119       dst->a_stride = src->a_stride;
    120     }
    121   } else {
    122     dst->argb = src->argb + top * src->argb_stride + left;
    123     dst->argb_stride = src->argb_stride;
    124   }
    125   return 1;
    126 }
    127 
    128 #if !defined(WEBP_REDUCE_SIZE)
    129 //------------------------------------------------------------------------------
    130 // Picture cropping
    131 
    132 int WebPPictureCrop(WebPPicture* pic,
    133                     int left, int top, int width, int height) {
    134   WebPPicture tmp;
    135 
    136   if (pic == NULL) return 0;
    137   if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0;
    138 
    139   PictureGrabSpecs(pic, &tmp);
    140   tmp.width = width;
    141   tmp.height = height;
    142   if (!WebPPictureAlloc(&tmp)) {
    143     return WebPEncodingSetError(pic, tmp.error_code);
    144   }
    145 
    146   if (!pic->use_argb) {
    147     const int y_offset = top * pic->y_stride + left;
    148     const int uv_offset = (top / 2) * pic->uv_stride + left / 2;
    149     WebPCopyPlane(pic->y + y_offset, pic->y_stride,
    150                   tmp.y, tmp.y_stride, width, height);
    151     WebPCopyPlane(pic->u + uv_offset, pic->uv_stride,
    152                   tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
    153     WebPCopyPlane(pic->v + uv_offset, pic->uv_stride,
    154                   tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
    155 
    156     if (tmp.a != NULL) {
    157       const int a_offset = top * pic->a_stride + left;
    158       WebPCopyPlane(pic->a + a_offset, pic->a_stride,
    159                     tmp.a, tmp.a_stride, width, height);
    160     }
    161   } else {
    162     const uint8_t* const src =
    163         (const uint8_t*)(pic->argb + top * pic->argb_stride + left);
    164     WebPCopyPlane(src, pic->argb_stride * 4, (uint8_t*)tmp.argb,
    165                   tmp.argb_stride * 4, width * 4, height);
    166   }
    167   WebPPictureFree(pic);
    168   *pic = tmp;
    169   return 1;
    170 }
    171 
    172 //------------------------------------------------------------------------------
    173 // Simple picture rescaler
    174 
    175 static int RescalePlane(const uint8_t* src,
    176                         int src_width, int src_height, int src_stride,
    177                         uint8_t* dst,
    178                         int dst_width, int dst_height, int dst_stride,
    179                         rescaler_t* const work,
    180                         int num_channels) {
    181   WebPRescaler rescaler;
    182   int y = 0;
    183   if (!WebPRescalerInit(&rescaler, src_width, src_height,
    184                         dst, dst_width, dst_height, dst_stride,
    185                         num_channels, work)) {
    186     return 0;
    187   }
    188   while (y < src_height) {
    189     y += WebPRescalerImport(&rescaler, src_height - y,
    190                             src + y * src_stride, src_stride);
    191     WebPRescalerExport(&rescaler);
    192   }
    193   return 1;
    194 }
    195 
    196 static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) {
    197   assert(pic->argb != NULL);
    198   WebPMultARGBRows((uint8_t*)pic->argb, pic->argb_stride * sizeof(*pic->argb),
    199                    pic->width, pic->height, inverse);
    200 }
    201 
    202 static void AlphaMultiplyY(WebPPicture* const pic, int inverse) {
    203   if (pic->a != NULL) {
    204     WebPMultRows(pic->y, pic->y_stride, pic->a, pic->a_stride,
    205                  pic->width, pic->height, inverse);
    206   }
    207 }
    208 
    209 int WebPPictureRescale(WebPPicture* picture, int width, int height) {
    210   WebPPicture tmp;
    211   int prev_width, prev_height;
    212   rescaler_t* work;
    213 
    214   if (picture == NULL) return 0;
    215   prev_width = picture->width;
    216   prev_height = picture->height;
    217   if (!WebPRescalerGetScaledDimensions(
    218           prev_width, prev_height, &width, &height)) {
    219     return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
    220   }
    221 
    222   PictureGrabSpecs(picture, &tmp);
    223   tmp.width = width;
    224   tmp.height = height;
    225   if (!WebPPictureAlloc(&tmp)) {
    226     return WebPEncodingSetError(picture, tmp.error_code);
    227   }
    228 
    229   if (!picture->use_argb) {
    230     work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
    231     if (work == NULL) {
    232       WebPPictureFree(&tmp);
    233       return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
    234     }
    235     // If present, we need to rescale alpha first (for AlphaMultiplyY).
    236     if (picture->a != NULL) {
    237       WebPInitAlphaProcessing();
    238       if (!RescalePlane(picture->a, prev_width, prev_height, picture->a_stride,
    239                         tmp.a, width, height, tmp.a_stride, work, 1)) {
    240         return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
    241       }
    242     }
    243 
    244     // We take transparency into account on the luma plane only. That's not
    245     // totally exact blending, but still is a good approximation.
    246     AlphaMultiplyY(picture, 0);
    247     if (!RescalePlane(picture->y, prev_width, prev_height, picture->y_stride,
    248                       tmp.y, width, height, tmp.y_stride, work, 1) ||
    249         !RescalePlane(picture->u, HALVE(prev_width), HALVE(prev_height),
    250                       picture->uv_stride, tmp.u, HALVE(width), HALVE(height),
    251                       tmp.uv_stride, work, 1) ||
    252         !RescalePlane(picture->v, HALVE(prev_width), HALVE(prev_height),
    253                       picture->uv_stride, tmp.v, HALVE(width), HALVE(height),
    254                       tmp.uv_stride, work, 1)) {
    255       return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
    256     }
    257     AlphaMultiplyY(&tmp, 1);
    258   } else {
    259     work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
    260     if (work == NULL) {
    261       WebPPictureFree(&tmp);
    262       return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
    263     }
    264     // In order to correctly interpolate colors, we need to apply the alpha
    265     // weighting first (black-matting), scale the RGB values, and remove
    266     // the premultiplication afterward (while preserving the alpha channel).
    267     WebPInitAlphaProcessing();
    268     AlphaMultiplyARGB(picture, 0);
    269     if (!RescalePlane((const uint8_t*)picture->argb, prev_width, prev_height,
    270                       picture->argb_stride * 4, (uint8_t*)tmp.argb, width,
    271                       height, tmp.argb_stride * 4, work, 4)) {
    272       return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
    273     }
    274     AlphaMultiplyARGB(&tmp, 1);
    275   }
    276   WebPPictureFree(picture);
    277   WebPSafeFree(work);
    278   *picture = tmp;
    279   return 1;
    280 }
    281 
    282 #else  // defined(WEBP_REDUCE_SIZE)
    283 
    284 int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
    285   (void)src;
    286   (void)dst;
    287   return 0;
    288 }
    289 
    290 int WebPPictureCrop(WebPPicture* pic,
    291                     int left, int top, int width, int height) {
    292   (void)pic;
    293   (void)left;
    294   (void)top;
    295   (void)width;
    296   (void)height;
    297   return 0;
    298 }
    299 
    300 int WebPPictureRescale(WebPPicture* pic, int width, int height) {
    301   (void)pic;
    302   (void)width;
    303   (void)height;
    304   return 0;
    305 }
    306 #endif  // !defined(WEBP_REDUCE_SIZE)