On 15.06.2012 22:40, Søren Sandmann wrote:
Comments on the patch:

- I think the code that generated the tables should be included, and the
   tables generated as part of the build process.

Agree. I have reworked the build to do this.

- We can't break source level compatibility. Instead of adding a new
   field in the formats, how about simply adding

        PIXMAN_TYPE_ARGB_SRGB

   which is #defined to (PIXMAN_TYPE_ARGB | (1<<  5))? That still makes
   it possible to determine quickly whether the format is srgb encoded,
   but doesn't break source compatibility.

Can do. However, there will be a few places then which need to be taught about the new ARGB_SRGB pixel type, unless we also modify the PIXMAN_FORMAT_TYPE to filter out the color space bits. I did this to keep changes minimal. I added the PIXMAN_FORMAT_SPACE macro, however. I adopted your implicit suggestion of splitting the 8-bit type value to 3+5 bits.

- expand/contract seems like the wrong place to do the conversion. It
   makes more sense to me to do it directly in pixman-access.c for the
   format in question. Conceptually, everything pixman does internally is
   with linearly encoded pixels, and so expanding the output of an 8-bit
   fetcher is not subject to gamma conversions.

   Instead, gamma conversions belong as the first thing after fetching
   and the last thing before storing.

   That also means the 8 bit fetcher for this new format would be done by
   (a) fetching wide, gamma-converted pixels, and then (b) contracting
   them to 8 bit without gamma conversion. This loses a lot of
   information of course, but that's why the format is considered "wide".

I have defined the relevant 64-bit fetchers now that do the conversion while loading, and keep my hands off pixman_expand/contract.

- I'm not convinced that we need that pixman_image_get_space()
   accessor. There is already a pixman_image_get_format() that can be
   used to get the information.

Agreed, especially if Adrian Johnson is working on an alternative approach which may prove to be more flexible in the long run. The sRGB-based pixel fetcher and storer is nevertheless an isolated feature with fairly minimal impact to the rest of the code, but if Adrian's work goes in and we stop using sRGB as the primary cairo surface format for applications, instead opting for some 64-bit linear light color space instead, then this work should considered to be superseded by that optimal design.

- The format should be added to the test suite, at the very least the
   "composite" test.

Agree. I did this, and was able to see all composite tests pass. Not entirely without issues, though: because the test constructs all the colors from pixman fills, they will by construction always be linearly specified colors, because fills can not be specified in sRGB presently.

When placed on sRGB surface, the numeric pixel values of course change to reflect the source color's linear light nature. This however changes the quantization errors, and I had to simulate round_color() in sRGB space to get every subtest to pass. Pretty tricky, and I don't think I like the end code too much, but I will nevertheless submit it as it is now.

I believe that there should be ways to construct both fills and gradients in sRGB color space. Fills are simple to do, of course, but gradients are interpolated and this interpolation is presumably linear (I have not read the code). So a gradient constructed from sRGB points would appear to interpolate its colors in linear light when composed to sRGB destination, if we simply transform the gradient's input colors from sRGB to linear light on gradient construction time. This is probably how it should work, anyway.

- There are formatting issues. Please see COPYING.

I'll try to hone the whitespace arrangement to match rest of the code. I think this version attached should be acceptable. Please tell me what you think.

--
Antti
>From 915ff0067812638cd652845d00e6b495a19157e1 Mon Sep 17 00:00:00 2001
From: "Antti S. Lankila" <[email protected]>
Date: Sun, 10 Jun 2012 22:31:38 +0300
Subject: [PATCH] Add support for sRGB surfaces

The 8 bits in type field is split into two 3 and 5-bit wide fields, the
first being the color space of the format, and second the original format
type.

Use of sRGB format triggers wide processing, and the pixel fetch/store functions
handle the relevant conversion between color spaces. Pixman itself is thought to
perform its operations in a linear light color space.

Simple sRGB color blender test can be used to determine if the sRGB processing
works as expected. It adds a #f0f and #0f0 images together with linear alpha
ramp. If the midpoint result is #888 then the blending did not work as expected.
The correct value is approximately #bababa.

sRGB conversion is tabularized.

For sRGB to linear, we are using only 256 values because the expectation is
that all source formats are 8-bit precision only, and therefore any higher
precision would be wasted. If this assumption is broken by future changes,
the table should be recomputed.

For linear to sRGB, it turns out that only 4096 brightness levels were required
to generate all of the 256 sRGB color values, and therefore the lowest 4 bits
are discarded. This causes slight downwards bias in color lookup, but it only
concerns some very dark intensity levels, and as a special case, the
no-op sRGB->linear->sRGB conversion is constructed to be lossless.
---
 demos/Makefile.am       |    4 +-
 demos/srgb-test.c       |   94 ++++++++++
 pixman/Makefile.sources |    6 +
 pixman/make-srgb.pl     |   80 +++++++++
 pixman/pixman-access.c  |   83 ++++++++-
 pixman/pixman-private.h |    3 +-
 pixman/pixman-srgb.c    |  447 +++++++++++++++++++++++++++++++++++++++++++++++
 pixman/pixman-srgb.h    |    9 +
 pixman/pixman.c         |    1 +
 pixman/pixman.h         |   10 +-
 test/composite.c        |   90 ++++++++--
 11 files changed, 809 insertions(+), 18 deletions(-)
 create mode 100644 demos/srgb-test.c
 create mode 100644 pixman/make-srgb.pl
 create mode 100644 pixman/pixman-srgb.c
 create mode 100644 pixman/pixman-srgb.h

diff --git a/demos/Makefile.am b/demos/Makefile.am
index 9aac1f5..d8fb0da 100644
--- a/demos/Makefile.am
+++ b/demos/Makefile.am
@@ -20,7 +20,8 @@ DEMOS =				\
 	trap-test		\
 	tri-test		\
 	quad2quad		\
