• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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