summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Sandmann <ssp@redhat.com>2013-01-11 05:10:58 -0500
committerSøren Sandmann <ssp@redhat.com>2013-01-11 05:10:58 -0500
commitdfce703f762f5b1601e18cf6f976be45d7e0333b (patch)
treea2bf0d988421eab028d306a8a53bd828da8b63fc
parent71ce9cfa4f26ff41bde06226409b43bc94ea2ab8 (diff)
asdf
-rw-r--r--blend.txt231
-rw-r--r--pics.c217
2 files changed, 356 insertions, 92 deletions
diff --git a/blend.txt b/blend.txt
index 12c103d..3f81a59 100644
--- a/blend.txt
+++ b/blend.txt
@@ -1,16 +1,20 @@
-Porter/Duff, cardboard, blend modes, and CSS compositing
+Outstanding issues:
+
+ - Find out what's up with HSL blend modes
+ - Rethink structure of the document
+
+Porter/Duff and blend modes
Porter/Duff
-In the Porter/Duff compositing algebra, images are extended with an
+In the Porter/Duff compositing algebra, images are equipped with an
alpha channel that determines on a per-pixel basis whether the image
is there or not. When the alpha channel is 1, the image is fully
-there, when it is 0, the image isn't there at all. In other words, the
-alpha channel describes the the shape of the image, it does not
-describe opacity. The reason we have alpha values between 0 and 1 is
-so that the shape can be antialiased. The way to think of images with
-an alpha channel is as irregularly shaped pieces of cardboard, not as
-colored glass.
+there, when it is 0, the image isn't there at all. When it is in
+between, the imgae is partially there. In other words, the alpha
+channel describes the the shape of the image, it does not describe
+opacity. The way to think of images with an alpha channel is as
+irregularly shaped pieces of cardboard, not as colored glass.
Consider these two images:
@@ -25,9 +29,9 @@ When we combine them, each pixel can be divided into four regions:
/ \
/ \
-One where only the source is present, one where only the destination
-is present, one where both are present, and one where neither are
-present.
+One region where only the source is present, one where only the
+destination is present, one where both are present, and one where
+neither is present.
By deciding on what happens in each of the four regions, various
effects can be generated. For example, if the destination-only region
@@ -43,12 +47,12 @@ source image, and then held up in front of it:
SRC dest_atop DEST
-The Porter/Duff operator that does this is called DEST_ATOP.
+The Porter/Duff operator that does this is called "Dest Atop".
There are twelve of these operators, each one characterized by its
behavior in the three regions: source, destination and both (the
-'neither' region is always blank). The source-only region can be either
-'source' or 'blank'. The destination-only region can be either
+'neither' region is always blank). The source-only region can be
+either 'source' or 'blank'. The destination-only region can be either
'destination' or 'blank'. The 'both' region can be either 'blank',
'source', or 'destination'.
@@ -57,7 +61,8 @@ of the four regions, where the weights are the areas of each region:
A_src * { C_s, 0 } + A_dest * { C_d, 0 } + A_both * { C_s, C_d, 0 }
-The areas are given by these formulas:
+Where C_s is the source pixel and C_d is the destination pixel. The
+areas are given by these formulas:
A_src = as * (1 - ad)
A_dst = ad * (1 - as)
@@ -68,13 +73,17 @@ full coverage in all regions that are not blank:
A_src * { 1, 0 } + A_dest * ( 1, 0 } + A_both * { 1, 1, 0 }
+FIXME: something about alpha channels and premultiplication.
+
It is important to understand that despite being referred to as alpha
"blending", and despite alpha often being used to model opacity,
-conceptually Porter/Duff is *not* a way to blend the source and
+conceptually Porter/Duff is not a way to blend the source and
destination shapes. It is a way to overlay, combine and cut them as if
-they were pieces of cardboard.
+they were pieces of cardboard. The only places where source and
+destination pixels are actually blended is where the antialiased edges
+meet.
-Here are all of the operators:
+Here are all of the twelve Porter/Duff operators:
[table.png]
@@ -82,75 +91,174 @@ Here are all of the operators:
Blending
Photoshop and the Gimp have the concept of layers which are images
-stacked on top of each other. In Porter/Duff, stacking things on top
+stacked on top of each other. In Porter/Duff, stacking images on top
of each other is done with the "Over" operator, which is also what
Photoshop/Gimp use by default to composite layers:
<over>
Conceptually, two pieces of cardboard are held up with one in front of
-the other. Neither shape is tailored in any way, and in places where
-both are present, only the top layer is visible.
+the other. Neither shape is trimmed, and in places where both are
+present, only the top layer is visible.
-A layer in these programs also has an associated Blend Mode. The
-default blend mode is 'Normal' which corresponds to the regular Over
-operator from Porter/Duff. When a blend mode other than Normal is
-chosen, the behavior in the 'both' region of the Porter/Duff pixel is
-modified to be something other than simply 'source'. For example the
-'Color Dodge' blend mode computes a blend of source and destination
-according to this formula:
+A layer in these programs also has an associated *Blend Mode* which
+can be used to modify what happens in places where both are
+visible. For example, the 'Color Dodge' blend mode computes a mix of
+source and destination according to this formula:
- <color dodge formula>
+ B(s, d) = <color dodge formula>
-It looks like this:
+The pixel diagram is this:
- src ColorDodge dest
+ Zoomed Color Dodge
-The pixel diagram looks like this:
+And the result looks like this:
- Zoomed Color Dodge
+ src ColorDodge dest
-Here the source and destination are actually blended in the region
-where they overlap, where the regular Porter/Duff operators always
-choose either the source, the destination or neither in the
-overlapping region.
+Unlike the regular Over operator, in this case there is a substantial
+piece of the output where the source and the destination are actually
+mixed.
Layers in Photoshop and Gimp are not tailored to each other (except
-we'll ignore layer masks here), so the compositing of the layer stack
-is done with the source-only and destination-only region fixed to
-source and destination respectively. However, there is nothing in
-principle stopping us from setting the source-only and
-destination-only regions to blank in the blend mode operators so that
-we could support tailoring along with blending. For example, here is
-the operator that has 0 in the 'source-only' region, D in the
-'destination-only' region, and ColorDodge blending in the 'both'
-region:
-
- ColorDodge Atopish
+for layer masks, which we will ignore here), so the compositing of the
+layer stack is done with the source-only and destination-only region
+fixed to source and destination respectively. However, there is
+nothing in principle stopping us from setting the source-only and
+destination-only regions to blank, but keep the blend mode in the
+'both' region so that tailoring could be supported alongside
+blending. For example, we could set the 'source' region to blank, the
+'destination' region to destination, and the 'both' region to
+ColorDodge:
+
+ colordodge-dest-diagram.png
+
+The result looks like this:
+
+ ColorDodge dest
+
+The default blend mode in Photoshop/Gimp is called 'Normal' and the
+"blend" it computes is simply the top layer, which results in the
+'Over' operator that corresponds to stacking opaque images on top of
+each other.
This idea naturally leads to an extension of the Porter/Duff algebra,
-where the 'both' behavior can be chosen from a set of blend modes,
-three of which are the original 'source', 'dest', and 'none'. There is
-a large number of operators available in this model, and each of them
-is characterized by the answer to three questions:
+where the 'both' behavior is selected from a large set of blend
+modes. By using these three simple blend modes:
+
+ Source: B(s,d) = s
+ Dest: B(s,d) = d
+ Zero: B(s,d) = 0
+
+we get the twelve original Porter/Duff operators. By selecting other
+blend modes a large number of operators become available that allows
+various trimmings and mixings of the source and destination images.
+Each of these operators is characterized by the answers to three
+questions:
- Is the source tailored to the destination?
- Is the destination tailored to the source?
- What happens in the area where both source and destination are present?
-By answering 'none', 'source', or 'destination' to the third question,
-the twelve original porter/duff operators fall out as special
-cases. The general formula is exactly the same:
+By answering 'Zero', 'Source', or 'Dest' to the third question the
+original Porter/Duff operators fall out as special cases.
+
+The general formula is still an area weighted average:
A_src * { C_s, 0 } + A_dest * { C_d, 0 } + A_both * B(C_s, C_d)
-The only difference is that B(C_s, C_d) can now be defined by several
-other formulas than just { C_s, C_d, 0 }. With one additional blend
-mode, 'plus', the ADD operator falls out as well.
+where instead of { C_s, C_d, 0 }, the blend mode can now by chosen
+from a large set of formulas. In this way, each new blend modes gives
+rise to four new compositing operators defined by whether the source
+and destination regions are blank or contain the corresponding pixel
+value.
+
+Here is a table of the operators that are generated by various blend
+modes:
+
+ [ blend table.png ]
+
+The three first rows are the original Porter/Duff operators.
+
+
+Alpha Channels
+
+With the classic Porter/Duff operators the output alpha channel is
+generally computed by plugging 1 into the area weighted average
+whenever a region is filled with either source or destination, and 0
+whenever a region is blank.
-This model is a simple and coherent way to extend the Porter/Duff
-compositing algebra with blend modes.
+With most blend modes, the alpha value of the 'both' area can be
+considered 1, ie. 'covered', and so the formula still works. However,
+there are some exceptions. The first one is the blend mode 'Plus':
+ B(s, d) = s + d
+
+The 'both' variant of this blend mode is almost equivalent to the
+useful operator Add that simply adds all channels together. The reason
+it isn't exactly equivalent is that if we consider the alpha channel 1
+in the 'both' area, the output alpha value will be:
+
+ a_r = a_s + a_d - a_s * a_d
+
+where the Add operator has a_s * a_d. We can fix this by defining that
+the alpha value of the 'both' area to be B(1, 1). That is, plug s=1
+and d=1 into the blend mode formula. Most blend modes produce 1 when
+s=1 and d=1, but Plus produces 2, which means the resulting alpha
+channel is a_s + a_d as desired. This definition also works for the
+'Zero' blend mode: it produces 0 as desired. Using this definition
+also means that when computing with premultiplied pixels, the
+computation is exactly the same for all four channels. To see this,
+consider the blend computation with premultiplied pixels:
+
+ B_{pre} (s, d) = a_s * a_d * B (C_s / a_s, C_d / a_s)
+
+and see what happens when you plug in the alpha channel:
+
+ BP(as, ad) = a_s * a_d * B (a_s / a_s, a_d / a_d) = a_s * a_d * B(1, 1)
+
+There are still two annoying exceptions: The blend modes Exclusion and
+Difference do not produce 1 when you plug in (s = 1, d = 1), they
+produce 0. There is some logic to that, because these blend modes are
+concerned with the difference between source and destination, and the
+difference in coverage in the 'both' region (where source and
+destination are both present by definition) would in fact be
+zero. Nonetheless, making the alpha channel 0 in the 'both' region
+would mean that you couldn't see the blended result, so that's not a
+useful option.
+
+Finally, the HSL blend modes don't work on a channel-by-channel basis,
+so it will never be possible to do the same computation on all four
+channels. These will just have to be special cased.
+
+Instead, if we were to actually extend Pixman / cairo / Xrender along
+the lines of this post, it might be better to leave out those blend
+modes and instead add the inverted versions:
+
+ Inverted Exclusion: B(s,d) = 1 - Exclusion(s,d)
+ Inverted Difference: B(s,d) = 1 - Difference(s,d)
+
+These will produce the correct alpha channel, and users can still
+achieve the non-inverted blend modes by using solid white with the
+inverted blend modes using two passes if necessary.
+
+
+Conclusion
+
+It is possible to generalize the Porter/Duff compositing algebra by
+allowing more behaviors in the 'both' region of the Porter/Duff
+pixel. If the effect on the alpha channel is defined to be B(1,1),
+then all four channels can be computed with the same formula:
+
+ (1 - ad) * [s] * S + (1 - as) * [d] * D + as * ad * B(S/as, D/ad)
+
+where [s] and [d] are 1 or 0 depending on whether the source and
+destination are drawn, and B is the blend mode. When B(s,d) is chosen
+to be 0, s, or d, the formula reduces to the twelve original
+Porter/Duff operators.
+
+
+<!--
CSS compositing spec
@@ -204,3 +312,6 @@ the region where source and destination overlaps, and if they disagree
on what happens there, it is not clear what happens. The answer to
this is not to pick some formula that happens to produce useful
results in certain special cases.
+
+-->
+
diff --git a/pics.c b/pics.c
index 3c25a63..9c344de 100644
--- a/pics.c
+++ b/pics.c
@@ -3,8 +3,9 @@
#define CHECK_SIZE 16
-#define SOURCE_COLOR 0.8, 0.3, 0.4
+#define SOURCE_COLOR 0.8, 0.3, 0.2
#define DEST_COLOR 0.2, 0.2, 0.4
+#define LIGHT_CHECK 0.95, 0.95, 0.95
static cairo_t *
get_cairo (int width, int height)
@@ -22,7 +23,7 @@ get_cairo (int width, int height)
int light = !!(((i / CHECK_SIZE) ^ (j / CHECK_SIZE)) & 1);
if (light)
- cairo_set_source_rgb (cr, 0.95, 0.95, 0.95);
+ cairo_set_source_rgb (cr, LIGHT_CHECK);
else
cairo_set_source_rgb (cr, 0.92, 0.92, 0.92);
@@ -171,6 +172,9 @@ draw_cell (cairo_t *cr,
/* printf ("move to %f %f for %s\n", x, y, name); */
cairo_rectangle (cr, x - 10, y, CELL_WIDTH + 20, CELL_HEIGHT);
cairo_clip (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
/* Cairo's source and clear are clip-to-self, but we
* want to draw the actual Porter/Duff operator,
@@ -247,14 +251,14 @@ create_porter_duff_table (void)
static void
-draw_color_dodge_none (cairo_t *cr, double x, double y)
+draw_blend_none (cairo_t *cr, cairo_operator_t blend, double x, double y)
{
cairo_push_group (cr);
cairo_push_group (cr);
cairo_set_source_rgb (cr, DEST_COLOR);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
- cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
+ cairo_set_operator (cr, blend);
cairo_set_source_rgb (cr, SOURCE_COLOR);
cairo_paint (cr);
cairo_pop_group_to_source (cr);
@@ -273,7 +277,7 @@ draw_color_dodge_none (cairo_t *cr, double x, double y)
}
static void
-draw_color_dodge_source (cairo_t *cr, double x, double y)
+draw_blend_source (cairo_t *cr, cairo_operator_t blend, double x, double y)
{
cairo_push_group (cr);
@@ -286,7 +290,7 @@ draw_color_dodge_source (cairo_t *cr, double x, double y)
cairo_set_source_rgb (cr, DEST_COLOR);
cairo_paint (cr);
cairo_set_source_rgb (cr, SOURCE_COLOR);
- cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
+ cairo_set_operator (cr, blend);
cairo_paint (cr);
cairo_pop_group_to_source (cr);
@@ -308,7 +312,7 @@ draw_color_dodge_source (cairo_t *cr, double x, double y)
}
static void
-draw_color_dodge_dest (cairo_t *cr, double x, double y)
+draw_blend_dest (cairo_t *cr, cairo_operator_t blend, double x, double y)
{
cairo_move_to (cr, x, y);
draw_dest (cr);
@@ -318,7 +322,7 @@ draw_color_dodge_dest (cairo_t *cr, double x, double y)
cairo_set_source_rgb (cr, DEST_COLOR);
cairo_paint (cr);
cairo_set_source_rgb (cr, SOURCE_COLOR);
- cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
+ cairo_set_operator (cr, blend);
cairo_paint (cr);
cairo_pop_group_to_source (cr);
cairo_move_to (cr, x, y);
@@ -332,6 +336,51 @@ draw_color_dodge_dest (cairo_t *cr, double x, double y)
}
static void
+draw_blend_row (cairo_t *cr, double x, double y,
+ cairo_operator_t blend, const char *name)
+{
+ char text[128];
+
+#define ENDASH "\342\200\223"
+
+ draw_blend_none (cr, blend, x, y);
+ sprintf (text, "%s None", name);
+ cell_text (cr, x, y, ENDASH);
+ x += CELL_WIDTH;
+
+ draw_blend_source (cr, blend, x, y);
+ sprintf (text, "%s Source", name);
+ cell_text (cr, x, y, ENDASH);
+ x += CELL_WIDTH;
+
+ draw_blend_dest (cr, blend, x, y);
+ sprintf (text, "%s Dest", name);
+ cell_text (cr, x, y, ENDASH);
+ x += CELL_WIDTH;
+
+ draw_cell (cr, x, y, blend, "");
+ sprintf (text, "%s Both", name);
+ cell_text (cr, x, y, ENDASH);
+}
+
+#define FIRSTCOL 160
+
+static void
+blend_header (cairo_t *cr, double x, double y, const char *header)
+{
+ cairo_save (cr);
+ cairo_select_font_face (cr, "Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 16.0);
+
+ cairo_set_font_size (cr, 16.0);
+ cairo_move_to (cr, XPAD - FIRSTCOL + 8, y + CELL_HEIGHT / 2 + 6);
+ cairo_show_text (cr, header);
+ cairo_restore (cr);
+}
+
+static void
create_color_dodge_table (void)
{
static const op_info_t ops[] =
@@ -352,46 +401,110 @@ create_color_dodge_table (void)
"Dest Over", CAIRO_OPERATOR_DEST_OVER,
};
- cairo_t *cr = get_cairo (CELL_WIDTH * 4 + XPAD + 140,
- (CELL_HEIGHT) * 4 + 2 * YPAD);
+#define N_ROWS 9
+
+ cairo_t *cr = get_cairo (CELL_WIDTH * 4 + XPAD + FIRSTCOL,
+ (CELL_HEIGHT) * N_ROWS + 4 * YPAD);
double x, y;
int n_columns = 4;
int i;
- for (i = YPAD; i < CELL_HEIGHT * 4 + 2 * YPAD; i += 2 * CELL_HEIGHT)
+ cairo_rectangle (cr, 0, 0, CELL_WIDTH * 4 + XPAD + FIRSTCOL, 3 * YPAD);
+ cairo_rectangle (cr, 0, 3 * YPAD + N_ROWS * CELL_HEIGHT,
+ CELL_WIDTH * 4 + XPAD + FIRSTCOL, 3 * YPAD);
+ cairo_set_source_rgb (cr, LIGHT_CHECK);
+ cairo_fill (cr);
+
+ cairo_translate (cr, 0, 2 * YPAD);
+
+ for (i = YPAD; i < CELL_HEIGHT * N_ROWS + 2 * YPAD; i += 2 * CELL_HEIGHT)
{
- cairo_set_source_rgba (cr, 0, 0, 1, 0.02);
- cairo_rectangle (cr, 0, i, CELL_WIDTH * 4 + XPAD + 140, CELL_HEIGHT);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.02);
+ cairo_rectangle (cr, 0, i, CELL_WIDTH * 4 + XPAD + FIRSTCOL, CELL_HEIGHT);
cairo_fill (cr);
}
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_set_font_size (cr, 18.0);
+
+ cairo_select_font_face (cr, "Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+
+ cairo_move_to (cr, XPAD, -YPAD / 2);
+ cairo_show_text (cr, "Blend Mode");
+
+ cairo_move_to (cr, XPAD + FIRSTCOL + 110, - YPAD/2);
+ cairo_show_text (cr, "Source and Dest Regions");
+
+ cairo_select_font_face (cr, "Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 16.0);
+
+ cairo_move_to (cr, XPAD + FIRSTCOL + 7, YPAD / 2);
+ cairo_show_text (cr, "Neither");
+
+ cairo_move_to (cr, XPAD + FIRSTCOL + 2 + CELL_WIDTH, YPAD / 2);
+ cairo_show_text (cr, "Source");
+
+ cairo_move_to (cr, XPAD + FIRSTCOL + 17 + 2 * CELL_WIDTH, YPAD / 2);
+ cairo_show_text (cr, "Dest");
+
+ cairo_move_to (cr, XPAD + FIRSTCOL + 17 + 3 * CELL_WIDTH, YPAD / 2);
+ cairo_show_text (cr, "Both");
+
+ cairo_select_font_face (cr, "Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 16.0);
+
+ cairo_move_to (cr, XPAD, YPAD + CELL_HEIGHT / 2 + 6);
+ cairo_show_text (cr, "Zero");
+ cairo_move_to (cr, XPAD, YPAD + CELL_HEIGHT + CELL_HEIGHT / 2 + 6);
+ cairo_show_text (cr, "Source");
+ cairo_move_to (cr, XPAD, YPAD + 2 * CELL_HEIGHT + CELL_HEIGHT / 2 + 6);
+ cairo_show_text (cr, "Dest");
+
cairo_push_group (cr);
- cairo_translate (cr, 140, 8);
+ cairo_translate (cr, FIRSTCOL - 8, 8);
draw_table (cr, ops, sizeof (ops) / sizeof (ops[0]), n_columns);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
x = CELL_WIDTH * (12 % n_columns) + XPAD;
y = CELL_HEIGHT * (12 / n_columns) + YPAD;
- draw_color_dodge_none (cr, x, y);
- cell_text (cr, x, y, "CDodge None");
- x = CELL_WIDTH * (13 % n_columns) + XPAD;
- y = CELL_HEIGHT * (13 / n_columns) + YPAD;
- draw_color_dodge_source (cr, x, y);
- cell_text (cr, x, y, "CDodge Source");
+ draw_blend_row (cr, x, y, CAIRO_OPERATOR_SCREEN, "Screen");
+ blend_header (cr, x, y, "Screen");
+ y += CELL_HEIGHT;
+
+ draw_blend_row (cr, x, y, CAIRO_OPERATOR_MULTIPLY, "Multiply");
+ blend_header (cr, x, y, "Multiply");
+ y += CELL_HEIGHT;
- x = CELL_WIDTH * (14 % n_columns) + XPAD;
- y = CELL_HEIGHT * (14 / n_columns) + YPAD;
- draw_color_dodge_dest (cr, x, y);
- cell_text (cr, x, y, "CDodge Dest");
+ draw_blend_row (cr, x, y, CAIRO_OPERATOR_COLOR_DODGE, "CDodge");
+ blend_header (cr, x, y, "Color Dodge");
+ y += CELL_HEIGHT;
- x = CELL_WIDTH * (15 % n_columns) + XPAD;
- y = CELL_HEIGHT * (15 / n_columns) + YPAD;
- draw_cell (cr, x, y, CAIRO_OPERATOR_COLOR_DODGE, "");
- cell_text (cr, x, y, "CDodge Both");
+ draw_blend_row (cr, x, y, CAIRO_OPERATOR_HSL_LUMINOSITY, "HSL Lum");
+ blend_header (cr, x, y, "HSL Luminosity");
+ y += CELL_HEIGHT;
+
+ draw_blend_row (cr, x, y, CAIRO_OPERATOR_HARD_LIGHT, "HLight");
+ blend_header (cr, x, y, "Hard Light");
+ y += CELL_HEIGHT;
+
+ /* draw_blend_row (cr, x, y, CAIRO_OPERATOR_SOFT_LIGHT, "SLight"); */
+ /* blend_header (cr, x, y, "Soft Light"); */
+ /* y += CELL_HEIGHT; */
+
+ draw_blend_row (cr, x, y, CAIRO_OPERATOR_ADD, "SLight");
+ blend_header (cr, x, y, "Plus");
+ y += CELL_HEIGHT;
cairo_pop_group_to_source (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
@@ -529,12 +642,51 @@ color_dodge_diagram (const char *filename)
}
static void
+color_dodge_dest_diagram (const char *filename)
+{
+ cairo_t *cr = get_cairo (180, 180);
+
+ cairo_move_to (cr, 1, 1);
+ cairo_line_to (cr, 90, 90);
+ cairo_line_to (cr, 179, 1);
+ cairo_set_source_rgb (cr, DEST_COLOR);
+ cairo_fill_preserve (cr);
+ cairo_set_source_rgb (cr, SOURCE_COLOR);
+ cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
+ cairo_fill (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ centered_text (cr, 90, 45, "Both");
+
+ cairo_move_to (cr, 179, 1);
+ cairo_line_to (cr, 90, 90);
+ cairo_line_to (cr, 179, 179);
+ cairo_set_source_rgb (cr, DEST_COLOR);
+ cairo_fill (cr);
+
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ centered_text (cr, 35, 90, "Source");
+
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ centered_text (cr, 145, 90, "Dest");
+
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ centered_text (cr, 90, 147, "Neither");
+
+ draw_pixel_box (cr);
+
+ finish (cr, filename);
+}
+
+static void
color_dodge_none (void)
{
cairo_t *cr = get_cairo (CELL_WIDTH, CELL_HEIGHT);
cairo_pattern_t *pattern;
- draw_color_dodge_none (cr, 20, 16);
+ draw_blend_none (cr, CAIRO_OPERATOR_COLOR_DODGE, 20, 16);
finish (cr, "colordodge-none.png");
}
@@ -545,7 +697,7 @@ color_dodge_dest (void)
cairo_t *cr = get_cairo (CELL_WIDTH, CELL_HEIGHT);
cairo_pattern_t *pattern;
- draw_color_dodge_dest (cr, 20, 16);
+ draw_blend_dest (cr, CAIRO_OPERATOR_COLOR_DODGE, 20, 16);
finish (cr, "colordodge-dest.png");
}
@@ -556,9 +708,9 @@ color_dodge_source (void)
cairo_t *cr = get_cairo (CELL_WIDTH, CELL_HEIGHT);
cairo_pattern_t *pattern;
- draw_color_dodge_source (cr, 20, 16);
+ draw_blend_source (cr, CAIRO_OPERATOR_COLOR_DODGE, 20, 16);
- cell_text (cr, 20, 16, "asfdasdfasdf");
+ cell_text (cr, 20, 16, "");
finish (cr, "colordodge-source.png");
}
@@ -574,6 +726,7 @@ main (int argc, char *argv)
general_pixel_diagram ("diagram.png");
dest_atop_diagram ("destatop-diagram.png");
color_dodge_diagram ("colordodge-diagram.png");
+ color_dodge_dest_diagram ("colordodge-dest-diagram.png");
color_dodge_none ();
color_dodge_source ();