0,0 → 1,287 |
/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ |
/* |
* Copyright © 2000 SuSE, Inc. |
* Copyright © 2007 Red Hat, Inc. |
* Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. |
* 2005 Lars Knoll & Zack Rusin, Trolltech |
* |
* 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 Keith Packard not be used in |
* advertising or publicity pertaining to distribution of the software without |
* specific, written prior permission. Keith Packard 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. |
*/ |
|
#ifdef HAVE_CONFIG_H |
#include <config.h> |
#endif |
#include <stdlib.h> |
#include "pixman-private.h" |
|
static pixman_bool_t |
linear_gradient_is_horizontal (pixman_image_t *image, |
int x, |
int y, |
int width, |
int height) |
{ |
linear_gradient_t *linear = (linear_gradient_t *)image; |
pixman_vector_t v; |
pixman_fixed_32_32_t l; |
pixman_fixed_48_16_t dx, dy; |
double inc; |
|
if (image->common.transform) |
{ |
/* projective transformation */ |
if (image->common.transform->matrix[2][0] != 0 || |
image->common.transform->matrix[2][1] != 0 || |
image->common.transform->matrix[2][2] == 0) |
{ |
return FALSE; |
} |
|
v.vector[0] = image->common.transform->matrix[0][1]; |
v.vector[1] = image->common.transform->matrix[1][1]; |
v.vector[2] = image->common.transform->matrix[2][2]; |
} |
else |
{ |
v.vector[0] = 0; |
v.vector[1] = pixman_fixed_1; |
v.vector[2] = pixman_fixed_1; |
} |
|
dx = linear->p2.x - linear->p1.x; |
dy = linear->p2.y - linear->p1.y; |
|
l = dx * dx + dy * dy; |
|
if (l == 0) |
return FALSE; |
|
/* |
* compute how much the input of the gradient walked changes |
* when moving vertically through the whole image |
*/ |
inc = height * (double) pixman_fixed_1 * pixman_fixed_1 * |
(dx * v.vector[0] + dy * v.vector[1]) / |
(v.vector[2] * (double) l); |
|
/* check that casting to integer would result in 0 */ |
if (-1 < inc && inc < 1) |
return TRUE; |
|
return FALSE; |
} |
|
static uint32_t * |
linear_get_scanline_narrow (pixman_iter_t *iter, |
const uint32_t *mask) |
{ |
pixman_image_t *image = iter->image; |
int x = iter->x; |
int y = iter->y; |
int width = iter->width; |
uint32_t * buffer = iter->buffer; |
|
pixman_vector_t v, unit; |
pixman_fixed_32_32_t l; |
pixman_fixed_48_16_t dx, dy; |
gradient_t *gradient = (gradient_t *)image; |
linear_gradient_t *linear = (linear_gradient_t *)image; |
uint32_t *end = buffer + width; |
pixman_gradient_walker_t walker; |
|
_pixman_gradient_walker_init (&walker, gradient, image->common.repeat); |
|
/* reference point is the center of the pixel */ |
v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; |
v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; |
v.vector[2] = pixman_fixed_1; |
|
if (image->common.transform) |
{ |
if (!pixman_transform_point_3d (image->common.transform, &v)) |
return iter->buffer; |
|
unit.vector[0] = image->common.transform->matrix[0][0]; |
unit.vector[1] = image->common.transform->matrix[1][0]; |
unit.vector[2] = image->common.transform->matrix[2][0]; |
} |
else |
{ |
unit.vector[0] = pixman_fixed_1; |
unit.vector[1] = 0; |
unit.vector[2] = 0; |
} |
|
dx = linear->p2.x - linear->p1.x; |
dy = linear->p2.y - linear->p1.y; |
|
l = dx * dx + dy * dy; |
|
if (l == 0 || unit.vector[2] == 0) |
{ |
/* affine transformation only */ |
pixman_fixed_32_32_t t, next_inc; |
double inc; |
|
if (l == 0 || v.vector[2] == 0) |
{ |
t = 0; |
inc = 0; |
} |
else |
{ |
double invden, v2; |
|
invden = pixman_fixed_1 * (double) pixman_fixed_1 / |
(l * (double) v.vector[2]); |
v2 = v.vector[2] * (1. / pixman_fixed_1); |
t = ((dx * v.vector[0] + dy * v.vector[1]) - |
(dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; |
inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden; |
} |
next_inc = 0; |
|
if (((pixman_fixed_32_32_t )(inc * width)) == 0) |
{ |
register uint32_t color; |
|
color = _pixman_gradient_walker_pixel (&walker, t); |
while (buffer < end) |
*buffer++ = color; |
} |
else |
{ |
int i; |
|
i = 0; |
while (buffer < end) |
{ |
if (!mask || *mask++) |
{ |
*buffer = _pixman_gradient_walker_pixel (&walker, |
t + next_inc); |
} |
i++; |
next_inc = inc * i; |
buffer++; |
} |
} |
} |
else |
{ |
/* projective transformation */ |
double t; |
|
t = 0; |
|
while (buffer < end) |
{ |
if (!mask || *mask++) |
{ |
if (v.vector[2] != 0) |
{ |
double invden, v2; |
|
invden = pixman_fixed_1 * (double) pixman_fixed_1 / |
(l * (double) v.vector[2]); |
v2 = v.vector[2] * (1. / pixman_fixed_1); |
t = ((dx * v.vector[0] + dy * v.vector[1]) - |
(dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; |
} |
|
*buffer = _pixman_gradient_walker_pixel (&walker, t); |
} |
|
++buffer; |
|
v.vector[0] += unit.vector[0]; |
v.vector[1] += unit.vector[1]; |
v.vector[2] += unit.vector[2]; |
} |
} |
|
iter->y++; |
|
return iter->buffer; |
} |
|
static uint32_t * |
linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) |
{ |
uint32_t *buffer = linear_get_scanline_narrow (iter, NULL); |
|
pixman_expand_to_float ( |
(argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); |
|
return buffer; |
} |
|
void |
_pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) |
{ |
if (linear_gradient_is_horizontal ( |
iter->image, iter->x, iter->y, iter->width, iter->height)) |
{ |
if (iter->iter_flags & ITER_NARROW) |
linear_get_scanline_narrow (iter, NULL); |
else |
linear_get_scanline_wide (iter, NULL); |
|
iter->get_scanline = _pixman_iter_get_scanline_noop; |
} |
else |
{ |
if (iter->iter_flags & ITER_NARROW) |
iter->get_scanline = linear_get_scanline_narrow; |
else |
iter->get_scanline = linear_get_scanline_wide; |
} |
} |
|
PIXMAN_EXPORT pixman_image_t * |
pixman_image_create_linear_gradient (const pixman_point_fixed_t * p1, |
const pixman_point_fixed_t * p2, |
const pixman_gradient_stop_t *stops, |
int n_stops) |
{ |
pixman_image_t *image; |
linear_gradient_t *linear; |
|
image = _pixman_image_allocate (); |
|
if (!image) |
return NULL; |
|
linear = &image->linear; |
|
if (!_pixman_init_gradient (&linear->common, stops, n_stops)) |
{ |
free (image); |
return NULL; |
} |
|
linear->p1 = *p1; |
linear->p2 = *p2; |
|
image->type = LINEAR; |
|
return image; |
} |
|