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@arm.com)
69 * Author:  Jonathan Morton (jonathan.morton@movial.com)
70 * Author:  Markku Vire (markku.vire@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
79static 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
104static 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 */
121static 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
150static 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
170void 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
174static 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
280static 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
337PIXMAN_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