summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntti S. Lankila <alankila@bel.fi>2012-07-29 21:46:58 +0300
committerSøren Sandmann Pedersen <ssp@redhat.com>2012-07-30 15:37:26 -0400
commit7460457f80b1482338318f0ddcdf5311659fae7b (patch)
tree7004b38e2b38b02034149ebf559357d18a5c2059
parent1dcca0f7ae64e9a96f2feba85dd728c636744009 (diff)
Add support for sRGB surfaces
sRGB format is defined as a new format type, PIXMAN_TYPE_ARGB_SRGB. One form of this type is provided, PIXMAN_a8r8g8b8_sRGB. Use of an sRGB format triggers wide processing, and the pixel fetch/store functions handle the relevant conversion between color spaces. Pixman itself is thought to compose in the linearized sRGB color space. sRGB conversion is tabularized. For sRGB to linear, we are using only 256 values because the current source format uses 8 bits per component precision. For linear to sRGB, it turns out that only 4096 brightness levels are required to generate all of the 256 sRGB color values, and therefore only 12 bits per component are considered during store. As a special case, a no-op sRGB->linear->sRGB conversion is constructed to be lossless by adjusting the sRGB->linear conversion table where necessary.
-rw-r--r--.gitignore1
-rw-r--r--pixman/Makefile.sources5
-rw-r--r--pixman/make-srgb.pl112
-rw-r--r--pixman/pixman-access.c134
-rw-r--r--pixman/pixman-image.c3
-rw-r--r--pixman/pixman-private.h15
-rw-r--r--pixman/pixman.c1
-rw-r--r--pixman/pixman.h4
8 files changed, 272 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index b9853b1..3cdf3c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@ pixman/pixman-combine32.c
pixman/pixman-combine32.h
pixman/pixman-combine64.c
pixman/pixman-combine64.h
+pixman/pixman-srgb.c
pixman/pixman-version.h
test/a1-trap-test
test/affine-test
diff --git a/pixman/Makefile.sources b/pixman/Makefile.sources
index 6472994..cf7040f 100644
--- a/pixman/Makefile.sources
+++ b/pixman/Makefile.sources
@@ -25,6 +25,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 \
@@ -46,8 +47,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..ebde2ea
--- /dev/null
+++ b/pixman/make-srgb.pl
@@ -0,0 +1,112 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+sub linear_to_srgb
+{
+ my ($c) = @_;
+
+ if ($c < 0.0031308)
+ {
+ return $c * 12.92;
+ }
+ else
+ {
+ return 1.055 * $c ** (1.0/2.4) - 0.055;
+ }
+}
+
+sub srgb_to_linear
+{
+ my ($c) = @_;
+
+ if ($c < 0.04045)
+ {
+ return $c / 12.92;
+ }
+ else
+ {
+ return (($c + 0.055) / 1.055) ** 2.4
+ }
+}
+
+my @linear_to_srgb;
+for my $linear (0 .. 4095)
+{
+ my $srgb = int(linear_to_srgb($linear / 4095.0) * 255.0 + 0.5);
+ push @linear_to_srgb, $srgb;
+}
+
+my @srgb_to_linear;
+for my $srgb (0 .. 255)
+{
+ my $linear = int(srgb_to_linear($srgb / 255.0) * 65535.0 + 0.5);
+ push @srgb_to_linear, $linear;
+}
+
+# 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 sRGB-to-linear table, and keeps the actual
+# table lookup in the other direction as simple as possible.
+for my $srgb (0 .. $#srgb_to_linear)
+{
+ my $add = 0;
+ while (1)
+ {
+ my $linear = $srgb_to_linear[$srgb];
+ my $srgb_lossy = $linear_to_srgb[$linear >> 4];
+ last if $srgb == $srgb_lossy;
+
+ # Add slight bias to this component until it rounds correctly
+ $srgb_to_linear[$srgb] ++;
+ $add ++;
+ }
+ die "Too many adds at $srgb" if $add > 5;
+}
+
+print <<"PROLOG";
+/* WARNING: This file is generated by $0.
+ * Please edit that file instead of this one.
+ */
+
+#include <stdint.h>
+
+#include "config.h"
+#include "pixman-private.h"
+
+PROLOG
+
+print "const uint8_t linear_to_srgb[" . @linear_to_srgb . "] =\n";
+print "{\n";
+for my $linear (0 .. $#linear_to_srgb)
+{
+ if (($linear % 10) == 0)
+ {
+ print "\t";
+ }
+ print sprintf("%d, ", $linear_to_srgb[$linear]);
+ if (($linear % 10) == 9)
+ {
+ print "\n";
+ }
+}
+print "\n};\n";
+print "\n";
+
+print "const uint16_t srgb_to_linear[" . @srgb_to_linear . "] =\n";
+print "{\n";
+for my $srgb (0 .. $#srgb_to_linear)
+{
+ if (($srgb % 10) == 0)
+ {
+ print "\t";
+ }
+ print sprintf("%d, ", $srgb_to_linear[$srgb]);
+ if (($srgb % 10) == 9)
+ {
+ print "\n";
+ }
+}
+print "\n};\n";
+
diff --git a/pixman/pixman-access.c b/pixman/pixman-access.c
index 6743887..9feafc4 100644
--- a/pixman/pixman-access.c
+++ b/pixman/pixman-access.c
@@ -32,8 +32,8 @@
#include <string.h>
#include <assert.h>
-#include "pixman-private.h"
#include "pixman-accessor.h"
+#include "pixman-private.h"
#define CONVERT_RGB24_TO_Y15(s) \
(((((s) >> 16) & 0xff) * 153 + \
@@ -210,6 +210,7 @@ get_shifts (pixman_format_code_t format,
break;
case PIXMAN_TYPE_ARGB:
+ case PIXMAN_TYPE_ARGB_SRGB:
*b = 0;
*g = *b + PIXMAN_FORMAT_B (format);
*r = *g + PIXMAN_FORMAT_G (format);
@@ -1027,6 +1028,130 @@ fetch_pixel_generic_64 (bits_image_t *image,
return result;
}
+/* The 32_sRGB paths should be deleted after narrow processing
+ * is no longer invoked for formats that are considered wide.
+ * (Also see fetch_pixel_generic_lossy_32) */
+static void
+fetch_scanline_a8r8g8b8_32_sRGB (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t *buffer,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + y * image->bits.rowstride;
+ const uint32_t *pixel = (uint32_t *)bits + x;
+ const uint32_t *end = pixel + width;
+ uint32_t tmp;
+
+ while (pixel < end)
+ {
+ tmp = READ (image, pixel++);
+ *buffer++ = (tmp >> 24) << 24
+ | (srgb_to_linear[(tmp >> 16) & 0xff] >> 8) << 16
+ | (srgb_to_linear[(tmp >> 8) & 0xff] >> 8) << 8
+ | (srgb_to_linear[(tmp >> 0) & 0xff] >> 8) << 0;
+ }
+}
+
+static void
+fetch_scanline_a8r8g8b8_64_sRGB (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t *b,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + y * image->bits.rowstride;
+ const uint32_t *pixel = (uint32_t *)bits + x;
+ const uint32_t *end = pixel + width;
+ uint64_t *buffer = (uint64_t *)b;
+ uint32_t tmp;
+
+ while (pixel < end)
+ {
+ tmp = READ (image, pixel++);
+ *buffer++ = (uint64_t) ((tmp >> 24) * 257) << 48
+ | (uint64_t) srgb_to_linear[(tmp >> 16) & 0xff] << 32
+ | (uint64_t) srgb_to_linear[(tmp >> 8) & 0xff] << 16
+ | (uint64_t) srgb_to_linear[(tmp >> 0) & 0xff] << 0;
+ }
+}
+
+static uint32_t
+fetch_pixel_a8r8g8b8_32_sRGB (bits_image_t *image,
+ int offset,
+ int line)
+{
+ uint32_t *bits = image->bits + line * image->rowstride;
+ uint32_t tmp = READ (image, bits + offset);
+ return (tmp >> 24) << 24
+ | (srgb_to_linear[(tmp >> 16) & 0xff] >> 8) << 16
+ | (srgb_to_linear[(tmp >> 8) & 0xff] >> 8) << 8
+ | (srgb_to_linear[(tmp >> 0) & 0xff] >> 8) << 0;
+}
+
+static uint64_t
+fetch_pixel_a8r8g8b8_64_sRGB (bits_image_t *image,
+ int offset,
+ int line)
+{
+ uint32_t *bits = image->bits + line * image->rowstride;
+ uint32_t tmp = READ (image, bits + offset);
+ return (uint64_t) ((tmp >> 24) * 257) << 48
+ | (uint64_t) srgb_to_linear[(tmp >> 16) & 0xff] << 32
+ | (uint64_t) srgb_to_linear[(tmp >> 8) & 0xff] << 16
+ | (uint64_t) srgb_to_linear[(tmp >> 0) & 0xff] << 0;
+}
+
+static void
+store_scanline_a8r8g8b8_32_sRGB (bits_image_t *image,
+ int x,
+ int y,
+ int width,
+ const uint32_t *v)
+{
+ uint32_t *bits = image->bits + image->rowstride * y;
+ uint64_t *values = (uint64_t *)v;
+ uint32_t *pixel = bits + x;
+ uint64_t tmp;
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ tmp = values[i];
+ WRITE (image, pixel++,
+ ((uint32_t) (tmp >> 24 ) << 24)
+ | (linear_to_srgb[(tmp >> 16 << 4) & 0xfff] << 16)
+ | (linear_to_srgb[(tmp >> 8 << 4) & 0xfff] << 8)
+ | (linear_to_srgb[(tmp >> 0 << 4) & 0xfff] << 0));
+ }
+}
+
+static void
+store_scanline_a8r8g8b8_64_sRGB (bits_image_t *image,
+ int x,
+ int y,
+ int width,
+ const uint32_t *v)
+{
+ uint32_t *bits = image->bits + image->rowstride * y;
+ uint64_t *values = (uint64_t *)v;
+ uint32_t *pixel = bits + x;
+ uint64_t tmp;
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ tmp = values[i];
+ WRITE (image, pixel++,
+ ((uint32_t) (tmp >> 56) << 24)
+ | (linear_to_srgb[(tmp >> 36) & 0xfff] << 16)
+ | (linear_to_srgb[(tmp >> 20) & 0xfff] << 8)
+ | (linear_to_srgb[(tmp >> 4) & 0xfff] << 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 +1204,13 @@ static const format_info_t accessors[] =
FORMAT_INFO (r8g8b8x8),
FORMAT_INFO (x14r6g6b6),
+/* sRGB formats */
+ { PIXMAN_a8r8g8b8_sRGB,
+ fetch_scanline_a8r8g8b8_32_sRGB,
+ fetch_scanline_a8r8g8b8_64_sRGB,
+ fetch_pixel_a8r8g8b8_32_sRGB, fetch_pixel_a8r8g8b8_64_sRGB,
+ store_scanline_a8r8g8b8_32_sRGB, store_scanline_a8r8g8b8_64_sRGB },
+
/* 24bpp formats */
FORMAT_INFO (r8g8b8),
FORMAT_INFO (b8g8r8),
diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index 8b634a7..15597bd 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -909,7 +909,8 @@ _pixman_image_get_solid (pixman_implementation_t *imp,
}
/* If necessary, convert RGB <--> BGR. */
- if (PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_ARGB)
+ if (PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_ARGB
+ && PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_ARGB_SRGB)
{
result = (((result & 0xff000000) >> 0) |
((result & 0x00ff0000) >> 16) |
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index 4d8f64d..d5e6a72 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -887,7 +887,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_TYPE (f) == PIXMAN_TYPE_ARGB_SRGB)
#ifdef WORDS_BIGENDIAN
# define SCREEN_SHIFT_LEFT(x,n) ((x) << (n))
@@ -1083,6 +1084,18 @@ void pixman_timer_register (pixman_timer_t *timer);
#endif /* PIXMAN_TIMERS */
+/* sRGB<->linear conversion tables. Linear color space is the same
+ * as sRGB but the components are in linear light (gamma 1.0).
+ *
+ * linear_to_srgb maps linear value from 0 to 4095 ([0.0, 1.0])
+ * and returns 8-bit sRGB value.
+ *
+ * srgb_to_linear maps 8-bit sRGB value to 16-bit linear value
+ * with range 0 to 65535 ([0.0, 1.0]).
+ */
+extern const uint8_t linear_to_srgb[4096];
+extern const uint16_t srgb_to_linear[256];
+
#endif /* __ASSEMBLER__ */
#endif /* PIXMAN_PRIVATE_H */
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..e1cb90a 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -653,6 +653,7 @@ struct pixman_indexed
#define PIXMAN_TYPE_YV12 7
#define PIXMAN_TYPE_BGRA 8
#define PIXMAN_TYPE_RGBA 9
+#define PIXMAN_TYPE_ARGB_SRGB 10
#define PIXMAN_FORMAT_COLOR(f) \
(PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_ARGB || \
@@ -676,6 +677,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),