1 /*
2  * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 2 Clause License and
5  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6  * was not distributed with this source code in the LICENSE file, you can
7  * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8  * Media Patent License 1.0 was not distributed with this source code in the
9  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10  */
11 
12 /****************************************************************************
13  *
14  *   Module Title :     scale.c
15  *
16  *   Description  :     Image scaling functions.
17  *
18  ***************************************************************************/
19 
20 /****************************************************************************
21  *  Header Files
22  ****************************************************************************/
23 #include "config/aom_scale_rtcd.h"
24 
25 #include "aom_mem/aom_mem.h"
26 #include "aom_scale/aom_scale.h"
27 #include "aom_scale/yv12config.h"
28 
29 typedef struct {
30   int expanded_frame_width;
31   int expanded_frame_height;
32 
33   int HScale;
34   int HRatio;
35   int VScale;
36   int VRatio;
37 
38   YV12_BUFFER_CONFIG *src_yuv_config;
39   YV12_BUFFER_CONFIG *dst_yuv_config;
40 
41 } SCALE_VARS;
42 
43 /****************************************************************************
44  *
45  *  ROUTINE       : scale1d_2t1_i
46  *
47  *  INPUTS        : const unsigned char *source : Pointer to data to be scaled.
48  *                  int source_step             : Number of pixels to step on
49  *                                                in source.
50  *                  unsigned int source_scale   : Scale for source (UNUSED).
51  *                  unsigned int source_length  : Length of source (UNUSED).
52  *                  unsigned char *dest         : Pointer to output data array.
53  *                  int dest_step               : Number of pixels to step on
54  *                                                in destination.
55  *                  unsigned int dest_scale     : Scale for destination
56  *                                                (UNUSED).
57  *                  unsigned int dest_length    : Length of destination.
58  *
59  *  OUTPUTS       : None.
60  *
61  *  RETURNS       : void
62  *
63  *  FUNCTION      : Performs 2-to-1 interpolated scaling.
64  *
65  *  SPECIAL NOTES : None.
66  *
67  ****************************************************************************/
scale1d_2t1_i(const unsigned char * source,int source_step,unsigned int source_scale,unsigned int source_length,unsigned char * dest,int dest_step,unsigned int dest_scale,unsigned int dest_length)68 static void scale1d_2t1_i(const unsigned char *source, int source_step,
69                           unsigned int source_scale, unsigned int source_length,
70                           unsigned char *dest, int dest_step,
71                           unsigned int dest_scale, unsigned int dest_length) {
72   const unsigned char *const dest_end = dest + dest_length * dest_step;
73   (void)source_length;
74   (void)source_scale;
75   (void)dest_scale;
76 
77   source_step *= 2;  // Every other row.
78 
79   dest[0] = source[0];  // Special case: 1st pixel.
80   source += source_step;
81   dest += dest_step;
82 
83   while (dest < dest_end) {
84     const unsigned int a = 3 * source[-source_step];
85     const unsigned int b = 10 * source[0];
86     const unsigned int c = 3 * source[source_step];
87     *dest = (unsigned char)((8 + a + b + c) >> 4);
88     source += source_step;
89     dest += dest_step;
90   }
91 }
92 
93 /****************************************************************************
94  *
95  *  ROUTINE       : scale1d_2t1_ps
96  *
97  *  INPUTS        : const unsigned char *source : Pointer to data to be scaled.
98  *                  int source_step             : Number of pixels to step on
99  *                                                in source.
100  *                  unsigned int source_scale   : Scale for source (UNUSED).
101  *                  unsigned int source_length  : Length of source (UNUSED).
102  *                  unsigned char *dest         : Pointer to output data array.
103  *                  int dest_step               : Number of pixels to step on
104  *                                                in destination.
105  *                  unsigned int dest_scale     : Scale for destination
106  *                                                (UNUSED).
107  *                  unsigned int dest_length    : Length of destination.
108  *
109  *  OUTPUTS       : None.
110  *
111  *  RETURNS       : void
112  *
113  *  FUNCTION      : Performs 2-to-1 point subsampled scaling.
114  *
115  *  SPECIAL NOTES : None.
116  *
117  ****************************************************************************/
scale1d_2t1_ps(const unsigned char * source,int source_step,unsigned int source_scale,unsigned int source_length,unsigned char * dest,int dest_step,unsigned int dest_scale,unsigned int dest_length)118 static void scale1d_2t1_ps(const unsigned char *source, int source_step,
119                            unsigned int source_scale,
120                            unsigned int source_length, unsigned char *dest,
121                            int dest_step, unsigned int dest_scale,
122                            unsigned int dest_length) {
123   const unsigned char *const dest_end = dest + dest_length * dest_step;
124   (void)source_length;
125   (void)source_scale;
126   (void)dest_scale;
127 
128   source_step *= 2;  // Every other row.
129 
130   while (dest < dest_end) {
131     *dest = *source;
132     source += source_step;
133     dest += dest_step;
134   }
135 }
136 /****************************************************************************
137  *
138  *  ROUTINE       : scale1d_c
139  *
140  *  INPUTS        : const unsigned char *source : Pointer to data to be scaled.
141  *                  int source_step             : Number of pixels to step on
142  *                                                in source.
143  *                  unsigned int source_scale   : Scale for source.
144  *                  unsigned int source_length  : Length of source (UNUSED).
145  *                  unsigned char *dest         : Pointer to output data array.
146  *                  int dest_step               : Number of pixels to step on
147  *                                                in destination.
148  *                  unsigned int dest_scale     : Scale for destination.
149  *                  unsigned int dest_length    : Length of destination.
150  *
151  *  OUTPUTS       : None.
152  *
153  *  RETURNS       : void
154  *
155  *  FUNCTION      : Performs linear interpolation in one dimension.
156  *
157  *  SPECIAL NOTES : None.
158  *
159  ****************************************************************************/
scale1d_c(const unsigned char * source,int source_step,unsigned int source_scale,unsigned int source_length,unsigned char * dest,int dest_step,unsigned int dest_scale,unsigned int dest_length)160 static void scale1d_c(const unsigned char *source, int source_step,
161                       unsigned int source_scale, unsigned int source_length,
162                       unsigned char *dest, int dest_step,
163                       unsigned int dest_scale, unsigned int dest_length) {
164   const unsigned char *const dest_end = dest + dest_length * dest_step;
165   const unsigned int round_value = dest_scale / 2;
166   unsigned int left_modifier = dest_scale;
167   unsigned int right_modifier = 0;
168   unsigned char left_pixel = source[0];
169   unsigned char right_pixel = source[source_step];
170 
171   (void)source_length;
172 
173   /* These asserts are needed if there are boundary issues... */
174   /* assert ( dest_scale > source_scale );*/
175   /* assert ( (source_length - 1) * dest_scale >= (dest_length - 1) *
176    * source_scale);*/
177 
178   while (dest < dest_end) {
179     *dest = (unsigned char)((left_modifier * left_pixel +
180                              right_modifier * right_pixel + round_value) /
181                             dest_scale);
182 
183     right_modifier += source_scale;
184 
185     while (right_modifier > dest_scale) {
186       right_modifier -= dest_scale;
187       source += source_step;
188       left_pixel = source[0];
189       right_pixel = source[source_step];
190     }
191 
192     left_modifier = dest_scale - right_modifier;
193   }
194 }
195 
196 /****************************************************************************
197  *
198  *  ROUTINE       : Scale2D
199  *
200  *  INPUTS        : const unsigned char *source    : Pointer to data to be
201  *                                                   scaled.
202  *                  int source_pitch               : Stride of source image.
203  *                  unsigned int source_width      : Width of input image.
204  *                  unsigned int source_height     : Height of input image.
205  *                  unsigned char *dest            : Pointer to output data
206  *                                                   array.
207  *                  int dest_pitch                 : Stride of destination
208  *                                                   image.
209  *                  unsigned int dest_width        : Width of destination image.
210  *                  unsigned int dest_height       : Height of destination
211  *                                                   image.
212  *                  unsigned char *temp_area       : Pointer to temp work area.
213  *                  unsigned char temp_area_height : Height of temp work area.
214  *                  unsigned int hscale            : Horizontal scale factor
215  *                                                   numerator.
216  *                  unsigned int hratio            : Horizontal scale factor
217  *                                                   denominator.
218  *                  unsigned int vscale            : Vertical scale factor
219  *                                                   numerator.
220  *                  unsigned int vratio            : Vertical scale factor
221  *                                                   denominator.
222  *                  unsigned int interlaced        : Interlace flag.
223  *
224  *  OUTPUTS       : None.
225  *
226  *  RETURNS       : void
227  *
228  *  FUNCTION      : Performs 2-tap linear interpolation in two dimensions.
229  *
230  *  SPECIAL NOTES : Expansion is performed one band at a time to help with
231  *                  caching.
232  *
233  ****************************************************************************/
Scale2D(unsigned char * source,int source_pitch,unsigned int source_width,unsigned int source_height,unsigned char * dest,int dest_pitch,unsigned int dest_width,unsigned int dest_height,unsigned char * temp_area,unsigned char temp_area_height,unsigned int hscale,unsigned int hratio,unsigned int vscale,unsigned int vratio,unsigned int interlaced)234 static void Scale2D(
235     /*const*/
236     unsigned char *source, int source_pitch, unsigned int source_width,
237     unsigned int source_height, unsigned char *dest, int dest_pitch,
238     unsigned int dest_width, unsigned int dest_height, unsigned char *temp_area,
239     unsigned char temp_area_height, unsigned int hscale, unsigned int hratio,
240     unsigned int vscale, unsigned int vratio, unsigned int interlaced) {
241   unsigned int i, j, k;
242   unsigned int bands;
243   unsigned int dest_band_height;
244   unsigned int source_band_height;
245 
246   typedef void (*Scale1D)(const unsigned char *source, int source_step,
247                           unsigned int source_scale, unsigned int source_length,
248                           unsigned char *dest, int dest_step,
249                           unsigned int dest_scale, unsigned int dest_length);
250 
251   Scale1D Scale1Dv = scale1d_c;
252   Scale1D Scale1Dh = scale1d_c;
253 
254   void (*horiz_line_scale)(const unsigned char *, unsigned int, unsigned char *,
255                            unsigned int) = NULL;
256   void (*vert_band_scale)(unsigned char *, int, unsigned char *, int,
257                           unsigned int) = NULL;
258 
259   int ratio_scalable = 1;
260   int interpolation = 0;
261 
262   unsigned char *source_base;
263   unsigned char *line_src;
264 
265   source_base = (unsigned char *)source;
266 
267   if (source_pitch < 0) {
268     int offset;
269 
270     offset = (source_height - 1);
271     offset *= source_pitch;
272 
273     source_base += offset;
274   }
275 
276   /* find out the ratio for each direction */
277   switch (hratio * 10 / hscale) {
278     case 8:
279       /* 4-5 Scale in Width direction */
280       horiz_line_scale = aom_horizontal_line_5_4_scale;
281       break;
282     case 6:
283       /* 3-5 Scale in Width direction */
284       horiz_line_scale = aom_horizontal_line_5_3_scale;
285       break;
286     case 5:
287       /* 1-2 Scale in Width direction */
288       horiz_line_scale = aom_horizontal_line_2_1_scale;
289       break;
290     default:
291       /* The ratio is not acceptable now */
292       /* throw("The ratio is not acceptable for now!"); */
293       ratio_scalable = 0;
294       break;
295   }
296 
297   switch (vratio * 10 / vscale) {
298     case 8:
299       /* 4-5 Scale in vertical direction */
300       vert_band_scale = aom_vertical_band_5_4_scale;
301       source_band_height = 5;
302       dest_band_height = 4;
303       break;
304     case 6:
305       /* 3-5 Scale in vertical direction */
306       vert_band_scale = aom_vertical_band_5_3_scale;
307       source_band_height = 5;
308       dest_band_height = 3;
309       break;
310     case 5:
311       /* 1-2 Scale in vertical direction */
312 
313       if (interlaced) {
314         /* if the content is interlaced, point sampling is used */
315         vert_band_scale = aom_vertical_band_2_1_scale;
316       } else {
317         interpolation = 1;
318         /* if the content is progressive, interplo */
319         vert_band_scale = aom_vertical_band_2_1_scale_i;
320       }
321 
322       source_band_height = 2;
323       dest_band_height = 1;
324       break;
325     default:
326       /* The ratio is not acceptable now */
327       /* throw("The ratio is not acceptable for now!"); */
328       ratio_scalable = 0;
329       break;
330   }
331 
332   if (ratio_scalable) {
333     if (source_height == dest_height) {
334       /* for each band of the image */
335       for (k = 0; k < dest_height; ++k) {
336         horiz_line_scale(source, source_width, dest, dest_width);
337         source += source_pitch;
338         dest += dest_pitch;
339       }
340 
341       return;
342     }
343 
344     if (interpolation) {
345       if (source < source_base) source = source_base;
346 
347       horiz_line_scale(source, source_width, temp_area, dest_width);
348     }
349 
350     for (k = 0; k < (dest_height + dest_band_height - 1) / dest_band_height;
351          ++k) {
352       /* scale one band horizontally */
353       for (i = 0; i < source_band_height; ++i) {
354         /* Trap case where we could read off the base of the source buffer */
355 
356         line_src = source + i * source_pitch;
357 
358         if (line_src < source_base) line_src = source_base;
359 
360         horiz_line_scale(line_src, source_width,
361                          temp_area + (i + 1) * dest_pitch, dest_width);
362       }
363 
364       /* Vertical scaling is in place */
365       vert_band_scale(temp_area + dest_pitch, dest_pitch, dest, dest_pitch,
366                       dest_width);
367 
368       if (interpolation)
369         memcpy(temp_area, temp_area + source_band_height * dest_pitch,
370                dest_width);
371 
372       /* Next band... */
373       source += (unsigned long)source_band_height * source_pitch;
374       dest += (unsigned long)dest_band_height * dest_pitch;
375     }
376 
377     return;
378   }
379 
380   if (hscale == 2 && hratio == 1) Scale1Dh = scale1d_2t1_ps;
381 
382   if (vscale == 2 && vratio == 1) {
383     if (interlaced)
384       Scale1Dv = scale1d_2t1_ps;
385     else
386       Scale1Dv = scale1d_2t1_i;
387   }
388 
389   if (source_height == dest_height) {
390     /* for each band of the image */
391     for (k = 0; k < dest_height; ++k) {
392       Scale1Dh(source, 1, hscale, source_width + 1, dest, 1, hratio,
393                dest_width);
394       source += source_pitch;
395       dest += dest_pitch;
396     }
397 
398     return;
399   }
400 
401   if (dest_height > source_height) {
402     dest_band_height = temp_area_height - 1;
403     source_band_height = dest_band_height * source_height / dest_height;
404   } else {
405     source_band_height = temp_area_height - 1;
406     dest_band_height = source_band_height * vratio / vscale;
407   }
408 
409   /* first row needs to be done so that we can stay one row ahead for vertical
410    * zoom */
411   Scale1Dh(source, 1, hscale, source_width + 1, temp_area, 1, hratio,
412            dest_width);
413 
414   /* for each band of the image */
415   bands = (dest_height + dest_band_height - 1) / dest_band_height;
416 
417   for (k = 0; k < bands; ++k) {
418     /* scale one band horizontally */
419     for (i = 1; i < source_band_height + 1; ++i) {
420       if (k * source_band_height + i < source_height) {
421         Scale1Dh(source + i * source_pitch, 1, hscale, source_width + 1,
422                  temp_area + i * dest_pitch, 1, hratio, dest_width);
423       } else { /*  Duplicate the last row */
424         /* copy temp_area row 0 over from last row in the past */
425         memcpy(temp_area + i * dest_pitch, temp_area + (i - 1) * dest_pitch,
426                dest_pitch);
427       }
428     }
429 
430     /* scale one band vertically */
431     for (j = 0; j < dest_width; ++j) {
432       Scale1Dv(&temp_area[j], dest_pitch, vscale, source_band_height + 1,
433                &dest[j], dest_pitch, vratio, dest_band_height);
434     }
435 
436     /* copy temp_area row 0 over from last row in the past */
437     memcpy(temp_area, temp_area + source_band_height * dest_pitch, dest_pitch);
438 
439     /* move to the next band */
440     source += source_band_height * source_pitch;
441     dest += dest_band_height * dest_pitch;
442   }
443 }
444 
445 /****************************************************************************
446  *
447  *  ROUTINE       : aom_scale_frame
448  *
449  *  INPUTS        : YV12_BUFFER_CONFIG *src        : Pointer to frame to be
450  *                                                   scaled.
451  *                  YV12_BUFFER_CONFIG *dst        : Pointer to buffer to hold
452  *                                                   scaled frame.
453  *                  unsigned char *temp_area       : Pointer to temp work area.
454  *                  unsigned char temp_area_height : Height of temp work area.
455  *                  unsigned int hscale            : Horizontal scale factor
456  *                                                   numerator.
457  *                  unsigned int hratio            : Horizontal scale factor
458  *                                                   denominator.
459  *                  unsigned int vscale            : Vertical scale factor
460  *                                                   numerator.
461  *                  unsigned int vratio            : Vertical scale factor
462  *                                                   denominator.
463  *                  unsigned int interlaced        : Interlace flag.
464  *
465  *  OUTPUTS       : None.
466  *
467  *  RETURNS       : void
468  *
469  *  FUNCTION      : Performs 2-tap linear interpolation in two dimensions.
470  *
471  *  SPECIAL NOTES : Expansion is performed one band at a time to help with
472  *                  caching.
473  *
474  ****************************************************************************/
aom_scale_frame(YV12_BUFFER_CONFIG * src,YV12_BUFFER_CONFIG * dst,unsigned char * temp_area,unsigned char temp_height,unsigned int hscale,unsigned int hratio,unsigned int vscale,unsigned int vratio,unsigned int interlaced,const int num_planes)475 void aom_scale_frame(YV12_BUFFER_CONFIG *src, YV12_BUFFER_CONFIG *dst,
476                      unsigned char *temp_area, unsigned char temp_height,
477                      unsigned int hscale, unsigned int hratio,
478                      unsigned int vscale, unsigned int vratio,
479                      unsigned int interlaced, const int num_planes) {
480   const int dw = (hscale - 1 + src->y_width * hratio) / hscale;
481   const int dh = (vscale - 1 + src->y_height * vratio) / vscale;
482 
483   for (int plane = 0; plane < num_planes; ++plane) {
484     const int is_uv = plane > 0;
485     const int plane_dw = dw >> is_uv;
486     const int plane_dh = dh >> is_uv;
487 
488     Scale2D((unsigned char *)src->buffers[plane], src->strides[is_uv],
489             src->widths[is_uv], src->heights[is_uv],
490             (unsigned char *)dst->buffers[plane], dst->strides[is_uv], plane_dw,
491             plane_dh, temp_area, temp_height, hscale, hratio, vscale, vratio,
492             interlaced);
493 
494     if (plane_dw < dst->widths[is_uv])
495       for (int i = 0; i < plane_dh; ++i)
496         memset(dst->buffers[plane] + i * dst->strides[is_uv] + plane_dw - 1,
497                dst->buffers[plane][i * dst->strides[is_uv] + plane_dw - 2],
498                dst->widths[is_uv] - plane_dw + 1);
499 
500     if (plane_dh < dst->heights[is_uv])
501       for (int i = plane_dh - 1; i < dst->heights[is_uv]; ++i)
502         memcpy(dst->buffers[plane] + i * dst->strides[is_uv],
503                dst->buffers[plane] + (plane_dh - 2) * dst->strides[is_uv],
504                dst->widths[is_uv] + 1);
505   }
506 }
507