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

Consider a DISJOINT_ATOP operation with the following pixels: - source: 0xff (8 bits) - source alpha: 0x01 (8 bits) - mask alpha: 0x7b (8 bits) - dest: 0x00 (8 bits) - dest alpha: 0xff (8 bits) When (src IN mask) is computed in 8 bits, the resulting alpha channel is 0 due to rounding: floor ((0x01 * 0x7b) / 255.0 + 0.5) = floor (0.9823) = 0 which means that since Render defines any division by zero as infinity, the Fa and Fb for this operator end up as follows: Fa = max (1 - (1 - 1) / 0, 0) = 0 Fb = min (1, (1 - 0) / 1) = 1 and so since dest is 0x00, the overall result is 0. However, when computed in full precision, the alpha value no longer rounds to 0, and so Fa ends up being Fa = max (1 - (1 - 1) / 0.0001, 0) = 1 and so the result is now s * ma * Fa + d * Fb = (1.0 * (0x7b / 255.0) * 1) + d * 0 = 0x7b / 255.0 = 0.4823 so the error in this case ends up being 0.48235294, which is clearly not something that can be considered acceptable. In order to avoid this problem, we need to do all arithmetic in such a way that a multiplication of two tiny numbers can never end up being zero unless one of the input numbers is itself zero. This patch makes all computations that involve divisions take place in floating point, which is sufficient to fix the test cases This brings the number of failures in pixel-test down to 14.
173 lines
3.4 KiB
C
173 lines
3.4 KiB
C
#include "utils.h"
|
|
|
|
#ifndef HAVE_PTHREADS
|
|
|
|
int main ()
|
|
{
|
|
printf ("Skipped thread-test - pthreads not supported\n");
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
|
|
typedef struct
|
|
{
|
|
int thread_no;
|
|
uint32_t *dst_buf;
|
|
prng_t prng_state;
|
|
} info_t;
|
|
|
|
static const pixman_op_t operators[] =
|
|
{
|
|
PIXMAN_OP_SRC,
|
|
PIXMAN_OP_OVER,
|
|
PIXMAN_OP_ADD,
|
|
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_MULTIPLY,
|
|
PIXMAN_OP_SCREEN,
|
|
PIXMAN_OP_OVERLAY,
|
|
PIXMAN_OP_DARKEN,
|
|
PIXMAN_OP_LIGHTEN,
|
|
PIXMAN_OP_HARD_LIGHT,
|
|
PIXMAN_OP_DIFFERENCE,
|
|
PIXMAN_OP_EXCLUSION,
|
|
};
|
|
|
|
static const pixman_format_code_t formats[] =
|
|
{
|
|
PIXMAN_a8r8g8b8,
|
|
PIXMAN_r5g6b5,
|
|
PIXMAN_a8,
|
|
PIXMAN_a4,
|
|
PIXMAN_a1,
|
|
PIXMAN_b5g6r5,
|
|
PIXMAN_r8g8b8a8,
|
|
PIXMAN_a4r4g4b4
|
|
};
|
|
|
|
#define N_ROUNDS 8192
|
|
|
|
#define RAND_ELT(arr) \
|
|
arr[prng_rand_r(&info->prng_state) % ARRAY_LENGTH (arr)]
|
|
|
|
#define DEST_WIDTH (7)
|
|
|
|
static void *
|
|
thread (void *data)
|
|
{
|
|
info_t *info = data;
|
|
uint32_t crc32 = 0x0;
|
|
uint32_t src_buf[64];
|
|
pixman_image_t *dst_img, *src_img;
|
|
int i;
|
|
|
|
prng_srand_r (&info->prng_state, info->thread_no);
|
|
|
|
for (i = 0; i < N_ROUNDS; ++i)
|
|
{
|
|
pixman_op_t op;
|
|
int rand1, rand2;
|
|
|
|
prng_randmemset_r (&info->prng_state, info->dst_buf,
|
|
DEST_WIDTH * sizeof (uint32_t), 0);
|
|
prng_randmemset_r (&info->prng_state, src_buf,
|
|
sizeof (src_buf), 0);
|
|
|
|
src_img = pixman_image_create_bits (
|
|
RAND_ELT (formats), 4, 4, src_buf, 16);
|
|
dst_img = pixman_image_create_bits (
|
|
RAND_ELT (formats), DEST_WIDTH, 1, info->dst_buf,
|
|
DEST_WIDTH * sizeof (uint32_t));
|
|
|
|
image_endian_swap (src_img);
|
|
image_endian_swap (dst_img);
|
|
|
|
rand2 = prng_rand_r (&info->prng_state) % 4;
|
|
rand1 = prng_rand_r (&info->prng_state) % 4;
|
|
op = RAND_ELT (operators);
|
|
|
|
pixman_image_composite32 (
|
|
op,
|
|
src_img, NULL, dst_img,
|
|
rand1, rand2, 0, 0, 0, 0, DEST_WIDTH, 1);
|
|
|
|
crc32 = compute_crc32_for_image (crc32, dst_img);
|
|
|
|
pixman_image_unref (src_img);
|
|
pixman_image_unref (dst_img);
|
|
}
|
|
|
|
return (void *)(uintptr_t)crc32;
|
|
}
|
|
|
|
static inline uint32_t
|
|
byteswap32 (uint32_t x)
|
|
{
|
|
return ((x & ((uint32_t)0xFF << 24)) >> 24) |
|
|
((x & ((uint32_t)0xFF << 16)) >> 8) |
|
|
((x & ((uint32_t)0xFF << 8)) << 8) |
|
|
((x & ((uint32_t)0xFF << 0)) << 24);
|
|
}
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
uint32_t dest[16 * DEST_WIDTH];
|
|
info_t info[16] = { { 0 } };
|
|
pthread_t threads[16];
|
|
void *retvals[16];
|
|
uint32_t crc32s[16], crc32;
|
|
int i;
|
|
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
info[i].thread_no = i;
|
|
info[i].dst_buf = &dest[i * DEST_WIDTH];
|
|
}
|
|
|
|
for (i = 0; i < 16; ++i)
|
|
pthread_create (&threads[i], NULL, thread, &info[i]);
|
|
|
|
for (i = 0; i < 16; ++i)
|
|
pthread_join (threads[i], &retvals[i]);
|
|
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
crc32s[i] = (uintptr_t)retvals[i];
|
|
|
|
if (is_little_endian())
|
|
crc32s[i] = byteswap32 (crc32s[i]);
|
|
}
|
|
|
|
crc32 = compute_crc32 (0, crc32s, sizeof crc32s);
|
|
|
|
#define EXPECTED 0x82C4D9FB
|
|
|
|
if (crc32 != EXPECTED)
|
|
{
|
|
printf ("thread-test failed. Got checksum 0x%08X, expected 0x%08X\n",
|
|
crc32, EXPECTED);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|