-	checkerboard
+	checkerboard		\
+	srgb-test
 
 EXTRA_DIST = parrot.c parrot.jpg
 
@@ -35,6 +36,7 @@ convolution_test_SOURCES = convolution-test.c $(GTK_UTILS)
 radial_test_SOURCES = radial-test.c $(GTK_UTILS)
 tri_test_SOURCES = tri-test.c $(GTK_UTILS)
 checkerboard_SOURCES = checkerboard.c $(GTK_UTILS)
+srgb_test_SOURCES = srgb-test.c $(GTK_UTILS)
 
 noinst_PROGRAMS = $(DEMOS)
 
diff --git a/demos/srgb-test.c b/demos/srgb-test.c
new file mode 100644
index 0000000..5ae067c
--- /dev/null
+++ b/demos/srgb-test.c
@@ -0,0 +1,94 @@
+#include <math.h>
+
+#include "pixman.h"
+#include "gtk-utils.h"
+
+static uint32_t
+linear_argb_to_premult_argb (float a,
+			     float r,
+			     float g,
+			     float b)
+{
+    r *= a;
+    g *= a;
+    b *= a;
+    return (uint32_t) (a * 255.0f + 0.5f) << 24
+	 | (uint32_t) (r * 255.0f + 0.5f) << 16
+	 | (uint32_t) (g * 255.0f + 0.5f) << 8
+	 | (uint32_t) (b * 255.0f + 0.5f) << 0;
+}
+
+static float
+lin2srgb (float linear) {
+    if (linear < 0.0031308f) {
+	return linear * 12.92f;
+    } else {
+	return 1.055f * powf (linear, 1.0f/2.4f) - 0.055f;
+    }
+}
+
+static uint32_t
+linear_argb_to_premult_srgb_argb (float a,
+				  float r,
+				  float g,
+				  float b)
+{
+    r = lin2srgb (r * a);
+    g = lin2srgb (g * a);
+    b = lin2srgb (b * a);
+    return (uint32_t) (a * 255.0f + 0.5f) << 24
+	 | (uint32_t) (r * 255.0f + 0.5f) << 16
+	 | (uint32_t) (g * 255.0f + 0.5f) << 8
+	 | (uint32_t) (b * 255.0f + 0.5f) << 0;
+}
+
+int
+main (int argc, char **argv)
+{
+#define WIDTH 400
+#define HEIGHT 200
+    int y, x, p;
+    float alpha;
+ 
+    uint32_t *dest = malloc (WIDTH * HEIGHT * 4);
+    uint32_t *src1 = malloc (WIDTH * HEIGHT * 4);
+    pixman_image_t *dest_img, *src1_img;
+   
+    dest_img = pixman_image_create_bits (PIXMAN_a8r8g8b8_sRGB,
+					 WIDTH, HEIGHT,
+					 dest,
+					 WIDTH * 4);
+    src1_img = pixman_image_create_bits (PIXMAN_a8r8g8b8,
+					 WIDTH, HEIGHT,
+					 src1,
+					 WIDTH * 4);
+
+    for (y = 0; y < HEIGHT; y ++) {
+	p = WIDTH * y;
+	for (x = 0; x < WIDTH; x ++) {
+	     alpha = (float) x / WIDTH;
+	     src1[p + x] = linear_argb_to_premult_argb (alpha, 1, 0, 1);
+	     dest[p + x] = linear_argb_to_premult_srgb_argb (1-alpha, 0, 1, 0);
+	}
+    }
+    
+    pixman_image_composite (PIXMAN_OP_ADD, src1_img, NULL, dest_img,
+			    0, 0, 0, 0, 0, 0, WIDTH, HEIGHT);
+    pixman_image_unref (src1_img);
+    free (src1);
+
+    pixman_image_unref (dest_img);
+
+    /* Now that the picture has been correctly constructed,
+     * we hand it over to our support library as argb which it
+     * knows how to handle (it doesn't understand _sRGB format). */
+    dest_img = pixman_image_create_bits (PIXMAN_a8r8g8b8,
+			 		 WIDTH, HEIGHT,
+					 dest,
+					 WIDTH * 4);
+    show_image (dest_img);
+    pixman_image_unref (dest_img);
+    free (dest);
+    
+    return 0;
+}
diff --git a/pixman/Makefile.sources b/pixman/Makefile.sources
index 11f959d..c30fa39 100644
--- a/pixman/Makefile.sources
+++ b/pixman/Makefile.sources
@@ -22,6 +22,7 @@ libpixman_sources =			\
 	pixman-region16.c		\
 	pixman-region32.c		\
 	pixman-solid-fill.c		\
+	pixman-srgb.c			\
 	pixman-timer.c			\
 	pixman-trap.c			\
 	pixman-utils.c			\
@@ -36,6 +37,7 @@ libpixman_headers =			\
 	pixman-edge-imp.h		\
 	pixman-inlines.h		\
 	pixman-private.h		\
+	pixman-srgb.h			\
 	$(NULL)
 
 BUILT_SOURCES =				\
@@ -43,8 +45,12 @@ BUILT_SOURCES =				\
 	pixman-combine32.h		\
 	pixman-combine64.c		\
 	pixman-combine64.h		\
+	pixman-srgb.c			\
 	$(NULL)
 
+pixman-srgb.c: make-srgb.pl
+	$(PERL) $< > $@ || ($(RM) $@; exit 1)
+
 pixman-combine32.c: pixman-combine.c.template make-combine.pl
 	$(PERL) $(lastword $+) 8 < $< > $@ || ($(RM) $@; exit 1)
 pixman-combine32.h: pixman-combine.h.template make-combine.pl
