xref: /aosp_15_r20/external/webp/extras/extras.c (revision b2055c353e87c8814eb2b6b1b11112a1562253bd)
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