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