go-libwebp

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

extras.c (11009B)


      1 // Copyright 2015 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 //  Additional WebP utilities.
     11 //
     12 
     13 #include "extras/extras.h"
     14 
     15 #include <assert.h>
     16 #include <limits.h>
     17 #include <string.h>
     18 
     19 #include "extras/sharpyuv_risk_table.h"
     20 #include "sharpyuv/sharpyuv.h"
     21 #include "src/dsp/dsp.h"
     22 #include "src/utils/utils.h"
     23 #include "src/webp/encode.h"
     24 #include "webp/format_constants.h"
     25 #include "webp/types.h"
     26 
     27 #define XTRA_MAJ_VERSION 1
     28 #define XTRA_MIN_VERSION 6
     29 #define XTRA_REV_VERSION 0
     30 
     31 //------------------------------------------------------------------------------
     32 
     33 int WebPGetExtrasVersion(void) {
     34   return (XTRA_MAJ_VERSION << 16) | (XTRA_MIN_VERSION << 8) | XTRA_REV_VERSION;
     35 }
     36 
     37 //------------------------------------------------------------------------------
     38 
     39 int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) {
     40   int y, width, uv_width;
     41   if (pic == NULL || gray_data == NULL) return 0;
     42   pic->colorspace = WEBP_YUV420;
     43   if (!WebPPictureAlloc(pic)) return 0;
     44   width = pic->width;
     45   uv_width = (width + 1) >> 1;
     46   for (y = 0; y < pic->height; ++y) {
     47     memcpy(pic->y + y * pic->y_stride, gray_data, width);
     48     gray_data += width;    // <- we could use some 'data_stride' here if needed
     49     if ((y & 1) == 0) {
     50       memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width);
     51       memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width);
     52     }
     53   }
     54   return 1;
     55 }
     56 
     57 int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic) {
     58   int x, y;
     59   uint32_t* dst;
     60   if (pic == NULL || rgb565 == NULL) return 0;
     61   pic->colorspace = WEBP_YUV420;
     62   pic->use_argb = 1;
     63   if (!WebPPictureAlloc(pic)) return 0;
     64   dst = pic->argb;
     65   for (y = 0; y < pic->height; ++y) {
     66     const int width = pic->width;
     67     for (x = 0; x < width; ++x) {
     68 #if defined(WEBP_SWAP_16BIT_CSP) && (WEBP_SWAP_16BIT_CSP == 1)
     69       const uint32_t rg = rgb565[2 * x + 1];
     70       const uint32_t gb = rgb565[2 * x + 0];
     71 #else
     72       const uint32_t rg = rgb565[2 * x + 0];
     73       const uint32_t gb = rgb565[2 * x + 1];
     74 #endif
     75       uint32_t r = rg & 0xf8;
     76       uint32_t g = ((rg << 5) | (gb >> 3)) & 0xfc;
     77       uint32_t b = (gb << 5);
     78       // dithering
     79       r = r | (r >> 5);
     80       g = g | (g >> 6);
     81       b = b | (b >> 5);
     82       dst[x] = (0xffu << 24) | (r << 16) | (g << 8) | b;
     83     }
     84     rgb565 += 2 * width;
     85     dst += pic->argb_stride;
     86   }
     87   return 1;
     88 }
     89 
     90 int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic) {
     91   int x, y;
     92   uint32_t* dst;
     93   if (pic == NULL || rgb4444 == NULL) return 0;
     94   pic->colorspace = WEBP_YUV420;
     95   pic->use_argb = 1;
     96   if (!WebPPictureAlloc(pic)) return 0;
     97   dst = pic->argb;
     98   for (y = 0; y < pic->height; ++y) {
     99     const int width = pic->width;
    100     for (x = 0; x < width; ++x) {
    101 #if defined(WEBP_SWAP_16BIT_CSP) && (WEBP_SWAP_16BIT_CSP == 1)
    102       const uint32_t rg = rgb4444[2 * x + 1];
    103       const uint32_t ba = rgb4444[2 * x + 0];
    104 #else
    105       const uint32_t rg = rgb4444[2 * x + 0];
    106       const uint32_t ba = rgb4444[2 * x + 1];
    107 #endif
    108       uint32_t r = rg & 0xf0;
    109       uint32_t g = (rg << 4);
    110       uint32_t b = (ba & 0xf0);
    111       uint32_t a = (ba << 4);
    112       // dithering
    113       r = r | (r >> 4);
    114       g = g | (g >> 4);
    115       b = b | (b >> 4);
    116       a = a | (a >> 4);
    117       dst[x] = (a << 24) | (r << 16) | (g << 8) | b;
    118     }
    119     rgb4444 += 2 * width;
    120     dst += pic->argb_stride;
    121   }
    122   return 1;
    123 }
    124 
    125 int WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
    126                               const uint32_t palette[], int palette_size,
    127                               WebPPicture* pic) {
    128   int x, y;
    129   uint32_t* dst;
    130   // 256 as the input buffer is uint8_t.
    131   assert(MAX_PALETTE_SIZE <= 256);
    132   if (pic == NULL || indexed == NULL || indexed_stride < pic->width ||
    133       palette == NULL || palette_size > MAX_PALETTE_SIZE || palette_size <= 0) {
    134     return 0;
    135   }
    136   pic->use_argb = 1;
    137   if (!WebPPictureAlloc(pic)) return 0;
    138   dst = pic->argb;
    139   for (y = 0; y < pic->height; ++y) {
    140     for (x = 0; x < pic->width; ++x) {
    141       // Make sure we are within the palette.
    142       if (indexed[x] >= palette_size) {
    143         WebPPictureFree(pic);
    144         return 0;
    145       }
    146       dst[x] = palette[indexed[x]];
    147     }
    148     indexed += indexed_stride;
    149     dst += pic->argb_stride;
    150   }
    151   return 1;
    152 }
    153 
    154 //------------------------------------------------------------------------------
    155 
    156 int WebPUnmultiplyARGB(WebPPicture* pic) {
    157   int y;
    158   uint32_t* dst;
    159   if (pic == NULL || pic->use_argb != 1 || pic->argb == NULL) return 0;
    160   WebPInitAlphaProcessing();
    161   dst = pic->argb;
    162   for (y = 0; y < pic->height; ++y) {
    163     WebPMultARGBRow(dst, pic->width, /*inverse=*/1);
    164     dst += pic->argb_stride;
    165   }
    166   return 1;
    167 }
    168 
    169 //------------------------------------------------------------------------------
    170 // 420 risk metric
    171 
    172 #define YUV_FIX 16  // fixed-point precision for RGB->YUV
    173 static const int kYuvHalf = 1 << (YUV_FIX - 1);
    174 
    175 // Maps a value in [0, (256 << YUV_FIX) - 1] to [0,
    176 // precomputed_scores_table_sampling - 1]. It is important that the extremal
    177 // values are preserved and 1:1 mapped:
    178 //  ConvertValue(0) = 0
    179 //  ConvertValue((256 << 16) - 1) = rgb_sampling_size - 1
    180 static int SharpYuvConvertValueToSampledIdx(int v, int rgb_sampling_size) {
    181   v = (v + kYuvHalf) >> YUV_FIX;
    182   v = (v < 0) ? 0 : (v > 255) ? 255 : v;
    183   return (v * (rgb_sampling_size - 1)) / 255;
    184 }
    185 
    186 #undef YUV_FIX
    187 
    188 // For each pixel, computes the index to look up that color in a precomputed
    189 // risk score table where the YUV space is subsampled to a size of
    190 // precomputed_scores_table_sampling^3 (see sharpyuv_risk_table.h)
    191 static int SharpYuvConvertToYuvSharpnessIndex(
    192     int r, int g, int b, const SharpYuvConversionMatrix* matrix,
    193     int precomputed_scores_table_sampling) {
    194   const int y = SharpYuvConvertValueToSampledIdx(
    195       matrix->rgb_to_y[0] * r + matrix->rgb_to_y[1] * g +
    196           matrix->rgb_to_y[2] * b + matrix->rgb_to_y[3],
    197       precomputed_scores_table_sampling);
    198   const int u = SharpYuvConvertValueToSampledIdx(
    199       matrix->rgb_to_u[0] * r + matrix->rgb_to_u[1] * g +
    200           matrix->rgb_to_u[2] * b + matrix->rgb_to_u[3],
    201       precomputed_scores_table_sampling);
    202   const int v = SharpYuvConvertValueToSampledIdx(
    203       matrix->rgb_to_v[0] * r + matrix->rgb_to_v[1] * g +
    204           matrix->rgb_to_v[2] * b + matrix->rgb_to_v[3],
    205       precomputed_scores_table_sampling);
    206   return y + u * precomputed_scores_table_sampling +
    207          v * precomputed_scores_table_sampling *
    208              precomputed_scores_table_sampling;
    209 }
    210 
    211 static void SharpYuvRowToYuvSharpnessIndex(
    212     const uint8_t* r_ptr, const uint8_t* g_ptr, const uint8_t* b_ptr,
    213     int rgb_step, int rgb_bit_depth, int width, uint16_t* dst,
    214     const SharpYuvConversionMatrix* matrix,
    215     int precomputed_scores_table_sampling) {
    216   int i;
    217   assert(rgb_bit_depth == 8);
    218   (void)rgb_bit_depth;  // Unused for now.
    219   for (i = 0; i < width;
    220        ++i, r_ptr += rgb_step, g_ptr += rgb_step, b_ptr += rgb_step) {
    221     dst[i] =
    222         SharpYuvConvertToYuvSharpnessIndex(r_ptr[0], g_ptr[0], b_ptr[0], matrix,
    223                                            precomputed_scores_table_sampling);
    224   }
    225 }
    226 
    227 #define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((uint64_t)(W) * (H), sizeof(T)))
    228 
    229 static int DoEstimateRisk(const uint8_t* r_ptr, const uint8_t* g_ptr,
    230                           const uint8_t* b_ptr, int rgb_step, int rgb_stride,
    231                           int rgb_bit_depth, int width, int height,
    232                           const SharpYuvOptions* options,
    233                           const uint8_t precomputed_scores_table[],
    234                           int precomputed_scores_table_sampling,
    235                           float* score_out) {
    236   const int sampling3 = precomputed_scores_table_sampling *
    237                         precomputed_scores_table_sampling *
    238                         precomputed_scores_table_sampling;
    239   const int kNoiseLevel = 4;
    240   double total_score = 0;
    241   double count = 0;
    242   // Rows of indices in
    243   uint16_t* row1 = SAFE_ALLOC(width, 1, uint16_t);
    244   uint16_t* row2 = SAFE_ALLOC(width, 1, uint16_t);
    245   uint16_t* tmp;
    246   int i, j;
    247 
    248   if (row1 == NULL || row2 == NULL) {
    249     WebPFree(row1);
    250     WebPFree(row2);
    251     return 0;
    252   }
    253 
    254   // Convert the first row ahead.
    255   SharpYuvRowToYuvSharpnessIndex(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth,
    256                                  width, row2, options->yuv_matrix,
    257                                  precomputed_scores_table_sampling);
    258 
    259   for (j = 1; j < height; ++j) {
    260     r_ptr += rgb_stride;
    261     g_ptr += rgb_stride;
    262     b_ptr += rgb_stride;
    263     // Swap row 1 and row 2.
    264     tmp = row1;
    265     row1 = row2;
    266     row2 = tmp;
    267     // Convert the row below.
    268     SharpYuvRowToYuvSharpnessIndex(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth,
    269                                    width, row2, options->yuv_matrix,
    270                                    precomputed_scores_table_sampling);
    271     for (i = 0; i < width - 1; ++i) {
    272       const int idx0 = row1[i + 0];
    273       const int idx1 = row1[i + 1];
    274       const int idx2 = row2[i + 0];
    275       const int score = precomputed_scores_table[idx0 + sampling3 * idx1] +
    276                         precomputed_scores_table[idx0 + sampling3 * idx2] +
    277                         precomputed_scores_table[idx1 + sampling3 * idx2];
    278       if (score > kNoiseLevel) {
    279         total_score += score;
    280         count += 1.0;
    281       }
    282     }
    283   }
    284   if (count > 0.) total_score /= count;
    285 
    286   // If less than 1% of pixels were evaluated -> below noise level.
    287   if (100. * count / (width * height) < 1.) total_score = 0.;
    288 
    289   // Rescale to [0:100]
    290   total_score = (total_score > 25.) ? 100. : total_score * 100. / 25.;
    291 
    292   WebPFree(row1);
    293   WebPFree(row2);
    294 
    295   *score_out = (float)total_score;
    296   return 1;
    297 }
    298 
    299 #undef SAFE_ALLOC
    300 
    301 int SharpYuvEstimate420Risk(const void* r_ptr, const void* g_ptr,
    302                             const void* b_ptr, int rgb_step, int rgb_stride,
    303                             int rgb_bit_depth, int width, int height,
    304                             const SharpYuvOptions* options, float* score) {
    305   if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX ||
    306       r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || options == NULL ||
    307       score == NULL) {
    308     return 0;
    309   }
    310   if (rgb_bit_depth != 8) {
    311     return 0;
    312   }
    313 
    314   if (width <= 4 || height <= 4) {
    315     *score = 0.0f;  // too small, no real risk.
    316     return 1;
    317   }
    318 
    319   return DoEstimateRisk(
    320       (const uint8_t*)r_ptr, (const uint8_t*)g_ptr, (const uint8_t*)b_ptr,
    321       rgb_step, rgb_stride, rgb_bit_depth, width, height, options,
    322       kSharpYuvPrecomputedRisk, kSharpYuvPrecomputedRiskYuvSampling, score);
    323 }
    324 
    325 //------------------------------------------------------------------------------