Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

View File

@ -0,0 +1,51 @@
Andy Thomas (alt@gimp.org) 9th July 1999
This plug-in will take a selection and convert it into a path.
For the purpose of the plug-in the selection boundary is defined
in a similar manner to that worked out for the "marching ants" markers
of the selection. I think this gives the best user feel/feedback since
the created path "follows" the "marching ants".
I cannot claim responsibility for the underlying algorithms. These
were taken directly from the GNU font utilities (the "limn" program
in particular) written by Karl Berry and Kathryn Hargreaves.
Their email addresses quoted in the README are:-
Karl Berry karl@cs.umb.edu
Kathryn Hargreaves letters@cs.umb.edu
Please see fontutils-0.6 package for more details. I have included the
README from the limn part of the package.
I thank Karl & Kathryn for producing such a well written set of utilities.
I have just added a pika front-end onto them.
How to use it.
~~~~~~~~~~~~~~
Simply select an area and then select either "<Image>/Selection/To Path"
menu item or the "Selection To Image" button in the paths dialog. The new
path will be created. Currently if the LCP dialog has not been activated
then the path will not be visible... A bug I have just found - simply
bring up the LCP dialog and select the Paths tab to see the newly created
path.
An additional function can be obtained by having the "Shift" modifier pressed
while using the button in the paths dialog. This will pop-up a "power-users"
menu where the parameters to the underlying algorithms can be modified.
WARNING:- Some values may cause the plugin to enter extremely long operations.
You have been warned.
Have fun!
Andy.
PS. Please direct any bugs etc found in this plugin to either
myself or the pika-developer mailing list. Thank.

View File

@ -0,0 +1,56 @@
This program converts bitmap fonts to a homegrown outline format, bezier
(BZR). The program `bzrto' converts that format to something usable for
typesetting.
We used two main sources in writing the program:
@mastersthesis{Schneider:PIC-88,
author = "Philip J. Schneider",
title = "Phoenix: An Interactive Curve Design System Based on the
Automatic Fitting of Hand-Sketched Curves",
school = inst-u-wash,
year = 1988,
}
@article{Plass:CG-17-229,
author = "Michael Plass and Maureen Stone",
title = "Curve-fitting with Piecewise Parametric Cubics",
journal = j-comp-graphics,
year = 1983,
volume = 17,
number = 3,
month = jul,
pages = "229-239",
}
We had access to the code for Phoenix, thanks to Philip, but none of our
code is based on his (mostly because his task was allow interactive
sketching, and ours to fit bitmap characters, and the two require
different data structures). The general outline of the fitting
algorithm does come from Phoenix.
We also found this article helpful:
@Inproceedings{Gonczarowski:RIDT91-1,
author = "Jakob Gonczarowski",
title = "A Fast Approach to Auto-tracing (with Parametric
Cubics)",
pages = "1--15",
crossref = "Morris:RIDT91",
acknowledgement = ack-kb,
}
@String{proc-RIDT91 = "Raster Imaging and Digital Typography II"}
@Proceedings{Morris:RIDT91,
title = proc-RIDT91,
booktitle = proc-RIDT91,
year = "1991",
editor = "Robert A. Morris and Jacques Andr{\'e}",
publisher = pub-CUP,
address = pub-CUP:adr,
acknowledgement = ack-kb,
}
(These BibTeX entries are from the type.bib and ep.bib files on
math.utah.edu:pub/tex/bib.)

View File

@ -0,0 +1,112 @@
/* bitmap.h: definition for a bitmap type. No packing is done by
* default; each pixel is represented by an entire byte. Among other
* things, this means the type can be used for both grayscale and binary
* images.
*
* 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/>.
*/
#ifndef BITMAP_H
#define BITMAP_H
#include <stdio.h>
#include "bounding-box.h"
#include "types.h"
/* If the bitmap holds 8-bit values, rather than one-bit, the
definition of BLACK here is wrong. So don't use it in that case! */
#define WHITE 0
#define BLACK 1
/* The basic structure and macros to access it. */
typedef struct
{
dimensions_type dimensions;
one_byte *bitmap;
} bitmap_type;
/* The dimensions of the bitmap, in pixels. */
#define BITMAP_DIMENSIONS(b) ((b).dimensions)
/* The pixels, represented as an array of bytes (in contiguous storage).
Each pixel is a single byte, even for binary fonts. */
#define BITMAP_BITS(b) ((b).bitmap)
/* These are convenient abbreviations for getting inside the members. */
#define BITMAP_WIDTH(b) DIMENSIONS_WIDTH (BITMAP_DIMENSIONS (b))
#define BITMAP_HEIGHT(b) DIMENSIONS_HEIGHT (BITMAP_DIMENSIONS (b))
/* This is the address of the first pixel in the row ROW. */
#define BITMAP_ROW(b, row) (BITMAP_BITS (b) + (row) * BITMAP_WIDTH (b))
/* This is the pixel at [ROW,COL]. */
#define BITMAP_PIXEL(b, row, col) \
(*(BITMAP_BITS (b) + (row) * BITMAP_WIDTH (b) + (col)))
#define BITMAP_VALID_PIXEL(b, row, col) \
(0 <= (row) && (row) < BITMAP_HEIGHT (b) \
&& 0 <= (col) && (col) < BITMAP_WIDTH (b))
/* Assume that the pixel at [ROW,COL] itself is black. */
#define BITMAP_INTERIOR_PIXEL(b, row, col) \
(0 != (row) && (row) != BITMAP_HEIGHT (b) - 1 \
&& 0 != (col) && (col) != BITMAP_WIDTH (b) - 1 \
&& BITMAP_PIXEL (b, row - 1, col - 1) == BLACK \
&& BITMAP_PIXEL (b, row - 1, col) == BLACK \
&& BITMAP_PIXEL (b, row - 1, col + 1) == BLACK \
&& BITMAP_PIXEL (b, row, col - 1) == BLACK \
&& BITMAP_PIXEL (b, row, col + 1) == BLACK \
&& BITMAP_PIXEL (b, row + 1, col - 1) == BLACK \
&& BITMAP_PIXEL (b, row + 1, col) == BLACK \
&& BITMAP_PIXEL (b, row + 1, col + 1) == BLACK)
/* Allocate storage for the bits, set them all to white, and return an
initialized structure. */
extern bitmap_type new_bitmap (dimensions_type);
/* Free that storage. */
extern void free_bitmap (bitmap_type *);
/* Make a fresh copy of BITMAP in a new structure, and return it. */
extern bitmap_type copy_bitmap (bitmap_type bitmap);
/* Return the pixels in the bitmap B enclosed by the bounding box BB.
The result is put in newly allocated storage. */
extern bitmap_type extract_subbitmap (bitmap_type b, bounding_box_type bb);
/* Consider the dimensions of a bitmap as a bounding box. The bounding
box returned is in bitmap coordinates, rather than Cartesian, and
refers to pixels, rather than edges. Specifically, this means that
the maximum column is one less than results from `dimensions_to_bb
(BITMAP_DIMENSIONS ())'. */
extern bounding_box_type bitmap_to_bb (const bitmap_type);
/* Return a vector of zero-based column numbers marking transitions from
black to white or white to black in ROW, which is of length WIDTH.
The end of the vector is marked with an element of length WIDTH + 1.
The first element always marks a white-to-black transition (or it's
0, if the first pixel in ROW is black). */
extern unsigned *bitmap_find_transitions (const one_byte *row, unsigned width);
/* Print part of or all of a bitmap. */
extern void print_bounded_bitmap (FILE *, bitmap_type, bounding_box_type);
extern void print_bitmap (FILE *, bitmap_type);
#endif /* not BITMAP_H */

View File

@ -0,0 +1,63 @@
/* bounding-box.h: operations on both real- and integer-valued bounding boxes.
*
* 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/>.
*/
#ifndef BOUNDING_BOX_H
#define BOUNDING_BOX_H
#include "types.h"
/* The bounding box's numbers are usually in Cartesian/Metafont
coordinates: (0,0) is towards the lower left. */
typedef struct
{
signed_4_bytes min_row, max_row;
signed_4_bytes min_col, max_col;
} bounding_box_type;
typedef struct
{
real min_row, max_row;
real min_col, max_col;
} real_bounding_box_type;
/* These accessing macros work for both types of bounding boxes, since
the member names are the same. */
#define MIN_ROW(bb) ((bb).min_row)
#define MAX_ROW(bb) ((bb).max_row)
#define MIN_COL(bb) ((bb).min_col)
#define MAX_COL(bb) ((bb).max_col)
/* See the comments at `get_character_bitmap' in gf_input.c for why the
width and height are treated asymmetrically. */
#define BB_WIDTH(bb) (MAX_COL (bb) - MIN_COL (bb))
#define BB_HEIGHT(bb) (MAX_ROW (bb) - MIN_ROW (bb) + 1)
/* Convert a dimensions structure to an integer bounding box, and vice
versa. */
extern bounding_box_type dimensions_to_bb (dimensions_type);
extern dimensions_type bb_to_dimensions (bounding_box_type);
/* Update the bounding box BB from the point P. */
extern void update_real_bounding_box (real_bounding_box_type *bb,
real_coordinate_type p);
extern void update_bounding_box (bounding_box_type *bb, coordinate_type p);
#endif /* not BOUNDING_BOX_H */

View File

