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 //------------------------------------------------------------------------------