mirror of
https://salsa.debian.org/xorg-team/lib/pixman
synced 2025-09-01 18:46:11 +00:00

Currently, if you attempt to use component alpha on source images or images without RGB channels, Pixman will silently just use unified alpha instead. This patch makes such images supported for component alpha. There is no particularly compelling usecase at the moment, but this patch does get rid of a bit of special-case code both in pixman-general.c and in test/composite.c.
526 lines
12 KiB
C
526 lines
12 KiB
C
/*
|
|
* Copyright © 2005 Eric Anholt
|
|
* Copyright © 2009 Chris Wilson
|
|
* Copyright © 2010 Soeren Sandmann
|
|
* Copyright © 2010 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 Eric Anholt not be used in
|
|
* advertising or publicity pertaining to distribution of the software without
|
|
* specific, written prior permission. Eric Anholt makes no
|
|
* representations about the suitability of this software for any purpose. It
|
|
* is provided "as is" without express or implied warranty.
|
|
*
|
|
* ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL ERIC ANHOLT 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.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h> /* abort() */
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include "utils.h"
|
|
|
|
typedef struct image_t image_t;
|
|
|
|
static const color_t colors[] =
|
|
{
|
|
{ 1.0, 1.0, 1.0, 1.0 },
|
|
{ 1.0, 1.0, 1.0, 0.0 },
|
|
{ 0.0, 0.0, 0.0, 1.0 },
|
|
{ 0.0, 0.0, 0.0, 0.0 },
|
|
{ 1.0, 0.0, 0.0, 1.0 },
|
|
{ 0.0, 1.0, 0.0, 1.0 },
|
|
{ 0.0, 0.0, 1.0, 1.0 },
|
|
{ 0.5, 0.0, 0.0, 0.5 },
|
|
};
|
|
|
|
static uint16_t
|
|
_color_double_to_short (double d)
|
|
{
|
|
uint32_t i;
|
|
|
|
i = (uint32_t) (d * 65536);
|
|
i -= (i >> 16);
|
|
|
|
return i;
|
|
}
|
|
|
|
static void
|
|
compute_pixman_color (const color_t *color,
|
|
pixman_color_t *out)
|
|
{
|
|
out->red = _color_double_to_short (color->r);
|
|
out->green = _color_double_to_short (color->g);
|
|
out->blue = _color_double_to_short (color->b);
|
|
out->alpha = _color_double_to_short (color->a);
|
|
}
|
|
|
|
#define REPEAT 0x01000000
|
|
#define FLAGS 0xff000000
|
|
|
|
static const int sizes[] =
|
|
{
|
|
0,
|
|
1,
|
|
1 | REPEAT,
|
|
10
|
|
};
|
|
|
|
static const pixman_format_code_t formats[] =
|
|
{
|
|
/* 32 bpp formats */
|
|
PIXMAN_a8r8g8b8,
|
|
PIXMAN_x8r8g8b8,
|
|
PIXMAN_a8b8g8r8,
|
|
PIXMAN_x8b8g8r8,
|
|
PIXMAN_b8g8r8a8,
|
|
PIXMAN_b8g8r8x8,
|
|
PIXMAN_r8g8b8a8,
|
|
PIXMAN_r8g8b8x8,
|
|
PIXMAN_x2r10g10b10,
|
|
PIXMAN_x2b10g10r10,
|
|
PIXMAN_a2r10g10b10,
|
|
PIXMAN_a2b10g10r10,
|
|
|
|
/* sRGB formats */
|
|
PIXMAN_a8r8g8b8_sRGB,
|
|
|
|
/* 24 bpp formats */
|
|
PIXMAN_r8g8b8,
|
|
PIXMAN_b8g8r8,
|
|
PIXMAN_r5g6b5,
|
|
PIXMAN_b5g6r5,
|
|
|
|
/* 16 bpp formats */
|
|
PIXMAN_x1r5g5b5,
|
|
PIXMAN_x1b5g5r5,
|
|
PIXMAN_a1r5g5b5,
|
|
PIXMAN_a1b5g5r5,
|
|
PIXMAN_a4b4g4r4,
|
|
PIXMAN_x4b4g4r4,
|
|
PIXMAN_a4r4g4b4,
|
|
PIXMAN_x4r4g4b4,
|
|
|
|
/* 8 bpp formats */
|
|
PIXMAN_a8,
|
|
PIXMAN_r3g3b2,
|
|
PIXMAN_b2g3r3,
|
|
PIXMAN_a2r2g2b2,
|
|
PIXMAN_a2b2g2r2,
|
|
PIXMAN_x4a4,
|
|
|
|
/* 4 bpp formats */
|
|
PIXMAN_a4,
|
|
PIXMAN_r1g2b1,
|
|
PIXMAN_b1g2r1,
|
|
PIXMAN_a1r1g1b1,
|
|
PIXMAN_a1b1g1r1,
|
|
|
|
/* 1 bpp formats */
|
|
PIXMAN_a1,
|
|
};
|
|
|
|
struct image_t
|
|
{
|
|
pixman_image_t *image;
|
|
pixman_format_code_t format;
|
|
const color_t *color;
|
|
pixman_repeat_t repeat;
|
|
int size;
|
|
};
|
|
|
|
static const pixman_op_t operators[] =
|
|
{
|
|
PIXMAN_OP_CLEAR,
|
|
PIXMAN_OP_SRC,
|
|
PIXMAN_OP_DST,
|
|
PIXMAN_OP_OVER,
|
|
PIXMAN_OP_OVER_REVERSE,
|
|
PIXMAN_OP_IN,
|
|
PIXMAN_OP_IN_REVERSE,
|
|
PIXMAN_OP_OUT,
|
|
PIXMAN_OP_OUT_REVERSE,
|
|
PIXMAN_OP_ATOP,
|
|
PIXMAN_OP_ATOP_REVERSE,
|
|
PIXMAN_OP_XOR,
|
|
PIXMAN_OP_ADD,
|
|
PIXMAN_OP_SATURATE,
|
|
|
|
PIXMAN_OP_DISJOINT_CLEAR,
|
|
PIXMAN_OP_DISJOINT_SRC,
|
|
PIXMAN_OP_DISJOINT_DST,
|
|
PIXMAN_OP_DISJOINT_OVER,
|
|
PIXMAN_OP_DISJOINT_OVER_REVERSE,
|
|
PIXMAN_OP_DISJOINT_IN,
|
|
PIXMAN_OP_DISJOINT_IN_REVERSE,
|
|
PIXMAN_OP_DISJOINT_OUT,
|
|
PIXMAN_OP_DISJOINT_OUT_REVERSE,
|
|
PIXMAN_OP_DISJOINT_ATOP,
|
|
PIXMAN_OP_DISJOINT_ATOP_REVERSE,
|
|
PIXMAN_OP_DISJOINT_XOR,
|
|
|
|
PIXMAN_OP_CONJOINT_CLEAR,
|
|
PIXMAN_OP_CONJOINT_SRC,
|
|
PIXMAN_OP_CONJOINT_DST,
|
|
PIXMAN_OP_CONJOINT_OVER,
|
|
PIXMAN_OP_CONJOINT_OVER_REVERSE,
|
|
PIXMAN_OP_CONJOINT_IN,
|
|
PIXMAN_OP_CONJOINT_IN_REVERSE,
|
|
PIXMAN_OP_CONJOINT_OUT,
|
|
PIXMAN_OP_CONJOINT_OUT_REVERSE,
|
|
PIXMAN_OP_CONJOINT_ATOP,
|
|
PIXMAN_OP_CONJOINT_ATOP_REVERSE,
|
|
PIXMAN_OP_CONJOINT_XOR,
|
|
};
|
|
|
|
static uint32_t
|
|
get_value (pixman_image_t *image)
|
|
{
|
|
uint32_t value = *(uint32_t *)pixman_image_get_data (image);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
{
|
|
pixman_format_code_t format = pixman_image_get_format (image);
|
|
value >>= 8 * sizeof(value) - PIXMAN_FORMAT_BPP (format);
|
|
}
|
|
#endif
|
|
|
|
return value;
|
|
}
|
|
|
|
static char *
|
|
describe_image (image_t *info, char *buf)
|
|
{
|
|
if (info->size)
|
|
{
|
|
sprintf (buf, "%s, %dx%d%s",
|
|
format_name (info->format),
|
|
info->size, info->size,
|
|
info->repeat ? " R" :"");
|
|
}
|
|
else
|
|
{
|
|
sprintf (buf, "solid");
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static char *
|
|
describe_color (const color_t *color, char *buf)
|
|
{
|
|
sprintf (buf, "%.3f %.3f %.3f %.3f",
|
|
color->r, color->g, color->b, color->a);
|
|
|
|
return buf;
|
|
}
|
|
|
|
static pixman_bool_t
|
|
composite_test (image_t *dst,
|
|
pixman_op_t op,
|
|
image_t *src,
|
|
image_t *mask,
|
|
pixman_bool_t component_alpha,
|
|
int testno)
|
|
{
|
|
color_t expected, tdst, tsrc, tmsk;
|
|
pixel_checker_t checker;
|
|
|
|
if (mask)
|
|
{
|
|
pixman_image_set_component_alpha (mask->image, component_alpha);
|
|
|
|
pixman_image_composite (op, src->image, mask->image, dst->image,
|
|
0, 0, 0, 0, 0, 0, dst->size, dst->size);
|
|
}
|
|
else
|
|
{
|
|
pixman_image_composite (op, src->image, NULL, dst->image,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
dst->size, dst->size);
|
|
}
|
|
|
|
tdst = *dst->color;
|
|
tsrc = *src->color;
|
|
|
|
if (mask)
|
|
{
|
|
tmsk = *mask->color;
|
|
}
|
|
|
|
/* It turns out that by construction all source, mask etc. colors are
|
|
* linear because they are made from fills, and fills are always in linear
|
|
* color space. However, if they have been converted to bitmaps, we need
|
|
* to simulate the sRGB approximation to pass the test cases.
|
|
*/
|
|
if (src->size)
|
|
{
|
|
if (PIXMAN_FORMAT_TYPE (src->format) == PIXMAN_TYPE_ARGB_SRGB)
|
|
{
|
|
tsrc.r = convert_linear_to_srgb (tsrc.r);
|
|
tsrc.g = convert_linear_to_srgb (tsrc.g);
|
|
tsrc.b = convert_linear_to_srgb (tsrc.b);
|
|
round_color (src->format, &tsrc);
|
|
tsrc.r = convert_srgb_to_linear (tsrc.r);
|
|
tsrc.g = convert_srgb_to_linear (tsrc.g);
|
|
tsrc.b = convert_srgb_to_linear (tsrc.b);
|
|
}
|
|
else
|
|
{
|
|
round_color (src->format, &tsrc);
|
|
}
|
|
}
|
|
|
|
if (mask && mask->size)
|
|
{
|
|
if (PIXMAN_FORMAT_TYPE (mask->format) == PIXMAN_TYPE_ARGB_SRGB)
|
|
{
|
|
tmsk.r = convert_linear_to_srgb (tmsk.r);
|
|
tmsk.g = convert_linear_to_srgb (tmsk.g);
|
|
tmsk.b = convert_linear_to_srgb (tmsk.b);
|
|
round_color (mask->format, &tmsk);
|
|
tmsk.r = convert_srgb_to_linear (tmsk.r);
|
|
tmsk.g = convert_srgb_to_linear (tmsk.g);
|
|
tmsk.b = convert_srgb_to_linear (tmsk.b);
|
|
}
|
|
else
|
|
{
|
|
round_color (mask->format, &tmsk);
|
|
}
|
|
}
|
|
|
|
if (PIXMAN_FORMAT_TYPE (dst->format) == PIXMAN_TYPE_ARGB_SRGB)
|
|
{
|
|
tdst.r = convert_linear_to_srgb (tdst.r);
|
|
tdst.g = convert_linear_to_srgb (tdst.g);
|
|
tdst.b = convert_linear_to_srgb (tdst.b);
|
|
round_color (dst->format, &tdst);
|
|
tdst.r = convert_srgb_to_linear (tdst.r);
|
|
tdst.g = convert_srgb_to_linear (tdst.g);
|
|
tdst.b = convert_srgb_to_linear (tdst.b);
|
|
}
|
|
else
|
|
{
|
|
round_color (dst->format, &tdst);
|
|
}
|
|
|
|
do_composite (op,
|
|
&tsrc,
|
|
mask? &tmsk : NULL,
|
|
&tdst,
|
|
&expected,
|
|
component_alpha);
|
|
|
|
pixel_checker_init (&checker, dst->format);
|
|
|
|
if (!pixel_checker_check (&checker, get_value (dst->image), &expected))
|
|
{
|
|
char buf[40], buf2[40];
|
|
int a, r, g, b;
|
|
uint32_t pixel;
|
|
|
|
printf ("---- Test %d failed ----\n", testno);
|
|
printf ("Operator: %s %s\n",
|
|
operator_name (op), component_alpha ? "CA" : "");
|
|
|
|
printf ("Source: %s\n", describe_image (src, buf));
|
|
if (mask != NULL)
|
|
printf ("Mask: %s\n", describe_image (mask, buf));
|
|
|
|
printf ("Destination: %s\n\n", describe_image (dst, buf));
|
|
printf (" R G B A Rounded\n");
|
|
printf ("Source color: %s %s\n",
|
|
describe_color (src->color, buf),
|
|
describe_color (&tsrc, buf2));
|
|
if (mask)
|
|
{
|
|
printf ("Mask color: %s %s\n",
|
|
describe_color (mask->color, buf),
|
|
describe_color (&tmsk, buf2));
|
|
}
|
|
printf ("Dest. color: %s %s\n",
|
|
describe_color (dst->color, buf),
|
|
describe_color (&tdst, buf2));
|
|
|
|
pixel = get_value (dst->image);
|
|
|
|
printf ("Expected: %s\n", describe_color (&expected, buf));
|
|
|
|
pixel_checker_split_pixel (&checker, pixel, &a, &r, &g, &b);
|
|
|
|
printf ("Got: %5d %5d %5d %5d [pixel: 0x%08x]\n", r, g, b, a, pixel);
|
|
pixel_checker_get_min (&checker, &expected, &a, &r, &g, &b);
|
|
printf ("Min accepted: %5d %5d %5d %5d\n", r, g, b, a);
|
|
pixel_checker_get_max (&checker, &expected, &a, &r, &g, &b);
|
|
printf ("Max accepted: %5d %5d %5d %5d\n", r, g, b, a);
|
|
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
image_init (image_t *info,
|
|
int color,
|
|
int format,
|
|
int size)
|
|
{
|
|
pixman_color_t fill;
|
|
|
|
info->color = &colors[color];
|
|
compute_pixman_color (info->color, &fill);
|
|
|
|
info->format = formats[format];
|
|
info->size = sizes[size] & ~FLAGS;
|
|
info->repeat = PIXMAN_REPEAT_NONE;
|
|
|
|
if (info->size)
|
|
{
|
|
pixman_image_t *solid;
|
|
|
|
info->image = pixman_image_create_bits (info->format,
|
|
info->size, info->size,
|
|
NULL, 0);
|
|
|
|
solid = pixman_image_create_solid_fill (&fill);
|
|
pixman_image_composite32 (PIXMAN_OP_SRC, solid, NULL, info->image,
|
|
0, 0, 0, 0, 0, 0, info->size, info->size);
|
|
pixman_image_unref (solid);
|
|
|
|
if (sizes[size] & REPEAT)
|
|
{
|
|
pixman_image_set_repeat (info->image, PIXMAN_REPEAT_NORMAL);
|
|
info->repeat = PIXMAN_REPEAT_NORMAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
info->image = pixman_image_create_solid_fill (&fill);
|
|
}
|
|
}
|
|
|
|
static void
|
|
image_fini (image_t *info)
|
|
{
|
|
pixman_image_unref (info->image);
|
|
}
|
|
|
|
static int
|
|
random_size (void)
|
|
{
|
|
return prng_rand_n (ARRAY_LENGTH (sizes));
|
|
}
|
|
|
|
static int
|
|
random_color (void)
|
|
{
|
|
return prng_rand_n (ARRAY_LENGTH (colors));
|
|
}
|
|
|
|
static int
|
|
random_format (void)
|
|
{
|
|
return prng_rand_n (ARRAY_LENGTH (formats));
|
|
}
|
|
|
|
static pixman_bool_t
|
|
run_test (uint32_t seed)
|
|
{
|
|
image_t src, mask, dst;
|
|
pixman_op_t op;
|
|
int ca;
|
|
int ok;
|
|
|
|
prng_srand (seed);
|
|
|
|
image_init (&dst, random_color(), random_format(), 1);
|
|
image_init (&src, random_color(), random_format(), random_size());
|
|
image_init (&mask, random_color(), random_format(), random_size());
|
|
|
|
op = operators [prng_rand_n (ARRAY_LENGTH (operators))];
|
|
|
|
ca = prng_rand_n (3);
|
|
|
|
switch (ca)
|
|
{
|
|
case 0:
|
|
ok = composite_test (&dst, op, &src, NULL, FALSE, seed);
|
|
break;
|
|
case 1:
|
|
ok = composite_test (&dst, op, &src, &mask, FALSE, seed);
|
|
break;
|
|
case 2:
|
|
ok = composite_test (&dst, op, &src, &mask,
|
|
mask.size? TRUE : FALSE, seed);
|
|
break;
|
|
default:
|
|
ok = FALSE;
|
|
break;
|
|
}
|
|
|
|
image_fini (&src);
|
|
image_fini (&mask);
|
|
image_fini (&dst);
|
|
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
#define N_TESTS (8 * 1024 * 1024)
|
|
int result = 0;
|
|
uint32_t seed;
|
|
int32_t i;
|
|
|
|
if (argc > 1)
|
|
{
|
|
char *end;
|
|
|
|
i = strtol (argv[1], &end, 0);
|
|
|
|
if (end != argv[1])
|
|
{
|
|
if (!run_test (i))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
printf ("Usage:\n\n %s <number>\n\n", argv[0]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (getenv ("PIXMAN_RANDOMIZE_TESTS"))
|
|
seed = get_random_seed();
|
|
else
|
|
seed = 1;
|
|
|
|
#ifdef USE_OPENMP
|
|
# pragma omp parallel for default(none) shared(result, argv, seed)
|
|
#endif
|
|
for (i = 0; i <= N_TESTS; ++i)
|
|
{
|
|
if (!result && !run_test (i + seed))
|
|
{
|
|
printf ("Test 0x%08X failed.\n", seed + i);
|
|
|
|
result = seed + i;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|