[cairo] Subtractive API, part 0

Arjen Nienhuis a.g.nienhuis at gmail.com
Thu Jan 28 04:09:13 PST 2010


Your proposal assumes that its possible to do simple calculations on
CMYK values. It's not.

cmyk(0, 0, 1, 1) is (almost) black. Painting this with 50% opacity on
a white background should be (almost) gray. cmyk(0, 0, .5, .5) is
beige and not gray. That means that if i try to draw a cmyk(0, 0, 1,
1) box that is not pixel aligned I will get beige borders.

New proposal:

I think Cairo should not implement CMYK pixel surfaces and only output
CMYK values to PS, SVG and PDF. Cairo can implement the SVG model and
use:

cairo_set_source_icc_color(cairo_t *cr, float srgb_fallback_r, float
srgb_fallback_g, float srgb_fallback_b, cairo_profile_t *profile,
<array of float>)

Cairo doesn't need to process the icc-profiles and just emit the
icc-profiles to the supporting backends. All the other backends can
use the fallback.

Groeten, Arjen

On Mon, Jan 25, 2010 at 2:54 AM, ecir hana <ecir.hana at gmail.com> wrote:
>
> > Are you to a point where you could start capturing the above as
> > concretely as an API proposal for cairo? That also might help me
> > understand better what I'm missing.
>
> Hello,
>
> here's a first try, thanks in advance for your feedback! It has at
> least one issue but as they say: "release early, release often".
>
> * CMYK
>
> If I understood it correctly, this could be fairly simple (color,
> pattern, image):
>
> void cairo_set_source_cmyk(cairo_t *cr, double cyan, double magenta,
> double yellow, double black);
> cairo_pattern_t* cairo_pattern_create_cmyk(double cyan, double
> magenta, double yellow, double black);
> void cairo_pattern_add_color_stop_cmyk(cairo_pattern_t *pattern,
> double offset, double cyan, double magenta, double yellow, double
> black);
> CAIRO_FORMAT_CMYK32 in cairo_format_t
>
> * DeviceN
>
> This is a bit worse... I wanted to add as few new functions as
> possible and to break none of the current ones. I also did not try to
> come up with API which was overly generic, rather I took the advice
> from Mike Pall (of LuaJIT) when he said "I think it's mandatory to
> write some standard usage examples before designing and implementing
> an API" and wrote a few examples (see bellow). Also, it would be great
> if one could use the same sequence of operators to draw, say, to the
> new meta surface, replay it on screen for zooming and panning and
> finally output it as print-ready PDF.
>
> First, instead of creating a new generic color space and assign it to
> every surface as its property, I thought it might be simpler just to
> create a completely new kind of object:
>
> cairo_devicen_t* cairo_devicen_create();
> cairo_devicen_destroy(cairo_devicen_t *devicen);
> void cairo_devicen_add_spot_color_cmyk(cairo_devicen_t *devicen, const
> char *name, double fallback_cyan, double fallback_magenta, double
> fallback_yellow, double fallback_black);
>
> This way DeviceN becomes separate from the rest of API so it would
> stay out of way if one does not want to use it and it might be
> possible in the future to provide other kinds of fallbacks (e.g.
> cairo_devicen_add_spot_color_srgb_icc()). It could be also possible to
> create many fallbacks for a given name so one could specify a fallback
> for RGB, CMYK, ... It somewhat looks like Cairo pattern API. On
> fallbacks, please read bellow for more.
>
> Then, we could write something similar as for CMYK, passing the
> DeviceN as additional argument:
>
> void cairo_set_source_devicen(cairo_t *cr, cairo_devicen_t *devicen, ...);
> cairo_pattern_t* cairo_pattern_create_devicen(cairo_devicen_t *devicen, ...);
> void cairo_pattern_add_color_stop_devicen(cairo_pattern_t *pattern,
> cairo_devicen_t *devicen, double offset, ...);
> CAIRO_FORMAT_DEVICEN in cairo_format_t
>
> "..." means tint values for each colorant of the DeviceN (vararg doubles).
>
> There is one (or more?) problem, thought. It is sometimes useful to
> create multi-channel images, e.g. a pixmap with just two channels,
> so-called "duotone" image. It needs the possibility of creating a
> Cairo surface out of such image and to assign a spot color to each
> channel (so that the image pixel values become DeviceN tint values).
>
> Currently, there is no way how to create such image surfaces.
> "cairo_image_surface_create_for_data()" would need to know how many
> bits a pixel contains and how many channels there are and PNG
> ("cairo_image_surface_create_from_png()") has predefined set of
> allowed image types. We can assume that the images would have either 8
> or 16 bit color depth. That could go to cairo_format_t as
> CAIRO_FORMAT_DEVICEN8 and CAIRO_FORMAT_DEVICE16. Then, if we knew how
> long "*data" is it would be possible to calculate the number of
> channels. But even if we knew all this there is still the need to
> define the spot colors for each channel.
>
> Or maybe, somehow, possibly, there could be two functions:
> cairo_surface_set_colorant_names(cairo_surface_t *surface, const char **names);
> cairo_devicen_get_colorant_names(cairo_devicen_t *devicen);
> and not care what kind of data and image surface is built on, just
> force it to be something and hope you know what you are doing.. kind
> of like C cast.
>
> But I'm affraid this is all crap... Help!
>
> Anyway...
>
> Thoughts?
>
>
> Appendix A, Examples:
>
> * CMYK
> ** rectangle
> cairo_set_source_cmyk(cr, 0.0, 0.0, 0.0, 1.0);
> cairo_rectangle(cr, 0.0, 0.0, 10.0, 10.0);
> cairo_fill(cr);
>
> ** pattern
> lp = cairo_pattern_create_linear(0.0, 0.0, 1.0, 1.0);
> cairo_pattern_add_color_stop_cmyk(lp, 0.0, 0.0, 0.0, 0.0, 0.0);
> cairo_pattern_add_color_stop_cmyk(lp, 1.0, 1.0, 1.0, 1.0, 1.0);
> cairo_rectangle(cr, 0.0, 0.0, 10.0, 10.0);
> cairo_set_source(cr, lp);
> cairo_fill(cr);
>
> ** image
> cairo_surface_t *im =
> cairo_image_surface_create_from_jpeg("image.jpg"); // PNG does not
> support CMYK
> cairo_set_source_surface(cr, im, 0.0, 0.0);
> cairo_paint(cr);
> cairo_devicen_destroy(dn);
>
> * 2 spot colors
> ** rectangle
> dn = cairo_devicen_create()
> cairo_devicen_add_spot_color_cmyk(dn, "SpotColorA", 0.1, 0.2, 0.3, 0.4);
> cairo_devicen_add_spot_color_cmyk(dn, "SpotColorB", 0.6, 0.7, 0.9, 0.9);
> cairo_set_source_devicen(cr, dn, 1.0, 1.0);
> cairo_rectangle(cr, 0.0, 0.0, 10.0, 10.0);
> cairo_fill(cr);
> cairo_devicen_destroy(dn);
>
> ** pattern
> dn = cairo_devicen_create()
> cairo_devicen_add_spot_color_cmyk(dn, "SpotColorA", 0.1, 0.2, 0.3, 0.4);
> cairo_devicen_add_spot_color_cmyk(dn, "SpotColorB", 0.6, 0.7, 0.9, 0.9);
> lp = cairo_pattern_create_linear(0.0, 0.0, 1.0, 1.0);
> cairo_pattern_add_color_stop_devicen(lp, dn, 0.0, 0.0, 0.0);
> cairo_pattern_add_color_stop_devicen(lp, dn, 1.0, 1.0, 1.0);
> cairo_rectangle(cr, 0.0, 0.0, 10.0, 10.0);
> cairo_set_source(cr, lp);
> cairo_fill(cr);
> cairo_devicen_destroy(dn);
>
> ** image
> dn = cairo_devicen_create()
> cairo_devicen_add_spot_color_cmyk(dn, "SpotColorA", 0.1, 0.2, 0.3, 0.4);
> cairo_devicen_add_spot_color_cmyk(dn, "SpotColorB", 0.6, 0.7, 0.9, 0.9);
> cairo_surface_t *im = cairo_image_surface_create_for_data(data,
> CAIRO_FORMAT_DEVICEN8, 100, 100, cairo_format_stride_for_width(100));
> cairo_surface_set_colorant_names({"SpotColorB", "SpotColorB"}); // ???
> FIXME: here it breaks!
> cairo_set_source_surface(cr, im, 0.0, 0.0);
> cairo_paint(cr);
> cairo_devicen_destroy(dn);
>
>
> Appendix B, DeviceN like CMYK (just for fun):
>
> dn = cairo_devicen_create()
> cairo_devicen_add_spot_color_cmyk(dn, "Cyan",    1.0, 0.0, 0.0, 0.0);
> cairo_devicen_add_spot_color_cmyk(dn, "Magenta", 0.0, 1.0, 0.0, 0.0);
> cairo_devicen_add_spot_color_cmyk(dn, "Yellow",  0.0, 0.0, 1.0, 0.0);
> cairo_devicen_add_spot_color_cmyk(dn, "Black",   0.0, 0.0, 0.0, 1.0);
> cairo_devicen_destroy(dn);
>
>
> Appendix C, Fallbacks:
>
> In my previous post I wrote that if a color is not available on the
> output device there should be a fallback mapping (tintTranform &
> sampled function) which assigns a tint of the spot color to a mix of
> device's colors values. It turns out, it is actually rather easy to
> generate the whole mapping if CMYK of 100% spot color is known, so the
> table (i.e. samples) could be generated on the fly and the user would
> need to provide just that one CMYK. Small Python example:
>
> --- cmyk.py - begin ---
>
> class cmyk(object):
>
>    def __init__(self, c, m, y, k):
>        self.tints = c, m, y, k
>
>    def __add__(self, other):
>        return cmyk(*(a + b - a*b for a, b in zip(self.tints, other.tints)))
>
>    def __mul__(self, intensity):
>        return cmyk(*(tint * intensity for tint in self.tints))
>
>    def __str__(self):
>        return '%.2f %.2f %.2f %.2f' % self.tints
>
>    def to_ascii(self):
>        return '%03d %03d %03d %03d' % tuple(round(tint * 255) for
> tint in self.tints)
>
> ##print cmyk(1, 1, 0, 0) + cmyk(0, 0, 1, 1)
> ##print cmyk(0.89, 0.43, 0, 0) + cmyk(0.03, 0.89, 0, 0)
> ##print cmyk(1, 1, 0, 0) * 0.5
>
> a = cmyk(0.89, 0.43, 0, 0)
> b = cmyk(0.03, 0.89, 0, 0)
>
> # Generate 1D mapping from one spot color to CMYK
> ##SIZE = 255
> ##y = []
> ##for j in range(SIZE):
> ##    y.append((a * (j / (SIZE - 1.0))).to_ascii())
>
> # Generate 2D mapping from two spot colors to CMYK
> SIZE = 45
> y = []
> for j in range(SIZE):
>    x = []
>    for i in range(SIZE):
>        x.append((a * (i / (SIZE - 1.0)) + b * (j / (SIZE - 1.0))).to_ascii())
>    y.append(x)
>
> for x in y:
>    print x
>
> --- cmyk.py - end ---
> --
> cairo mailing list
> cairo at cairographics.org
> http://lists.cairographics.org/mailman/listinfo/cairo


More information about the cairo mailing list