1 // Copyright 2011 Google Inc.
2 //
3 // This code is licensed under the same terms as WebM:
4 // Software License Agreement: http://www.webmproject.org/license/software/
5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6 // -----------------------------------------------------------------------------
7 //
8 // functions for sample output.
9 //
10 // Author: Skal (pascal.massimino@gmail.com)
11
12 #include <assert.h>
13 #include <stdlib.h>
14 #include "vp8i.h"
15 #include "webpi.h"
16 #include "yuv.h"
17
18 #if defined(__cplusplus) || defined(c_plusplus)
19 extern "C" {
20 #endif
21
22 #define FANCY_UPSAMPLING // undefined to remove fancy upsampling support
23
24 //------------------------------------------------------------------------------
25 // Fancy upsampler
26
27 #ifdef FANCY_UPSAMPLING
28
29 // Given samples laid out in a square as:
30 // [a b]
31 // [c d]
32 // we interpolate u/v as:
33 // ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16
34 // ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16
35
36 // We process u and v together stashed into 32bit (16bit each).
37 #define LOAD_UV(u,v) ((u) | ((v) << 16))
38
39 #define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
40 static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
41 const uint8_t* top_u, const uint8_t* top_v, \
42 const uint8_t* cur_u, const uint8_t* cur_v, \
43 uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
44 int x; \
45 const int last_pixel_pair = (len - 1) >> 1; \
46 uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \
47 uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \
48 if (top_y) { \
49 const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
50 FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \
51 } \
52 if (bottom_y) { \
53 const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
54 FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \
55 } \
56 for (x = 1; x <= last_pixel_pair; ++x) { \
57 const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \
58 const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \
59 /* precompute invariant values associated with first and second diagonals*/\
60 const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \
61 const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \
62 const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \
63 if (top_y) { \
64 const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \
65 const uint32_t uv1 = (diag_03 + t_uv) >> 1; \
66 FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
67 top_dst + (2 * x - 1) * XSTEP); \
68 FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \
69 top_dst + (2 * x - 0) * XSTEP); \
70 } \
71 if (bottom_y) { \
72 const uint32_t uv0 = (diag_03 + l_uv) >> 1; \
73 const uint32_t uv1 = (diag_12 + uv) >> 1; \
74 FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
75 bottom_dst + (2 * x - 1) * XSTEP); \
76 FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \
77 bottom_dst + (2 * x + 0) * XSTEP); \
78 } \
79 tl_uv = t_uv; \
80 l_uv = uv; \
81 } \
82 if (!(len & 1)) { \
83 if (top_y) { \
84 const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
85 FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
86 top_dst + (len - 1) * XSTEP); \
87 } \
88 if (bottom_y) { \
89 const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
90 FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
91 bottom_dst + (len - 1) * XSTEP); \
92 } \
93 } \
94 }
95
96 // All variants implemented.
97 UPSAMPLE_FUNC(UpsampleRgbLinePair, VP8YuvToRgb, 3)
98 UPSAMPLE_FUNC(UpsampleBgrLinePair, VP8YuvToBgr, 3)
99 UPSAMPLE_FUNC(UpsampleRgbaLinePair, VP8YuvToRgba, 4)
100 UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4)
101 UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4)
102 UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2)
103 UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2)
104 // These two don't erase the alpha value
105 UPSAMPLE_FUNC(UpsampleRgbKeepAlphaLinePair, VP8YuvToRgb, 4)
106 UPSAMPLE_FUNC(UpsampleBgrKeepAlphaLinePair, VP8YuvToBgr, 4)
107 UPSAMPLE_FUNC(UpsampleArgbKeepAlphaLinePair, VP8YuvToArgbKeepA, 4)
108 UPSAMPLE_FUNC(UpsampleRgba4444KeepAlphaLinePair, VP8YuvToRgba4444KeepA, 2)
109
110 #undef LOAD_UV
111 #undef UPSAMPLE_FUNC
112
113 // Fancy upsampling functions to convert YUV to RGB
114 WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST];
115 WebPUpsampleLinePairFunc WebPUpsamplersKeepAlpha[MODE_LAST];
116
InitUpsamplers(void)117 static void InitUpsamplers(void) {
118 WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair;
119 WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair;
120 WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair;
121 WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair;
122 WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair;
123 WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair;
124 WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair;
125
126 WebPUpsamplersKeepAlpha[MODE_RGB] = UpsampleRgbLinePair;
127 WebPUpsamplersKeepAlpha[MODE_RGBA] = UpsampleRgbKeepAlphaLinePair;
128 WebPUpsamplersKeepAlpha[MODE_BGR] = UpsampleBgrLinePair;
129 WebPUpsamplersKeepAlpha[MODE_BGRA] = UpsampleBgrKeepAlphaLinePair;
130 WebPUpsamplersKeepAlpha[MODE_ARGB] = UpsampleArgbKeepAlphaLinePair;
131 WebPUpsamplersKeepAlpha[MODE_RGBA_4444] = UpsampleRgba4444KeepAlphaLinePair;
132 WebPUpsamplersKeepAlpha[MODE_RGB_565] = UpsampleRgb565LinePair;
133
134 // If defined, use CPUInfo() to overwrite some pointers with faster versions.
135 if (VP8DecGetCPUInfo) {
136 if (VP8DecGetCPUInfo(kSSE2)) {
137 #if defined(__SSE2__) || defined(_MSC_VER)
138 WebPInitUpsamplersSSE2();
139 #endif
140 }
141 }
142 }
143
144 #endif // FANCY_UPSAMPLING
145
146 //------------------------------------------------------------------------------
147 // simple point-sampling
148
149 #define SAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
150 static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
151 const uint8_t* u, const uint8_t* v, \
152 uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
153 int i; \
154 for (i = 0; i < len - 1; i += 2) { \
155 FUNC(top_y[0], u[0], v[0], top_dst); \
156 FUNC(top_y[1], u[0], v[0], top_dst + XSTEP); \
157 FUNC(bottom_y[0], u[0], v[0], bottom_dst); \
158 FUNC(bottom_y[1], u[0], v[0], bottom_dst + XSTEP); \
159 top_y += 2; \
160 bottom_y += 2; \
161 u++; \
162 v++; \
163 top_dst += 2 * XSTEP; \
164 bottom_dst += 2 * XSTEP; \
165 } \
166 if (i == len - 1) { /* last one */ \
167 FUNC(top_y[0], u[0], v[0], top_dst); \
168 FUNC(bottom_y[0], u[0], v[0], bottom_dst); \
169 } \
170 }
171
172 // All variants implemented.
173 SAMPLE_FUNC(SampleRgbLinePair, VP8YuvToRgb, 3)
174 SAMPLE_FUNC(SampleBgrLinePair, VP8YuvToBgr, 3)
175 SAMPLE_FUNC(SampleRgbaLinePair, VP8YuvToRgba, 4)
176 SAMPLE_FUNC(SampleBgraLinePair, VP8YuvToBgra, 4)
177 SAMPLE_FUNC(SampleArgbLinePair, VP8YuvToArgb, 4)
178 SAMPLE_FUNC(SampleRgba4444LinePair, VP8YuvToRgba4444, 2)
179 SAMPLE_FUNC(SampleRgb565LinePair, VP8YuvToRgb565, 2)
180
181 #undef SAMPLE_FUNC
182
183 // Main methods.
184 typedef void (*SampleLinePairFunc)(
185 const uint8_t* top_y, const uint8_t* bottom_y,
186 const uint8_t* u, const uint8_t* v,
187 uint8_t* top_dst, uint8_t* bottom_dst, int len);
188
189 static const SampleLinePairFunc kSamplers[MODE_LAST] = {
190 SampleRgbLinePair, // MODE_RGB
191 SampleRgbaLinePair, // MODE_RGBA
192 SampleBgrLinePair, // MODE_BGR
193 SampleBgraLinePair, // MODE_BGRA
194 SampleArgbLinePair, // MODE_ARGB
195 SampleRgba4444LinePair, // MODE_RGBA_4444
196 SampleRgb565LinePair // MODE_RGB_565
197 };
198
199 //------------------------------------------------------------------------------
200 // YUV444 converter
201
202 #define YUV444_FUNC(FUNC_NAME, FUNC, XSTEP) \
203 static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
204 uint8_t* dst, int len) { \
205 int i; \
206 for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * XSTEP]); \
207 }
208
209 YUV444_FUNC(Yuv444ToRgb, VP8YuvToRgb, 3)
210 YUV444_FUNC(Yuv444ToBgr, VP8YuvToBgr, 3)
211 YUV444_FUNC(Yuv444ToRgba, VP8YuvToRgba, 4)
212 YUV444_FUNC(Yuv444ToBgra, VP8YuvToBgra, 4)
213 YUV444_FUNC(Yuv444ToArgb, VP8YuvToArgb, 4)
214 YUV444_FUNC(Yuv444ToRgba4444, VP8YuvToRgba4444, 2)
215 YUV444_FUNC(Yuv444ToRgb565, VP8YuvToRgb565, 2)
216
217 #undef YUV444_FUNC
218
219 typedef void (*YUV444Func)(const uint8_t* y, const uint8_t* u, const uint8_t* v,
220 uint8_t* dst, int len);
221
222 static const YUV444Func kYUV444Converters[MODE_LAST] = {
223 Yuv444ToRgb, // MODE_RGB
224 Yuv444ToRgba, // MODE_RGBA
225 Yuv444ToBgr, // MODE_BGR
226 Yuv444ToBgra, // MODE_BGRA
227 Yuv444ToArgb, // MODE_ARGB
228 Yuv444ToRgba4444, // MODE_RGBA_4444
229 Yuv444ToRgb565 // MODE_RGB_565
230 };
231
232 //------------------------------------------------------------------------------
233 // Main YUV<->RGB conversion functions
234
EmitYUV(const VP8Io * const io,WebPDecParams * const p)235 static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
236 WebPDecBuffer* output = p->output;
237 const WebPYUVABuffer* const buf = &output->u.YUVA;
238 uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride;
239 uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride;
240 uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride;
241 const int mb_w = io->mb_w;
242 const int mb_h = io->mb_h;
243 const int uv_w = (mb_w + 1) / 2;
244 int j;
245 for (j = 0; j < mb_h; ++j) {
246 memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w);
247 }
248 for (j = 0; j < (mb_h + 1) / 2; ++j) {
249 memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w);
250 memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w);
251 }
252 return io->mb_h;
253 }
254
255 // Point-sampling U/V sampler.
EmitSampledRGB(const VP8Io * const io,WebPDecParams * const p)256 static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
257 WebPDecBuffer* output = p->output;
258 const WebPRGBABuffer* const buf = &output->u.RGBA;
259 uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
260 const uint8_t* y_src = io->y;
261 const uint8_t* u_src = io->u;
262 const uint8_t* v_src = io->v;
263 const SampleLinePairFunc sample = kSamplers[output->colorspace];
264 const int mb_w = io->mb_w;
265 const int last = io->mb_h - 1;
266 int j;
267 for (j = 0; j < last; j += 2) {
268 sample(y_src, y_src + io->y_stride, u_src, v_src,
269 dst, dst + buf->stride, mb_w);
270 y_src += 2 * io->y_stride;
271 u_src += io->uv_stride;
272 v_src += io->uv_stride;
273 dst += 2 * buf->stride;
274 }
275 if (j == last) { // Just do the last line twice
276 sample(y_src, y_src, u_src, v_src, dst, dst, mb_w);
277 }
278 return io->mb_h;
279 }
280
281 //------------------------------------------------------------------------------
282 // YUV444 -> RGB conversion
283
284 #if 0 // TODO(skal): this is for future rescaling.
285 static int EmitRGB(const VP8Io* const io, WebPDecParams* const p) {
286 WebPDecBuffer* output = p->output;
287 const WebPRGBABuffer* const buf = &output->u.RGBA;
288 uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
289 const uint8_t* y_src = io->y;
290 const uint8_t* u_src = io->u;
291 const uint8_t* v_src = io->v;
292 const YUV444Func convert = kYUV444Converters[output->colorspace];
293 const int mb_w = io->mb_w;
294 const int last = io->mb_h;
295 int j;
296 for (j = 0; j < last; ++j) {
297 convert(y_src, u_src, v_src, dst, mb_w);
298 y_src += io->y_stride;
299 u_src += io->uv_stride;
300 v_src += io->uv_stride;
301 dst += buf->stride;
302 }
303 return io->mb_h;
304 }
305 #endif
306
307 //------------------------------------------------------------------------------
308 // Fancy upsampling
309
310 #ifdef FANCY_UPSAMPLING
EmitFancyRGB(const VP8Io * const io,WebPDecParams * const p)311 static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
312 int num_lines_out = io->mb_h; // a priori guess
313 const WebPRGBABuffer* const buf = &p->output->u.RGBA;
314 uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
315 const WebPUpsampleLinePairFunc upsample =
316 io->a ? WebPUpsamplersKeepAlpha[p->output->colorspace]
317 : WebPUpsamplers[p->output->colorspace];
318 const uint8_t* cur_y = io->y;
319 const uint8_t* cur_u = io->u;
320 const uint8_t* cur_v = io->v;
321 const uint8_t* top_u = p->tmp_u;
322 const uint8_t* top_v = p->tmp_v;
323 int y = io->mb_y;
324 int y_end = io->mb_y + io->mb_h;
325 const int mb_w = io->mb_w;
326 const int uv_w = (mb_w + 1) / 2;
327
328 if (y == 0) {
329 // First line is special cased. We mirror the u/v samples at boundary.
330 upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
331 } else {
332 // We can finish the left-over line from previous call.
333 // Warning! Don't overwrite the alpha values (if any), as they
334 // are not lagging one line behind but are already written.
335 upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
336 dst - buf->stride, dst, mb_w);
337 num_lines_out++;
338 }
339 // Loop over each output pairs of row.
340 for (; y + 2 < y_end; y += 2) {
341 top_u = cur_u;
342 top_v = cur_v;
343 cur_u += io->uv_stride;
344 cur_v += io->uv_stride;
345 dst += 2 * buf->stride;
346 cur_y += 2 * io->y_stride;
347 upsample(cur_y - io->y_stride, cur_y,
348 top_u, top_v, cur_u, cur_v,
349 dst - buf->stride, dst, mb_w);
350 }
351 // move to last row
352 cur_y += io->y_stride;
353 if (io->crop_top + y_end < io->crop_bottom) {
354 // Save the unfinished samples for next call (as we're not done yet).
355 memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
356 memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
357 memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
358 // The fancy upsampler leaves a row unfinished behind
359 // (except for the very last row)
360 num_lines_out--;
361 } else {
362 // Process the very last row of even-sized picture
363 if (!(y_end & 1)) {
364 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
365 dst + buf->stride, NULL, mb_w);
366 }
367 }
368 return num_lines_out;
369 }
370
371 #endif /* FANCY_UPSAMPLING */
372
373 //------------------------------------------------------------------------------
374
375 #ifdef WEBP_EXPERIMENTAL_FEATURES
EmitAlphaYUV(const VP8Io * const io,WebPDecParams * const p)376 static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
377 const int mb_w = io->mb_w;
378 const int mb_h = io->mb_h;
379 int j;
380 const WebPYUVABuffer* const buf = &p->output->u.YUVA;
381 uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
382 const uint8_t* alpha = io->a;
383 if (alpha) {
384 for (j = 0; j < mb_h; ++j) {
385 memcpy(dst, alpha, mb_w * sizeof(*dst));
386 alpha += io->width;
387 dst += buf->a_stride;
388 }
389 }
390 return 0;
391 }
392
EmitAlphaRGB(const VP8Io * const io,WebPDecParams * const p)393 static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
394 const int mb_w = io->mb_w;
395 const int mb_h = io->mb_h;
396 int i, j;
397 const WebPRGBABuffer* const buf = &p->output->u.RGBA;
398 uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
399 const uint8_t* alpha = io->a;
400 if (alpha) {
401 for (j = 0; j < mb_h; ++j) {
402 for (i = 0; i < mb_w; ++i) {
403 dst[4 * i + 3] = alpha[i];
404 }
405 alpha += io->width;
406 dst += buf->stride;
407 }
408 }
409 return 0;
410 }
411
412 #endif /* WEBP_EXPERIMENTAL_FEATURES */
413
414 //------------------------------------------------------------------------------
415 // Simple picture rescaler
416
417 // TODO(skal): start a common library for encoder and decoder, and factorize
418 // this code in.
419
420 #define RFIX 30
421 #define MULT(x,y) (((int64_t)(x) * (y) + (1 << (RFIX - 1))) >> RFIX)
422
InitRescaler(WebPRescaler * const wrk,int src_width,int src_height,uint8_t * dst,int dst_width,int dst_height,int dst_stride,int x_add,int x_sub,int y_add,int y_sub,int32_t * work)423 static void InitRescaler(WebPRescaler* const wrk,
424 int src_width, int src_height,
425 uint8_t* dst,
426 int dst_width, int dst_height, int dst_stride,
427 int x_add, int x_sub, int y_add, int y_sub,
428 int32_t* work) {
429 wrk->x_expand = (src_width < dst_width);
430 wrk->src_width = src_width;
431 wrk->src_height = src_height;
432 wrk->dst_width = dst_width;
433 wrk->dst_height = dst_height;
434 wrk->dst = dst;
435 wrk->dst_stride = dst_stride;
436 // for 'x_expand', we use bilinear interpolation
437 wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add - x_sub;
438 wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub;
439 wrk->y_accum = y_add;
440 wrk->y_add = y_add;
441 wrk->y_sub = y_sub;
442 wrk->fx_scale = (1 << RFIX) / x_sub;
443 wrk->fy_scale = (1 << RFIX) / y_sub;
444 wrk->fxy_scale = wrk->x_expand ?
445 ((int64_t)dst_height << RFIX) / (x_sub * src_height) :
446 ((int64_t)dst_height << RFIX) / (x_add * src_height);
447 wrk->irow = work;
448 wrk->frow = work + dst_width;
449 }
450
ImportRow(const uint8_t * const src,WebPRescaler * const wrk)451 static inline void ImportRow(const uint8_t* const src,
452 WebPRescaler* const wrk) {
453 int x_in = 0;
454 int x_out;
455 int accum = 0;
456 if (!wrk->x_expand) {
457 int sum = 0;
458 for (x_out = 0; x_out < wrk->dst_width; ++x_out) {
459 accum += wrk->x_add;
460 for (; accum > 0; accum -= wrk->x_sub) {
461 sum += src[x_in++];
462 }
463 { // Emit next horizontal pixel.
464 const int32_t base = src[x_in++];
465 const int32_t frac = base * (-accum);
466 wrk->frow[x_out] = (sum + base) * wrk->x_sub - frac;
467 // fresh fractional start for next pixel
468 sum = MULT(frac, wrk->fx_scale);
469 }
470 }
471 } else { // simple bilinear interpolation
472 int left = src[0], right = src[0];
473 for (x_out = 0; x_out < wrk->dst_width; ++x_out) {
474 if (accum < 0) {
475 left = right;
476 right = src[++x_in];
477 accum += wrk->x_add;
478 }
479 wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
480 accum -= wrk->x_sub;
481 }
482 }
483 // Accumulate the new row's contribution
484 for (x_out = 0; x_out < wrk->dst_width; ++x_out) {
485 wrk->irow[x_out] += wrk->frow[x_out];
486 }
487 }
488
ExportRow(WebPRescaler * const wrk)489 static void ExportRow(WebPRescaler* const wrk) {
490 int x_out;
491 const int yscale = wrk->fy_scale * (-wrk->y_accum);
492 assert(wrk->y_accum <= 0);
493 for (x_out = 0; x_out < wrk->dst_width; ++x_out) {
494 const int frac = MULT(wrk->frow[x_out], yscale);
495 const int v = MULT(wrk->irow[x_out] - frac, wrk->fxy_scale);
496 wrk->dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
497 wrk->irow[x_out] = frac; // new fractional start
498 }
499 wrk->y_accum += wrk->y_add;
500 wrk->dst += wrk->dst_stride;
501 }
502
503 #undef MULT
504 #undef RFIX
505
506 //------------------------------------------------------------------------------
507 // YUV rescaling (no final RGB conversion needed)
508
Rescale(const uint8_t * src,int src_stride,int new_lines,WebPRescaler * const wrk)509 static int Rescale(const uint8_t* src, int src_stride,
510 int new_lines, WebPRescaler* const wrk) {
511 int num_lines_out = 0;
512 while (new_lines-- > 0) { // import new contribution of one source row.
513 ImportRow(src, wrk);
514 src += src_stride;
515 wrk->y_accum -= wrk->y_sub;
516 while (wrk->y_accum <= 0) { // emit output row(s)
517 ExportRow(wrk);
518 num_lines_out++;
519 }
520 }
521 return num_lines_out;
522 }
523
EmitRescaledYUV(const VP8Io * const io,WebPDecParams * const p)524 static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
525 const int mb_h = io->mb_h;
526 const int uv_mb_h = (mb_h + 1) >> 1;
527 const int num_lines_out = Rescale(io->y, io->y_stride, mb_h, &p->scaler_y);
528 Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u);
529 Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
530 return num_lines_out;
531 }
532
EmitRescaledAlphaYUV(const VP8Io * const io,WebPDecParams * const p)533 static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
534 if (io->a) {
535 Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
536 }
537 return 0;
538 }
539
IsAlphaMode(WEBP_CSP_MODE mode)540 static int IsAlphaMode(WEBP_CSP_MODE mode) {
541 return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||
542 mode == MODE_RGBA_4444 || mode == MODE_YUVA);
543 }
544
InitYUVRescaler(const VP8Io * const io,WebPDecParams * const p)545 static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
546 const int has_alpha = IsAlphaMode(p->output->colorspace);
547 const WebPYUVABuffer* const buf = &p->output->u.YUVA;
548 const int out_width = io->scaled_width;
549 const int out_height = io->scaled_height;
550 const int uv_out_width = (out_width + 1) >> 1;
551 const int uv_out_height = (out_height + 1) >> 1;
552 const int uv_in_width = (io->mb_w + 1) >> 1;
553 const int uv_in_height = (io->mb_h + 1) >> 1;
554 const size_t work_size = 2 * out_width; // scratch memory for luma rescaler
555 const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
556 size_t tmp_size;
557 int32_t* work;
558
559 tmp_size = work_size + 2 * uv_work_size;
560 if (has_alpha) {
561 tmp_size += work_size;
562 }
563 p->memory = calloc(1, tmp_size * sizeof(*work));
564 if (p->memory == NULL) {
565 return 0; // memory error
566 }
567 work = (int32_t*)p->memory;
568 InitRescaler(&p->scaler_y, io->mb_w, io->mb_h,
569 buf->y, out_width, out_height, buf->y_stride,
570 io->mb_w, out_width, io->mb_h, out_height,
571 work);
572 InitRescaler(&p->scaler_u, uv_in_width, uv_in_height,
573 buf->u, uv_out_width, uv_out_height, buf->u_stride,
574 uv_in_width, uv_out_width,
575 uv_in_height, uv_out_height,
576 work + work_size);
577 InitRescaler(&p->scaler_v, uv_in_width, uv_in_height,
578 buf->v, uv_out_width, uv_out_height, buf->v_stride,
579 uv_in_width, uv_out_width,
580 uv_in_height, uv_out_height,
581 work + work_size + uv_work_size);
582 p->emit = EmitRescaledYUV;
583 if (has_alpha) {
584 InitRescaler(&p->scaler_a, io->mb_w, io->mb_h,
585 buf->a, out_width, out_height, buf->a_stride,
586 io->mb_w, out_width, io->mb_h, out_height,
587 work + work_size + 2 * uv_work_size);
588 p->emit_alpha = EmitRescaledAlphaYUV;
589 }
590 return 1;
591 }
592
593 //------------------------------------------------------------------------------
594 // RGBA rescaling
595
596 // import new contributions until one row is ready to be output, or all input
597 // is consumed.
Import(const uint8_t * src,int src_stride,int new_lines,WebPRescaler * const wrk)598 static int Import(const uint8_t* src, int src_stride,
599 int new_lines, WebPRescaler* const wrk) {
600 int num_lines_in = 0;
601 while (num_lines_in < new_lines && wrk->y_accum > 0) {
602 ImportRow(src, wrk);
603 src += src_stride;
604 ++num_lines_in;
605 wrk->y_accum -= wrk->y_sub;
606 }
607 return num_lines_in;
608 }
609
ExportRGB(WebPDecParams * const p,int y_pos)610 static int ExportRGB(WebPDecParams* const p, int y_pos) {
611 const YUV444Func convert = kYUV444Converters[p->output->colorspace];
612 const WebPRGBABuffer* const buf = &p->output->u.RGBA;
613 uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
614 int num_lines_out = 0;
615 // For RGB rescaling, because of the YUV420, current scan position
616 // U/V can be +1/-1 line from the Y one. Hence the double test.
617 while (p->scaler_y.y_accum <= 0 && p->scaler_u.y_accum <= 0) {
618 assert(p->last_y + y_pos + num_lines_out < p->output->height);
619 assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
620 ExportRow(&p->scaler_y);
621 ExportRow(&p->scaler_u);
622 ExportRow(&p->scaler_v);
623 convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
624 dst, p->scaler_y.dst_width);
625 dst += buf->stride;
626 num_lines_out++;
627 }
628 return num_lines_out;
629 }
630
EmitRescaledRGB(const VP8Io * const io,WebPDecParams * const p)631 static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
632 const int mb_h = io->mb_h;
633 const int uv_mb_h = (mb_h + 1) >> 1;
634 int j = 0, uv_j = 0;
635 int num_lines_out = 0;
636 while (j < mb_h) {
637 const int y_lines_in = Import(io->y + j * io->y_stride, io->y_stride,
638 mb_h - j, &p->scaler_y);
639 const int u_lines_in = Import(io->u + uv_j * io->uv_stride, io->uv_stride,
640 uv_mb_h - uv_j, &p->scaler_u);
641 const int v_lines_in = Import(io->v + uv_j * io->uv_stride, io->uv_stride,
642 uv_mb_h - uv_j, &p->scaler_v);
643 (void)v_lines_in; // remove a gcc warning
644 assert(u_lines_in == v_lines_in);
645 j += y_lines_in;
646 uv_j += u_lines_in;
647 num_lines_out += ExportRGB(p, num_lines_out);
648 }
649 return num_lines_out;
650 }
651
ExportAlpha(WebPDecParams * const p,int y_pos)652 static int ExportAlpha(WebPDecParams* const p, int y_pos) {
653 const WebPRGBABuffer* const buf = &p->output->u.RGBA;
654 uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
655 int num_lines_out = 0;
656 while (p->scaler_a.y_accum <= 0) {
657 int i;
658 assert(p->last_y + y_pos + num_lines_out < p->output->height);
659 ExportRow(&p->scaler_a);
660 for (i = 0; i < p->scaler_a.dst_width; ++i) {
661 dst[4 * i + 3] = p->scaler_a.dst[i];
662 }
663 dst += buf->stride;
664 num_lines_out++;
665 }
666 return num_lines_out;
667 }
668
EmitRescaledAlphaRGB(const VP8Io * const io,WebPDecParams * const p)669 static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
670 if (io->a) {
671 int j = 0, pos = 0;
672 while (j < io->mb_h) {
673 j += Import(io->a + j * io->width, io->width, io->mb_h - j, &p->scaler_a);
674 pos += ExportAlpha(p, pos);
675 }
676 }
677 return 0;
678 }
679
InitRGBRescaler(const VP8Io * const io,WebPDecParams * const p)680 static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
681 const int has_alpha = IsAlphaMode(p->output->colorspace);
682 const int out_width = io->scaled_width;
683 const int out_height = io->scaled_height;
684 const int uv_in_width = (io->mb_w + 1) >> 1;
685 const int uv_in_height = (io->mb_h + 1) >> 1;
686 const size_t work_size = 2 * out_width; // scratch memory for one rescaler
687 int32_t* work; // rescalers work area
688 uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
689 size_t tmp_size1, tmp_size2;
690
691 tmp_size1 = 3 * work_size;
692 tmp_size2 = 3 * out_width;
693 if (has_alpha) {
694 tmp_size1 += work_size;
695 tmp_size2 += out_width;
696 }
697 p->memory =
698 calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
699 if (p->memory == NULL) {
700 return 0; // memory error
701 }
702 work = (int32_t*)p->memory;
703 tmp = (uint8_t*)(work + tmp_size1);
704 InitRescaler(&p->scaler_y, io->mb_w, io->mb_h,
705 tmp + 0 * out_width, out_width, out_height, 0,
706 io->mb_w, out_width, io->mb_h, out_height,
707 work + 0 * work_size);
708 InitRescaler(&p->scaler_u, uv_in_width, uv_in_height,
709 tmp + 1 * out_width, out_width, out_height, 0,
710 io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
711 work + 1 * work_size);
712 InitRescaler(&p->scaler_v, uv_in_width, uv_in_height,
713 tmp + 2 * out_width, out_width, out_height, 0,
714 io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
715 work + 2 * work_size);
716 p->emit = EmitRescaledRGB;
717
718 if (has_alpha) {
719 InitRescaler(&p->scaler_a, io->mb_w, io->mb_h,
720 tmp + 3 * out_width, out_width, out_height, 0,
721 io->mb_w, out_width, io->mb_h, out_height,
722 work + 3 * work_size);
723 p->emit_alpha = EmitRescaledAlphaRGB;
724 }
725 return 1;
726 }
727
728 //------------------------------------------------------------------------------
729 // Default custom functions
730
731 // Setup crop_xxx fields, mb_w and mb_h
InitFromOptions(const WebPDecoderOptions * const options,VP8Io * const io)732 static int InitFromOptions(const WebPDecoderOptions* const options,
733 VP8Io* const io) {
734 const int W = io->width;
735 const int H = io->height;
736 int x = 0, y = 0, w = W, h = H;
737
738 // Cropping
739 io->use_cropping = (options != NULL) && (options->use_cropping > 0);
740 if (io->use_cropping) {
741 w = options->crop_width;
742 h = options->crop_height;
743 // TODO(skal): take colorspace into account. Don't assume YUV420.
744 x = options->crop_left & ~1;
745 y = options->crop_top & ~1;
746 if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
747 return 0; // out of frame boundary error
748 }
749 }
750 io->crop_left = x;
751 io->crop_top = y;
752 io->crop_right = x + w;
753 io->crop_bottom = y + h;
754 io->mb_w = w;
755 io->mb_h = h;
756
757 // Scaling
758 io->use_scaling = (options != NULL) && (options->use_scaling > 0);
759 if (io->use_scaling) {
760 if (options->scaled_width <= 0 || options->scaled_height <= 0) {
761 return 0;
762 }
763 io->scaled_width = options->scaled_width;
764 io->scaled_height = options->scaled_height;
765 }
766
767 // Filter
768 io->bypass_filtering = options && options->bypass_filtering;
769
770 // Fancy upsampler
771 #ifdef FANCY_UPSAMPLING
772 io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
773 #endif
774
775 if (io->use_scaling) {
776 // disable filter (only for large downscaling ratio).
777 io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
778 (io->scaled_height < H * 3 / 4);
779 io->fancy_upsampling = 0;
780 }
781 return 1;
782 }
783
CustomSetup(VP8Io * io)784 static int CustomSetup(VP8Io* io) {
785 WebPDecParams* const p = (WebPDecParams*)io->opaque;
786 const int is_rgb = (p->output->colorspace < MODE_YUV);
787
788 p->memory = NULL;
789 p->emit = NULL;
790 p->emit_alpha = NULL;
791 if (!InitFromOptions(p->options, io)) {
792 return 0;
793 }
794
795 if (io->use_scaling) {
796 const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
797 if (!ok) {
798 return 0; // memory error
799 }
800 } else {
801 if (is_rgb) {
802 p->emit = EmitSampledRGB; // default
803 #ifdef FANCY_UPSAMPLING
804 if (io->fancy_upsampling) {
805 const int uv_width = (io->mb_w + 1) >> 1;
806 p->memory = malloc(io->mb_w + 2 * uv_width);
807 if (p->memory == NULL) {
808 return 0; // memory error.
809 }
810 p->tmp_y = (uint8_t*)p->memory;
811 p->tmp_u = p->tmp_y + io->mb_w;
812 p->tmp_v = p->tmp_u + uv_width;
813 p->emit = EmitFancyRGB;
814 InitUpsamplers();
815 }
816 #endif
817 } else {
818 p->emit = EmitYUV;
819 }
820 #ifdef WEBP_EXPERIMENTAL_FEATURES
821 if (IsAlphaMode(p->output->colorspace)) {
822 // We need transparency output
823 p->emit_alpha = is_rgb ? EmitAlphaRGB : EmitAlphaYUV;
824 }
825 #endif
826 }
827
828 if (is_rgb) {
829 VP8YUVInit();
830 }
831 return 1;
832 }
833
834 //------------------------------------------------------------------------------
835
CustomPut(const VP8Io * io)836 static int CustomPut(const VP8Io* io) {
837 WebPDecParams* p = (WebPDecParams*)io->opaque;
838 const int mb_w = io->mb_w;
839 const int mb_h = io->mb_h;
840 int num_lines_out;
841 assert(!(io->mb_y & 1));
842
843 if (mb_w <= 0 || mb_h <= 0) {
844 return 0;
845 }
846 num_lines_out = p->emit(io, p);
847 if (p->emit_alpha) {
848 p->emit_alpha(io, p);
849 }
850 p->last_y += num_lines_out;
851 return 1;
852 }
853
854 //------------------------------------------------------------------------------
855
CustomTeardown(const VP8Io * io)856 static void CustomTeardown(const VP8Io* io) {
857 WebPDecParams* const p = (WebPDecParams*)io->opaque;
858 free(p->memory);
859 p->memory = NULL;
860 }
861
862 //------------------------------------------------------------------------------
863 // Main entry point
864
WebPInitCustomIo(WebPDecParams * const params,VP8Io * const io)865 void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
866 io->put = CustomPut;
867 io->setup = CustomSetup;
868 io->teardown = CustomTeardown;
869 io->opaque = params;
870 }
871
872 //------------------------------------------------------------------------------
873
874 #if defined(__cplusplus) || defined(c_plusplus)
875 } // extern "C"
876 #endif
877