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