On Tuesday 02 March 2010, Soeren Sandmann wrote:
[...]
> There is a couple of things I'd _like_ to get in, but making it happen
> will require other people to help out:
>
> * Siarhei's fixes for gradient data types. As far as I can tell,
> moving to 16.16 won't be any _worse_ than what we have now, and it
> will be a speedup on ARM.
It will be worse in some cases because the calculated floating point
value 't' actually may not fit into the destination 16.16 fixed point type.
Floating floating point exceptions may be enabled by library user or they may
be even enabled by default on some platforms. Even the current 48.16
data type may be insufficient as demonstrated by the attached
example (a modified 'gradient-test.c' from pixman test).
$ ./gradient-test
Floating point exception
It is artificial hand crafted case, but it demonstrates that the calculated
value of 't' may be so large that its integer part does not fit even in 48
bits.
This can be solved (at least for the normal SVG radial gradients where the
focal point is inside outer circle) by for example taking the distance of the
furthest corner of the area being composited from the outer circle center, and
dividing it by the distance from the focal point to the outer perimeter. If
the calculated value is less than 32K, then 16.16 fixed point can be used
safely. Otherwise 't' may need to be clipped as floating point value before
converting to integer.
Probably normal people will never use radial gradients with the extreme corner
case parameters, the image just does not look right anyway, so it can't be
distorted any worse by additional checks.
By the way, technically gradients are performing point sampling. And it may
produce bad image quality and rough edges on the transition between stops.
--
Best regards,
Siarhei Siamashka
#include <stdio.h>
#include <stdlib.h>
#include <fenv.h>
#include "pixman.h"
#include "gtk-utils.h"
int
main (int argc, char **argv)
{
#define WIDTH 400
#define HEIGHT 200
uint32_t *dest = malloc (WIDTH * HEIGHT * 4);
pixman_image_t *src_img;
pixman_image_t *dest_img;
int i;
pixman_gradient_stop_t stops[2] =
{
{ pixman_int_to_fixed (0), { 0xffff, 0xeeee, 0xeeee, 0xeeee } },
{ pixman_int_to_fixed (1), { 0xffff, 0x1111, 0x1111, 0x1111 } }
};
pixman_point_fixed_t c_inner;
pixman_point_fixed_t c_outer;
pixman_fixed_t r_inner;
pixman_fixed_t r_outer;
/*
* Enable floating point exceptions on invalid operations, so that
* out of range 'double -> int64_t' conversion is caught
*/
feenableexcept(FE_INVALID);
for (i = 0; i < WIDTH * HEIGHT; ++i)
dest[i] = 0x4f00004f; /* pale blue */
dest_img = pixman_image_create_bits (PIXMAN_a8r8g8b8,
WIDTH, HEIGHT,
dest,
WIDTH * 4);
/*
* Set gradient parameters so that inner circle center is placed inside
* outer circle, but as close as possible to the outer circle perimeter
*/
c_inner.x = 519176; /* ~7.92199707031 */
c_inner.y = 522992; /* ~7.98022460938 */
c_outer.x = 0;
c_outer.y = 0;
r_inner = 0;
r_outer = 736929; /* ~11.244644165 */
src_img = pixman_image_create_radial_gradient (&c_inner, &c_outer,
r_inner, r_outer,
stops, 2);
pixman_image_set_repeat (src_img, PIXMAN_REPEAT_NORMAL);
pixman_image_composite (PIXMAN_OP_OVER, src_img, NULL, dest_img,
10000, 10000, 0, 0, 0, 0, WIDTH, HEIGHT);
show_image (dest_img);
pixman_image_unref (src_img);
pixman_image_unref (dest_img);
free (dest);
return 0;
}
_______________________________________________
Pixman mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/pixman