@ -0,0 +1,184 @@
/* curve.c: operations on the lists of pixels and lists of curves.
*
* 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 <glib.h>
#include "global.h"
#include "curve.h"
/* Return an entirely empty curve. */
curve_type
new_curve (void)
{
curve_type curve = g_new (struct curve, 1);
curve->point_list = NULL;
CURVE_LENGTH (curve) = 0;
CURVE_CYCLIC (curve) = false;
CURVE_START_TANGENT (curve) = CURVE_END_TANGENT (curve) = NULL;
PREVIOUS_CURVE (curve) = NEXT_CURVE (curve) = NULL;
return curve;
}
/* Start the returned curve off with COORD as the first point. */
curve_type
init_curve (coordinate_type coord)
{
curve_type curve = new_curve ();
curve->point_list = g_new (point_type, 1);
CURVE_LENGTH (curve) = 1;
CURVE_POINT (curve, 0) = int_to_real_coord (coord);
return curve;
}
/* Don't copy the points or tangents, but copy everything else. */
curve_type
copy_most_of_curve (curve_type old_curve)
{
curve_type curve = new_curve ();
CURVE_CYCLIC (curve) = CURVE_CYCLIC (old_curve);
PREVIOUS_CURVE (curve) = PREVIOUS_CURVE (old_curve);
NEXT_CURVE (curve) = NEXT_CURVE (old_curve);
return curve;
}
/* The length of CURVE will be zero if we ended up not being able to fit
it (which in turn implies a problem elsewhere in the program, but at
any rate, we shouldn't try here to free the nonexistent curve). */
void
free_curve (curve_type curve)
{
if (CURVE_LENGTH (curve) > 0)
safe_free ((address *) &(curve->point_list));
}
void
append_pixel (curve_type curve, coordinate_type coord)
{
append_point (curve, int_to_real_coord (coord));
}
void
append_point (curve_type curve, real_coordinate_type coord)
{
CURVE_LENGTH (curve)++;
curve->point_list = g_realloc (curve->point_list,CURVE_LENGTH (curve) * sizeof(point_type));
LAST_CURVE_POINT (curve) = coord;
/* The t value does not need to be set. */
}
/* Return an initialized but empty curve list. */
curve_list_type
new_curve_list (void)
{
curve_list_type curve_list;
curve_list.length = 0;
curve_list.data = NULL;
curve_list.clockwise = FALSE;
return curve_list;
}
/* Free a curve list and all the curves it contains. */
void
free_curve_list (curve_list_type *curve_list)
{
unsigned this_curve;
for (this_curve = 0; this_curve < curve_list->length; this_curve++)
free_curve (curve_list->data[this_curve]);
/* If the character was empty, it won't have any curves. */
if (curve_list->data != NULL)
safe_free ((address *) &(curve_list->data));
}
/* Add an element to a curve list. */
void
append_curve (curve_list_type *curve_list, curve_type curve)
{
curve_list->length++;
curve_list->data = g_realloc (curve_list->data,curve_list->length*sizeof(curve_type));
curve_list->data[curve_list->length - 1] = curve;
}
/* Return an initialized but empty curve list array. */
curve_list_array_type
new_curve_list_array (void)
{
curve_list_array_type curve_list_array;
CURVE_LIST_ARRAY_LENGTH (curve_list_array) = 0;
curve_list_array.data = NULL;
return curve_list_array;
}
/* Free a curve list array and all the curve lists it contains. */
void
free_curve_list_array (curve_list_array_type *curve_list_array)
{
unsigned this_list;
for (this_list = 0; this_list < CURVE_LIST_ARRAY_LENGTH (*curve_list_array);
this_list++)
free_curve_list (&CURVE_LIST_ARRAY_ELT (*curve_list_array, this_list));
/* If the character was empty, it won't have any curves. */
if (curve_list_array->data != NULL)
safe_free ((address *) &(curve_list_array->data));
}
/* Add an element to a curve list array. */
void
append_curve_list (curve_list_array_type *l, curve_list_type curve_list)
{
CURVE_LIST_ARRAY_LENGTH (*l)++;
l->data = g_realloc (l->data,( CURVE_LIST_ARRAY_LENGTH (*l))*sizeof(curve_list_type));
LAST_CURVE_LIST_ARRAY_ELT (*l) = curve_list;
}

View File

@ -0,0 +1,157 @@
/* curve.h: data structures for the conversion from pixels to splines.
*
* 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/>.
*/
#ifndef CURVE_H
#define CURVE_H
#include "types.h"
#include "vector.h"
/* We are simultaneously manipulating two different representations of
the same outline: one based on (x,y) positions in the plane, and one
based on parametric splines. (We are trying to match the latter to
the former.) Although the original (x,y)'s are pixel positions,
i.e., integers, after filtering they are reals. */
typedef struct
{
real_coordinate_type coord;
real t;
} point_type;
/* It turns out to be convenient to break the list of all the pixels in
the outline into sublists, divided at ``corners''. Then each of the
sublists is treated independently. Each of these sublists is a `curve'. */
struct curve
{
point_type *point_list;
int length;
boolean cyclic;
vector_type *start_tangent;
vector_type *end_tangent;
struct curve *previous;
struct curve *next;
};
typedef struct curve *curve_type;
/* Get at the coordinates and the t values. */
#define CURVE_POINT(c, n) ((c)->point_list[n].coord)
#define LAST_CURVE_POINT(c) ((c)->point_list[(c)->length-1].coord)
#define CURVE_T(c, n) ((c)->point_list[n].t)
#define LAST_CURVE_T(c) ((c)->point_list[(c)->length-1].t)
/* This is the length of `point_list'. */
#define CURVE_LENGTH(c) ((c)->length)
/* A curve is ``cyclic'' if it didn't have any corners, after all, so
the last point is adjacent to the first. */
#define CURVE_CYCLIC(c) ((c)->cyclic)
/* If the curve is cyclic, the next and previous points should wrap
around; otherwise, if we get to the end, we return CURVE_LENGTH and
-1, respectively. */
#define CURVE_NEXT(c, n) \
((n) + 1 >= CURVE_LENGTH (c) \
? CURVE_CYCLIC (c) ? ((n) + 1) % CURVE_LENGTH (c) : CURVE_LENGTH (c) \
: (n) + 1)
#define CURVE_PREV(c, n) \
((int) (n) - 1 < 0 \
? CURVE_CYCLIC (c) ? CURVE_LENGTH (c) + (int) (n) - 1 : -1 \
: (int) (n) - 1)
/* The tangents at the endpoints are computed using the neighboring curves. */
#define CURVE_START_TANGENT(c) ((c)->start_tangent)
#define CURVE_END_TANGENT(c) ((c)->end_tangent)
#define PREVIOUS_CURVE(c) ((c)->previous)
#define NEXT_CURVE(c) ((c)->next)
/* Return an entirely empty curve. */
extern curve_type new_curve (void);
/* Return a curve with the point P as its first element. */
extern curve_type init_curve (coordinate_type p);
/* Return a curve the same as C, except without any points. */
extern curve_type copy_most_of_curve (curve_type c);
/* Free the memory C uses. */
extern void free_curve (curve_type c);
/* Append the point P to the end of C's list. */
extern void append_pixel (curve_type c, coordinate_type p);
/* Like `append_pixel', for a point in real coordinates. */
extern void append_point (curve_type c, real_coordinate_type p);
/* Write some or all, respectively, of the curve C in human-readable
form to the log file, if logging is enabled. */
extern void log_curve (curve_type c, boolean print_t);
extern void log_entire_curve (curve_type c);
/* Display the curve C online, if displaying is enabled. */
extern void display_curve (curve_type);
/* So, an outline is a list of curves. */
typedef struct
{
curve_type *data;
unsigned length;
boolean clockwise;
} curve_list_type;
/* Number of curves in the list. */
#define CURVE_LIST_LENGTH(c_l) ((c_l).length)
/* Access the individual curves. */
#define CURVE_LIST_ELT(c_l, n) ((c_l).data[n])
#define LAST_CURVE_LIST_ELT(c_l) ((c_l).data[CURVE_LIST_LENGTH (c_l) - 1])
/* Says whether the outline that this curve list represents moves
clockwise or counterclockwise. */
#define CURVE_LIST_CLOCKWISE(c_l) ((c_l).clockwise)
extern curve_list_type new_curve_list (void);
extern void free_curve_list (curve_list_type *);
extern void append_curve (curve_list_type *, curve_type);
/* And a character is a list of outlines. I named this
`curve_list_array_type' because `curve_list_list_type' seemed pretty
monstrous. */
typedef struct
{
curve_list_type *data;
unsigned length;
} curve_list_array_type;
/* Turns out we can use the same definitions for lists of lists as for
just lists. But we define the usual names, just in case. */
#define CURVE_LIST_ARRAY_LENGTH CURVE_LIST_LENGTH
#define CURVE_LIST_ARRAY_ELT CURVE_LIST_ELT
#define LAST_CURVE_LIST_ARRAY_ELT LAST_CURVE_LIST_ELT
extern curve_list_array_type new_curve_list_array (void);
extern void free_curve_list_array (curve_list_array_type *);
extern void append_curve_list (curve_list_array_type *, curve_list_type);
#endif /* not CURVE_H */

View File

@ -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;
}

View File

