Add support for sRGB surfaces
authorAntti S. Lankila <alankila@bel.fi>
Sun, 29 Jul 2012 18:46:58 +0000 (21:46 +0300)
committerSøren Sandmann Pedersen <ssp@redhat.com>
Mon, 30 Jul 2012 19:37:26 +0000 (15:37 -0400)
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.

.gitignore
pixman/Makefile.sources
pixman/make-srgb.pl [new file with mode: 0644]
pixman/pixman-access.c
pixman/pixman-image.c
pixman/pixman-private.h
pixman/pixman.c
pixman/pixman.h

index b9853b1..3cdf3c1 100644 (file)
@@ -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
index 6472994..cf7040f 100644 (file)
@@ -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 (file)
index 0000000..ebde2ea
--- /dev/null
@@ -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";
+
index 6743887..9feafc4 100644 (file)
@@ -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),
index 8b634a7..15597bd 100644 (file)
@@ -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) |
index 4d8f64d..d5e6a72 100644 (file)
@@ -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 */
index 0137c3c..994ef38 100644 (file)
@@ -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:
index 7233ceb..e1cb90a 100644 (file)
@@ -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),