Home | History | Annotate | Download | only in pixman
      1 /*
      2  * Copyright  2013 The Android Open Source Project
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21  * DEALINGS IN THE SOFTWARE.
     22  */
     23 /*
     24  * Copyright  2000 SuSE, Inc.
     25  * Copyright  2007 Red Hat, Inc.
     26  *
     27  * Permission to use, copy, modify, distribute, and sell this software and its
     28  * documentation for any purpose is hereby granted without fee, provided that
     29  * the above copyright notice appear in all copies and that both that
     30  * copyright notice and this permission notice appear in supporting
     31  * documentation, and that the name of SuSE not be used in advertising or
     32  * publicity pertaining to distribution of the software without specific,
     33  * written prior permission.  SuSE makes no representations about the
     34  * suitability of this software for any purpose.  It is provided "as is"
     35  * without express or implied warranty.
     36  *
     37  * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
     38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
     39  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     40  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
     41  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     42  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     43  *
     44  * Author:  Keith Packard, SuSE, Inc.
     45  */
     46 /*
     47  * Copyright  2009 ARM Ltd, Movial Creative Technologies Oy
     48  *
     49  * Permission to use, copy, modify, distribute, and sell this software and its
     50  * documentation for any purpose is hereby granted without fee, provided that
     51  * the above copyright notice appear in all copies and that both that
     52  * copyright notice and this permission notice appear in supporting
     53  * documentation, and that the name of ARM Ltd not be used in
     54  * advertising or publicity pertaining to distribution of the software without
     55  * specific, written prior permission.  ARM Ltd makes no
     56  * representations about the suitability of this software for any purpose.  It
     57  * is provided "as is" without express or implied warranty.
     58  *
     59  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
     60  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
     61  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
     62  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     63  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     64  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     65  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     66  * SOFTWARE.
     67  *
     68  * Author:  Ian Rickards (ian.rickards (at) arm.com)
     69  * Author:  Jonathan Morton (jonathan.morton (at) movial.com)
     70  * Author:  Markku Vire (markku.vire (at) movial.com)
     71  *
     72  */
     73 
     74 #include "config.h"
     75 #include "pixman-android.h"
     76 #include "pixman-private.h"
     77 #include <cpu-features.h>
     78 
     79 static force_inline void scaled_nearest_scanline_8888_8888_none_SRC(
     80         uint32_t *dst, const uint32_t *src, int32_t w, pixman_fixed_t vx,
     81         pixman_fixed_t unit_x, pixman_fixed_t src_width_fixed) {
     82     uint32_t d;
     83     uint32_t s1, s2;
     84     uint8_t a1, a2;
     85     int x1, x2;
     86 
     87     while ((w -= 2) >= 0) {
     88         x1 = pixman_fixed_to_int(vx);
     89         vx += unit_x;
     90         s1 = *(src + x1);
     91         x2 = pixman_fixed_to_int(vx);
     92         vx += unit_x;
     93         s2 = *(src + x2);
     94         *dst++ = s1;
     95         *dst++ = s2;
     96     }
     97     if (w & 1) {
     98         x1 = pixman_fixed_to_int(vx);
     99         s1 = *(src + x1);
    100         *dst++ = s1;
    101     }
    102 }
    103 
    104 static force_inline int pixman_fixed_to_bilinear_weight(pixman_fixed_t x) {
    105     return (x >> (16 - BILINEAR_INTERPOLATION_BITS))
    106             & ((1 << BILINEAR_INTERPOLATION_BITS) - 1);
    107 }
    108 
    109 /*
    110  * For each scanline fetched from source image with PAD repeat:
    111  * - calculate how many pixels need to be padded on the left side
    112  * - calculate how many pixels need to be padded on the right side
    113  * - update width to only count pixels which are fetched from the image
    114  * All this information is returned via 'width', 'left_pad', 'right_pad'
    115  * arguments. The code is assuming that 'unit_x' is positive.
    116  *
    117  * Note: 64-bit math is used in order to avoid potential overflows, which
    118  *       is probably excessive in many cases. This particular function
    119  *       may need its own correctness test and performance tuning.
    120  */
    121 static force_inline void pad_repeat_get_scanline_bounds(
    122         int32_t source_image_width, pixman_fixed_t vx, pixman_fixed_t unit_x,
    123         int32_t * width, int32_t * left_pad, int32_t * right_pad) {
    124     int64_t max_vx = (int64_t) source_image_width << 16;
    125     int64_t tmp;
    126     if (vx < 0) {
    127         tmp = ((int64_t) unit_x - 1 - vx) / unit_x;
    128         if (tmp > *width) {
    129             *left_pad = *width;
    130             *width = 0;
    131         } else {
    132             *left_pad = (int32_t) tmp;
    133             *width -= (int32_t) tmp;
    134         }
    135     } else {
    136         *left_pad = 0;
    137     }
    138     tmp = ((int64_t) unit_x - 1 - vx + max_vx) / unit_x - *left_pad;
    139     if (tmp < 0) {
    140         *right_pad = *width;
    141         *width = 0;
    142     } else if (tmp >= *width) {
    143         *right_pad = 0;
    144     } else {
    145         *right_pad = *width - (int32_t) tmp;
    146         *width = (int32_t) tmp;
    147     }
    148 }
    149 
    150 static force_inline void bilinear_pad_repeat_get_scanline_bounds(
    151         int32_t source_image_width, pixman_fixed_t vx, pixman_fixed_t unit_x,
    152         int32_t * left_pad, int32_t * left_tz, int32_t * width,
    153         int32_t * right_tz, int32_t * right_pad) {
    154     int width1 = *width, left_pad1, right_pad1;
    155     int width2 = *width, left_pad2, right_pad2;
    156 
    157     pad_repeat_get_scanline_bounds(source_image_width, vx, unit_x, &width1,
    158             &left_pad1, &right_pad1);
    159     pad_repeat_get_scanline_bounds(source_image_width, vx + pixman_fixed_1,
    160             unit_x, &width2, &left_pad2, &right_pad2);
    161 
    162     *left_pad = left_pad2;
    163     *left_tz = left_pad1 - left_pad2;
    164     *right_tz = right_pad2 - right_pad1;
    165     *right_pad = right_pad1;
    166     *width -= *left_pad + *left_tz + *right_tz + *right_pad;
    167 }
    168 
    169 #ifdef USE_ARM_NEON
    170 void pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon(uint32_t *dst,
    171         const uint32_t *top, const uint32_t *bottom, int wt, int wb,
    172         pixman_fixed_t x, pixman_fixed_t ux, int width);
    173 
    174 static void android_bilinear_filter_neon(android_simple_image* src_image,
    175         android_simple_image* dst_image, float scale, int src_x, int src_y) {
    176     int32_t src_width = src_image->width;
    177     int32_t src_height = src_image->height;
    178     pixman_vector_t v;
    179     int32_t left_pad, left_tz, right_tz, right_pad;
    180     pixman_fixed_t unit_x, unit_y;
    181     int32_t width = dst_image->width;
    182     int32_t height = dst_image->height;
    183     uint32_t dst_line = 0;
    184     uint32_t* dst;
    185     int y1, y2;
    186     pixman_fixed_t vx, vy;
    187     /* reference point is the center of the pixel */
    188     v.vector[0] = pixman_double_to_fixed((src_x + 0.5f) * scale);
    189     v.vector[1] = pixman_double_to_fixed((src_y + 0.5f) * scale);
    190     v.vector[2] = pixman_fixed_1;
    191     unit_x = unit_y = pixman_double_to_fixed(scale);
    192     vy = v.vector[1];
    193     bilinear_pad_repeat_get_scanline_bounds(src_width, v.vector[0], unit_x,
    194             &left_pad, &left_tz, &width, &right_tz, &right_pad);
    195     v.vector[0] += left_pad * unit_x;
    196     while (--height >= 0) {
    197         int weight1, weight2;
    198         dst_image->get_scanline(dst_image, (void**)(&dst), dst_line);
    199         dst_line++;
    200         vx = v.vector[0];
    201         y1 = pixman_fixed_to_int(vy);
    202         weight2 = pixman_fixed_to_bilinear_weight(vy);
    203         if (weight2) {
    204             /* both weight1 and weight2 are smaller than BILINEAR_INTERPOLATION_RANGE */
    205             y2 = y1 + 1;
    206             weight1 = BILINEAR_INTERPOLATION_RANGE - weight2;
    207         } else {
    208             /* set both top and bottom row to the same scanline and tweak weights */
    209             y2 = y1;
    210             weight1 = weight2 = BILINEAR_INTERPOLATION_RANGE / 2;
    211         }
    212         vy += unit_y;
    213         uint32_t buf1[2];
    214         uint32_t buf2[2];
    215         uint32_t* src1;
    216         uint32_t* src2;
    217         /* handle top/bottom zero padding by just setting weights to 0 if needed */
    218         if (y1 < 0) {
    219             weight1 = 0;
    220             y1 = 0;
    221         }
    222         if (y1 >= src_height) {
    223             weight1 = 0;
    224             y1 = src_height - 1;
    225         }
    226         if (y2 < 0) {
    227             weight2 = 0;
    228             y2 = 0;
    229         }
    230         if (y2 >= src_height) {
    231             weight2 = 0;
    232             y2 = src_height - 1;
    233         }
    234         src_image->get_scanline(src_image, (void**)(&src1), y1);
    235         src_image->get_scanline(src_image, (void**)(&src2), y2);
    236         if (left_pad > 0) {
    237             buf1[0] = buf1[1] = 0;
    238             buf2[0] = buf2[1] = 0;
    239             pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon(
    240                     dst, buf1, buf2, weight1, weight2, 0, 0, left_pad);
    241             dst += left_pad;
    242         }
    243         if (left_tz > 0) {
    244             buf1[0] = 0;
    245             buf1[1] = src1[0];
    246             buf2[0] = 0;
    247             buf2[1] = src2[0];
    248             pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon(
    249                     dst, buf1, buf2, weight1, weight2,
    250                     pixman_fixed_frac(vx), unit_x, left_tz);
    251             dst += left_tz;
    252             vx += left_tz * unit_x;
    253         }
    254         if (width > 0) {
    255             pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon(
    256                     dst, src1, src2, weight1, weight2, vx, unit_x, width);
    257             dst += width;
    258             vx += width * unit_x;
    259         }
    260         if (right_tz > 0) {
    261             buf1[0] = src1[src_width - 1];
    262             buf1[1] = 0;
    263             buf2[0] = src2[src_width - 1];
    264             buf2[1] = 0;
    265             pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon(
    266                     dst, buf1, buf2, weight1, weight2,
    267                     pixman_fixed_frac(vx), unit_x, right_tz);
    268             dst += right_tz;
    269         }
    270         if (right_pad > 0) {
    271             buf1[0] = buf1[1] = 0;
    272             buf2[0] = buf2[1] = 0;
    273             pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon(
    274                     dst, buf1, buf2, weight1, weight2, 0, 0, right_pad);
    275         }
    276     }
    277 }
    278 #endif // ARM_USE_NEON
    279 
    280 static void android_nearest_filter(android_simple_image* src_image,
    281         android_simple_image* dst_image, float scale, int src_x, int src_y) {
    282     int32_t src_width = src_image->width;
    283     int32_t src_height = src_image->height;
    284     int32_t width = dst_image->width;
    285     int32_t height = dst_image->height;
    286     uint32_t dst_line = 0;
    287     int y;
    288     pixman_fixed_t src_width_fixed = pixman_int_to_fixed(src_width);
    289     pixman_fixed_t max_vy;
    290     pixman_vector_t v;
    291     pixman_fixed_t vx, vy;
    292     pixman_fixed_t unit_x, unit_y;
    293     int32_t left_pad, right_pad;
    294     uint32_t *src;
    295     uint32_t *dst;
    296     /* reference point is the center of the pixel */
    297     v.vector[0] = pixman_double_to_fixed((src_x + 0.5f) * scale);
    298     v.vector[1] = pixman_double_to_fixed((src_y + 0.5f) * scale);
    299     v.vector[2] = pixman_fixed_1;
    300     unit_x = unit_y = pixman_double_to_fixed(scale);
    301     vx = v.vector[0];
    302     vy = v.vector[1];
    303     pad_repeat_get_scanline_bounds(src_width, vx, unit_x,
    304             &width, &left_pad, &right_pad);
    305     vx += left_pad * unit_x;
    306     while (--height >= 0) {
    307         dst_image->get_scanline(dst_image, (void**)(&dst), dst_line);
    308         dst_line++;
    309         y = ((int) ((vy) >> 16));
    310         vy += unit_y;
    311         static const uint32_t zero[1] = { 0 };
    312         if (y < 0 || y >= src_height) {
    313             scaled_nearest_scanline_8888_8888_none_SRC(
    314                     dst, zero + 1, left_pad + width + right_pad,
    315                     -((pixman_fixed_t) 1), 0, src_width_fixed);
    316             continue;
    317         }
    318         src_image->get_scanline(src_image, (void**)(&src), y);
    319         if (left_pad > 0) {
    320             scaled_nearest_scanline_8888_8888_none_SRC(
    321                     dst, zero + 1, left_pad, -((pixman_fixed_t) 1), 0,
    322                     src_width_fixed);
    323         }
    324         if (width > 0) {
    325             scaled_nearest_scanline_8888_8888_none_SRC(
    326                     dst + left_pad, src + src_width, width,
    327                     vx - src_width_fixed, unit_x, src_width_fixed);
    328         }
    329         if (right_pad > 0) {
    330             scaled_nearest_scanline_8888_8888_none_SRC(
    331                     dst + left_pad + width, zero + 1, right_pad,
    332                     -((pixman_fixed_t) 1), 0, src_width_fixed);
    333         }
    334     }
    335 }
    336 
    337 PIXMAN_EXPORT void android_simple_scale(android_simple_image* src_image,
    338         android_simple_image* dst_image, float scale, int src_x, int src_y) {
    339 #ifdef USE_ARM_NEON
    340     if (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM
    341             && (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON)) {
    342         android_bilinear_filter_neon(src_image, dst_image, scale, src_x, src_y);
    343         return;
    344     }
    345 #endif
    346     android_nearest_filter(src_image, dst_image, scale, src_x, src_y);
    347 }
    348