summaryrefslogtreecommitdiffstats
path: root/plug-ins/selection-to-path/pxl-outline.c
blob: a339ff3dbc8c51beba3678be54bae59eed3dec62 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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);
}