summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Sandmann <ssp@redhat.com>2013-01-09 17:07:06 -0500
committerSøren Sandmann <ssp@redhat.com>2013-01-09 17:07:06 -0500
commit01c3c09be80d6fcd822ae60b575cd0ff5440a46f (patch)
tree88e2bb21ab96371f863d9578db2d516811ffd886
parent1bc7a8562261aad12957abe838e3c1ad4ae84b47 (diff)
asdf
-rw-r--r--blend.txt35
-rw-r--r--pics.c279
2 files changed, 268 insertions, 46 deletions
diff --git a/blend.txt b/blend.txt
index 72927cc..12c103d 100644
--- a/blend.txt
+++ b/blend.txt
@@ -52,20 +52,31 @@ behavior in the three regions: source, destination and both (the
'destination' or 'blank'. The 'both' region can be either 'blank',
'source', or 'destination'.
-Here are all of the Porter/Duff operators:
-
- [table.png]
-
The formula for the operators is a linear combination of the contents
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 }
-It is important to understand that despite being referred to as "alpha
-blending", and despite alpha often being used as a form of
-translucency, 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.
+The areas are given by these formulas:
+
+ A_src = as * (1 - ad)
+ A_dst = ad * (1 - as)
+ A_both = as * ad
+
+The resulting alpha channel is computed in a similar way. There is
+full coverage in all regions that are not blank:
+
+ A_src * { 1, 0 } + A_dest * ( 1, 0 } + A_both * { 1, 1, 0 }
+
+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
+destination shapes. It is a way to overlay, combine and cut them as if
+they were pieces of cardboard.
+
+Here are all of the operators:
+
+ [table.png]
Blending
@@ -73,7 +84,7 @@ 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
of each other is done with the "Over" operator, which is also what
-Photoshop/Gimp use by default to composite layers. It looks like this:
+Photoshop/Gimp use by default to composite layers:
<over>
@@ -82,10 +93,10 @@ the other. Neither shape is tailored in any way, 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
+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 'source'. For example the
+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:
diff --git a/pics.c b/pics.c
index 1dc33a3..84731b1 100644
--- a/pics.c
+++ b/pics.c
@@ -3,8 +3,8 @@
#define CHECK_SIZE 16
-#define SOURCE_COLOR 0.8, 0.5, 0.2
-#define DEST_COLOR 0.2, 0.2, 0.4
+#define SOURCE_COLOR 0.8, 0.3, 0.2
+#define DEST_COLOR 0.2, 0.4, 0.6
static cairo_t *
get_cairo (int width, int height)
@@ -24,7 +24,7 @@ get_cairo (int width, int height)
if (light)
cairo_set_source_rgb (cr, 0.95, 0.95, 0.95);
else
- cairo_set_source_rgb (cr, 0.9, 0.9, 0.9);
+ cairo_set_source_rgb (cr, 0.92, 0.92, 0.92);
cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE);
cairo_fill (cr);
@@ -50,7 +50,7 @@ finish (cairo_t *cr, const char *filename)
}
static void
-draw_ampersand (cairo_t *cr)
+ampersand_path (cairo_t *cr)
{
cairo_rel_move_to (cr, 5, 70.0);
cairo_select_font_face (cr, "Sans",
@@ -58,17 +58,29 @@ draw_ampersand (cairo_t *cr)
CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, 80);
cairo_text_path (cr, "&");
+
+}
+
+static void
+draw_ampersand (cairo_t *cr)
+{
+ ampersand_path (cr);
cairo_set_source_rgb (cr, DEST_COLOR);
cairo_fill (cr);
}
static void
-draw_polygon (cairo_t *cr)
+polygon_path (cairo_t *cr)
{
cairo_rel_move_to (cr, 10, 10);
cairo_rel_line_to (cr, 65, 35);
cairo_rel_line_to (cr, -38, 35);
-
+}
+
+static void
+draw_polygon (cairo_t *cr)
+{
+ polygon_path (cr);
cairo_set_source_rgb (cr, SOURCE_COLOR);
cairo_fill (cr);
}
@@ -95,26 +107,11 @@ create_polygon (void)
finish (cr, "polygon.png");
}
-static struct {
+typedef struct
+{
char name[16];
cairo_operator_t op;
-} ops[] =
- {
- "Source", CAIRO_OPERATOR_SOURCE,
- "Over", CAIRO_OPERATOR_OVER,
- "Atop", CAIRO_OPERATOR_ATOP,
- "In", CAIRO_OPERATOR_IN,
- "Out", CAIRO_OPERATOR_OUT,
-
- "Dest", CAIRO_OPERATOR_DEST,
- "Dest Over", CAIRO_OPERATOR_DEST_OVER,
- "Dest Atop", CAIRO_OPERATOR_DEST_ATOP,
- "Dest In", CAIRO_OPERATOR_DEST_IN,
- "Dest Out", CAIRO_OPERATOR_DEST_OUT,
-
- "Clear", CAIRO_OPERATOR_CLEAR,
- "Xor", CAIRO_OPERATOR_XOR,
- };
+} op_info_t;
static void
draw_source (cairo_t *cr)
@@ -143,7 +140,7 @@ centered_text (cairo_t *cr, double x, double y, const char *text)
cairo_text_path (cr, text);
cairo_path_extents (cr, &x1, &y1, &x2, &y2);
cairo_new_path (cr);
- printf ("in x %f %f %s\n", x, x2 - x1, text);
+ /* printf ("in x %f %f %s\n", x, x2 - x1, text); */
cairo_move_to (cr, x - (x2 - x1) / 2.0, y);
cairo_show_text (cr, text);
cairo_restore (cr);
@@ -162,7 +159,7 @@ draw_cell (cairo_t *cr,
{
cairo_save (cr);
- printf ("move to %f %f for %s\n", x, y, name);
+ /* 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);
@@ -194,28 +191,84 @@ draw_cell (cairo_t *cr,
cairo_restore (cr);
}
+#define COLS 4
+
static void
-create_porter_duff_table (void)
+draw_table (cairo_t *cr, const op_info_t *ops, int n_ops, int n_columns)
{
- cairo_t *cr = get_cairo (CELL_WIDTH * 5 + XPAD,
- (CELL_HEIGHT) * 3 + 2 * YPAD);
int i;
-
+
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
- for (i = 0; i < 12; ++i)
+ for (i = 0; i < n_ops; ++i)
{
- double x = CELL_WIDTH * (i % 5) + XPAD;
- double y = CELL_HEIGHT * (i / 5) + YPAD;
+ double x = CELL_WIDTH * (i % n_columns) + XPAD;
+ double y = CELL_HEIGHT * (i / n_columns) + YPAD;
draw_cell (cr, x, y, ops[i].op, ops[i].name);
}
-
+}
+
+static void
+create_porter_duff_table (void)
+{
+ static const op_info_t ops[] =
+ {
+ "Source", CAIRO_OPERATOR_SOURCE,
+ "Atop", CAIRO_OPERATOR_ATOP,
+ "Over", CAIRO_OPERATOR_OVER,
+ "In", CAIRO_OPERATOR_IN,
+ "Out", CAIRO_OPERATOR_OUT,
+
+ "Dest", CAIRO_OPERATOR_DEST,
+ "Dest Atop", CAIRO_OPERATOR_DEST_ATOP,
+ "Dest Over", CAIRO_OPERATOR_DEST_OVER,
+ "Dest In", CAIRO_OPERATOR_DEST_IN,
+ "Dest Out", CAIRO_OPERATOR_DEST_OUT,
+
+ "Clear", CAIRO_OPERATOR_CLEAR,
+ "Xor", CAIRO_OPERATOR_XOR,
+ };
+
+ cairo_t *cr = get_cairo (CELL_WIDTH * 5 + XPAD,
+ (CELL_HEIGHT) * 3 + 2 * YPAD);
+
+ draw_table (cr, ops, sizeof (ops) / sizeof (ops[0]), 5);
+
finish (cr, "table.png");
}
static void
+create_color_dodge_table (void)
+{
+ static const op_info_t ops[] =
+ {
+ "Clear", CAIRO_OPERATOR_CLEAR,
+ "Out", CAIRO_OPERATOR_OUT,
+ "Dest Out", CAIRO_OPERATOR_DEST_OUT,
+ "Xor", CAIRO_OPERATOR_XOR,
+
+ "In", CAIRO_OPERATOR_IN,
+ "Source", CAIRO_OPERATOR_SOURCE,
+ "Atop", CAIRO_OPERATOR_ATOP,
+ "Over", CAIRO_OPERATOR_OVER,
+
+ "Dest In", CAIRO_OPERATOR_DEST_IN,
+ "Dest Atop", CAIRO_OPERATOR_DEST_ATOP,
+ "Dest", CAIRO_OPERATOR_DEST,
+ "Dest Over", CAIRO_OPERATOR_DEST_OVER,
+ };
+
+ cairo_t *cr = get_cairo (CELL_WIDTH * 4 + XPAD,
+ (CELL_HEIGHT) * 4 + 2 * YPAD);
+
+ draw_table (cr, ops, sizeof (ops) / sizeof (ops[0]), 4);
+
+ finish (cr, "colordodge-table.png");
+}
+
+static void
create_op (cairo_operator_t op, const char *name, const char *filename)
{
cairo_t *cr = get_cairo (CELL_WIDTH + 20, CELL_HEIGHT);
@@ -291,11 +344,164 @@ dest_atop_diagram (const char *filename)
centered_text (cr, 145, 90, "Dest");
centered_text (cr, 90, 147, "Neither");
+ cairo_new_path (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ draw_pixel_box (cr);
+
+ finish (cr, filename);
+}
+
+static void
+color_dodge_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, 1, 1);
+ cairo_line_to (cr, 90, 90);
+ cairo_line_to (cr, 1, 179);
+ cairo_set_source_rgb (cr, SOURCE_COLOR);
+ cairo_fill (cr);
+
+ 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, 1, 1, 1);
+ centered_text (cr, 35, 90, "Source");
+
+ 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_atopish (void)
+{
+ cairo_t *cr = get_cairo (CELL_WIDTH + 20, CELL_HEIGHT);
+ cairo_pattern_t *pattern;
+
+ cairo_push_group (cr);
+
+ cairo_move_to (cr, 0, 0);
+
+ cairo_push_group (cr);
+
+ cairo_set_source_rgb (cr, SOURCE_COLOR);
+ cairo_paint (cr);
+
+ cairo_push_group (cr);
+ 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_paint (cr);
+ cairo_pop_group_to_source (cr);
+
+ cairo_move_to (cr, 30, 10);
+ ampersand_path (cr);
+ cairo_fill (cr);
+
+ cairo_pop_group_to_source (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_paint (cr);
+
+ cairo_pop_group_to_source (cr);
+
+ cairo_move_to (cr, 30, 10);
+ polygon_path (cr);
+
+ cairo_fill (cr);
+
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ centered_text (cr,
+ 30 + CELL_WIDTH / 2.0 - 20.0,
+ 10 + CELL_HEIGHT - 30, "asdfasdf");
+
+ finish (cr, "colordodge-atopish.png");
+}
+
+static void
+color_dodge_blend_only (void)
+{
+ cairo_t *cr = get_cairo (CELL_WIDTH + 20, CELL_HEIGHT);
+ cairo_pattern_t *pattern;
+
+ cairo_push_group (cr);
+ cairo_push_group (cr);
+ cairo_set_source_rgb (cr, DEST_COLOR);
+ cairo_paint (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
+ cairo_set_source_rgb (cr, SOURCE_COLOR);
+ cairo_paint (cr);
+ cairo_pop_group_to_source (cr);
+
+ cairo_move_to (cr, 30, 10);
+ ampersand_path (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_fill (cr);
+
+ cairo_pop_group_to_source (cr);
+
+ cairo_move_to (cr, 30, 10);
+ polygon_path (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_fill (cr);
+
+ finish (cr, "colordodge-blendonly.png");
+}
+
+static void
+dest_color_dodge (void)
+{
+ cairo_t *cr = get_cairo (CELL_WIDTH + 20, CELL_HEIGHT);
+ cairo_pattern_t *pattern;
+
+ cairo_move_to (cr, 30, 10);
+ draw_dest (cr);
+
+ cairo_push_group (cr);
+ cairo_push_group (cr);
+ 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_paint (cr);
+ cairo_pop_group_to_source (cr);
+ cairo_move_to (cr, 30, 10);
+ ampersand_path (cr);
+ cairo_fill (cr);
+ cairo_pop_group_to_source (cr);
+ cairo_move_to (cr, 30, 10);
+ polygon_path (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_fill (cr);
+
+ finish (cr, "asdf.png");
+}
+
int
main (int argc, char *argv)
{
@@ -307,4 +513,9 @@ main (int argc, char *argv)
create_porter_duff_table ();
general_pixel_diagram ("diagram.png");
dest_atop_diagram ("destatop-diagram.png");
+ color_dodge_diagram ("colordodge-diagram.png");
+ color_dodge_atopish ();
+ dest_color_dodge ();
+ color_dodge_blend_only ();
+ create_color_dodge_table ();
}