/src/libwebp/src/enc/picture_csp_enc.c
Line | Count | Source |
1 | | // Copyright 2014 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 | | // WebPPicture utils for colorspace conversion |
11 | | // |
12 | | // Author: Skal (pascal.massimino@gmail.com) |
13 | | |
14 | | #include <assert.h> |
15 | | #include <math.h> |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | |
19 | | #include "sharpyuv/sharpyuv.h" |
20 | | #include "sharpyuv/sharpyuv_csp.h" |
21 | | #include "src/dsp/cpu.h" |
22 | | #include "src/dsp/dsp.h" |
23 | | #include "src/dsp/lossless.h" |
24 | | #include "src/dsp/yuv.h" |
25 | | #include "src/enc/vp8i_enc.h" |
26 | | #include "src/utils/random_utils.h" |
27 | | #include "src/utils/utils.h" |
28 | | #include "src/webp/encode.h" |
29 | | #include "src/webp/types.h" |
30 | | |
31 | | #if defined(WEBP_USE_THREAD) && !defined(_WIN32) |
32 | | #include <pthread.h> |
33 | | #endif |
34 | | |
35 | 1.90k | #define ALPHA_OFFSET CHANNEL_OFFSET(0) |
36 | | |
37 | | //------------------------------------------------------------------------------ |
38 | | // Detection of non-trivial transparency |
39 | | |
40 | | // Returns true if alpha[] has non-0xff values. |
41 | | static int CheckNonOpaque(const uint8_t* alpha, int width, int height, |
42 | 13.6k | int x_step, ptrdiff_t y_step) { |
43 | 13.6k | if (alpha == NULL) return 0; |
44 | 9.56k | WebPInitAlphaProcessing(); |
45 | 9.56k | if (x_step == 1) { |
46 | 7.06k | for (; height-- > 0; alpha += y_step) { |
47 | 7.06k | if (WebPHasAlpha8b(alpha, width)) return 1; |
48 | 7.06k | } |
49 | 5.21k | } else { |
50 | 475k | for (; height-- > 0; alpha += y_step) { |
51 | 474k | if (WebPHasAlpha32b(alpha, width)) return 1; |
52 | 474k | } |
53 | 5.21k | } |
54 | 868 | return 0; |
55 | 9.56k | } |
56 | | |
57 | | // Checking for the presence of non-opaque alpha. |
58 | 6.80k | int WebPPictureHasTransparency(const WebPPicture* picture) { |
59 | 6.80k | if (picture == NULL) return 0; |
60 | 6.80k | if (picture->use_argb) { |
61 | 0 | if (picture->argb != NULL) { |
62 | 0 | return CheckNonOpaque( |
63 | 0 | (const uint8_t*)picture->argb + ALPHA_OFFSET, picture->width, |
64 | 0 | picture->height, 4, |
65 | 0 | (ptrdiff_t)picture->argb_stride * sizeof(*picture->argb)); |
66 | 0 | } |
67 | 0 | return 0; |
68 | 0 | } |
69 | 6.80k | return CheckNonOpaque(picture->a, picture->width, picture->height, 1, |
70 | 6.80k | picture->a_stride); |
71 | 6.80k | } |
72 | | |
73 | | extern VP8CPUInfo VP8GetCPUInfo; |
74 | | |
75 | | //------------------------------------------------------------------------------ |
76 | | // Sharp RGB->YUV conversion |
77 | | |
78 | | static const int kMinDimensionIterativeConversion = 4; |
79 | | |
80 | | //------------------------------------------------------------------------------ |
81 | | // Main function |
82 | | |
83 | | WEBP_NODISCARD static int PreprocessARGB(const uint8_t* r_ptr, |
84 | | const uint8_t* g_ptr, |
85 | | const uint8_t* b_ptr, int step, |
86 | | int rgb_stride, |
87 | 0 | WebPPicture* const picture) { |
88 | 0 | const int ok = SharpYuvConvert( |
89 | 0 | r_ptr, g_ptr, b_ptr, step, rgb_stride, /*rgb_bit_depth=*/8, picture->y, |
90 | 0 | picture->y_stride, picture->u, picture->uv_stride, picture->v, |
91 | 0 | picture->uv_stride, /*yuv_bit_depth=*/8, picture->width, picture->height, |
92 | 0 | SharpYuvGetConversionMatrix(kSharpYuvMatrixWebp)); |
93 | 0 | if (!ok) { |
94 | 0 | return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); |
95 | 0 | } |
96 | 0 | return ok; |
97 | 0 | } |
98 | | |
99 | | static WEBP_INLINE void ConvertRowToY(const uint8_t* const r_ptr, |
100 | | const uint8_t* const g_ptr, |
101 | | const uint8_t* const b_ptr, int step, |
102 | | uint8_t* const dst_y, int width, |
103 | 0 | VP8Random* const rg) { |
104 | 0 | int i, j; |
105 | 0 | for (i = 0, j = 0; i < width; i += 1, j += step) { |
106 | 0 | dst_y[i] = |
107 | 0 | VP8RGBToY(r_ptr[j], g_ptr[j], b_ptr[j], VP8RandomBits(rg, YUV_FIX)); |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | static WEBP_INLINE void ConvertRowsToUV(const uint16_t* rgb, |
112 | | uint8_t* const dst_u, |
113 | | uint8_t* const dst_v, int width, |
114 | 0 | VP8Random* const rg) { |
115 | 0 | int i; |
116 | 0 | for (i = 0; i < width; i += 1, rgb += 4) { |
117 | 0 | const int r = rgb[0], g = rgb[1], b = rgb[2]; |
118 | 0 | dst_u[i] = VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); |
119 | 0 | dst_v[i] = VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | extern void SharpYuvInit(VP8CPUInfo cpu_info_func); |
124 | | |
125 | | WEBP_NODISCARD static int ImportYUVAFromRGBA( |
126 | | const uint8_t* r_ptr, const uint8_t* g_ptr, const uint8_t* b_ptr, |
127 | | const uint8_t* a_ptr, |
128 | | int step, // bytes per pixel |
129 | | int rgb_stride, // bytes per scanline |
130 | 6.80k | float dithering, int use_iterative_conversion, WebPPicture* const picture) { |
131 | 6.80k | int y; |
132 | 6.80k | const int width = picture->width; |
133 | 6.80k | const int height = picture->height; |
134 | 6.80k | const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); |
135 | 6.80k | const WebPEncCSP orig_colorspace = picture->colorspace; |
136 | 6.80k | const int orig_use_argb = picture->use_argb; |
137 | | |
138 | 6.80k | picture->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; |
139 | 6.80k | picture->use_argb = 0; |
140 | | |
141 | | // disable smart conversion if source is too small (overkill). |
142 | 6.80k | if (width < kMinDimensionIterativeConversion || |
143 | 5.66k | height < kMinDimensionIterativeConversion) { |
144 | 1.40k | use_iterative_conversion = 0; |
145 | 1.40k | } |
146 | | |
147 | 6.80k | if (!WebPPictureAllocYUVA(picture)) { |
148 | 0 | picture->colorspace = orig_colorspace; |
149 | 0 | picture->use_argb = orig_use_argb; |
150 | 0 | return 0; |
151 | 0 | } |
152 | 6.80k | if (has_alpha) { |
153 | 4.34k | assert(step == 4); |
154 | 4.34k | } |
155 | | |
156 | 6.80k | if (use_iterative_conversion) { |
157 | 0 | SharpYuvInit(VP8GetCPUInfo); |
158 | 0 | if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) { |
159 | 0 | WebPSafeFree(picture->memory_); |
160 | 0 | picture->memory_ = NULL; |
161 | 0 | picture->y = picture->u = picture->v = picture->a = NULL; |
162 | 0 | picture->y_stride = picture->uv_stride = picture->a_stride = 0; |
163 | 0 | picture->colorspace = orig_colorspace; |
164 | 0 | picture->use_argb = orig_use_argb; |
165 | 0 | return 0; |
166 | 0 | } |
167 | 0 | if (has_alpha) { |
168 | 0 | WebPExtractAlpha(a_ptr, rgb_stride, width, height, picture->a, |
169 | 0 | picture->a_stride); |
170 | 0 | } |
171 | 6.80k | } else { |
172 | 6.80k | const int uv_width = (width + 1) >> 1; |
173 | | // temporary storage for accumulated R/G/B values during conversion to U/V |
174 | 6.80k | uint16_t* const tmp_rgb = |
175 | 6.80k | (uint16_t*)WebPSafeMalloc(4 * uv_width, sizeof(*tmp_rgb)); |
176 | 6.80k | uint8_t* dst_y = picture->y; |
177 | 6.80k | uint8_t* dst_u = picture->u; |
178 | 6.80k | uint8_t* dst_v = picture->v; |
179 | 6.80k | uint8_t* dst_a = picture->a; |
180 | | |
181 | 6.80k | VP8Random base_rg; |
182 | 6.80k | VP8Random* rg = NULL; |
183 | 6.80k | if (dithering > 0.) { |
184 | 0 | VP8InitRandom(&base_rg, dithering); |
185 | 0 | rg = &base_rg; |
186 | 0 | } |
187 | 6.80k | WebPInitConvertARGBToYUV(); |
188 | 6.80k | WebPInitGammaTables(); |
189 | | |
190 | 6.80k | if (tmp_rgb == NULL) { |
191 | 0 | WebPSafeFree(picture->memory_); |
192 | 0 | picture->memory_ = NULL; |
193 | 0 | picture->y = picture->u = picture->v = picture->a = NULL; |
194 | 0 | picture->y_stride = picture->uv_stride = picture->a_stride = 0; |
195 | 0 | picture->colorspace = orig_colorspace; |
196 | 0 | picture->use_argb = orig_use_argb; |
197 | 0 | return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); |
198 | 0 | } |
199 | | |
200 | 6.80k | if (rg == NULL) { |
201 | | // Downsample Y/U/V planes, two rows at a time |
202 | 6.80k | WebPImportYUVAFromRGBA( |
203 | 6.80k | r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, has_alpha, width, |
204 | 6.80k | height, tmp_rgb, picture->y_stride, picture->uv_stride, |
205 | 6.80k | picture->uv_stride, picture->a_stride, dst_y, dst_u, dst_v, dst_a); |
206 | 6.80k | if (height & 1) { |
207 | 2.29k | dst_y += (height - 1) * (ptrdiff_t)picture->y_stride; |
208 | 2.29k | dst_u += (height >> 1) * (ptrdiff_t)picture->uv_stride; |
209 | 2.29k | dst_v += (height >> 1) * (ptrdiff_t)picture->uv_stride; |
210 | 2.29k | r_ptr += (height - 1) * (ptrdiff_t)rgb_stride; |
211 | 2.29k | b_ptr += (height - 1) * (ptrdiff_t)rgb_stride; |
212 | 2.29k | g_ptr += (height - 1) * (ptrdiff_t)rgb_stride; |
213 | 2.29k | if (has_alpha) { |
214 | 1.06k | dst_a += (height - 1) * (ptrdiff_t)picture->a_stride; |
215 | 1.06k | a_ptr += (height - 1) * (ptrdiff_t)rgb_stride; |
216 | 1.06k | } |
217 | 2.29k | WebPImportYUVAFromRGBALastLine(r_ptr, g_ptr, b_ptr, a_ptr, step, |
218 | 2.29k | has_alpha, width, tmp_rgb, dst_y, dst_u, |
219 | 2.29k | dst_v, dst_a); |
220 | 2.29k | } |
221 | 6.80k | } else { |
222 | | // Copy of WebPImportYUVAFromRGBA/WebPImportYUVAFromRGBALastLine, |
223 | | // but with dithering. |
224 | 0 | for (y = 0; y < (height >> 1); ++y) { |
225 | 0 | int rows_have_alpha = has_alpha; |
226 | 0 | ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg); |
227 | 0 | ConvertRowToY(r_ptr + rgb_stride, g_ptr + rgb_stride, |
228 | 0 | b_ptr + rgb_stride, step, dst_y + picture->y_stride, |
229 | 0 | width, rg); |
230 | 0 | dst_y += 2 * (ptrdiff_t)picture->y_stride; |
231 | 0 | if (has_alpha) { |
232 | 0 | rows_have_alpha &= !WebPExtractAlpha(a_ptr, rgb_stride, width, 2, |
233 | 0 | dst_a, picture->a_stride); |
234 | 0 | dst_a += 2 * (ptrdiff_t)picture->a_stride; |
235 | 0 | } |
236 | | // Collect averaged R/G/B(/A) |
237 | 0 | if (!rows_have_alpha) { |
238 | 0 | WebPAccumulateRGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, tmp_rgb, |
239 | 0 | width); |
240 | 0 | } else { |
241 | 0 | WebPAccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, rgb_stride, tmp_rgb, |
242 | 0 | width); |
243 | 0 | } |
244 | | // Convert to U/V |
245 | 0 | ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg); |
246 | 0 | dst_u += picture->uv_stride; |
247 | 0 | dst_v += picture->uv_stride; |
248 | 0 | r_ptr += 2 * (ptrdiff_t)rgb_stride; |
249 | 0 | b_ptr += 2 * (ptrdiff_t)rgb_stride; |
250 | 0 | g_ptr += 2 * (ptrdiff_t)rgb_stride; |
251 | 0 | if (has_alpha) a_ptr += 2 * (ptrdiff_t)rgb_stride; |
252 | 0 | } |
253 | 0 | if (height & 1) { // extra last row |
254 | 0 | int row_has_alpha = has_alpha; |
255 | 0 | ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg); |
256 | 0 | if (row_has_alpha) { |
257 | 0 | row_has_alpha &= !WebPExtractAlpha(a_ptr, 0, width, 1, dst_a, 0); |
258 | 0 | } |
259 | | // Collect averaged R/G/B(/A) |
260 | 0 | if (!row_has_alpha) { |
261 | | // Collect averaged R/G/B |
262 | 0 | WebPAccumulateRGB(r_ptr, g_ptr, b_ptr, step, /*rgb_stride=*/0, |
263 | 0 | tmp_rgb, width); |
264 | 0 | } else { |
265 | 0 | WebPAccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, /*rgb_stride=*/0, |
266 | 0 | tmp_rgb, width); |
267 | 0 | } |
268 | 0 | ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg); |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | 6.80k | WebPSafeFree(tmp_rgb); |
273 | 6.80k | } |
274 | 6.80k | return 1; |
275 | 6.80k | } |
276 | | |
277 | | #undef SUM4 |
278 | | #undef SUM2 |
279 | | #undef SUM4ALPHA |
280 | | #undef SUM2ALPHA |
281 | | |
282 | | //------------------------------------------------------------------------------ |
283 | | // call for ARGB->YUVA conversion |
284 | | |
285 | | // Validates that the source buffer of 'picture' (ARGB if 'use_argb' is true, |
286 | | // YUVA otherwise) is allocated and within bounds, regardless of the current |
287 | | // value of 'picture->use_argb', while preserving 'picture->use_argb'. |
288 | | WEBP_NODISCARD static int ValidateAllocatedPicture(WebPPicture* const picture, |
289 | 2.46k | int use_argb) { |
290 | 2.46k | int orig_use_argb, ok; |
291 | 2.46k | if (picture == NULL) return 0; |
292 | 2.46k | orig_use_argb = picture->use_argb; |
293 | 2.46k | picture->use_argb = use_argb; |
294 | 2.46k | ok = WebPValidateAllocatedPicture(picture); |
295 | 2.46k | picture->use_argb = orig_use_argb; |
296 | 2.46k | return ok; |
297 | 2.46k | } |
298 | | |
299 | | WEBP_NODISCARD static int PictureARGBToYUVA(WebPPicture* picture, |
300 | | WebPEncCSP colorspace, |
301 | | float dithering, |
302 | 2.46k | int use_iterative_conversion) { |
303 | 2.46k | if (!ValidateAllocatedPicture(picture, /*use_argb=*/1)) return 0; |
304 | 2.46k | if ((colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) { |
305 | 0 | return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); |
306 | 2.46k | } else { |
307 | 2.46k | const uint8_t* const argb = (const uint8_t*)picture->argb; |
308 | 2.46k | const uint8_t* const a = argb + CHANNEL_OFFSET(0); |
309 | 2.46k | const uint8_t* const r = argb + CHANNEL_OFFSET(1); |
310 | 2.46k | const uint8_t* const g = argb + CHANNEL_OFFSET(2); |
311 | 2.46k | const uint8_t* const b = argb + CHANNEL_OFFSET(3); |
312 | | |
313 | 2.46k | return ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, |
314 | 2.46k | dithering, use_iterative_conversion, picture); |
315 | 2.46k | } |
316 | 2.46k | } |
317 | | |
318 | | int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, |
319 | 2.46k | float dithering) { |
320 | 2.46k | return PictureARGBToYUVA(picture, colorspace, dithering, 0); |
321 | 2.46k | } |
322 | | |
323 | 0 | int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { |
324 | 0 | return PictureARGBToYUVA(picture, colorspace, 0.f, 0); |
325 | 0 | } |
326 | | |
327 | 0 | int WebPPictureSharpARGBToYUVA(WebPPicture* picture) { |
328 | 0 | return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1); |
329 | 0 | } |
330 | | // for backward compatibility |
331 | 0 | int WebPPictureSmartARGBToYUVA(WebPPicture* picture) { |
332 | 0 | return WebPPictureSharpARGBToYUVA(picture); |
333 | 0 | } |
334 | | |
335 | | //------------------------------------------------------------------------------ |
336 | | // call for YUVA -> ARGB conversion |
337 | | |
338 | 0 | int WebPPictureYUVAToARGB(WebPPicture* picture) { |
339 | 0 | if (!ValidateAllocatedPicture(picture, /*use_argb=*/0)) return 0; |
340 | | // Allocate a new argb buffer (discarding the previous one). |
341 | 0 | if (!WebPPictureAllocARGB(picture)) return 0; |
342 | 0 | picture->use_argb = 1; |
343 | | |
344 | | // Convert |
345 | 0 | { |
346 | 0 | int y; |
347 | 0 | const int width = picture->width; |
348 | 0 | const int height = picture->height; |
349 | 0 | const ptrdiff_t argb_stride = 4 * (ptrdiff_t)picture->argb_stride; |
350 | 0 | uint8_t* dst = (uint8_t*)picture->argb; |
351 | 0 | const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y; |
352 | 0 | WebPUpsampleLinePairFunc upsample = |
353 | 0 | WebPGetLinePairConverter(ALPHA_OFFSET > 0); |
354 | | |
355 | | // First row, with replicated top samples. |
356 | 0 | upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); |
357 | 0 | cur_y += picture->y_stride; |
358 | 0 | dst += argb_stride; |
359 | | // Center rows. |
360 | 0 | for (y = 1; y + 1 < height; y += 2) { |
361 | 0 | const uint8_t* const top_u = cur_u; |
362 | 0 | const uint8_t* const top_v = cur_v; |
363 | 0 | cur_u += picture->uv_stride; |
364 | 0 | cur_v += picture->uv_stride; |
365 | 0 | upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v, |
366 | 0 | dst, dst + argb_stride, width); |
367 | 0 | cur_y += 2 * (ptrdiff_t)picture->y_stride; |
368 | 0 | dst += 2 * argb_stride; |
369 | 0 | } |
370 | | // Last row (if needed), with replicated bottom samples. |
371 | 0 | if (height > 1 && !(height & 1)) { |
372 | 0 | upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); |
373 | 0 | } |
374 | | // Insert alpha values if needed, in replacement for the default 0xff ones. |
375 | 0 | if (picture->colorspace & WEBP_CSP_ALPHA_BIT) { |
376 | 0 | for (y = 0; y < height; ++y) { |
377 | 0 | uint32_t* const argb_dst = |
378 | 0 | picture->argb + (ptrdiff_t)y * picture->argb_stride; |
379 | 0 | const uint8_t* const src = |
380 | 0 | picture->a + (ptrdiff_t)y * picture->a_stride; |
381 | 0 | int x; |
382 | 0 | for (x = 0; x < width; ++x) { |
383 | 0 | argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24); |
384 | 0 | } |
385 | 0 | } |
386 | 0 | } |
387 | 0 | } |
388 | 0 | return 1; |
389 | 0 | } |
390 | | |
391 | | //------------------------------------------------------------------------------ |
392 | | // automatic import / conversion |
393 | | |
394 | | WEBP_NODISCARD static int Import(WebPPicture* const picture, const uint8_t* rgb, |
395 | | int rgb_stride, int step, int swap_rb, |
396 | 6.80k | int import_alpha) { |
397 | 6.80k | int y; |
398 | 6.80k | const uint8_t* r_ptr; |
399 | 6.80k | const uint8_t* g_ptr; |
400 | 6.80k | const uint8_t* b_ptr; |
401 | 6.80k | int width, height; |
402 | | |
403 | 6.80k | if (picture == NULL) return 0; |
404 | 6.80k | if (rgb == NULL) { |
405 | 0 | return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); |
406 | 0 | } |
407 | 6.80k | r_ptr = rgb + (swap_rb ? 2 : 0); |
408 | 6.80k | g_ptr = rgb + 1; |
409 | 6.80k | b_ptr = rgb + (swap_rb ? 0 : 2); |
410 | | |
411 | 6.80k | WebPPictureFree(picture); |
412 | | |
413 | 6.80k | if (!WebPValidatePicture(picture)) return 0; |
414 | | |
415 | 6.80k | width = picture->width; |
416 | 6.80k | height = picture->height; |
417 | 6.80k | if (!CheckStride(rgb_stride, step, width)) { |
418 | 0 | return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); |
419 | 0 | } |
420 | | |
421 | 6.80k | if (!picture->use_argb) { |
422 | 4.34k | const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL; |
423 | 4.34k | return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, |
424 | 4.34k | 0.f /* no dithering */, 0, picture); |
425 | 4.34k | } |
426 | 2.46k | if (!WebPPictureAlloc(picture)) return 0; |
427 | | |
428 | 2.46k | VP8LDspInit(); |
429 | 2.46k | WebPInitAlphaProcessing(); |
430 | | |
431 | 2.46k | if (import_alpha) { |
432 | | // dst[] byte order is {a,r,g,b} for big-endian, {b,g,r,a} for little endian |
433 | 1.90k | uint32_t* dst = picture->argb; |
434 | 1.90k | const int do_copy = (ALPHA_OFFSET == 3) && swap_rb; |
435 | 1.90k | assert(step == 4); |
436 | 1.90k | if (do_copy) { |
437 | 0 | for (y = 0; y < height; ++y) { |
438 | 0 | memcpy(dst, rgb, width * 4); |
439 | 0 | rgb += rgb_stride; |
440 | 0 | dst += picture->argb_stride; |
441 | 0 | } |
442 | 1.90k | } else { |
443 | 1.44M | for (y = 0; y < height; ++y) { |
444 | | #ifdef WORDS_BIGENDIAN |
445 | | // BGRA or RGBA input order. |
446 | | const uint8_t* a_ptr = rgb + 3; |
447 | | WebPPackARGB(a_ptr, r_ptr, g_ptr, b_ptr, width, dst); |
448 | | r_ptr += rgb_stride; |
449 | | g_ptr += rgb_stride; |
450 | | b_ptr += rgb_stride; |
451 | | #else |
452 | | // RGBA input order. Need to swap R and B. |
453 | 1.44M | if (WEBP_IS_ALIGNED(rgb, uint32_t)) { |
454 | 1.44M | VP8LConvertBGRAToRGBA((const uint32_t*)rgb, width, (uint8_t*)dst); |
455 | 1.44M | } else { |
456 | | // 'rgb' (or 'rgb_stride') is not 4-byte aligned: reading it through |
457 | | // a uint32_t* would be undefined behavior, so go through bytes. |
458 | 0 | int x; |
459 | 0 | for (x = 0; x < width; ++x) { |
460 | 0 | const uint8_t* const p = rgb + 4 * x; |
461 | 0 | dst[x] = ((uint32_t)p[3] << 24) | ((uint32_t)p[0] << 16) | |
462 | 0 | ((uint32_t)p[1] << 8) | (uint32_t)p[2]; |
463 | 0 | } |
464 | 0 | } |
465 | 1.44M | #endif |
466 | 1.44M | rgb += rgb_stride; |
467 | 1.44M | dst += picture->argb_stride; |
468 | 1.44M | } |
469 | 1.90k | } |
470 | 1.90k | } else { |
471 | 559 | uint32_t* dst = picture->argb; |
472 | 559 | assert(step >= 3); |
473 | 111k | for (y = 0; y < height; ++y) { |
474 | 110k | WebPPackRGB(r_ptr, g_ptr, b_ptr, width, step, dst); |
475 | 110k | r_ptr += rgb_stride; |
476 | 110k | g_ptr += rgb_stride; |
477 | 110k | b_ptr += rgb_stride; |
478 | 110k | dst += picture->argb_stride; |
479 | 110k | } |
480 | 559 | } |
481 | 2.46k | return 1; |
482 | 2.46k | } |
483 | | |
484 | | // Public API |
485 | | |
486 | | #if !defined(WEBP_REDUCE_CSP) |
487 | | |
488 | | int WebPPictureImportBGR(WebPPicture* picture, const uint8_t* bgr, |
489 | 0 | int bgr_stride) { |
490 | 0 | return Import(picture, bgr, bgr_stride, 3, 1, 0); |
491 | 0 | } |
492 | | |
493 | | int WebPPictureImportBGRA(WebPPicture* picture, const uint8_t* bgra, |
494 | 0 | int bgra_stride) { |
495 | 0 | return Import(picture, bgra, bgra_stride, 4, 1, 1); |
496 | 0 | } |
497 | | |
498 | | int WebPPictureImportBGRX(WebPPicture* picture, const uint8_t* bgrx, |
499 | 0 | int bgrx_stride) { |
500 | 0 | return Import(picture, bgrx, bgrx_stride, 4, 1, 0); |
501 | 0 | } |
502 | | |
503 | | #endif // WEBP_REDUCE_CSP |
504 | | |
505 | | int WebPPictureImportRGB(WebPPicture* picture, const uint8_t* rgb, |
506 | 2.15k | int rgb_stride) { |
507 | 2.15k | return Import(picture, rgb, rgb_stride, 3, 0, 0); |
508 | 2.15k | } |
509 | | |
510 | | int WebPPictureImportRGBA(WebPPicture* picture, const uint8_t* rgba, |
511 | 4.65k | int rgba_stride) { |
512 | 4.65k | return Import(picture, rgba, rgba_stride, 4, 0, 1); |
513 | 4.65k | } |
514 | | |
515 | | int WebPPictureImportRGBX(WebPPicture* picture, const uint8_t* rgbx, |
516 | 0 | int rgbx_stride) { |
517 | 0 | return Import(picture, rgbx, rgbx_stride, 4, 0, 0); |
518 | 0 | } |
519 | | |
520 | | //------------------------------------------------------------------------------ |