blob: a7edb123fad5d7e9b1af63e85108d977cf918091 [file] [log] [blame]
/*
* Copyright © 2013 The Android Open Source Project
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/*
* Copyright © 2000 SuSE, Inc.
* Copyright © 2007 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of SuSE not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. SuSE makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Keith Packard, SuSE, Inc.
*/
/*
* Copyright © 2009 ARM Ltd, Movial Creative Technologies Oy
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of ARM Ltd not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. ARM Ltd makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*
* Author: Ian Rickards (ian.rickards@arm.com)
* Author: Jonathan Morton (jonathan.morton@movial.com)
* Author: Markku Vire (markku.vire@movial.com)
*
*/
#include "config.h"
#include "pixman-android.h"
#include "pixman-private.h"
void pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon(uint32_t *dst,
const uint32_t *top, const uint32_t *bottom, int wt, int wb,
pixman_fixed_t x, pixman_fixed_t ux, int width);
static inline void scaled_bilinear_scanline_neon_8888_8888_SRC(uint32_t * dst,
const uint32_t * mask, const uint32_t * src_top,
const uint32_t * src_bottom, int32_t w, int wt, int wb,
pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx,
pixman_bool_t zero_src) {
pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon(dst, src_top,
src_bottom, wt, wb, vx, unit_x, w);
}
static inline int pixman_fixed_to_bilinear_weight(pixman_fixed_t x) {
return (x >> (16 - BILINEAR_INTERPOLATION_BITS))
& ((1 << BILINEAR_INTERPOLATION_BITS) - 1);
}
/*
* For each scanline fetched from source image with PAD repeat:
* - calculate how many pixels need to be padded on the left side
* - calculate how many pixels need to be padded on the right side
* - update width to only count pixels which are fetched from the image
* All this information is returned via 'width', 'left_pad', 'right_pad'
* arguments. The code is assuming that 'unit_x' is positive.
*
* Note: 64-bit math is used in order to avoid potential overflows, which
* is probably excessive in many cases. This particular function
* may need its own correctness test and performance tuning.
*/
static force_inline void pad_repeat_get_scanline_bounds(
int32_t source_image_width, pixman_fixed_t vx, pixman_fixed_t unit_x,
int32_t * width, int32_t * left_pad, int32_t * right_pad) {
int64_t max_vx = (int64_t) source_image_width << 16;
int64_t tmp;
if (vx < 0) {
tmp = ((int64_t) unit_x - 1 - vx) / unit_x;
if (tmp > *width) {
*left_pad = *width;
*width = 0;
} else {
*left_pad = (int32_t) tmp;
*width -= (int32_t) tmp;
}
} else {
*left_pad = 0;
}
tmp = ((int64_t) unit_x - 1 - vx + max_vx) / unit_x - *left_pad;
if (tmp < 0) {
*right_pad = *width;
*width = 0;
} else if (tmp >= *width) {
*right_pad = 0;
} else {
*right_pad = *width - (int32_t) tmp;
*width = (int32_t) tmp;
}
}
static force_inline void bilinear_pad_repeat_get_scanline_bounds(
int32_t source_image_width, pixman_fixed_t vx, pixman_fixed_t unit_x,
int32_t * left_pad, int32_t * left_tz, int32_t * width,
int32_t * right_tz, int32_t * right_pad) {
int width1 = *width, left_pad1, right_pad1;
int width2 = *width, left_pad2, right_pad2;
pad_repeat_get_scanline_bounds(source_image_width, vx, unit_x, &width1,
&left_pad1, &right_pad1);
pad_repeat_get_scanline_bounds(source_image_width, vx + pixman_fixed_1,
unit_x, &width2, &left_pad2, &right_pad2);
*left_pad = left_pad2;
*left_tz = left_pad1 - left_pad2;
*right_tz = right_pad2 - right_pad1;
*right_pad = right_pad1;
*width -= *left_pad + *left_tz + *right_tz + *right_pad;
}
PIXMAN_EXPORT void android_bilinear_filter(android_simple_image* src_image,
android_simple_image* dst_image, float scale, int src_x, int src_y) {
int32_t src_width = src_image->width;
int32_t src_height = src_image->height;
pixman_vector_t v;
int32_t left_pad, left_tz, right_tz, right_pad;
pixman_fixed_t unit_x, unit_y;
int32_t width = dst_image->width;
int32_t height = dst_image->height;
uint32_t dst_line = 0;
uint32_t* dst;
int y1, y2;
pixman_fixed_t vx, vy;
/* reference point is the center of the pixel */
v.vector[0] = pixman_double_to_fixed((src_x + 0.5f) * scale);
v.vector[1] = pixman_double_to_fixed((src_y + 0.5f) * scale);
v.vector[2] = pixman_fixed_1;
unit_x = unit_y = pixman_double_to_fixed(scale);
vy = v.vector[1];
bilinear_pad_repeat_get_scanline_bounds(src_width, v.vector[0], unit_x,
&left_pad, &left_tz, &width, &right_tz, &right_pad);
v.vector[0] += left_pad * unit_x;
while (--height >= 0) {
int weight1, weight2;
dst_image->get_scanline(dst_image, (void**)(&dst), dst_line);
dst_line++;
vx = v.vector[0];
y1 = pixman_fixed_to_int(vy);
weight2 = pixman_fixed_to_bilinear_weight(vy);
if (weight2) {
/* both weight1 and weight2 are smaller than BILINEAR_INTERPOLATION_RANGE */
y2 = y1 + 1;
weight1 = BILINEAR_INTERPOLATION_RANGE - weight2;
} else {
/* set both top and bottom row to the same scanline and tweak weights */
y2 = y1;
weight1 = weight2 = BILINEAR_INTERPOLATION_RANGE / 2;
}
vy += unit_y;
uint32_t buf1[2];
uint32_t buf2[2];
uint32_t* src1;
uint32_t* src2;
/* handle top/bottom zero padding by just setting weights to 0 if needed */
if (y1 < 0) {
weight1 = 0;
y1 = 0;
}
if (y1 >= src_height) {
weight1 = 0;
y1 = src_height - 1;
}
if (y2 < 0) {
weight2 = 0;
y2 = 0;
}
if (y2 >= src_height) {
weight2 = 0;
y2 = src_height - 1;
}
src_image->get_scanline(src_image, (void**)(&src1), y1);
src_image->get_scanline(src_image, (void**)(&src2), y2);
if (left_pad > 0) {
buf1[0] = buf1[1] = 0;
buf2[0] = buf2[1] = 0;
scaled_bilinear_scanline_neon_8888_8888_SRC(dst, NULL, buf1, buf2,
left_pad, weight1, weight2, 0, 0, 0, 1);
dst += left_pad;
}
if (left_tz > 0) {
buf1[0] = 0;
buf1[1] = src1[0];
buf2[0] = 0;
buf2[1] = src2[0];
scaled_bilinear_scanline_neon_8888_8888_SRC(dst, NULL, buf1, buf2,
left_tz, weight1, weight2,
pixman_fixed_frac(vx), unit_x, 0, 0);
dst += left_tz;
vx += left_tz * unit_x;
}
if (width > 0) {
scaled_bilinear_scanline_neon_8888_8888_SRC(dst, NULL, src1, src2,
width, weight1, weight2, vx, unit_x, 0, 0);
dst += width;
vx += width * unit_x;
}
if (right_tz > 0) {
buf1[0] = src1[src_width - 1];
buf1[1] = 0;
buf2[0] = src2[src_width - 1];
buf2[1] = 0;
scaled_bilinear_scanline_neon_8888_8888_SRC(dst, NULL, buf1, buf2,
right_tz, weight1, weight2,
pixman_fixed_frac(vx), unit_x, 0, 0);
dst += right_tz;
}
if (right_pad > 0) {
buf1[0] = buf1[1] = 0;
buf2[0] = buf2[1] = 0;
scaled_bilinear_scanline_neon_8888_8888_SRC(dst, NULL, buf1, buf2,
right_pad, weight1, weight2, 0, 0, 0, 1);
}
}
}