diff options
Diffstat (limited to 'plug-ins/selection-to-path/pxl-outline.c')
-rw-r--r-- | plug-ins/selection-to-path/pxl-outline.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/plug-ins/selection-to-path/pxl-outline.c b/plug-ins/selection-to-path/pxl-outline.c new file mode 100644 index 0000000..a339ff3 --- /dev/null +++ b/plug-ins/selection-to-path/pxl-outline.c @@ -0,0 +1,254 @@ +/* pxl-outline.c: find the edges of the bitmap image; we call each such + * edge an ``outline''; each outline is made up of one or more pixels; + * and each pixel participates via one or more edges. + * + * 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 "global.h" +#include "selection-to-path.h" +#include "bitmap.h" +#include "edge.h" +#include "pxl-outline.h" + +#include "libgimp/stdplugins-intl.h" + +static pixel_outline_type find_one_outline (edge_type, + unsigned, unsigned, bitmap_type *); +static void append_pixel_outline (pixel_outline_list_type *, + pixel_outline_type); +static pixel_outline_type new_pixel_outline (void); +static void append_outline_pixel (pixel_outline_type *, coordinate_type); +static void append_coordinate (pixel_outline_type *, int, int, edge_type); + + +static bitmap_type +local_new_bitmap (unsigned width,unsigned height) +{ + bitmap_type answer; + unsigned size = width * height; + + + BITMAP_HEIGHT(answer) = height; + BITMAP_WIDTH(answer) = width; + + BITMAP_BITS (answer) = g_new0 (one_byte, size); /* g_new returns NULL if size == 0 */ + +/* printf("local_new_bitmap size = %d @[%p]\n",size,BITMAP_BITS (answer)); */ + + return answer; +} + +static void +local_free_bitmap (bitmap_type *b) +{ + if (BITMAP_BITS (*b) != NULL) + safe_free ((address *) &BITMAP_BITS (*b)); +} + +/* A character is made up of a list of one or more outlines. Here, we + go through a character's bitmap top to bottom, left to right, looking + for the next pixel with an unmarked edge also on the character's outline. + Each one of these we find is the starting place for one outline. We + find these outlines and put them in a list to return. */ + +pixel_outline_list_type +find_outline_pixels (void) +{ + pixel_outline_list_type outline_list; + unsigned row, col; + gint height; + gint width; + bitmap_type marked = local_new_bitmap (sel_get_width(),sel_get_height()); + +/* printf("width = %d, height = %d\n",BITMAP_WIDTH(marked),BITMAP_HEIGHT(marked)); */ + + gimp_progress_init (_("Selection to Path")); + + O_LIST_LENGTH (outline_list) = 0; + outline_list.data = NULL; + + height = sel_get_height (); + width = sel_get_width (); + + for (row = 0; row < height; row++) + { + for (col = 0; col < width; col++) + { + edge_type edge; + + if (sel_pixel_is_white(row, col)) + continue; + + edge = next_unmarked_outline_edge (row, col, START_EDGE,marked); + + if (edge != no_edge) + { + pixel_outline_type outline; + boolean clockwise = edge == bottom; + + outline = find_one_outline (edge, row, col, &marked); + + /* Outside outlines will start at a top edge, and move + counterclockwise, and inside outlines will start at a + bottom edge, and move clockwise. This happens because of + the order in which we look at the edges. */ + O_CLOCKWISE (outline) = clockwise; + append_pixel_outline (&outline_list, outline); + + } + } + + if ((row & 0xf) == 0) + gimp_progress_update (((gdouble)row) / height); + } + + gimp_progress_update (1.0); + + local_free_bitmap (&marked); + + return outline_list; +} + + +/* Here we find one of a character C's outlines. We're passed the + position (ORIGINAL_ROW and ORIGINAL_COL) of a starting pixel and one + of its unmarked edges, ORIGINAL_EDGE. We traverse the adjacent edges + of the outline pixels, appending to the coordinate list. We keep + track of the marked edges in MARKED, so it should be initialized to + zeros when we first get it. */ + +static pixel_outline_type +find_one_outline (edge_type original_edge, + unsigned original_row, unsigned original_col, + bitmap_type *marked) +{ + pixel_outline_type outline = new_pixel_outline (); + unsigned row = original_row, col = original_col; + edge_type edge = original_edge; + + do + { + /* Put this edge on to the output list, changing to Cartesian, and + taking account of the side bearings. */ + append_coordinate (&outline, col, + sel_get_height() - row, edge); + + mark_edge (edge, row, col, marked); + next_outline_edge (&edge, &row, &col); + } + while (row != original_row || col != original_col || edge != original_edge); + + return outline; +} + + +/* Append an outline to an outline list. This is called when we have + completed an entire pixel outline. */ + +static void +append_pixel_outline (pixel_outline_list_type *outline_list, + pixel_outline_type outline) +{ + O_LIST_LENGTH (*outline_list)++; + outline_list->data = (pixel_outline_type *)g_realloc(outline_list->data,outline_list->length *sizeof(pixel_outline_type)); + O_LIST_OUTLINE (*outline_list, O_LIST_LENGTH (*outline_list) - 1) = outline; +} + + +/* Here is a routine that frees a list of such lists. */ + +void +free_pixel_outline_list (pixel_outline_list_type *outline_list) +{ + unsigned this_outline; + + for (this_outline = 0; this_outline < outline_list->length; this_outline++) + { + pixel_outline_type o = outline_list->data[this_outline]; + safe_free ((address *) &(o.data)); + } + + if (outline_list->data != NULL) + safe_free ((address *) &(outline_list->data)); +} + + +/* Return an empty list of pixels. */ + + +static pixel_outline_type +new_pixel_outline (void) +{ + pixel_outline_type pixel_outline; + + O_LENGTH (pixel_outline) = 0; + pixel_outline.data = NULL; + + return pixel_outline; +} + + +/* Add the coordinate C to the pixel list O. */ + +static void +append_outline_pixel (pixel_outline_type *o, coordinate_type c) +{ + O_LENGTH (*o)++; + o->data = (coordinate_type *)g_realloc(o->data, O_LENGTH (*o)*sizeof(coordinate_type)); + O_COORDINATE (*o, O_LENGTH (*o) - 1) = c; +} + + +/* We are given an (X,Y) in Cartesian coordinates, and the edge of the pixel + we're on. We append a corner of that pixel as our coordinate. + If we're on a top edge, we use the upper-left hand corner; right edge + => upper right; bottom edge => lower right; left edge => lower left. */ + +static void +append_coordinate (pixel_outline_type *o, int x, int y, edge_type edge) +{ + coordinate_type c; + + c.x = x; + c.y = y; + + switch (edge) + { + case top: + c.y++; + break; + + case right: + c.x++; + c.y++; + break; + + case bottom: + c.x++; + break; + + case left: + break; + + default: + g_printerr ("append_coordinate: Bad edge (%d)", edge); + } + + append_outline_pixel (o, c); +} |