summaryrefslogtreecommitdiffstats
path: root/plug-ins/selection-to-path/edge.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/selection-to-path/edge.c')
-rw-r--r--plug-ins/selection-to-path/edge.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/plug-ins/selection-to-path/edge.c b/plug-ins/selection-to-path/edge.c
new file mode 100644
index 0000000..8aa20be
--- /dev/null
+++ b/plug-ins/selection-to-path/edge.c
@@ -0,0 +1,268 @@
+/* edge.c: operations on edges in bitmaps.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+
+#include "types.h"
+#include "selection-to-path.h"
+#include "edge.h"
+
+/* We can move in any of eight directions as we are traversing
+ the outline. These numbers are not arbitrary; TRY_PIXEL depends on
+ them. */
+
+typedef enum
+{
+ north = 0, northwest = 1, west = 2, southwest = 3, south = 4,
+ southeast = 5, east = 6, northeast = 7
+} direction_type;
+
+
+static boolean is_marked_edge (edge_type, unsigned, unsigned, bitmap_type);
+static boolean is_outline_edge (edge_type, unsigned, unsigned);
+static edge_type next_edge (edge_type);
+
+/* The following macros are used (directly or indirectly) by the
+ `next_outline_edge' routine. */
+
+/* Given the direction DIR of the pixel to test, decide which edge on
+ that pixel we are supposed to test. Because we've chosen the mapping
+ from directions to numbers carefully, we don't have to do much. */
+
+#define FIND_TEST_EDGE(dir) ((dir) / 2)
+
+
+/* Find how to move in direction DIR on the axis AXIS (either `ROW' or
+ `COL'). We are in the ``display'' coordinate system, with y
+ increasing downward and x increasing to the right. Therefore, we are
+ implementing the following table:
+
+ direction row delta col delta
+ north -1 0
+ south +1 0
+ east 0 +1
+ west 0 +1
+
+ with the other four directions (e.g., northwest) being the sum of
+ their components (e.g., north + west).
+
+ The first macro, `COMPUTE_DELTA', handles splitting up the latter
+ cases, all of which have been assigned odd numbers. */
+
+#define COMPUTE_DELTA(axis, dir) \
+ ((dir) % 2 != 0 \
+ ? COMPUTE_##axis##_DELTA ((dir) - 1) \
+ + COMPUTE_##axis##_DELTA (((dir) + 1) % 8) \
+ : COMPUTE_##axis##_DELTA (dir) \
+ )
+
+/* Now it is trivial to implement the four cardinal directions. */
+#define COMPUTE_ROW_DELTA(dir) \
+ ((dir) == north ? -1 : (dir) == south ? +1 : 0)
+
+#define COMPUTE_COL_DELTA(dir) \
+ ((dir) == west ? -1 : (dir) == east ? +1 : 0)
+
+
+/* See if the appropriate edge on the pixel from (row,col) in direction
+ DIR is on the outline. If so, update `row', `col', and `edge', and
+ break. We also use the variable `character' as the bitmap in which
+ to look. */
+
+#define TRY_PIXEL(dir) \
+ { \
+ int delta_r = COMPUTE_DELTA (ROW, dir); \
+ int delta_c = COMPUTE_DELTA (COL, dir); \
+ int test_row = *row + delta_r; \
+ int test_col = *col + delta_c; \
+ edge_type test_edge = FIND_TEST_EDGE (dir); \
+ \
+ if (sel_valid_pixel(test_row, test_col) \
+ && is_outline_edge (test_edge, test_row, test_col)) \
+ { \
+ *row = test_row; \
+ *col = test_col; \
+ *edge = test_edge; \
+ break; \
+ } \
+ }
+
+/* Finally, we are ready to implement the routine that finds the next
+ edge on the outline. We look first for an adjacent edge that is not
+ on the current pixel. We want to go around outside outlines
+ counterclockwise, and inside outlines clockwise (because that is how
+ both Metafont and Adobe Type 1 format want their curves to be drawn).
+
+ The very first outline (an outside one) on each character starts on a
+ top edge (STARTING_EDGE in edge.h defines this); so, if we're at a
+ top edge, we want to go only to the left (on the pixel to the west)
+ or down (on the same pixel), to begin with. Then, when we're on a
+ left edge, we want to go to the top edge (on the southwest pixel) or
+ to the left edge (on the south pixel).
+
+ All well and good. But if you draw a rasterized circle (or whatever),
+ eventually we have to come back around to the beginning; at that
+ point, we'll be on a top edge, and we'll have to go to the right edge
+ on the northwest pixel. Draw pictures.
+
+ The upshot is, if we find an edge on another pixel, we return (in ROW
+ and COL) the position of the new pixel, and (in EDGE) the kind of
+ edge it is. If we don't find such an edge, we return (in EDGE) the
+ next (in a counterclockwise direction) edge on the current pixel. */
+
+void
+next_outline_edge (edge_type *edge,
+ unsigned *row, unsigned *col)
+{
+ unsigned original_row = *row;
+ unsigned original_col = *col;
+
+ switch (*edge)
+ {
+ case right:
+ TRY_PIXEL (north);
+ TRY_PIXEL (northeast);
+ break;
+
+ case top:
+ TRY_PIXEL (west);
+ TRY_PIXEL (northwest);
+ break;
+
+ case left:
+ TRY_PIXEL (south);
+ TRY_PIXEL (southwest);
+ break;
+
+ case bottom:
+ TRY_PIXEL (east);
+ TRY_PIXEL (southeast);
+ break;
+
+ default:
+ printf ("next_outline_edge: Bad edge value (%d)", *edge);
+
+ }
+
+ /* If we didn't find an adjacent edge on another pixel, return the
+ next edge on the current pixel. */
+ if (*row == original_row && *col == original_col)
+ *edge = next_edge (*edge);
+}
+
+/* We return the next edge on the pixel at position ROW and COL which is
+ an unmarked outline edge. By ``next'' we mean either the one sent in
+ in STARTING_EDGE, if it qualifies, or the next such returned by
+ `next_edge'. */
+
+edge_type
+next_unmarked_outline_edge (unsigned row, unsigned col,
+ edge_type starting_edge,
+ bitmap_type marked)
+{
+ edge_type edge = starting_edge;
+
+ assert (edge != no_edge);
+
+ while (is_marked_edge (edge, row, col, marked)
+ || !is_outline_edge (edge, row, col))
+ {
+ edge = next_edge (edge);
+ if (edge == starting_edge)
+ return no_edge;
+ }
+
+ return edge;
+}
+
+
+/* We check to see if the edge EDGE of the pixel at position ROW and COL
+ is an outline edge; i.e., that it is a black pixel which shares that
+ edge with a white pixel. The position ROW and COL should be inside
+ the bitmap CHARACTER. */
+
+static boolean
+is_outline_edge (edge_type edge,
+ unsigned row, unsigned col)
+{
+ /* If this pixel isn't black, it's not part of the outline. */
+ if (sel_pixel_is_white(row, col))
+ return false;
+
+ switch (edge)
+ {
+ case left:
+ return col == 0 || sel_pixel_is_white(row, col - 1);
+
+ case top:
+ return row == 0 || sel_pixel_is_white(row - 1, col);
+
+ case right:
+ return (col == sel_get_width() - 1)
+ || sel_pixel_is_white(row, col + 1);
+
+ case bottom:
+ return (row == sel_get_height() - 1)
+ || sel_pixel_is_white(row + 1, col);
+
+ case no_edge:
+ default:
+ printf ("is_outline_edge: Bad edge value(%d)", edge);
+ }
+
+ return 0; /* NOTREACHED */
+}
+
+/* If EDGE is not already marked, we mark it; otherwise, it's a fatal error.
+ The position ROW and COL should be inside the bitmap MARKED. EDGE can
+ be `no_edge'; we just return false. */
+
+void
+mark_edge (edge_type edge, unsigned row, unsigned col, bitmap_type *marked)
+{
+ /* printf("row = %d, col = %d \n",row,col); */
+ assert (!is_marked_edge (edge, row, col, *marked));
+
+ if (edge != no_edge)
+ BITMAP_PIXEL (*marked, row, col) |= 1 << edge;
+}
+
+
+/* Test if the edge EDGE at ROW/COL in MARKED is marked. */
+
+static boolean
+is_marked_edge (edge_type edge, unsigned row, unsigned col, bitmap_type marked)
+{
+ return
+ edge == no_edge ? false : BITMAP_PIXEL (marked, row, col) & (1 << edge);
+}
+
+
+/* Return the edge which is counterclockwise-adjacent to EDGE. This
+ code makes use of the ``numericness'' of C enumeration constants;
+ sorry about that. */
+
+#define NUM_EDGES no_edge
+
+static edge_type
+next_edge (edge_type edge)
+{
+ return edge == no_edge ? edge : (edge + 1) % NUM_EDGES;
+}