@ -0,0 +1,59 @@
/* edge.h: declarations for edge traversing.
*
* 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/>.
*/
#ifndef EDGE_H
#define EDGE_H
#include "bitmap.h"
/* We consider each pixel to consist of four edges, and we travel along
edges, instead of through pixel centers. This is necessary for those
unfortunate times when a single pixel is on both an inside and an
outside outline.
The numbers chosen here are not arbitrary; the code that figures out
which edge to move to depends on particular values. See the
`TRY_PIXEL' macro in `edge.c'. To emphasize this, I've written in the
numbers we need for each edge value. */
typedef enum
{
top = 1, left = 2, bottom = 3, right = 0, no_edge = 4
} edge_type;
/* This choice is also not arbitrary: starting at the top edge makes the
code find outside outlines before inside ones, which is certainly
what we want. */
#define START_EDGE top
/* Return the next outline edge on B in EDGE, ROW, and COL. */
extern void next_outline_edge (edge_type *edge,
unsigned *row, unsigned *col);
/* Return the next edge after START on the pixel ROW/COL in B that is
unmarked, according to the MARKED array. */
extern edge_type next_unmarked_outline_edge (unsigned row, unsigned col,
edge_type start,
bitmap_type marked);
/* Mark the edge E at the pixel ROW/COL in MARKED. */
extern void mark_edge (edge_type e, unsigned row, unsigned col,
bitmap_type *marked);
#endif /* not EDGE_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
/* fit.h: convert the pixel representation to splines.
*
* 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/>.
*/
#ifndef FIT_H
#define FIT_H
#include "pxl-outline.h"
#include "spline.h"
/* See fit.c for descriptions of these variables, all of which can be
set using options. */
extern real align_threshold;
extern real corner_always_threshold;
extern unsigned corner_surround;
extern real corner_threshold;
extern real error_threshold;
extern unsigned filter_alternative_surround;
extern real filter_epsilon;
extern unsigned filter_iteration_count;
extern real filter_percent;
extern unsigned filter_surround;
extern boolean keep_knees;
extern real line_reversion_threshold;
extern real line_threshold;
extern real reparameterize_improvement;
extern real reparameterize_threshold;
extern real subdivide_search;
extern unsigned subdivide_surround;
extern real subdivide_threshold;
extern unsigned tangent_surround;
/* Fit splines and lines to LIST. */
extern spline_list_array_type fitted_splines (pixel_outline_list_type list);
void fit_set_params(SELVALS *);
void fit_set_default_params(SELVALS *);
#endif /* not FIT_H */

View File

@ -0,0 +1,213 @@
/* global.h: extend the standard programming environment a little. This
* is included from config.h, which everyone includes.
*
* 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/>.
*/
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdlib.h>
#include "types.h"
/* Define common sorts of messages. */
/* This should be called only after a system call fails. */
#define FATAL_PERROR(s) do { perror (s); exit (errno); } while (0)
#define START_FATAL() do { fputs ("fatal: ", stderr)
#define END_FATAL() fputs (".\n", stderr); exit (1); } while (0)
#define FATAL(x) \
START_FATAL (); fprintf (stderr, "%s", x); END_FATAL ()
#define FATAL1(s, e1) \
START_FATAL (); fprintf (stderr, s, e1); END_FATAL ()
#define FATAL2(s, e1, e2) \
START_FATAL (); fprintf (stderr, s, e1, e2); END_FATAL ()
#define FATAL3(s, e1, e2, e3) \
START_FATAL (); fprintf (stderr, s, e1, e2, e3); END_FATAL ()
#define FATAL4(s, e1, e2, e3, e4) \
START_FATAL (); fprintf (stderr, s, e1, e2, e3, e4); END_FATAL ()
#define START_WARNING() do { fputs ("warning: ", stderr)
#define END_WARNING() fputs (".\n", stderr); fflush (stderr); } while (0)
#define WARNING(x) \
START_WARNING (); fprintf (stderr, "%s", x); END_WARNING ()
#define WARNING1(s, e1) \
START_WARNING (); fprintf (stderr, s, e1); END_WARNING ()
#define WARNING2(s, e1, e2) \
START_WARNING (); fprintf (stderr, s, e1, e2); END_WARNING ()
#define WARNING3(s, e1, e2, e3) \
START_WARNING (); fprintf (stderr, s, e1, e2, e3); END_WARNING ()
#define WARNING4(s, e1, e2, e3, e4) \
START_WARNING (); fprintf (stderr, s, e1, e2, e3, e4); END_WARNING ()
/* Define useful abbreviations. */
/* This is the maximum number of numerals that result when a 64-bit
integer is converted to a string, plus one for a trailing null byte,
plus one for a sign. */
#define MAX_INT_LENGTH 21
/* Printer's points, as defined by TeX (and good typesetters everywhere). */
#define POINTS_PER_INCH 72.27
/* Convert a number V in pixels to printer's points, and vice versa,
assuming a resolution of DPI pixels per inch. */
#define PIXELS_TO_POINTS(v, dpi) (POINTS_PER_INCH * (v) / (dpi))
#define POINTS_TO_REAL_PIXELS(v, dpi) ((v) * (dpi) / POINTS_PER_INCH)
#define POINTS_TO_PIXELS(v, dpi) ((int) (POINTS_TO_REAL_PIXELS (v, dpi) + .5))
/* Some simple numeric operations. It is possible to define these much
more cleanly in GNU C, but we haven't done that (yet). */
#define SQUARE(x) ((x) * (x))
#define CUBE(x) ((x) * (x) * (x))
#define SAME_SIGN(u,v) ((u) >= 0 && (v) >= 0 || (u) < 0 && (v) < 0)
#define SIGN(x) ((x) > 0 ? 1 : (x) < 0 ? -1 : 0)
#define SROUND(x) ((int) ((int) (x) + .5 * SIGN (x)))
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/* Too bad C doesn't define operators for these. */
#define MAX_EQUALS(var, expr) if ((expr) > (var)) (var) = (expr)
#define MIN_EQUALS(var, expr) if ((expr) < (var)) (var) = (expr)
#define STREQ(s1, s2) (strcmp (s1, s2) == 0)
/* Declarations for commonly-used routines we provide ourselves. The
ones here are only needed by us, so we do not provide them in
unprototyped form. Others are declared both ways in lib.h. */
#if 0 /* These aren't actually defined anywhere */
/* Return the current date and time a la date(1). */
extern string now (void);
/* Check if a string is a valid floating-point or decimal integer.
Returns false if passed NULL. */
extern boolean float_ok (string);
extern boolean integer_ok (string);
/* Like `atoi', but disallow negative numbers. */
extern const unsigned atou (string);
/* The converses of atoi, atou, and atof. These all return dynamically
allocated strings. `dtoa' is so-named because `ftoa' is a library
function on some systems (the IBM RT), and the loader complains that
is defined twice, for reasons I don't understand. */
extern string itoa (int);
extern string utoa (unsigned);
extern string dtoa (double);
#endif
/* Like their stdio counterparts, but abort on error, after calling
perror(3) with FILENAME as its argument. */
/* extern FILE *xfopen (string filename, string mode); */
/* extern void xfclose (FILE *, string filename); */
/* extern void xfseek (FILE *, long, int, string filename); */
/* extern four_bytes xftell (FILE *, string filename); */
/* Copies the file FROM to the file TO, then unlinks FROM. */
extern void xrename (string from, string to);
/* Return NAME with any leading path stripped off. This returns a
pointer into NAME. */
/* ALT extern string basename (string name); */
/* If P or *P is null, abort. Otherwise, call free(3) on P,
and then set *P to NULL. */
extern void safe_free (address *p);
/* Math functions. */
/* Says whether V1 and V2 are within REAL_EPSILON of each other.
Fixed-point arithmetic would be better, to guarantee machine
independence, but it's so much more painful to work with. The value
here is smaller than can be represented in either a `fix_word' or a
`scaled_num', so more precision than this will be lost when we
output, anyway. */
#define REAL_EPSILON 0.00001
extern boolean epsilon_equal (real v1, real v2);
/* Arc cosine, in degrees. */
extern real my_acosd (real);
/* Return the Euclidean distance between the two points. */
extern real distance (real_coordinate_type, real_coordinate_type);
extern real int_distance (coordinate_type, coordinate_type);
/* Slope between two points (delta y per unit x). */
extern real slope (real_coordinate_type, real_coordinate_type);
/* Make a real coordinate from an integer one, and vice versa. */
extern real_coordinate_type int_to_real_coord (coordinate_type);
extern coordinate_type real_to_int_coord (real_coordinate_type);
/* Test if two integer points are adjacent. */
extern boolean points_adjacent_p (int row1, int col1, int r2, int c2);
/* Find the largest and smallest elements of an array. */
extern void find_bounds (real values[], unsigned value_count,
/* returned: */ real *the_min, real *the_max);
/* Make all the elements in the array between zero and one. */
extern real *map_to_unit (real * values, unsigned value_count);
/* String functions. */
/* Return (a fresh copy of) SOURCE beginning at START and ending at
LIMIT. (Or NULL if LIMIT < START.) */
extern string substring (string source, const unsigned start,
const unsigned limit);
/* Change all uppercase letters in S to lowercase. */
extern string lowercasify (string s);
/* Character code parsing. */
/* If the string S parses as a character code, this sets *VALID to
`true' and returns the number. If it doesn't, it sets *VALID to
`false' and the return value is garbage.
We allow any of the following possibilities: a single character, as in
`a' or `0'; a decimal number, as in `21'; an octal number, as in `03'
or `0177'; a hexadecimal number, as in `0x3' or `0xff'. */
extern charcode_type parse_charcode (string s, boolean *valid);
/* Like `parse_charcode', but gives a fatal error if the string isn't a
valid character code. */
extern charcode_type xparse_charcode (string s);
/* The environment variable name with which to look up auxiliary files. */
#ifndef LIB_ENVVAR
#define LIB_ENVVAR "FONTUTIL_LIB"
#endif
#endif /* not GLOBAL_H */

View File