diff --git a/pixman/make-srgb.pl b/pixman/make-srgb.pl
new file mode 100644
index 0000000..f5e22e6
--- /dev/null
+++ b/pixman/make-srgb.pl
@@ -0,0 +1,80 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my @lin2srgb;
+for my $x (0 .. 4095) {
+	my $a = $x / 4095.0;
+	if ($a < 0.0031308) {
+		$a *= 12.92;
+	} else {
+		$a = 1.055 * $a ** (1/2.4) - 0.055;
+	}
+	$a = int($a * 255.0 + 0.5);
+	push @lin2srgb, $a;
+}
+
+my @srgb2lin;
+for my $x (0 .. 255) {
+	my $a = $x / 255.0;
+	if ($a < 0.04045) {
+		$a /= 12.92;
+	} else {
+		$a = (($a + 0.055) / 1.055) ** 2.4
+	}
+	$a = int($a * 65535.0 + 0.5);
+	push @srgb2lin, $a;
+}
+
+# Ensure that we have a lossless sRGB and back conversion loop.
+# some of the darkest shades need a little bias -- maximum is just
+# 5 increments out of 16. This gives us useful property with
+# least amount of error in the srgb2lin table, and keeps the actual
+# table lookup in the other direction as simple as possible.
+for my $x (0 .. $#srgb2lin) {
+	my $add = 0;
+	while (1) {
+		my $lin = $srgb2lin[$x];
+		my $x2 = $lin2srgb[$lin >> 4];
+		if ($x == $x2) {
+			last;
+		}
+		# Add slight bias to this component until it rounds correctly
+		$srgb2lin[$x] ++;
+		$add ++;
+	}
+	die "Too many adds at $x" if $add > 5;
+}
+
+print <<"PROLOG";
+/* WARNING: This file is generated by $0.
+   Please edit that file instead of this one. */
+
+#include <stdint.h>
+#include "pixman-srgb.h"
+
+PROLOG
+print "const uint8_t lin2srgb[" . @lin2srgb . "] = {\n";
+for my $x (0 .. $#lin2srgb) {
+	if (($x % 10) == 0) {
+		print "\t";
+	}
+	print sprintf("%d, ", $lin2srgb[$x]);
+	if (($x % 10) == 9) {
+		print "\n";
+	}
+}
+print "\n};\n";
+print "\n";
+print "const uint16_t srgb2lin[" . @srgb2lin . "] = {\n";
+for my $x (0 .. $#srgb2lin) {
+	if (($x % 10) == 0) {
+		print "\t";
+	}
+	print sprintf("%d, ", $srgb2lin[$x]);
+	if (($x % 10) == 9) {
+		print "\n";
+	}
+}
+print "\n};\n";
+
diff --git a/pixman/pixman-access.c b/pixman/pixman-access.c
index 6743887..98cddbe 100644
--- a/pixman/pixman-access.c
+++ b/pixman/pixman-access.c
@@ -32,8 +32,9 @@
 #include <string.h>
 #include <assert.h>
 
-#include "pixman-private.h"
 #include "pixman-accessor.h"
