[cairo] Color space API

Adrian Johnson ajohnson at redneon.com
Fri Jul 27 07:55:42 PDT 2012

In January/February 2010 there was a long thread[1] on adding a color
space API to cairo. There were a number of ideas proposed but at the
time no one progressed it any further. Recently I've had some time to
start looking at this. I'm interested in working out what would be the
minimum API required to support the color space requirements of SVG
and PDF.

To experiment with color space API in cairo I've developed a very basic
implementation of the API in my cairo and pixman repositories[2]. I
have also modified poppler (for rendering using pdftocairo) and
inkscape (for creating color managed PDFs)[2]. This is to help test
the implementation and understand the needs of PDF and SVG
rendering/generating applications.

To simplify the implementation, my code currently only supports RGB
color spaces and only works with the image and PDF surfaces. There is
nothing in this API that restricts color spaces to RGB other than the
need to add additional color space constructors and image formats to
support non RGB color spaces.

The API is based on the PDF color space model. Source patterns can
be created with a color space. Surfaces (transparency groups in PDF)
can also have a color space. During compositing, if the color space
of the source is difference to the color space of the destination
surface, the source colors are converted to the destination color
space before compositing.

The API incorporates ideas from Andrea Canciani's color space work[3]. 

The basic idea is to add a cairo_color_space_t type to define the
color space. cairo_color_space_t constructors are provided for
different color spaces. Additional pattern constructors that accept a
color space are provided. There is a function for setting the
surfaces color space.

These two functions allow creating a color space from a given ICC
file. Both SVG and PDF both support ICC profiles.

  cairo_color_space_t *
  cairo_color_space_create_icc_from_file (const char *filename);

  cairo_color_space_t *
  cairo_color_space_create_icc_from_memory (const char *data,
                                            int length);

The device_rgb color space is based on the PDF DeviceRGB color
space. When both source and destination are in the device_rgb color
space no color conversion is performed.

  cairo_color_space_t *
  cairo_color_space_create_device_rgb (void);

The following two color spaces are used by SVG. There is an sRGB color
space and a linear color space with the same primaries as sRGB but a
linear gamma.

  cairo_color_space_t *
  cairo_color_space_create_srgb (void);

  cairo_color_space_t *
  cairo_color_space_create_linear_srgb (void);

The standard cairo functions for referencing, destroying and obtaining
the status and type.

  cairo_color_space_t *
  cairo_color_space_reference (cairo_color_space_t *color_space);

  cairo_color_space_destroy (cairo_color_space_t *color_space);

  unsigned int
  cairo_color_space_get_reference_count (
                             cairo_color_space_t *color_space);

  cairo_color_space_status (cairo_color_space_t *color_space);

  typedef enum _cairo_color_space_type {
  } cairo_color_space_type_t;

  cairo_color_space_get_type (cairo_color_space_t *color_space);

ICC color spaces may contain different types of color spaces. It is
useful be able to find the type of an ICC color space.

  typedef enum _cairo_color_space_class {
  } cairo_color_space_class_t;

  cairo_color_space_get_class (cairo_color_space_t *color_space);

We also need to know the number of components.

  unsigned int
  cairo_color_space_get_number_of_components (
                               cairo_color_space_t *color_space);

While patching poppler and inkscape to use this API I found the need
to compare color space objects.

  cairo_color_space_equal (const cairo_color_space_t *cs_a,
                           const cairo_color_space_t *cs_b);

To avoid adding solid color pattern create functions for every color
space I've created one generic solid color constructor that supports
multiple color spaces. The number of components must match the number
of components in the color space.

  cairo_pattern_t *
  cairo_pattern_create_color (cairo_color_space_t *color_space,
                              double *components, double alpha);

  cairo_set_source_color (cairo_t *cr, cairo_color_space_t *color_space,
                          double *components, double alpha);

And a function for getting the color space of patterns. Patterns
created with the existing API have device_rgb color space.

  cairo_color_space_t *
  cairo_pattern_get_color_space (cairo_pattern_t *pattern);

The following functions create gradients with a color space. Like PDF,
the color space of a gradient is both the color space of the source
pattern and the interpolating color space for the gradient.

  cairo_pattern_t *
  cairo_pattern_create_linear_with_color_space (
                                   cairo_color_space_t *color_space,
                                                double x0, double y0,
                                                double x1, double y1);

  cairo_pattern_t *
  cairo_pattern_create_radial_with_color_space (
                               cairo_color_space_t *color_space,
                               double cx0, double cy0, double radius0,
                               double cx1, double cy1, double radius1);

  cairo_pattern_t *
  cairo_pattern_create_mesh_with_color_space (
                                    cairo_color_space_t *color_space);

These functions allow adding color stops when the gradient color space
is not RGB.

  cairo_pattern_add_color_stop (cairo_pattern_t *pattern, double offset,
                                double *components,  double alpha);

  cairo_mesh_pattern_set_corner_color (cairo_pattern_t *pattern,
                                     unsigned int corner_num,
                                     double *components, double alpha);

The cairo_surface_set_color_space function allows setting a color space
of a surface (for use as either a source or destination). The default
surface color space is device_rgb but if it is to be changed it must
be done before any drawing operations. The PDF surface also allows
each page to have a different color space.

  cairo_surface_set_color_space (cairo_surface_t *surface,
                                 cairo_color_space_t *color_space);

  cairo_color_space_t *
  cairo_surface_get_color_space (cairo_surface_t *surface);

cairo_push_group and cairo_surface_create_similar create a surface with
the same color space as the existing surface. The following two functions
allow the similar surface to be created with a different color space.

  cairo_push_group_with_color_space (cairo_t *cr,
                                     cairo_content_t content,
                                     cairo_color_space_t *color_space);

  cairo_surface_t *
  cairo_surface_create_similar_with_color_space (cairo_surface_t *other,
                                      cairo_content_t content,
                                      cairo_color_space_t *color_space,
                                      int width, int height);

Some other ideas I have not yet implemented:

Spot colors could be implemented with a color space constructor that
takes the name of the spot color and returns a 1 component color
space. The one component selects the tint value. The constructor also
specifies an alternative color space and color to use for surfaces
that do not support spot colors. When falling back to using the
alternate color the tint value can be used as the alpha of the
alternate color.

  cairo_color_space_t *
  cairo_color_space_create_spot_color (const char *name,
                                    cairo_color_space_t *alternate_cs,
                                    double *alt_components);

Antti Lankila has created a pixman patch for linear color space
processing[4]. In this case the source and destination are converted
from sRGB to linear rgb, composited, then converted back to sRGB
before writing to the destination. This could possibly be implemented
with a function like cairo_set_compositing_color_space() or

[1] http://lists.cairographics.org/archives/cairo/2009-December/018717.html
[2] http://cgit.freedesktop.org/~ajohnson/  (in the color-space branches)
[3] http://cgit.freedesktop.org/~ranma42/cairo/log/?h=wip/cs11
[4] http://lists.freedesktop.org/archives/pixman/2012-June/002036.html

More information about the cairo mailing list