@ -0,0 +1,177 @@
/* math.c: define some simple array operations, and other functions.
*
* 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 <errno.h>
#include <math.h>
#include <stdio.h>
#include "libpika/pika.h"
#include "types.h"
#include "global.h"
/* Numerical errors sometimes make a floating point number just slightly
larger or smaller than its true value. When it matters, we need to
compare with some tolerance, REAL_EPSILON, defined in kbase.h. */
boolean
epsilon_equal (real v1, real v2)
{
return
v1 == v2 /* Usually they'll be exactly equal, anyway. */
|| fabs (v1 - v2) <= REAL_EPSILON;
}
/* Return the Euclidean distance between P1 and P2. */
real
distance (real_coordinate_type p1, real_coordinate_type p2)
{
return hypot (p1.x - p2.x, p1.y - p2.y);
}
/* Same thing, for integer points. */
real
int_distance (coordinate_type p1, coordinate_type p2)
{
return hypot ((double) p1.x - p2.x, (double) p1.y - p2.y);
}
/* Return the arc cosine of V, in degrees in the range zero to 180. V
is taken to be in radians. */
real
my_acosd (real v)
{
real a;
if (epsilon_equal (v, 1.0))
v = 1.0;
else if (epsilon_equal (v, -1.0))
v = -1.0;
errno = 0;
a = acos (v);
if (errno == ERANGE || errno == EDOM)
FATAL_PERROR ("acosd");
return a * 180.0 / G_PI;
}
/* The slope of the line defined by COORD1 and COORD2. */
real
slope (real_coordinate_type coord1, real_coordinate_type coord2)
{
g_assert (coord2.x - coord1.x != 0);
return (coord2.y - coord1.y) / (coord2.x - coord1.x);
}
/* Turn an integer point into a real one, and vice versa. */
real_coordinate_type
int_to_real_coord (coordinate_type int_coord)
{
real_coordinate_type real_coord;
real_coord.x = int_coord.x;
real_coord.y = int_coord.y;
return real_coord;
}
coordinate_type
real_to_int_coord (real_coordinate_type real_coord)
{
coordinate_type int_coord;
int_coord.x = SROUND (real_coord.x);
int_coord.y = SROUND (real_coord.y);
return int_coord;
}
/* See if two points (described by their row and column) are adjacent. */
boolean
points_adjacent_p (int row1, int col1, int row2, int col2)
{
int row_diff = abs (row1 - row2);
int col_diff = abs (col1 - col2);
return
(row_diff == 1 && col_diff == 1)
|| (row_diff == 0 && col_diff == 1)
|| (row_diff == 1 && col_diff == 0);
}
/* Find the largest and smallest elements in an array of reals. */
void
find_bounds (real *values, unsigned value_count, real *min, real *max)
{
unsigned this_value;
/* We must use FLT_MAX and FLT_MIN, instead of the corresponding
values for double, because gcc uses the native atof to parse
floating point constants, and many atof's choke on the extremes. */
*min = FLT_MAX;
*max = FLT_MIN;
for (this_value = 0; this_value < value_count; this_value++)
{
if (values[this_value] < *min)
*min = values[this_value];
if (values[this_value] > *max)
*max = values[this_value];
}
}
/* Map a range of numbers, some positive and some negative, into all
positive, with the greatest being at one and the least at zero.
This allocates new memory. */
real *
map_to_unit (real *values, unsigned value_count)
{
real smallest, largest;
int this_value;
real *mapped_values = g_new (real, value_count);
find_bounds (values, value_count, &smallest, &largest);
largest -= smallest; /* We never care about largest itself. */
for (this_value = 0; this_value < value_count; this_value++)
mapped_values[this_value] = (values[this_value] - smallest) / largest;
return mapped_values;
}

View File

@ -0,0 +1,39 @@
plugin_name = 'selection-to-path'
plugin_sources = [
'curve.c',
'edge.c',
'fit.c',
'math.c',
'pxl-outline.c',
'selection-to-path-dialog.c',
'selection-to-path.c',
'spline.c',
'vector.c',
]
if platform_windows
plugin_sources += windows.compile_resources(
pika_plugins_rc,
args: [
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
],
include_directories: [
rootInclude, appInclude,
],
)
endif
plugin_selection_to_path_deps = [
libpikaui_dep,
math,
]
selection_to_path = executable(plugin_name,
plugin_sources,
dependencies: plugin_selection_to_path_deps,
install: true,
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
)

View File

@ -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 "libpika/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)); */
pika_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)
pika_progress_update (((gdouble)row) / height);
}
pika_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);
}

View File

@ -0,0 +1,68 @@
/* pxl-outline.h: find a list of outlines which make up one character.
*
* 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/>.
*/
#ifndef PXL_OUTLINE_H
#define PXL_OUTLINE_H
#include "types.h"
/* This is a list of contiguous points on the bitmap. */
typedef struct
{
coordinate_type *data;
unsigned length;
boolean clockwise;
} pixel_outline_type;
/* The Nth coordinate in the list. */
#define O_COORDINATE(p_o, n) ((p_o).data[n])
/* The length of the list. */
#define O_LENGTH(p_o) ((p_o).length)
/* Whether the outline moves clockwise or counterclockwise. */
#define O_CLOCKWISE(p_o) ((p_o).clockwise)
/* Since a pixel outline is cyclic, the index of the next coordinate
after the last is the first, and the previous coordinate before the
first is the last. */
#define O_NEXT(p_o, n) (((n) + 1) % O_LENGTH (p_o))
#define O_PREV(p_o, n) ((n) == 0 ? O_LENGTH (p_o) - 1 : (n) - 1)
/* And the character turns into a list of such lists. */
typedef struct
{
pixel_outline_type *data;
unsigned length;
} pixel_outline_list_type;
/* The Nth list in the list of lists. */
#define O_LIST_OUTLINE(p_o_l, n) ((p_o_l).data[n])
/* The length of the list of lists. */
#define O_LIST_LENGTH(p_o_l) ((p_o_l).length)
/* Find all pixels on the outline in the character C. */
extern pixel_outline_list_type find_outline_pixels (void);
/* Free the memory in the list. */
extern void free_pixel_outline_list (pixel_outline_list_type *);
#endif /* not PXL_OUTLINE_H */

View File