+#include "pixman-private.h"
+#include "pixman-srgb.h"
 
 #define CONVERT_RGB24_TO_Y15(s)						\
     (((((s) >> 16) & 0xff) * 153 +					\
@@ -967,6 +968,37 @@ store_scanline_generic_64 (bits_image_t *  image,
     free (argb8_pixels);
 }
 
+static void
+store_scanline_generic_64_sRGB (bits_image_t *  image,
+                                int             x,
+                                int             y,
+                                int             width,
+                                const uint32_t *values32)
+{
+    assert (image->common.type == BITS);
+
+    uint32_t *argb8_pixels;
+    uint64_t *values64 = (uint64_t *) values32;
+    uint64_t tmp;
+    int i;
+    
+    argb8_pixels = pixman_malloc_ab (width, sizeof(uint32_t));
+    if (!argb8_pixels)
+	return;
+   
+    for (i = 0; i < width; i ++) {
+	tmp = values64[i];
+	argb8_pixels[i] = (uint32_t)          (tmp >> 56)          << 24	/* bits 63 .. 56 */
+			| (uint32_t) lin2srgb[(tmp >> 36) & 0xfff] << 16	/* bits 47 .. 36 */
+			| (uint32_t) lin2srgb[(tmp >> 20) & 0xfff] <<  8	/* bits 31 .. 20 */
+			| (uint32_t) lin2srgb[(tmp >>  4) & 0xfff] <<  0;	/* bits 15 ..  4 */
+    }
+
+    image->store_scanline_32 (image, x, y, width, argb8_pixels);
+    
+    free (argb8_pixels);
+}
+
 /* Despite the type, this function expects both buffer
  * and mask to be uint64_t
  */
@@ -1000,6 +1032,36 @@ fetch_scanline_generic_64 (pixman_image_t *image,
     pixman_expand ((uint64_t *)buffer, buffer, format, width);
 }
 
+static void
+fetch_scanline_generic_64_sRGB (pixman_image_t *image,
+                                int             x,
+                                int             y,
+                                int             width,
+                                uint32_t *      buffer,
+                                const uint32_t *mask)
+{
+    union {
+	uint32_t *src;
+	uint64_t *dst;
+    } ubuf;
+    int i;
+    uint32_t tmp;
+    ubuf.src = buffer;
+
+    /* Fetch the pixels into the first half of buffer and then expand them in
+     * place.
+     */
+    image->bits.fetch_scanline_32 (image, x, y, width, ubuf.src, NULL);
+
+    for (i = width - 1; i >= 0; i --) {
+	tmp = ubuf.src[i];
+	ubuf.dst[i] = (uint64_t)         ((tmp >> 24) * 257)  << 48
+		    | (uint64_t) srgb2lin[(tmp >> 16) & 0xff] << 32
+		    | (uint64_t) srgb2lin[(tmp >>  8) & 0xff] << 16
+		    | (uint64_t) srgb2lin[(tmp >>  0) & 0xff] <<  0;
+    }
+}
+
 /* Despite the type, this function expects a uint64_t *buffer */
 static uint64_t
 fetch_pixel_generic_64 (bits_image_t *image,
@@ -1027,6 +1089,18 @@ fetch_pixel_generic_64 (bits_image_t *image,
     return result;
 }
 
+static uint64_t
+fetch_pixel_generic_64_sRGB (bits_image_t *image,
+			     int	   offset,
+			     int           line)
+{
+    uint32_t tmp = image->fetch_pixel_32 (image, offset, line);
+    return (uint64_t)         ((tmp >> 24) * 257)  << 48
+	 | (uint64_t) srgb2lin[(tmp >> 16) & 0xff] << 32
+	 | (uint64_t) srgb2lin[(tmp >>  8) & 0xff] << 16
+	 | (uint64_t) srgb2lin[(tmp >>  0) & 0xff] <<  0;
+}
+
 /*
  * XXX: The transformed fetch path only works at 32-bpp so far.  When all
  * paths have wide versions, this can be removed.
@@ -1079,6 +1153,13 @@ static const format_info_t accessors[] =
     FORMAT_INFO (r8g8b8x8),
     FORMAT_INFO (x14r6g6b6),
 
+/* sRGB formats */
+  { PIXMAN_a8r8g8b8_sRGB,
+    fetch_scanline_a8r8g8b8,
+    fetch_scanline_generic_64_sRGB,
+    fetch_pixel_a8r8g8b8, fetch_pixel_generic_64_sRGB,
+    store_scanline_a8r8g8b8, store_scanline_generic_64_sRGB },
+
 /* 24bpp formats */
     FORMAT_INFO (r8g8b8),
     FORMAT_INFO (b8g8r8),
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index 72e3b4f..38f48d9 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -856,7 +856,8 @@ pixman_list_move_to_front (pixman_list_t *list, pixman_link_t *link)
     (PIXMAN_FORMAT_A (f) > 8 ||						\
      PIXMAN_FORMAT_R (f) > 8 ||						\
      PIXMAN_FORMAT_G (f) > 8 ||						\
-     PIXMAN_FORMAT_B (f) > 8)
+     PIXMAN_FORMAT_B (f) > 8 ||						\
+     PIXMAN_FORMAT_SPACE (f) != PIXMAN_SPACE_LINEAR)
 
 #ifdef WORDS_BIGENDIAN
 #   define SCREEN_SHIFT_LEFT(x,n)	((x) << (n))
diff --git a/pixman/pixman-srgb.c b/pixman/pixman-srgb.c
new file mode 100644
index 0000000..66b52cc
--- /dev/null
+++ b/pixman/pixman-srgb.c
@@ -0,0 +1,447 @@
+/* WARNING: This file is generated by make-srgb.pl.
+   Please edit that file instead of this one. */
+
+#include <stdint.h>
+#include "pixman-srgb.h"
+
+const uint8_t lin2srgb[4096] = {
+	0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 
+	8, 9, 10, 10, 11, 12, 13, 13, 14, 15, 
+	15, 16, 16, 17, 18, 18, 19, 19, 20, 20, 
+	21, 21, 22, 22, 23, 23, 23, 24, 24, 25, 
+	25, 25, 26, 26, 27, 27, 27, 28, 28, 29, 
+	29, 29, 30, 30, 30, 31, 31, 31, 32, 32, 
+	32, 33, 33, 33, 34, 34, 34, 34, 35, 35, 
+	35, 36, 36, 36, 37, 37, 37, 37, 38, 38, 
+	38, 38, 39, 39, 39, 40, 40, 40, 40, 41, 
+	41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 
+	43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 
+	46, 46, 46, 46, 46, 47, 47, 47, 47, 48, 
+	48, 48, 48, 48, 49, 49, 49, 49, 49, 50, 
+	50, 50, 50, 50, 51, 51, 51, 51, 51, 52, 
+	52, 52, 52, 52, 53, 53, 53, 53, 53, 54, 
+	54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 
+	56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 
+	57, 58, 58, 58, 58, 58, 58, 59, 59, 59, 
+	59, 59, 59, 60, 60, 60, 60, 60, 60, 61, 
+	61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 
+	62, 63, 63, 63, 63, 63, 63, 64, 64, 64, 
+	64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 
+	66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 
+	67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 
+	68, 69, 69, 69, 69, 69, 69, 69, 70, 70, 
+	70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 
+	71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 
+	73, 73, 73, 73, 73, 73, 73, 74, 74, 74, 
+	74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 
+	75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 
+	77, 77, 77, 77, 77, 77, 77, 77, 78, 78, 
+	78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 
+	79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 
+	80, 80, 80, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 82, 82, 82, 82, 82, 82, 82, 82, 
+	83, 83, 83, 83, 83, 83, 83, 83, 83, 84, 
+	84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 
+	85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 
+	86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 
+	87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 
+	88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 
+	89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 
+	90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 
+	91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 
+	92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 
+	93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 
+	94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 
+	95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 
+	96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 
+	97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 
+	98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 
+	99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 
+	100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 
+	101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 
+	102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 
+	103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 
+	103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 
+	104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 
+	105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 
+	106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 
+	107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 
+	108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 
+	108, 109, 109, 109, 109, 109, 109, 109, 109, 109, 
+	109, 109, 109, 110, 110, 110, 110, 110, 110, 110, 
+	110, 110, 110, 110, 110, 111, 111, 111, 111, 111, 
+	111, 111, 111, 111, 111, 111, 111, 111, 112, 112, 
+	112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 
+	113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 
+	113, 113, 113, 114, 114, 114, 114, 114, 114, 114, 
+	114, 114, 114, 114, 114, 114, 115, 115, 115, 115, 
+	115, 115, 115, 115, 115, 115, 115, 115, 115, 116, 
+	116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 
+	116, 116, 117, 117, 117, 117, 117, 117, 117, 117, 
+	117, 117, 117, 117, 117, 117, 118, 118, 118, 118, 
+	118, 118, 118, 118, 118, 118, 118, 118, 118, 119, 
+	119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 
+	119, 119, 119, 120, 120, 120, 120, 120, 120, 120, 
+	120, 120, 120, 120, 120, 120, 120, 121, 121, 121, 
+	121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 
+	122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 
+	122, 122, 122, 122, 122, 123, 123, 123, 123, 123, 
+	123, 123, 123, 123, 123, 123, 123, 123, 123, 124, 
+	124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 
+	124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 
+	125, 125, 125, 125, 125, 125, 125, 125, 126, 126, 
+	126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 
+	126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 
+	127, 127, 127, 127, 127, 127, 127, 128, 128, 128, 
+	128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 
+	128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 
+	129, 129, 129, 129, 129, 129, 129, 130, 130, 130, 
+	130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 
+	130, 130, 131, 131, 131, 131, 131, 131, 131, 131, 
+	131, 131, 131, 131, 131, 131, 131, 131, 132, 132, 
+	132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 
+	132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 
+	133, 133, 133, 133, 133, 133, 133, 133, 133, 134, 
+	134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 
+	134, 134, 134, 134, 134, 135, 135, 135, 135, 135, 
+	135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 
+	135, 136, 136, 136, 136, 136, 136, 136, 136, 136, 
+	136, 136, 136, 136, 136, 136, 136, 137, 137, 137, 
+	137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 
+	137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 
+	138, 138, 138, 138, 138, 138, 138, 138, 138, 139, 
+	139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 
+	139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 
+	140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 
+	140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 
+	141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 
+	142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 
+	142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 
+	143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 
+	143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 
+	144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 
+	144, 145, 145, 145, 145, 145, 145, 145, 145, 145, 
+	145, 145, 145, 145, 145, 145, 145, 145, 145, 146, 
+	146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 
+	146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 
+	147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 
+	147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 
+	148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 
+	148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 
+	149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 
+	150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 
+	150, 150, 150, 150, 150, 150, 150, 150, 150, 151, 
+	151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 
+	151, 151, 151, 151, 151, 151, 151, 152, 152, 152, 
+	152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 
+	152, 152, 152, 152, 152, 152, 153, 153, 153, 153, 
+	153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 
+	153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 
+	154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 
+	154, 154, 154, 155, 155, 155, 155, 155, 155, 155, 
+	155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 
+	155, 155, 156, 156, 156, 156, 156, 156, 156, 156, 
+	156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 
+	156, 156, 157, 157, 157, 157, 157, 157, 157, 157, 
+	157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
+	157, 158, 158, 158, 158, 158, 158, 158, 158, 158, 
+	158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 
+	159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 
+	159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 
+	160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 
+	160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 
+	161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 
+	162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 
+	162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 
+	163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 
+	163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 
+	164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 
+	164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 
+	164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 
+	165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 
+	165, 165, 166, 166, 166, 166, 166, 166, 166, 166, 
+	166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 
+	166, 166, 167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 168, 168, 168, 168, 168, 168, 168, 
+	168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 
+	168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 
+	169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 
+	169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 
+	170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 
+	170, 170, 170, 170, 170, 170, 170, 171, 171, 171, 
+	171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 
+	171, 171, 171, 171, 171, 171, 171, 171, 171, 172, 
+	172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 
+	172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 
+	172, 173, 173, 173, 173, 173, 173, 173, 173, 173, 
+	173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 
+	173, 173, 173, 174, 174, 174, 174, 174, 174, 174, 
+	174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 
+	174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 
+	175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 
+	175, 175, 175, 175, 175, 175, 175, 176, 176, 176, 
+	176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 
+	176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 
+	177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 
+	177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 
+	177, 177, 178, 178, 178, 178, 178, 178, 178, 178, 
+	178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 
+	178, 178, 178, 178, 178, 179, 179, 179, 179, 179, 
+	179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 
+	179, 179, 179, 179, 179, 179, 179, 179, 180, 180, 
+	180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 
+	180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 
+	180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 
+	181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 
+	181, 181, 181, 181, 182, 182, 182, 182, 182, 182, 
+	182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 
+	182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 
+	183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 
+	183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 
+	183, 184, 184, 184, 184, 184, 184, 184, 184, 184, 
+	184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 
+	184, 184, 184, 184, 184, 185, 185, 185, 185, 185, 
+	185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 
+	185, 185, 185, 185, 185, 185, 185, 185, 185, 186, 
+	186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 
+	186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 
+	186, 186, 186, 187, 187, 187, 187, 187, 187, 187, 
+	187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 
+	187, 187, 187, 187, 187, 187, 187, 187, 188, 188, 
+	188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 
+	188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 
+	188, 188, 189, 189, 189, 189, 189, 189, 189, 189, 
+	189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 
+	189, 189, 189, 189, 189, 189, 189, 190, 190, 190, 
+	190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 
+	190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 
+	190, 190, 191, 191, 191, 191, 191, 191, 191, 191, 
+	191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+	191, 191, 191, 191, 191, 191, 192, 192, 192, 192, 
+	192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 
+	192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 
+	192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 
+	193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 
+	193, 193, 193, 193, 193, 193, 193, 194, 194, 194, 
+	194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 
+	194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 
+	194, 194, 195, 195, 195, 195, 195, 195, 195, 195, 
+	195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 
+	195, 195, 195, 195, 195, 195, 195, 195, 196, 196, 
+	196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 
+	196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 
+	196, 196, 196, 196, 197, 197, 197, 197, 197, 197, 
+	197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 
+	197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 
+	198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 
+	198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 
+	198, 198, 198, 198, 198, 198, 199, 199, 199, 199, 
+	199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 
+	199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 
+	199, 199, 200, 200, 200, 200, 200, 200, 200, 200, 
+	200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 
+	200, 200, 200, 200, 200, 200, 200, 200, 200, 201, 
+	201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 
+	201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 
+	201, 201, 201, 201, 201, 201, 202, 202, 202, 202, 
+	202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 
+	202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 
+	202, 202, 202, 203, 203, 203, 203, 203, 203, 203, 
+	203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 
+	203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 
+	204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 
+	204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 
+	204, 204, 204, 204, 204, 204, 204, 205, 205, 205, 
+	205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 
+	205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 
+	205, 205, 205, 205, 206, 206, 206, 206, 206, 206, 
+	206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 
+	206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 
+	206, 206, 207, 207, 207, 207, 207, 207, 207, 207, 
+	207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 
+	207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 
+	208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 
+	208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 
+	208, 208, 208, 208, 208, 208, 208, 209, 209, 209, 
+	209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 
+	209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 
+	209, 209, 209, 209, 209, 209, 210, 210, 210, 210, 
+	210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 
+	210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 
+	210, 210, 210, 210, 211, 211, 211, 211, 211, 211, 
+	211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 
+	211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 
+	211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 
+	212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 
+	212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 
+	212, 213, 213, 213, 213, 213, 213, 213, 213, 213, 
+	213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 
+	213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 
+	214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 
+	214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 
+	214, 214, 214, 214, 214, 214, 214, 214, 214, 215, 
+	215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 
+	215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 
+	215, 215, 215, 215, 215, 215, 215, 215, 216, 216, 
+	216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 
+	216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 
+	216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 
+	217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 
+	217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 
+	217, 217, 217, 217, 217, 217, 217, 218, 218, 218, 
+	218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 
+	218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 
+	218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 
+	219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 
+	219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 
+	219, 219, 219, 219, 219, 219, 220, 220, 220, 220, 
+	220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 
+	220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 
+	220, 220, 220, 220, 220, 220, 221, 221, 221, 221, 
+	221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 
+	221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 
+	221, 221, 221, 221, 221, 221, 221, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 223, 223, 223, 
+	223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 
+	223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 
+	223, 223, 223, 223, 223, 223, 223, 223, 224, 224, 
+	224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 
+	224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 
+	224, 224, 224, 224, 224, 224, 224, 224, 225, 225, 
+	225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 
+	225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 
+	225, 225, 225, 225, 225, 225, 225, 225, 225, 226, 
+	226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 
+	226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 
+	226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 
+	227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 
+	227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 
+	227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 
+	227, 227, 228, 228, 228, 228, 228, 228, 228, 228, 
+	228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 
+	228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 
+	228, 228, 228, 229, 229, 229, 229, 229, 229, 229, 
+	229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 
+	229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 
+	229, 229, 229, 229, 229, 230, 230, 230, 230, 230, 
+	230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 
+	230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 
+	230, 230, 230, 230, 230, 230, 230, 231, 231, 231, 
+	231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 
+	231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 
+	231, 231, 231, 231, 231, 231, 231, 231, 231, 232, 
+	232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 
+	232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 
+	232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 
+	232, 233, 233, 233, 233, 233, 233, 233, 233, 233, 
+	233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 
+	233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 
+	233, 233, 233, 233, 234, 234, 234, 234, 234, 234, 
+	234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 
+	234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 
+	234, 234, 234, 234, 234, 234, 235, 235, 235, 235, 
+	235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 
+	235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 
+	235, 235, 235, 235, 235, 235, 235, 235, 235, 236, 
+	236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 
+	236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 
+	236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 
+	236, 236, 237, 237, 237, 237, 237, 237, 237, 237, 
+	237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 
+	237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 
+	237, 237, 237, 237, 237, 238, 238, 238, 238, 238, 
+	238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 
+	238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 
+	238, 238, 238, 238, 238, 238, 238, 238, 239, 239, 
+	239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 
+	239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 
+	239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 
+	239, 239, 240, 240, 240, 240, 240, 240, 240, 240, 
+	240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 
+	240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 
+	240, 240, 240, 240, 240, 240, 241, 241, 241, 241, 
+	241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 
+	241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 
+	241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 
+	242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 243, 243, 243, 243, 243, 243, 
+	243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 
+	243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 
+	243, 243, 243, 243, 243, 243, 243, 243, 244, 244, 
+	244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 
+	244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 
+	244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 
+	244, 244, 245, 245, 245, 245, 245, 245, 245, 245, 
+	245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 
+	245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 
+	245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 
+	246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 
+	246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 
+	246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 
+	246, 246, 247, 247, 247, 247, 247, 247, 247, 247, 
+	247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 
+	247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 
+	247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 
+	248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 
+	248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 
+	248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 
+	248, 248, 249, 249, 249, 249, 249, 249, 249, 249, 
+	249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 
+	249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 
+	249, 249, 249, 249, 249, 249, 249, 250, 250, 250, 
+	250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 
+	250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 
+	250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 
+	250, 250, 250, 251, 251, 251, 251, 251, 251, 251, 
+	251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 
+	251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 
+	251, 251, 251, 251, 251, 251, 251, 251, 251, 252, 
+	252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 
+	252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 
+	252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 
+	252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 
+	253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 
+	253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 
+	253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 
+	253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 
+	254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 
+	254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 
+	254, 254, 254, 254, 254, 254, 254, 255, 255, 255, 
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
+	255, 255, 255, 255, 255, 255, 
+};
+
+const uint16_t srgb2lin[256] = {
+	0, 20, 40, 64, 80, 99, 119, 144, 160, 179, 
+	199, 224, 241, 264, 288, 313, 340, 368, 396, 427, 
+	458, 491, 526, 562, 599, 637, 677, 718, 761, 805, 
+	851, 898, 947, 997, 1048, 1101, 1156, 1212, 1270, 1330, 
+	1391, 1453, 1517, 1583, 1651, 1720, 1790, 1863, 1937, 2013, 
+	2090, 2170, 2250, 2333, 2418, 2504, 2592, 2681, 2773, 2866, 
+	2961, 3058, 3157, 3258, 3360, 3464, 3570, 3678, 3788, 3900, 
+	4014, 4129, 4247, 4366, 4488, 4611, 4736, 4864, 4993, 5124, 
+	5257, 5392, 5530, 5669, 5810, 5953, 6099, 6246, 6395, 6547, 
+	6700, 6856, 7014, 7174, 7335, 7500, 7666, 7834, 8004, 8177, 
+	8352, 8528, 8708, 8889, 9072, 9258, 9445, 9635, 9828, 10022, 
+	10219, 10417, 10619, 10822, 11028, 11235, 11446, 11658, 11873, 12090, 
+	12309, 12530, 12754, 12980, 13209, 13440, 13673, 13909, 14146, 14387, 
+	14629, 14874, 15122, 15371, 15623, 15878, 16135, 16394, 16656, 16920, 
+	17187, 17456, 17727, 18001, 18277, 18556, 18837, 19121, 19407, 19696, 
+	19987, 20281, 20577, 20876, 21177, 21481, 21787, 22096, 22407, 22721, 
+	23038, 23357, 23678, 24002, 24329, 24658, 24990, 25325, 25662, 26001, 
+	26344, 26688, 27036, 27386, 27739, 28094, 28452, 28813, 29176, 29542, 
+	29911, 30282, 30656, 31033, 31412, 31794, 32179, 32567, 32957, 33350, 
+	33745, 34143, 34544, 34948, 35355, 35764, 36176, 36591, 37008, 37429, 
+	37852, 38278, 38706, 39138, 39572, 40009, 40449, 40891, 41337, 41785, 
+	42236, 42690, 43147, 43606, 44069, 44534, 45002, 45473, 45947, 46423, 
+	46903, 47385, 47871, 48359, 48850, 49344, 49841, 50341, 50844, 51349, 
+	51858, 52369, 52884, 53401, 53921, 54445, 54971, 55500, 56032, 56567, 
+	57105, 57646, 58190, 58737, 59287, 59840, 60396, 60955, 61517, 62082, 
+	62650, 63221, 63795, 64372, 64952, 65535, 
+};
diff --git a/pixman/pixman-srgb.h b/pixman/pixman-srgb.h
new file mode 100644
index 0000000..859465b
--- /dev/null
+++ b/pixman/pixman-srgb.h
@@ -0,0 +1,9 @@
+#ifndef PIXMAN_SRGB__
+#define PIXMAN_SRGB__
+
+#include <stdint.h>
+
+extern const uint8_t lin2srgb[4096];
+extern const uint16_t srgb2lin[256];
+
+#endif
diff --git a/pixman/pixman.c b/pixman/pixman.c
index 0137c3c..994ef38 100644
--- a/pixman/pixman.c
+++ b/pixman/pixman.c
@@ -1017,6 +1017,7 @@ pixman_format_supported_source (pixman_format_code_t format)
     case PIXMAN_a2r10g10b10:
     case PIXMAN_x2r10g10b10:
     case PIXMAN_a8r8g8b8:
+    case PIXMAN_a8r8g8b8_sRGB:
     case PIXMAN_x8r8g8b8:
     case PIXMAN_a8b8g8r8:
     case PIXMAN_x8b8g8r8:
diff --git a/pixman/pixman.h b/pixman/pixman.h
index 7233ceb..e84342b 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -631,7 +631,8 @@ struct pixman_indexed
 					 ((b)))
 
 #define PIXMAN_FORMAT_BPP(f)	(((f) >> 24)       )
-#define PIXMAN_FORMAT_TYPE(f)	(((f) >> 16) & 0xff)
+#define PIXMAN_FORMAT_SPACE(f)	(((f) >> 21) & 0x07)
+#define PIXMAN_FORMAT_TYPE(f)	(((f) >> 16) & 0x1f)
 #define PIXMAN_FORMAT_A(f)	(((f) >> 12) & 0x0f)
 #define PIXMAN_FORMAT_R(f)	(((f) >>  8) & 0x0f)
 #define PIXMAN_FORMAT_G(f)	(((f) >>  4) & 0x0f)
@@ -643,6 +644,9 @@ struct pixman_indexed
 				 PIXMAN_FORMAT_G(f) +	\
 				 PIXMAN_FORMAT_B(f))
 
