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