@ -0,0 +1,387 @@
/*
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for PIKA.
*
* Plugin to convert a selection to a path.
*
* Copyright (C) 1999 Andy Thomas alt@gimp.org
*
* 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 of the License, 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/>.
*
*/
/* Change log:-
* 0.1 First version.
*/
#include "config.h"
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "types.h"
#include "selection-to-path.h"
#include "libpika/stdplugins-intl.h"
static GSList * adjust_widgets = NULL;
void scale_entry_update_double (PikaLabelSpin *entry,
gdouble *value);
/* Reset to recommended defaults */
void
reset_adv_dialog (void)
{
GSList *list;
GObject *widget;
gdouble *value;
for (list = adjust_widgets; list; list = g_slist_next (list))
{
widget = G_OBJECT (list->data);
value = (gdouble *) g_object_get_data (G_OBJECT (widget),
"default_value");
if (GTK_IS_ADJUSTMENT (widget))
{
gtk_adjustment_set_value (GTK_ADJUSTMENT (widget),
*value);
}
else if (GTK_IS_TOGGLE_BUTTON (widget))
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
(gboolean)(*value));
}
else if (PIKA_IS_SCALE_ENTRY (widget))
{
pika_label_spin_set_value (PIKA_LABEL_SPIN (widget),
*value);
}
else
g_warning ("Internal widget list error");
}
}
static gpointer
def_val (gdouble default_value)
{
gdouble *value = g_new0 (gdouble, 1);
*value = default_value;
return (value);
}
GtkWidget *
dialog_create_selection_area (SELVALS *sels)
{
GtkWidget *scrolled_win;
GtkWidget *grid;
GtkWidget *check;
GtkWidget *scale;
gint row;
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_size_request (scrolled_win, -1, 400);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_SHADOW_NONE);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_POLICY_NEVER,
GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_overlay_scrolling (GTK_SCROLLED_WINDOW (scrolled_win),
FALSE);
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_container_add (GTK_CONTAINER (scrolled_win), grid);
gtk_widget_show (grid);
row = 0;
scale = pika_scale_entry_new (_("Align Threshold:"), sels->align_threshold, 0.2, 2.0, 2);
pika_help_set_help_data (scale,
_("If two endpoints are closer than this, "
"they are made to be equal."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->align_threshold);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.5));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Corner Always Threshold:"), sels->corner_always_threshold, 30, 180, 2);
pika_help_set_help_data (scale,
_("If the angle defined by a point and its predecessors "
"and successors is smaller than this, it's a corner, "
"even if it's within 'corner_surround' pixels of a "
"point with a smaller angle."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->corner_always_threshold);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (60.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Corner Surround:"), sels->corner_surround, 3, 8, 0);
pika_help_set_help_data (scale,
_("Number of points to consider when determining if a "
"point is a corner or not."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->corner_surround);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (4.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Corner Threshold:"), sels->corner_threshold, 0, 180, 2);
pika_help_set_help_data (scale,
_("If a point, its predecessors, and its successors "
"define an angle smaller than this, it's a corner."),
NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->corner_threshold);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (100.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Error Threshold:"), sels->error_threshold, 0.2, 10, 2);
pika_help_set_help_data (scale,
_("Amount of error at which a fitted spline is "
"unacceptable. If any pixel is further away "
"than this from the fitted curve, we try again."),
NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->error_threshold);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.40));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Filter Alternative Surround:"), sels->filter_alternative_surround, 1, 10, 0);
pika_help_set_help_data (scale,
_("A second number of adjacent points to consider "
"when filtering."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->filter_alternative_surround);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (1.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Filter Epsilon:"), sels->filter_epsilon, 5, 40, 2);
pika_help_set_help_data (scale,
_("If the angles between the vectors produced by "
"filter_surround and filter_alternative_surround "
"points differ by more than this, use the one from "
"filter_alternative_surround."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->filter_epsilon);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (10.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Filter Iteration Count:"), sels->filter_iteration_count, 4, 70, 0);
pika_help_set_help_data (scale,
_("Number of times to smooth original data points. "
"Increasing this number dramatically --- to 50 or "
"so --- can produce vastly better results. But if "
"any points that 'should' be corners aren't found, "
"the curve goes to hell around that point."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->filter_iteration_count);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (4.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Filter Percent:"), sels->filter_percent, 0, 1, 2);
pika_help_set_help_data (scale,
_("To produce the new point, use the old point plus "
"this times the neighbors."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->filter_percent);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.33));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Filter Secondary Surround:"), sels->filter_secondary_surround, 3, 10, 0);
pika_help_set_help_data (scale,
_("Number of adjacent points to consider if "
"'filter_surround' points defines a straight line."),
NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->filter_secondary_surround);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (3.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Filter Surround:"), sels->filter_surround, 2, 10, 0);
pika_help_set_help_data (scale,
_("Number of adjacent points to consider when filtering."),
NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->filter_surround);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (2.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
check = gtk_check_button_new_with_label (_("Keep Knees"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), sels->keep_knees);
gtk_grid_attach (GTK_GRID (grid), check, 1, row, 2, 1);
pika_help_set_help_data (GTK_WIDGET (check),
_("Says whether or not to remove 'knee' "
"points after finding the outline."), NULL);
g_signal_connect (check, "toggled",
G_CALLBACK (pika_toggle_button_update),
&sels->keep_knees);
gtk_widget_show (check);
adjust_widgets = g_slist_append (adjust_widgets, check);
g_object_set_data (G_OBJECT (check), "default_value", def_val ((gdouble)FALSE));
row++;
scale = pika_scale_entry_new (_("Line Reversion Threshold:"), sels->line_reversion_threshold, 0.01, 0.2, 3);
pika_help_set_help_data (scale,
_("If a spline is closer to a straight line than this, "
"it remains a straight line, even if it would otherwise "
"be changed back to a curve. This is weighted by the "
"square of the curve length, to make shorter curves "
"more likely to be reverted."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->line_reversion_threshold);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.01));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Line Threshold:"), sels->line_threshold, 0.2, 4, 2);
pika_help_set_help_data (scale,
_("How many pixels (on the average) a spline can "
"diverge from the line determined by its endpoints "
"before it is changed to a straight line."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->line_threshold);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.5));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Reparametrize Improvement:"), sels->reparameterize_improvement, 0, 1, 2);
pika_help_set_help_data (scale,
_("If reparameterization doesn't improve the fit by this "
"much percent, stop doing it. ""Amount of error at which "
"it is pointless to reparameterize."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->reparameterize_improvement);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.01));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Reparametrize Threshold:"), sels->reparameterize_threshold, 1, 50, 2);
pika_help_set_help_data (scale,
_("Amount of error at which it is pointless to reparameterize. "
"This happens, for example, when we are trying to fit the "
"outline of the outside of an 'O' with a single spline. "
"The initial fit is not good enough for the Newton-Raphson "
"iteration to improve it. It may be that it would be better "
"to detect the cases where we didn't find any corners."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->reparameterize_threshold);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (1.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Subdivide Search:"), sels->subdivide_search, 0.05, 1, 2);
pika_help_set_help_data (scale,
_("Percentage of the curve away from the worst point "
"to look for a better place to subdivide."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->subdivide_search);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.1));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Subdivide Surround:"), sels->subdivide_surround, 2, 10, 0);
pika_help_set_help_data (scale,
_("Number of points to consider when deciding whether "
"a given point is a better place to subdivide."),
NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->subdivide_surround);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (4.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Subdivide Threshold:"), sels->subdivide_threshold, 0.01, 1, 2);
pika_help_set_help_data (scale,
_("How many pixels a point can diverge from a straight "
"line and still be considered a better place to "
"subdivide."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->subdivide_threshold);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.03));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
scale = pika_scale_entry_new (_("Tangent Surround:"), sels->tangent_surround, 2, 10, 0);
pika_help_set_help_data (scale,
_("Number of points to look at on either side of a "
"point when computing the approximation to the "
"tangent at that point."), NULL);
g_signal_connect (scale, "value-changed",
G_CALLBACK (scale_entry_update_double),
&sels->tangent_surround);
adjust_widgets = g_slist_append (adjust_widgets, scale);
g_object_set_data (G_OBJECT (scale), "default_value", def_val (3.0));
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
gtk_widget_show (scale);
return scrolled_win;
}
void
scale_entry_update_double (PikaLabelSpin *entry,
gdouble *value)
{
*value = pika_label_spin_get_value (entry);
}

View File

@ -0,0 +1,640 @@
/*
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for PIKA.
*
* Plugin to convert a selection to a path.
*
* Copyright (C) 1999 Andy Thomas alt@gimp.org
*
* 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 of the License, 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/>.
*
*/
/* Change log:-
* 0.1 First version.
*/
#include "config.h"
#include <string.h>
#include "libpika/pika.h"
#include "libpika/pikaui.h"
#include "libpikamath/pikamath.h"
#include "global.h"
#include "types.h"
#include "pxl-outline.h"
#include "fit.h"
#include "spline.h"
#include "selection-to-path.h"
#include "libpika/stdplugins-intl.h"
#define PLUG_IN_PROC "plug-in-sel2path"
#define PLUG_IN_BINARY "selection-to-path"
#define PLUG_IN_ROLE "pika-selection-to-path"
#define RESPONSE_RESET 1
#define MID_POINT 127
typedef struct _Sel2path Sel2path;
typedef struct _Sel2pathClass Sel2pathClass;
struct _Sel2path
{
PikaPlugIn parent_instance;
};
struct _Sel2pathClass
{
PikaPlugInClass parent_class;
};
#define SEL2PATH_TYPE (sel2path_get_type ())
#define SEL2PATH (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEL2PATH_TYPE, Sel2path))
GType sel2path_get_type (void) G_GNUC_CONST;
static GList * sel2path_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * sel2path_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * sel2path_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data);
static gint sel2path_dialog (SELVALS *sels);
static void sel2path_response (GtkWidget *widget,
gint response_id,
gpointer data);
static void dialog_print_selVals (SELVALS *sels);
static gboolean sel2path (PikaImage *image);
G_DEFINE_TYPE (Sel2path, sel2path, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (SEL2PATH_TYPE)
DEFINE_STD_SET_I18N
static gint sel_x1, sel_y1, sel_x2, sel_y2;
static gint has_sel, sel_width, sel_height;
static SELVALS selVals;
static GeglSampler *sel_sampler;
static gboolean retVal = TRUE; /* Toggle if cancel button clicked */
static void
sel2path_class_init (Sel2pathClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = sel2path_query_procedures;
plug_in_class->create_procedure = sel2path_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
sel2path_init (Sel2path *sel2path)
{
}
static GList *
sel2path_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
sel2path_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_image_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
sel2path_run, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_sensitivity_mask (procedure,
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
pika_procedure_set_documentation (procedure,
"Converts a selection to a path",
"Converts a selection to a path",
name);
pika_procedure_set_attribution (procedure,
"Andy Thomas",
"Andy Thomas",
"1999");
PIKA_PROC_ARG_DOUBLE (procedure, "align-threshold",
"Align threshold",
"Align threshold",
0.2, 2.0, 0.5,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "corner-always-threshold",
"Corner always threshold",
"Corner always threshold",
30, 180, 60.0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "corner-surround",
"Corner surround",
"Corner surround",
3, 8, 4,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "corner-threshold",
"Corner threshold",
"Corner threshold",
0, 180, 100.0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "error-threshold",
"Error threshold",
"Error threshold",
0.2, 10, 0.4,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "filter-alternative-surround",
"Filter alternative surround",
"Filter alternative surround",
1, 10, 1,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "filter-epsilon",
"Filter epsilon",
"Filter epsilon",
5, 40, 10.0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "filter-iteration-count",
"Filter iteration count",
"Filter iteration count",
4, 70, 4,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "filter-percent",
"Filter percent",
"Filter percent",
0, 1, 0.33,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "filter-secondary-surround",
"Filter secondary surround",
"Filter secondary surround",
3, 10, 3,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "filter-surround",
"Filter surround",
"Filter surround",
2, 10, 2,
G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (procedure, "keep-knees",
"Keep knees",
"Keep knees",
FALSE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "line-reversion-threshold",
"Line reversion threshold",
"Line reversion threshold",
0.01, 0.2, 0.01,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "line-threshold",
"Line threshold",
"Line threshold",
0.2, 4, 0.5,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "reparametrize-improvement",
"Reparametrize improvement",
"Reparametrize improvement",
0, 1, 0.01,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "reparametrize-threshold",
"Reparametrize threshold",
"Reparametrize threshold",
1, 50, 1.0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "subdivide-search",
"Subdivide search",
"Subdivide search",
0.05, 1, 0.1,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "subdivide-surround",
"Subdivide surround",
"Subdivide surround",
2, 10, 4,
G_PARAM_READWRITE);
PIKA_PROC_ARG_DOUBLE (procedure, "subdivide-threshold",
"Subdivide threshold",
"Subdivide threshold",
0.01, 1, 0.03,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "tangent-surround",
"Tangent surround",
"Tangent surround",
2, 10, 3,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
sel2path_run (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
const PikaValueArray *args,
gpointer run_data)
{
gegl_init (NULL, NULL);
if (pika_selection_is_empty (image))
{
g_message (_("No selection to convert"));
return pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
}
fit_set_default_params (&selVals);
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
if (pika_get_data_size (PLUG_IN_PROC) > 0)
pika_get_data (PLUG_IN_PROC, &selVals);
if (! sel2path_dialog (&selVals))
return pika_procedure_new_return_values (procedure,
PIKA_PDB_CANCEL,
NULL);
/* Get the current settings */
fit_set_params (&selVals);
break;
case PIKA_RUN_NONINTERACTIVE:
selVals.align_threshold = PIKA_VALUES_GET_DOUBLE (args, 0);
selVals.corner_always_threshold = PIKA_VALUES_GET_DOUBLE (args, 1);
selVals.corner_surround = PIKA_VALUES_GET_INT (args, 2);
selVals.corner_threshold = PIKA_VALUES_GET_DOUBLE (args, 3);
selVals.error_threshold = PIKA_VALUES_GET_DOUBLE (args, 4);
selVals.filter_alternative_surround = PIKA_VALUES_GET_INT (args, 5);
selVals.filter_epsilon = PIKA_VALUES_GET_DOUBLE (args, 6);
selVals.filter_iteration_count = PIKA_VALUES_GET_INT (args, 7);
selVals.filter_percent = PIKA_VALUES_GET_DOUBLE (args, 8);
selVals.filter_secondary_surround = PIKA_VALUES_GET_INT (args, 9);
selVals.filter_surround = PIKA_VALUES_GET_INT (args, 10);
selVals.keep_knees = PIKA_VALUES_GET_BOOLEAN (args, 11);
selVals.line_reversion_threshold = PIKA_VALUES_GET_DOUBLE (args, 12);
selVals.line_threshold = PIKA_VALUES_GET_DOUBLE (args, 13);
selVals.reparameterize_improvement = PIKA_VALUES_GET_DOUBLE (args, 14);
selVals.reparameterize_threshold = PIKA_VALUES_GET_DOUBLE (args, 15);
selVals.subdivide_search = PIKA_VALUES_GET_DOUBLE (args, 16);
selVals.subdivide_surround = PIKA_VALUES_GET_INT (args, 17);
selVals.subdivide_threshold = PIKA_VALUES_GET_DOUBLE (args, 18);
selVals.tangent_surround = PIKA_VALUES_GET_INT (args, 19);
fit_set_params (&selVals);
break;
case PIKA_RUN_WITH_LAST_VALS:
if (pika_get_data_size (PLUG_IN_PROC) > 0)
{
pika_get_data (PLUG_IN_PROC, &selVals);
/* Set up the last values */
fit_set_params (&selVals);
}
break;
default:
break;
}
if (! sel2path (image))
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
NULL);
dialog_print_selVals (&selVals);
if (run_mode == PIKA_RUN_INTERACTIVE)
pika_set_data (PLUG_IN_PROC, &selVals, sizeof(SELVALS));
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static void
dialog_print_selVals (SELVALS *sels)
{
#if 0
printf ("selVals.align_threshold %g\n", selVals.align_threshold);
printf ("selVals.corner_always_threshol %g\n", selVals.corner_always_threshold);
printf ("selVals.corner_surround %g\n", selVals.corner_surround);
printf ("selVals.corner_threshold %g\n", selVals.corner_threshold);
printf ("selVals.error_threshold %g\n", selVals.error_threshold);
printf ("selVals.filter_alternative_surround %g\n", selVals.filter_alternative_surround);
printf ("selVals.filter_epsilon %g\n", selVals.filter_epsilon);
printf ("selVals.filter_iteration_count %g\n", selVals.filter_iteration_count);
printf ("selVals.filter_percent %g\n", selVals.filter_percent);
printf ("selVals.filter_secondary_surround %g\n", selVals.filter_secondary_surround);
printf ("selVals.filter_surround %g\n", selVals.filter_surround);
printf ("selVals.keep_knees %d\n", selVals.keep_knees);
printf ("selVals.line_reversion_threshold %g\n", selVals.line_reversion_threshold);
printf ("selVals.line_threshold %g\n", selVals.line_threshold);
printf ("selVals.reparameterize_improvement %g\n", selVals.reparameterize_improvement);
printf ("selVals.reparameterize_threshold %g\n", selVals.reparameterize_threshold);
printf ("selVals.subdivide_search %g\n" selVals.subdivide_search);
printf ("selVals.subdivide_surround %g\n", selVals.subdivide_surround);
printf ("selVals.subdivide_threshold %g\n", selVals.subdivide_threshold);
printf ("selVals.tangent_surround %g\n", selVals.tangent_surround);
#endif /* 0 */
}
/* Build the dialog up. This was the hard part! */
static gint
sel2path_dialog (SELVALS *sels)
{
GtkWidget *dlg;
GtkWidget *table;
retVal = FALSE;
pika_ui_init (PLUG_IN_BINARY);
dlg = pika_dialog_new (_("Selection to Path Advanced Settings"),
PLUG_IN_ROLE,
NULL, 0,
pika_standard_help_func, PLUG_IN_PROC,
_("_Reset"), RESPONSE_RESET,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
RESPONSE_RESET,
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
pika_window_set_transient (GTK_WINDOW (dlg));
g_signal_connect (dlg, "response",
G_CALLBACK (sel2path_response),
NULL);
g_signal_connect (dlg, "destroy",
G_CALLBACK (gtk_main_quit),
NULL);
table = dialog_create_selection_area (sels);
gtk_container_set_border_width (GTK_CONTAINER (table), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
table, TRUE, TRUE, 0);
gtk_widget_show (table);
gtk_widget_show (dlg);
gtk_main ();
return retVal;
}
static void
sel2path_response (GtkWidget *widget,
gint response_id,
gpointer data)
{
switch (response_id)
{
case RESPONSE_RESET:
reset_adv_dialog ();
fit_set_params (&selVals);
break;
case GTK_RESPONSE_OK:
retVal = TRUE;
default:
gtk_widget_destroy (widget);
break;
}
}
guchar
sel_pixel_value (gint row,
gint col)
{
guchar ret;
if (col > sel_width || row > sel_height)
{
g_warning ("sel_pixel_value [%d,%d] out of bounds", col, row);
return 0;
}
gegl_sampler_get (sel_sampler,
col + sel_x1, row + sel_y1, NULL, &ret, GEGL_ABYSS_NONE);
return ret;
}
gboolean
sel_pixel_is_white (gint row,
gint col)
{
if (sel_pixel_value (row, col) < MID_POINT)
return TRUE;
else
return FALSE;
}
gint
sel_get_width (void)
{
return sel_width;
}
gint
sel_get_height (void)
{
return sel_height;
}
gboolean
sel_valid_pixel (gint row,
gint col)
{
return (0 <= (row) && (row) < sel_get_height ()
&& 0 <= (col) && (col) < sel_get_width ());
}
static void
do_points (spline_list_array_type in_splines,
PikaImage *image)
{
PikaVectors *vectors;
gint32 stroke;
gint i, j;
gboolean have_points = FALSE;
spline_list_type spline_list;
/* check if there really is something to do... */
for (i = 0; i < SPLINE_LIST_ARRAY_LENGTH (in_splines); i++)
{
spline_list = SPLINE_LIST_ARRAY_ELT (in_splines, i);
/* Ignore single points that are on their own */
if (SPLINE_LIST_LENGTH (spline_list) < 2)
continue;
have_points = TRUE;
break;
}
if (! have_points)
return;
vectors = pika_vectors_new (image, _("Selection"));
for (j = 0; j < SPLINE_LIST_ARRAY_LENGTH (in_splines); j++)
{
spline_type seg;
spline_list = SPLINE_LIST_ARRAY_ELT (in_splines, j);
/* Ignore single points that are on their own */
if (SPLINE_LIST_LENGTH (spline_list) < 2)
continue;
/*
* we're constructing the path backwards
* to have the result of least surprise for "Text along Path".
*/
seg = SPLINE_LIST_ELT (spline_list, SPLINE_LIST_LENGTH (spline_list) - 1);
stroke = pika_vectors_bezier_stroke_new_moveto (vectors,
END_POINT (seg).x,
END_POINT (seg).y);
for (i = SPLINE_LIST_LENGTH (spline_list); i > 0; i--)
{
seg = SPLINE_LIST_ELT (spline_list, i-1);
if (SPLINE_DEGREE (seg) == LINEAR)
pika_vectors_bezier_stroke_lineto (vectors, stroke,
START_POINT (seg).x,
START_POINT (seg).y);
else if (SPLINE_DEGREE (seg) == CUBIC)
pika_vectors_bezier_stroke_cubicto (vectors, stroke,
CONTROL2 (seg).x,
CONTROL2 (seg).y,
CONTROL1 (seg).x,
CONTROL1 (seg).y,
START_POINT (seg).x,
START_POINT (seg).y);
else
g_warning ("print_spline: strange degree (%d)",
SPLINE_DEGREE (seg));
}
pika_vectors_stroke_close (vectors, stroke);
/* transform to PIKAs coordinate system, taking the selections
* bounding box into account */
pika_vectors_stroke_scale (vectors, stroke, 1.0, -1.0);
pika_vectors_stroke_translate (vectors, stroke,
sel_x1, sel_y1 + sel_height + 1);
}
pika_image_insert_vectors (image, vectors, NULL, -1);
}
static gboolean
sel2path (PikaImage *image)
{
PikaSelection *selection;
GeglBuffer *sel_buffer;
pixel_outline_list_type olt;
spline_list_array_type splines;
pika_selection_bounds (image, &has_sel,
&sel_x1, &sel_y1, &sel_x2, &sel_y2);
sel_width = sel_x2 - sel_x1;
sel_height = sel_y2 - sel_y1;
/* Now get the selection channel */
selection = pika_image_get_selection (image);
if (! selection)
return FALSE;
sel_buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (selection));
sel_sampler = gegl_buffer_sampler_new (sel_buffer,
babl_format ("Y u8"),
GEGL_SAMPLER_NEAREST);
olt = find_outline_pixels ();
splines = fitted_splines (olt);
do_points (splines, image);
g_object_unref (sel_sampler);
g_object_unref (sel_buffer);
pika_displays_flush ();
return TRUE;
}
void
safe_free (address *item)
{
g_free (*item);
*item = NULL;
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for PIKA.
*
* Plugin to convert a selection to a path.
*
* Copyright (C) 1999 Andy Thomas alt@gimp.org
*
* 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 of the License, 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 "gtk/gtk.h"
#include "libpika/pika.h"
guchar sel_pixel_value (gint, gint);
gint sel_pixel_is_white (gint, gint);
gint sel_get_width (void);
gint sel_get_height (void);
gboolean sel_valid_pixel (gint, gint);
void reset_adv_dialog (void);
GtkWidget * dialog_create_selection_area(SELVALS *);

View File

@ -0,0 +1,233 @@
/* spline.c: spline and spline list (represented as arrays) manipulation.
*
* 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 <glib.h>
#include "global.h"
#include "bounding-box.h"
#include "spline.h"
#include "vector.h"
/* Return a new spline structure, initialized with (recognizable)
garbage. */
spline_type
new_spline (void)
{
real_coordinate_type coord = { -100.0, -100.0 };
spline_type spline;
START_POINT (spline)
= CONTROL1 (spline)
= CONTROL2 (spline)
= END_POINT (spline)
= coord;
SPLINE_DEGREE (spline) = -1;
SPLINE_LINEARITY (spline) = 0;
return spline;
}
/* Print a spline in human-readable form. */
void
print_spline (FILE *f, spline_type s)
{
if (SPLINE_DEGREE (s) == LINEAR)
fprintf (f, "(%.3f,%.3f)--(%.3f,%.3f).\n",
START_POINT (s).x, START_POINT (s).y,
END_POINT (s).x, END_POINT (s).y);
else if (SPLINE_DEGREE (s) == CUBIC)
fprintf (f, "(%.3f,%.3f)..ctrls(%.3f,%.3f)&(%.3f,%.3f)..(%.3f,%.3f).\n",
START_POINT (s).x, START_POINT (s).y,
CONTROL1 (s).x, CONTROL1 (s).y,
CONTROL2 (s).x, CONTROL2 (s).y,
END_POINT (s).x, END_POINT (s).y);
else
{
/* FATAL1 ("print_spline: strange degree (%d)", SPLINE_DEGREE (s)); */
}
}
/* Evaluate the spline S at a given T value. This is an implementation
of de Casteljau's algorithm. See Schneider's thesis (reference in
../limn/README), p.37. The variable names are taken from there. */
real_coordinate_type
evaluate_spline (spline_type s, real t)
{
spline_type V[4]; /* We need degree+1 splines, but assert degree <= 3. */
unsigned i, j;
real one_minus_t = 1.0 - t;
polynomial_degree degree = SPLINE_DEGREE (s);
for (i = 0; i <= degree; i++)
V[0].v[i] = s.v[i];
for (j = 1; j <= degree; j++)
for (i = 0; i <= degree - j; i++)
{
#if defined (__GNUC__)
real_coordinate_type t1 = Pmult_scalar (V[j - 1].v[i], one_minus_t);
real_coordinate_type t2 = Pmult_scalar (V[j - 1].v[i + 1], t);
V[j].v[i] = Padd (t1, t2);
#else
/* HB: the above is really nice, but is there any other compiler
* supporting this ??
*/
real_coordinate_type t1;
real_coordinate_type t2;
t1.x = V[j - 1].v[i].x * one_minus_t;
t1.y = V[j - 1].v[i].y * one_minus_t;
t2.x = V[j - 1].v[i + 1].x * t;
t2.y = V[j - 1].v[i + 1].y * t;
V[j].v[i].x = t1.x + t2.x;
V[j].v[i].y = t1.y + t2.y;
#endif
}
return V[degree].v[0];
}
/* Return a new, empty, spline list. */
spline_list_type *
new_spline_list (void)
{
spline_list_type *answer = g_new (spline_list_type, 1);
SPLINE_LIST_DATA (*answer) = NULL;
SPLINE_LIST_LENGTH (*answer) = 0;
return answer;
}
/* Return a new spline list with SPLINE as the first element. */
spline_list_type *
init_spline_list (spline_type spline)
{
spline_list_type *answer = g_new (spline_list_type, 1);
SPLINE_LIST_DATA (*answer) = g_new (spline_type, 1);
SPLINE_LIST_ELT (*answer, 0) = spline;
SPLINE_LIST_LENGTH (*answer) = 1;
return answer;
}
/* Free the storage in a spline list. We don't have to free the
elements, since they are arrays in automatic storage. And we don't
want to free the list if it was empty. */
void
free_spline_list (spline_list_type *spline_list)
{
if (SPLINE_LIST_DATA (*spline_list) != NULL)
safe_free ((address *) &(SPLINE_LIST_DATA (*spline_list)));
}
/* Append the spline S to the list SPLINE_LIST. */
void
append_spline (spline_list_type *l, spline_type s)
{
assert (l != NULL);
SPLINE_LIST_LENGTH (*l)++;
SPLINE_LIST_DATA (*l) = g_realloc (SPLINE_LIST_DATA (*l),
SPLINE_LIST_LENGTH (*l) * sizeof (spline_type));
LAST_SPLINE_LIST_ELT (*l) = s;
}
/* Tack the elements in the list S2 onto the end of S1.
S2 is not changed. */
void
concat_spline_lists (spline_list_type *s1, spline_list_type s2)
{
unsigned this_spline;
unsigned new_length;
assert (s1 != NULL);
new_length = SPLINE_LIST_LENGTH (*s1) + SPLINE_LIST_LENGTH (s2);
SPLINE_LIST_DATA (*s1) = g_realloc(SPLINE_LIST_DATA (*s1),new_length * sizeof(spline_type));
for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (s2); this_spline++)
SPLINE_LIST_ELT (*s1, SPLINE_LIST_LENGTH (*s1)++)
= SPLINE_LIST_ELT (s2, this_spline);
}
/* Return a new, empty, spline list array. */
spline_list_array_type
new_spline_list_array (void)
{
spline_list_array_type answer;
SPLINE_LIST_ARRAY_DATA (answer) = NULL;
SPLINE_LIST_ARRAY_LENGTH (answer) = 0;
return answer;
}
/* Free the storage in a spline list array. We don't
want to free the list if it is empty. */
void
free_spline_list_array (spline_list_array_type *spline_list_array)
{
unsigned this_list;
for (this_list = 0;
this_list < SPLINE_LIST_ARRAY_LENGTH (*spline_list_array);
this_list++)
free_spline_list (&SPLINE_LIST_ARRAY_ELT (*spline_list_array, this_list));
if (SPLINE_LIST_ARRAY_DATA (*spline_list_array) != NULL)
safe_free ((address *) &(SPLINE_LIST_ARRAY_DATA (*spline_list_array)));
}
/* Append the spline S to the list SPLINE_LIST_ARRAY. */
void
append_spline_list (spline_list_array_type *l, spline_list_type s)
{
SPLINE_LIST_ARRAY_LENGTH (*l)++;
SPLINE_LIST_ARRAY_DATA (*l) = g_realloc(SPLINE_LIST_ARRAY_DATA (*l),(SPLINE_LIST_ARRAY_LENGTH (*l))*sizeof(spline_list_type));
LAST_SPLINE_LIST_ARRAY_ELT (*l) = s;
}

View File

@ -0,0 +1,124 @@
/* spline.h: manipulate the spline representation.
*
* 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/>.
*/
#ifndef SPLINE_H
#define SPLINE_H
#include <stdio.h>
#include "bounding-box.h"
#include "types.h"
/* Third degree is the highest we deal with. */
typedef enum
{
LINEAR = 1, QUADRATIC = 2, CUBIC = 3
} polynomial_degree;
/* A Bezier spline can be represented as four points in the real plane:
a starting point, ending point, and two control points. The
curve always lies in the convex hull defined by the four points. It
is also convenient to save the divergence of the spline from the
straight line defined by the endpoints. */
typedef struct
{
real_coordinate_type v[4]; /* The control points. */
polynomial_degree degree;
real linearity;
} spline_type;
#define START_POINT(spl) ((spl).v[0])
#define CONTROL1(spl) ((spl).v[1])
#define CONTROL2(spl) ((spl).v[2])
#define END_POINT(spl) ((spl).v[3])
#define SPLINE_DEGREE(spl) ((spl).degree)
#define SPLINE_LINEARITY(spl) ((spl).linearity)
/* Return a spline structure. */
extern spline_type new_spline (void);
/* Print a spline on the given file. */
extern void print_spline (FILE *, spline_type);
/* Evaluate SPLINE at the given T value. */
extern real_coordinate_type evaluate_spline (spline_type spline, real t);
/* Each outline in a character is typically represented by many
splines. So, here is a list structure for that: */
typedef struct
{
spline_type *data;
unsigned length;
} spline_list_type;
/* An empty list will have length zero (and null data). */
#define SPLINE_LIST_LENGTH(s_l) ((s_l).length)
/* The address of the beginning of the array of data. */
#define SPLINE_LIST_DATA(s_l) ((s_l).data)
/* The element INDEX in S_L. */
#define SPLINE_LIST_ELT(s_l, index) (SPLINE_LIST_DATA (s_l)[index])
/* The last element in S_L. */
#define LAST_SPLINE_LIST_ELT(s_l) \
(SPLINE_LIST_DATA (s_l)[SPLINE_LIST_LENGTH (s_l) - 1])
/* The previous and next elements to INDEX in S_L. */
#define NEXT_SPLINE_LIST_ELT(s_l, index) \
SPLINE_LIST_ELT (s_l, ((index) + 1) % SPLINE_LIST_LENGTH (s_l))
#define PREV_SPLINE_LIST_ELT(s_l, index) \
SPLINE_LIST_ELT (s_l, index == 0 \
? SPLINE_LIST_LENGTH (s_l) - 1 \
: index - 1)
/* Construct and destroy new `spline_list_type' objects. */
extern spline_list_type *new_spline_list (void);
extern spline_list_type *init_spline_list (spline_type);
extern void free_spline_list (spline_list_type *);
/* Append the spline S to the list S_LIST. */
extern void append_spline (spline_list_type *s_list, spline_type s);
/* Append the elements in list S2 to S1, changing S1. */
extern void concat_spline_lists (spline_list_type *s1, spline_list_type s2);
/* Each character is in general made up of many outlines. So here is one
more list structure. */
typedef struct
{
spline_list_type *data;
unsigned length;
} spline_list_array_type;
/* Turns out we can use the same definitions for lists of lists as for
just lists. But we define the usual names, just in case. */
#define SPLINE_LIST_ARRAY_LENGTH SPLINE_LIST_LENGTH
#define SPLINE_LIST_ARRAY_DATA SPLINE_LIST_DATA
#define SPLINE_LIST_ARRAY_ELT SPLINE_LIST_ELT
#define LAST_SPLINE_LIST_ARRAY_ELT LAST_SPLINE_LIST_ELT
/* The usual routines. */
extern spline_list_array_type new_spline_list_array (void);
extern void free_spline_list_array (spline_list_array_type *);
extern void append_spline_list (spline_list_array_type *, spline_list_type);
#endif /* not SPLINE_H */

View File

@ -0,0 +1,147 @@
/* types.h: general types.
*
* 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/>.
*/
#ifndef TYPES_H
#define TYPES_H
/* Booleans. */
typedef enum { false = 0, true = 1 } boolean;
/* The X11 library defines `FALSE' and `TRUE', and so we only want to
define them if necessary. */
#ifndef FALSE
#define FALSE false
#define TRUE true
#endif /* FALSE */
/* The usual null-terminated string. */
typedef char *string;
/* A generic pointer in ANSI C. */
typedef void *address;
/* We use `real' for our floating-point variables. */
typedef double real;
/* A character code. Perhaps someday we will allow for 16-bit
character codes, but for now we are restricted to 256 characters per
font (like TeX and PostScript). */
typedef unsigned char charcode_type;
/* Used in file formats. */
typedef unsigned char one_byte;
typedef signed char signed_byte;
typedef unsigned short two_bytes;
typedef short signed_2_bytes;
typedef unsigned int four_bytes;
typedef int signed_4_bytes;
typedef int byte_count_type;
/* These are intended to be used for output in file formats where a
``byte'' is defined to be eight bits, regardless of the hardware. */
#define ONE_BYTE_BIG (1 << 8)
#define TWO_BYTES_BIG (1 << 16)
#define THREE_BYTES_BIG (1 << 24)
/* Complex numbers. */
typedef struct
{
real real;
real imag;
} complex;
typedef enum { first_complex_part, second_complex_part} complex_part_type;
typedef enum { polar_rep, rectangular_rep} complex_rep_type;
/* Dimensions of a rectangle. */
typedef struct
{
unsigned height, width;
} dimensions_type;
#define DIMENSIONS_HEIGHT(d) ((d).height)
#define DIMENSIONS_WIDTH(d) ((d).width)
/* Cartesian points. */
typedef struct
{
int x, y;
} coordinate_type;
typedef struct
{
double x, y;
} real_coordinate_type;
#if 0
typedef struct
{
double align_threshold;
double corner_always_threshold;
unsigned int corner_surround;
double corner_threshold;
double error_threshold;
unsigned int filter_alternative_surround;
double filter_epsilon;
unsigned int filter_iteration_count;
double filter_percent;
unsigned int filter_secondary_surround;
unsigned int filter_surround;
boolean keep_knees;
double line_reversion_threshold;
double line_threshold;
double reparameterize_improvement;
double reparameterize_threshold;
double subdivide_search;
unsigned int subdivide_surround;
double subdivide_threshold;
unsigned int tangent_surround;
} SELVALS;
#else
typedef struct
{
double align_threshold;
double corner_always_threshold;
double corner_surround;
double corner_threshold;
double error_threshold;
double filter_alternative_surround;
double filter_epsilon;
double filter_iteration_count;
double filter_percent;
double filter_secondary_surround;
double filter_surround;
boolean keep_knees;
double line_reversion_threshold;
double line_threshold;
double reparameterize_improvement;
double reparameterize_threshold;
double subdivide_search;
double subdivide_surround;
double subdivide_threshold;
double tangent_surround;
} SELVALS;
#endif /* 1 */
#endif /* not TYPES_H */

View File

@ -0,0 +1,249 @@
/* vector.c: vector/point operations.
*
* 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 <glib.h>
#include <math.h>
#include <assert.h>
#include "global.h"
#include "config.h"
#include "vector.h"
/* Given the point COORD, return the corresponding vector. */
vector_type
make_vector (const real_coordinate_type c)
{
vector_type v;
v.dx = c.x;
v.dy = c.y;
return v;
}
/* And the converse: given a vector, return the corresponding point. */
real_coordinate_type
vector_to_point (const vector_type v)
{
real_coordinate_type coord;
coord.x = v.dx;
coord.y = v.dy;
return coord;
}
real
magnitude (const vector_type v)
{
return hypot (v.dx, v.dy);
}
vector_type
normalize (const vector_type v)
{
vector_type new_v;
real m = magnitude (v);
assert (m > 0.0);
new_v.dx = v.dx / m;
new_v.dy = v.dy / m;
return new_v;
}
vector_type
Vadd (const vector_type v1, const vector_type v2)
{
vector_type new_v;
new_v.dx = v1.dx + v2.dx;
new_v.dy = v1.dy + v2.dy;
return new_v;
}
real
Vdot (const vector_type v1, const vector_type v2)
{
return v1.dx * v2.dx + v1.dy * v2.dy;
}
vector_type
Vmult_scalar (const vector_type v, const real r)
{
vector_type new_v;
new_v.dx = v.dx * r;
new_v.dy = v.dy * r;
return new_v;
}
/* Given the IN_VECTOR and OUT_VECTOR, return the angle between them in
degrees, in the range zero to 180. */
real
Vangle (const vector_type in_vector, const vector_type out_vector)
{
vector_type v1 = normalize (in_vector);
vector_type v2 = normalize (out_vector);
return my_acosd (Vdot (v2, v1));
}
real_coordinate_type
Vadd_point (const real_coordinate_type c, const vector_type v)
{
real_coordinate_type new_c;
new_c.x = c.x + v.dx;
new_c.y = c.y + v.dy;
return new_c;
}
real_coordinate_type
Vsubtract_point (const real_coordinate_type c, const vector_type v)
{
real_coordinate_type new_c;
new_c.x = c.x - v.dx;
new_c.y = c.y - v.dy;
return new_c;
}
coordinate_type
Vadd_int_point (const coordinate_type c, const vector_type v)
{
coordinate_type a;
a.x = SROUND ((real) c.x + v.dx);
a.y = SROUND ((real) c.y + v.dy);
return a;
}
vector_type
Vabs (const vector_type v)
{
vector_type new_v;
new_v.dx = fabs (v.dx);
new_v.dy = fabs (v.dy);
return new_v;
}
/* Operations on points. */
vector_type
Psubtract (const real_coordinate_type c1, const real_coordinate_type c2)
{
vector_type v;
v.dx = c1.x - c2.x;
v.dy = c1.y - c2.y;
return v;
}
/* Operations on integer points. */
vector_type
IPsubtract (const coordinate_type coord1, const coordinate_type coord2)
{
vector_type v;
v.dx = coord1.x - coord2.x;
v.dy = coord1.y - coord2.y;
return v;
}
coordinate_type
IPsubtractP (const coordinate_type c1, const coordinate_type c2)
{
coordinate_type c;
c.x = c1.x - c2.x;
c.y = c1.y - c2.y;
return c;
}
coordinate_type
IPadd (const coordinate_type c1, const coordinate_type c2)
{
coordinate_type c;
c.x = c1.x + c2.x;
c.y = c1.y + c2.y;
return c;
}
coordinate_type
IPmult_scalar (const coordinate_type c, const int i)
{
coordinate_type a;
a.x = c.x * i;
a.y = c.y * i;
return a;
}
real_coordinate_type
IPmult_real (const coordinate_type c, const real r)
{
real_coordinate_type a;
a.x = c.x * r;
a.y = c.y * r;
return a;
}
boolean
IPequal (const coordinate_type c1, const coordinate_type c2)
{
return c1.x == c2.x && c1.y == c2.y;
}

View File

@ -0,0 +1,95 @@
/* vector.h: operations on vectors and points.
*
* 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/>.
*/
#ifndef VECTOR_H
#define VECTOR_H
#include "types.h"
/* Our vectors are represented as displacements along the x and y axes. */
typedef struct
{
real dx, dy;
} vector_type;
/* Consider a point as a vector from the origin. */
extern vector_type make_vector (const real_coordinate_type);
/* And a vector as a point, i.e., a displacement from the origin. */
extern real_coordinate_type vector_to_point (const vector_type);
/* Definitions for these common operations can be found in any decent
linear algebra book, and most calculus books. */
extern real magnitude (const vector_type);
extern vector_type normalize (const vector_type);
extern vector_type Vadd (const vector_type, const vector_type);
extern real Vdot (const vector_type, const vector_type);
extern vector_type Vmult_scalar (const vector_type, const real);
extern real Vangle (const vector_type in, const vector_type out);
/* These operations could have been named `P..._vector' just as well as
V..._point, so we may as well allow both names. */
#define Padd_vector Vadd_point
extern real_coordinate_type Vadd_point
(const real_coordinate_type, const vector_type);
#define Psubtract_vector Vsubtract_point
extern real_coordinate_type Vsubtract_point
(const real_coordinate_type, const vector_type);
/* This returns the rounded sum. */
#define IPadd_vector Vadd_int_point
extern coordinate_type Vadd_int_point
(const coordinate_type, const vector_type);
/* Take the absolute value of both components. */
extern vector_type Vabs (const vector_type);
/* Operations on points with real coordinates. It is not orthogonal,
but more convenient, to have the subtraction operator return a
vector, and the addition operator return a point. */
extern vector_type Psubtract
(const real_coordinate_type, const real_coordinate_type);
/* These are heavily used in spline fitting, so we define them as macros
instead of functions. */
#define Padd(rc1, rc2) \
((real_coordinate_type) { (rc1).x + (rc2).x, (rc1).y + (rc2).y })
#define Pmult_scalar(rc, r) \
((real_coordinate_type) { (rc).x * (r), (rc).y * (r) })
/* Similarly, for points with integer coordinates; here, a subtraction
operator that does return another point is useful. */
extern vector_type IPsubtract
(const coordinate_type, const coordinate_type);
extern coordinate_type IPsubtractP
(const coordinate_type, const coordinate_type);
extern coordinate_type IPadd
(const coordinate_type, const coordinate_type);
extern coordinate_type IPmult_scalar (const coordinate_type, const int);
extern real_coordinate_type IPmult_real
(const coordinate_type, const real);
extern boolean IPequal (const coordinate_type, const coordinate_type);
#endif /* not VECTOR_H */