+#define PIXMAN_SPACE_LINEAR	0
+#define PIXMAN_SPACE_SRGB	1
+
 #define PIXMAN_TYPE_OTHER	0
 #define PIXMAN_TYPE_A		1
 #define PIXMAN_TYPE_ARGB	2
@@ -653,6 +657,7 @@ struct pixman_indexed
 #define PIXMAN_TYPE_YV12	7
 #define PIXMAN_TYPE_BGRA	8
 #define PIXMAN_TYPE_RGBA	9
+#define PIXMAN_TYPE_ARGB_SRGB	(PIXMAN_TYPE_ARGB | (PIXMAN_SPACE_SRGB << 5))
 
 #define PIXMAN_FORMAT_COLOR(f)				\
 	(PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_ARGB ||	\
@@ -676,6 +681,9 @@ typedef enum {
     PIXMAN_x2b10g10r10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,0,10,10,10),
     PIXMAN_a2b10g10r10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,2,10,10,10),
 
+/* sRGB formats */
+    PIXMAN_a8r8g8b8_sRGB = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB_SRGB,8,8,8,8),
+
 /* 24bpp formats */
     PIXMAN_r8g8b8 =	 PIXMAN_FORMAT(24,PIXMAN_TYPE_ARGB,0,8,8,8),
     PIXMAN_b8g8r8 =	 PIXMAN_FORMAT(24,PIXMAN_TYPE_ABGR,0,8,8,8),
