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