diff --git a/test/composite.c b/test/composite.c
index bdecd75..5881bf9 100644
--- a/test/composite.c
+++ b/test/composite.c
@@ -50,6 +50,26 @@ static const color_t colors[] =
     { 0.5, 0.0, 0.0, 0.5 },
 };
 
+static double
+srgb_to_linear (double c)
+{
+    if (c <= 0.04045) {
+	return c / 12.92;
+    } else {
+	return powf ((c + 0.055) / 1.055, 2.4);
+    }
+}
+
+static double
+linear_to_srgb (double c)
+{
+    if (c <= 0.0031308) {
+	return c * 12.92;
+    } else {
+	return 1.055 * powf (c, 1.0/2.4) - 0.055;
+    }
+}
+
 static uint16_t
 _color_double_to_short (double d)
 {
@@ -99,6 +119,9 @@ static const format_t formats[] =
     P(x2b10g10r10),
     P(a2r10g10b10),
     P(a2b10g10r10),
+    
+    /* sRGB formats */
+    P(a8r8g8b8_sRGB),
 
     /* 24 bpp formats */
     P(r8g8b8),
@@ -524,17 +547,8 @@ composite_test (image_t *dst,
 		pixman_bool_t component_alpha,
 		int testno)
 {
-    pixman_color_t fill;
     color_t expected, tdst, tsrc, tmsk;
     pixel_checker_t checker;
-    pixman_image_t *solid;
-
-    /* Initialize dst */
-    compute_pixman_color (dst->color, &fill);
-    solid = pixman_image_create_solid_fill (&fill);
-    pixman_image_composite32 (PIXMAN_OP_SRC, solid, NULL, dst->image,
-			      0, 0, 0, 0, 0, 0, dst->size, dst->size);
-    pixman_image_unref (solid);
 
     if (mask)
     {
@@ -553,17 +567,47 @@ composite_test (image_t *dst,
     }
 
     tdst = *dst->color;
-    round_color (dst->format->format, &tdst);
-
     tsrc = *src->color;
-    if (src->size)
-	round_color (src->format->format, &tsrc);
 
     if (mask)
     {
 	tmsk = *mask->color;
-	if (mask->size)
+    }
+
+    /* 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_SPACE (src->format->format) == PIXMAN_SPACE_SRGB) {
+	    tsrc.r = linear_to_srgb (tsrc.r);
+	    tsrc.g = linear_to_srgb (tsrc.g);
+	    tsrc.b = linear_to_srgb (tsrc.b);
+	    round_color (src->format->format, &tsrc);
+	    tsrc.r = srgb_to_linear (tsrc.r);
+	    tsrc.g = srgb_to_linear (tsrc.g);
+	    tsrc.b = srgb_to_linear (tsrc.b);
+	} else {
+	    round_color (src->format->format, &tsrc);
+	}
+    }
+
+    if (mask && mask->size) {
+	if (PIXMAN_FORMAT_SPACE (mask->format->format) == PIXMAN_SPACE_SRGB) {
+	    tmsk.r = linear_to_srgb (tmsk.r);
+	    tmsk.g = linear_to_srgb (tmsk.g);
+	    tmsk.b = linear_to_srgb (tmsk.b);
+	    round_color (mask->format->format, &tmsk);
+	    tmsk.r = srgb_to_linear (tmsk.r);
+	    tmsk.g = srgb_to_linear (tmsk.g);
+	    tmsk.b = srgb_to_linear (tmsk.b);
+	} else {
 	    round_color (mask->format->format, &tmsk);
+	}
+    }
+
+    if (mask) {
 	if (component_alpha && PIXMAN_FORMAT_R (mask->format->format) == 0)
 	{
 	    /* Ax component-alpha masks expand alpha into
@@ -573,6 +617,18 @@ composite_test (image_t *dst,
 	}
     }
 
+    if (PIXMAN_FORMAT_SPACE (dst->format->format) == PIXMAN_SPACE_SRGB) {
+	tdst.r = linear_to_srgb (tdst.r);
+	tdst.g = linear_to_srgb (tdst.g);
+	tdst.b = linear_to_srgb (tdst.b);
+    	round_color (dst->format->format, &tdst);
+	tdst.r = srgb_to_linear (tdst.r);
+	tdst.g = srgb_to_linear (tdst.g);
+	tdst.b = srgb_to_linear (tdst.b);
+    } else {
+    	round_color (dst->format->format, &tdst);
+    }
+
     do_composite (op->op,
 		  &tsrc,
 		  mask? &tmsk : NULL,
@@ -580,6 +636,12 @@ composite_test (image_t *dst,
 		  &expected,
 		  component_alpha);
 
+    if (PIXMAN_FORMAT_SPACE (dst->format->format) == PIXMAN_SPACE_SRGB) {
+	expected.r = linear_to_srgb (expected.r);
+	expected.g = linear_to_srgb (expected.g);
+	expected.b = linear_to_srgb (expected.b);
+    }
+
     pixel_checker_init (&checker, dst->format->format);
 
     if (!pixel_checker_check (&checker, get_value (dst->image), &expected))
-- 
1.7.9.5

_______________________________________________
Pixman mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/pixman

Reply via email to