summaryrefslogtreecommitdiffstats
path: root/app/core/gimp-transform-resize.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
commit5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch)
treecbffb45144febf451e54061db2b21395faf94bfe /app/core/gimp-transform-resize.c
parentInitial commit. (diff)
downloadgimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.tar.xz
gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.zip
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/core/gimp-transform-resize.c')
-rw-r--r--app/core/gimp-transform-resize.c841
1 files changed, 841 insertions, 0 deletions
diff --git a/app/core/gimp-transform-resize.c b/app/core/gimp-transform-resize.c
new file mode 100644
index 0000000..7aabd67
--- /dev/null
+++ b/app/core/gimp-transform-resize.c
@@ -0,0 +1,841 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
+ *
+ * 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 "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+#include <gegl.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "core-types.h"
+
+#include "gimp-transform-resize.h"
+#include "gimp-transform-utils.h"
+#include "gimp-utils.h"
+
+
+#if defined (HAVE_ISFINITE)
+#define FINITE(x) isfinite(x)
+#elif defined (HAVE_FINITE)
+#define FINITE(x) finite(x)
+#elif defined (G_OS_WIN32)
+#define FINITE(x) _finite(x)
+#else
+#error "no FINITE() implementation available?!"
+#endif
+
+#define EPSILON 0.00000001
+
+
+typedef struct
+{
+ GimpVector2 a, b, c, d;
+ gdouble area;
+ gdouble aspect;
+} Rectangle;
+
+
+static void gimp_transform_resize_adjust (const GimpVector2 *points,
+ gint n_points,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2);
+static void gimp_transform_resize_crop (const GimpVector2 *points,
+ gint n_points,
+ gdouble aspect,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2);
+
+static void add_rectangle (const GimpVector2 *points,
+ gint n_points,
+ Rectangle *r,
+ GimpVector2 a,
+ GimpVector2 b,
+ GimpVector2 c,
+ GimpVector2 d);
+static gboolean intersect (GimpVector2 a,
+ GimpVector2 b,
+ GimpVector2 c,
+ GimpVector2 d,
+ GimpVector2 *i);
+static gboolean intersect_x (GimpVector2 a,
+ GimpVector2 b,
+ GimpVector2 c,
+ GimpVector2 *i);
+static gboolean intersect_y (GimpVector2 a,
+ GimpVector2 b,
+ GimpVector2 c,
+ GimpVector2 *i);
+static gboolean in_poly (const GimpVector2 *points,
+ gint n_points,
+ GimpVector2 p);
+static gboolean point_on_border (const GimpVector2 *points,
+ gint n_points,
+ GimpVector2 p);
+
+static void find_two_point_rectangle (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p);
+static void find_three_point_rectangle_corner (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p);
+static void find_three_point_rectangle (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p);
+static void find_three_point_rectangle_triangle (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p);
+static void find_maximum_aspect_rectangle (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p);
+
+
+/*
+ * This function wants to be passed the inverse transformation matrix!!
+ */
+gboolean
+gimp_transform_resize_boundary (const GimpMatrix3 *inv,
+ GimpTransformResize resize,
+ gdouble u1,
+ gdouble v1,
+ gdouble u2,
+ gdouble v2,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2)
+{
+ GimpVector2 bounds[4];
+ GimpVector2 points[5];
+ gint n_points;
+ gboolean valid;
+ gint i;
+
+ g_return_val_if_fail (inv != NULL, FALSE);
+
+ /* initialize with the original boundary */
+ *x1 = floor (u1);
+ *y1 = floor (v1);
+ *x2 = ceil (u2);
+ *y2 = ceil (v2);
+
+ /* if clipping then just return the original rectangle */
+ if (resize == GIMP_TRANSFORM_RESIZE_CLIP)
+ return TRUE;
+
+ bounds[0] = (GimpVector2) { u1, v1 };
+ bounds[1] = (GimpVector2) { u2, v1 };
+ bounds[2] = (GimpVector2) { u2, v2 };
+ bounds[3] = (GimpVector2) { u1, v2 };
+
+ gimp_transform_polygon (inv, bounds, 4, TRUE,
+ points, &n_points);
+
+ valid = (n_points >= 2);
+
+ /* check if the transformation matrix is valid at all */
+ for (i = 0; i < n_points && valid; i++)
+ valid = (FINITE (points[i].x) && FINITE (points[i].y));
+
+ if (! valid)
+ {
+ /* since there is no sensible way to deal with this, just do the same as
+ * with GIMP_TRANSFORM_RESIZE_CLIP: return
+ */
+ return FALSE;
+ }
+
+ switch (resize)
+ {
+ case GIMP_TRANSFORM_RESIZE_ADJUST:
+ /* return smallest rectangle (with sides parallel to x- and y-axis)
+ * that surrounds the new points */
+ gimp_transform_resize_adjust (points, n_points,
+ x1, y1, x2, y2);
+ break;
+
+ case GIMP_TRANSFORM_RESIZE_CROP:
+ gimp_transform_resize_crop (points, n_points,
+ 0.0,
+ x1, y1, x2, y2);
+ break;
+
+ case GIMP_TRANSFORM_RESIZE_CROP_WITH_ASPECT:
+ gimp_transform_resize_crop (points, n_points,
+ (u2 - u1) / (v2 - v1),
+ x1, y1, x2, y2);
+ break;
+
+ case GIMP_TRANSFORM_RESIZE_CLIP:
+ /* Remove warning about not handling all enum values. We handle
+ * this case in the beginning of the function
+ */
+ break;
+ }
+
+ /* ensure that resulting rectangle has at least area 1 */
+ if (*x1 == *x2)
+ (*x2)++;
+
+ if (*y1 == *y2)
+ (*y2)++;
+
+ return TRUE;
+}
+
+/* this calculates the smallest rectangle (with sides parallel to x- and
+ * y-axis) that contains the points d1 to d4
+ */
+static void
+gimp_transform_resize_adjust (const GimpVector2 *points,
+ gint n_points,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2)
+{
+ GimpVector2 top_left;
+ GimpVector2 bottom_right;
+ gint i;
+
+ top_left = bottom_right = points[0];
+
+ for (i = 1; i < n_points; i++)
+ {
+ top_left.x = MIN (top_left.x, points[i].x);
+ top_left.y = MIN (top_left.y, points[i].y);
+
+ bottom_right.x = MAX (bottom_right.x, points[i].x);
+ bottom_right.y = MAX (bottom_right.y, points[i].y);
+ }
+
+ *x1 = (gint) floor (top_left.x + EPSILON);
+ *y1 = (gint) floor (top_left.y + EPSILON);
+
+ *x2 = (gint) ceil (bottom_right.x - EPSILON);
+ *y2 = (gint) ceil (bottom_right.y - EPSILON);
+}
+
+static void
+gimp_transform_resize_crop (const GimpVector2 *orig_points,
+ gint n_points,
+ gdouble aspect,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2)
+{
+ GimpVector2 points[5];
+ Rectangle r;
+ GimpVector2 t,a;
+ gint i, j;
+ gint min;
+
+ memcpy (points, orig_points, sizeof (GimpVector2) * n_points);
+
+ /* find lowest, rightmost corner of surrounding rectangle */
+ a.x = 0;
+ a.y = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (points[i].x < a.x)
+ a.x = points[i].x;
+
+ if (points[i].y < a.y)
+ a.y = points[i].y;
+ }
+
+ /* and translate all the points to the first quadrant */
+ for (i = 0; i < n_points; i++)
+ {
+ points[i].x += (-a.x) * 2;
+ points[i].y += (-a.y) * 2;
+ }
+
+ /* find the convex hull using Jarvis's March as the points are passed
+ * in different orders due to gimp_matrix3_transform_point()
+ */
+ min = 0;
+ for (i = 0; i < n_points; i++)
+ {
+ if (points[i].y < points[min].y)
+ min = i;
+ }
+
+ t = points[0];
+ points[0] = points[min];
+ points[min] = t;
+
+ for (i = 1; i < n_points - 1; i++)
+ {
+ gdouble min_theta;
+ gdouble min_mag;
+ int next;
+
+ next = n_points - 1;
+ min_theta = 2.0 * G_PI;
+ min_mag = DBL_MAX;
+
+ for (j = i; j < n_points; j++)
+ {
+ gdouble theta;
+ gdouble sy;
+ gdouble sx;
+ gdouble mag;
+
+ sy = points[j].y - points[i - 1].y;
+ sx = points[j].x - points[i - 1].x;
+
+ if ((sx == 0.0) && (sy == 0.0))
+ {
+ next = j;
+ break;
+ }
+
+ theta = atan2 (-sy, -sx);
+ mag = (sx * sx) + (sy * sy);
+
+ if ((theta < min_theta) ||
+ ((theta == min_theta) && (mag < min_mag)))
+ {
+ min_theta = theta;
+ min_mag = mag;
+ next = j;
+ }
+ }
+
+ t = points[i];
+ points[i] = points[next];
+ points[next] = t;
+ }
+
+ /* reverse the order of points */
+ for (i = 0; i < n_points / 2; i++)
+ {
+ t = points[i];
+ points[i] = points[n_points - i - 1];
+ points[n_points - i - 1] = t;
+ }
+
+ r.a.x = r.a.y = r.b.x = r.b.y = r.c.x = r.c.y = r.d.x = r.d.y = r.area = 0;
+ r.aspect = aspect;
+
+ if (aspect != 0)
+ {
+ for (i = 0; i < n_points; i++)
+ find_maximum_aspect_rectangle (&r, points, n_points, i);
+ }
+ else
+ {
+ for (i = 0; i < n_points; i++)
+ {
+ find_three_point_rectangle (&r, points, n_points, i);
+ find_three_point_rectangle_corner (&r, points, n_points, i);
+ find_two_point_rectangle (&r, points, n_points, i);
+ find_three_point_rectangle_triangle (&r, points, n_points, i);
+ }
+ }
+
+ if (r.area == 0)
+ {
+ /* saveguard if something went wrong, adjust and give warning */
+ gimp_transform_resize_adjust (orig_points, n_points,
+ x1, y1, x2, y2);
+ g_printerr ("no rectangle found by algorithm, no cropping done\n");
+ return;
+ }
+ else
+ {
+ /* round and translate the calculated points back */
+ *x1 = floor (r.a.x + 0.5);
+ *y1 = floor (r.a.y + 0.5);
+ *x2 = ceil (r.c.x - 0.5);
+ *y2 = ceil (r.c.y - 0.5);
+
+ *x1 = *x1 - ((-a.x) * 2);
+ *y1 = *y1 - ((-a.y) * 2);
+ *x2 = *x2 - ((-a.x) * 2);
+ *y2 = *y2 - ((-a.y) * 2);
+ return;
+ }
+}
+
+static void
+find_three_point_rectangle (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p)
+{
+ GimpVector2 a = points[p % n_points]; /* 0 1 2 3 */
+ GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
+ GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
+ GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
+ GimpVector2 i1; /* intersection point */
+ GimpVector2 i2; /* intersection point */
+ GimpVector2 i3; /* intersection point */
+
+ if (intersect_x (b, c, a, &i1) &&
+ intersect_y (c, d, i1, &i2) &&
+ intersect_x (d, a, i2, &i3))
+ add_rectangle (points, n_points, r, i3, i3, i1, i1);
+
+ if (intersect_y (b, c, a, &i1) &&
+ intersect_x (c, d, i1, &i2) &&
+ intersect_y (d, a, i2, &i3))
+ add_rectangle (points, n_points, r, i3, i3, i1, i1);
+
+ if (intersect_x (d, c, a, &i1) &&
+ intersect_y (c, b, i1, &i2) &&
+ intersect_x (b, a, i2, &i3))
+ add_rectangle (points, n_points, r, i3, i3, i1, i1);
+
+ if (intersect_y (d, c, a, &i1) &&
+ intersect_x (c, b, i1, &i2) &&
+ intersect_y (b, a, i2, &i3))
+ add_rectangle (points, n_points, r, i3, i3, i1, i1);
+}
+
+static void
+find_three_point_rectangle_corner (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p)
+{
+ GimpVector2 a = points[p % n_points]; /* 0 1 2 3 */
+ GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
+ GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 2 */
+ GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 2 1 */
+ GimpVector2 i1; /* intersection point */
+ GimpVector2 i2; /* intersection point */
+
+ if (intersect_x (b, c, a , &i1) &&
+ intersect_y (c, d, i1, &i2))
+ add_rectangle (points, n_points, r, a, a, i1, i2);
+
+ if (intersect_y (b, c, a , &i1) &&
+ intersect_x (c, d, i1, &i2))
+ add_rectangle (points, n_points, r, a, a, i1, i2);
+
+ if (intersect_x (c, d, a , &i1) &&
+ intersect_y (b, c, i1, &i2))
+ add_rectangle (points, n_points, r, a, a, i1, i2);
+
+ if (intersect_y (c, d, a , &i1) &&
+ intersect_x (b, c, i1, &i2))
+ add_rectangle (points, n_points, r, a, a, i1, i2);
+}
+
+static void
+find_two_point_rectangle (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p)
+{
+ GimpVector2 a = points[ p % n_points]; /* 0 1 2 3 */
+ GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
+ GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
+ GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
+ GimpVector2 i1; /* intersection point */
+ GimpVector2 i2; /* intersection point */
+ GimpVector2 mid; /* Mid point */
+
+ add_rectangle (points, n_points, r, a, a, c, c);
+ add_rectangle (points, n_points, r, b, b, d, d);
+
+ if (intersect_x (c, b, a, &i1) &&
+ intersect_y (c, b, a, &i2))
+ {
+ mid.x = ( i1.x + i2.x ) / 2.0;
+ mid.y = ( i1.y + i2.y ) / 2.0;
+
+ add_rectangle (points, n_points, r, a, a, mid, mid);
+ }
+}
+
+static void
+find_three_point_rectangle_triangle (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p)
+{
+ GimpVector2 a = points[p % n_points]; /* 0 1 2 3 */
+ GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
+ GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
+ GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
+ GimpVector2 i1; /* intersection point */
+ GimpVector2 i2; /* intersection point */
+ GimpVector2 mid;
+
+ mid.x = (a.x + b.x) / 2.0;
+ mid.y = (a.y + b.y) / 2.0;
+
+ if (intersect_x (b, c, mid, &i1) &&
+ intersect_y (a, d, mid, &i2))
+ add_rectangle (points, n_points, r, mid, mid, i1, i2);
+
+ if (intersect_y (b, c, mid, &i1) &&
+ intersect_x (a, d, mid, &i2))
+ add_rectangle (points, n_points, r, mid, mid, i1, i2);
+
+ if (intersect_x (a, d, mid, &i1) &&
+ intersect_y (b, c, mid, &i2))
+ add_rectangle (points, n_points, r, mid, mid, i1, i2);
+
+ if (intersect_y (a, d, mid, &i1) &&
+ intersect_x (b, c, mid, &i2))
+ add_rectangle (points, n_points, r, mid, mid, i1, i2);
+}
+
+static void
+find_maximum_aspect_rectangle (Rectangle *r,
+ const GimpVector2 *points,
+ gint n_points,
+ gint p)
+{
+ GimpVector2 a = points[ p % n_points]; /* 0 1 2 3 */
+ GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
+ GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
+ GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
+ GimpVector2 i1; /* intersection point */
+ GimpVector2 i2; /* intersection point */
+ GimpVector2 i3; /* intersection point */
+
+ if (intersect_x (b, c, a, &i1))
+ {
+ i2.x = i1.x + 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (c, d, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ i2.x = i1.x - 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (c, d, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+ }
+
+ if (intersect_y (b, c, a, &i1))
+ {
+ i2.x = i1.x + 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (c, d, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ i2.x = i1.x - 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (c, d, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+ }
+
+ if (intersect_x (c, d, a, &i1))
+ {
+ i2.x = i1.x + 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (b, c, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ i2.x = i1.x - 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (b, c, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+ }
+
+ if (intersect_y (c, d, a, &i1))
+ {
+ i2.x = i1.x + 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (b, c, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ i2.x = i1.x - 1.0 * r->aspect;
+ i2.y = i1.y + 1.0;
+
+ if (intersect (d, a, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (a, b, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+
+ if (intersect (b, c, i1, i2, &i3))
+ add_rectangle (points, n_points, r, i1, i3, i1, i3);
+ }
+}
+
+/* check if point is inside the polygon "points", if point is on border
+ * its still inside.
+ */
+static gboolean
+in_poly (const GimpVector2 *points,
+ gint n_points,
+ GimpVector2 p)
+{
+ GimpVector2 p1, p2;
+ gint counter = 0;
+ gint i;
+
+ p1 = points[0];
+
+ for (i = 1; i <= n_points; i++)
+ {
+ p2 = points[i % n_points];
+
+ if (p.y > MIN (p1.y, p2.y))
+ {
+ if (p.y <= MAX (p1.y, p2.y))
+ {
+ if (p.x <= MAX (p1.x, p2.x))
+ {
+ if (p1.y != p2.y)
+ {
+ gdouble xinters = ((p.y - p1.y) * (p2.x - p1.x) /
+ (p2.y - p1.y) + p1.x);
+
+ if (p1.x == p2.x || p.x <= xinters)
+ counter++;
+ }
+ }
+ }
+ }
+
+ p1 = p2;
+ }
+
+ /* border check */
+ if (point_on_border (points, n_points, p))
+ return TRUE;
+
+ return (counter % 2 != 0);
+}
+
+/* check if the point p lies on the polygon "points"
+ */
+static gboolean
+point_on_border (const GimpVector2 *points,
+ gint n_points,
+ GimpVector2 p)
+{
+ gint i;
+
+ for (i = 0; i <= n_points; i++)
+ {
+ GimpVector2 a = points[i % n_points];
+ GimpVector2 b = points[(i + 1) % n_points];
+ gdouble a1 = (b.y - a.y);
+ gdouble b1 = (a.x - b.x);
+ gdouble c1 = a1 * a.x + b1 * a.y;
+ gdouble c2 = a1 * p.x + b1 * p.y;
+
+ if (ABS (c1 - c2) < EPSILON &&
+ MIN (a.x, b.x) <= p.x &&
+ MAX (a.x, b.x) >= p.x &&
+ MIN (a.y, b.y) <= p.y &&
+ MAX (a.y, b.y) >= p.y)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* calculate the intersection point of the line a-b with the line c-d
+ * and write it to i, if existing.
+ */
+static gboolean
+intersect (GimpVector2 a,
+ GimpVector2 b,
+ GimpVector2 c,
+ GimpVector2 d,
+ GimpVector2 *i)
+{
+ gdouble a1 = (b.y - a.y);
+ gdouble b1 = (a.x - b.x);
+ gdouble c1 = a1 * a.x + b1 * a.y;
+
+ gdouble a2 = (d.y - c.y);
+ gdouble b2 = (c.x - d.x);
+ gdouble c2 = a2 * c.x + b2 * c.y;
+ gdouble det = a1 * b2 - a2 * b1;
+
+ if (det == 0)
+ return FALSE;
+
+ i->x = (b2 * c1 - b1 * c2) / det;
+ i->y = (a1 * c2 - a2 * c1) / det;
+
+ return TRUE;
+}
+
+/* calculate the intersection point of the line a-b with the vertical line
+ * through c and write it to i, if existing.
+ */
+static gboolean
+intersect_x (GimpVector2 a,
+ GimpVector2 b,
+ GimpVector2 c,
+ GimpVector2 *i)
+{
+ GimpVector2 d = c;
+ d.y += 1;
+
+ return intersect(a,b,c,d,i);
+}
+
+/* calculate the intersection point of the line a-b with the horizontal line
+ * through c and write it to i, if existing.
+ */
+static gboolean
+intersect_y (GimpVector2 a,
+ GimpVector2 b,
+ GimpVector2 c,
+ GimpVector2 *i)
+{
+ GimpVector2 d = c;
+ d.x += 1;
+
+ return intersect(a,b,c,d,i);
+}
+
+/* this takes the smallest ortho-aligned (the sides of the rectangle are
+ * parallel to the x- and y-axis) rectangle fitting around the points a to d,
+ * checks if the whole rectangle is inside the polygon described by points and
+ * writes it to r if the area is bigger than the rectangle already stored in r.
+ */
+static void
+add_rectangle (const GimpVector2 *points,
+ gint n_points,
+ Rectangle *r,
+ GimpVector2 a,
+ GimpVector2 b,
+ GimpVector2 c,
+ GimpVector2 d)
+{
+ gdouble width;
+ gdouble height;
+ gdouble minx, maxx;
+ gdouble miny, maxy;
+
+ /* get the orthoaligned (the sides of the rectangle are parallel to the x-
+ * and y-axis) rectangle surrounding the points a to d.
+ */
+ minx = MIN4 (a.x, b.x, c.x, d.x);
+ maxx = MAX4 (a.x, b.x, c.x, d.x);
+ miny = MIN4 (a.y, b.y, c.y, d.y);
+ maxy = MAX4 (a.y, b.y, c.y, d.y);
+
+ a.x = minx;
+ a.y = miny;
+
+ b.x = maxx;
+ b.y = miny;
+
+ c.x = maxx;
+ c.y = maxy;
+
+ d.x = minx;
+ d.y = maxy;
+
+ width = maxx - minx;
+ height = maxy - miny;
+
+ /* check if this rectangle is inside the polygon "points" */
+ if (in_poly (points, n_points, a) &&
+ in_poly (points, n_points, b) &&
+ in_poly (points, n_points, c) &&
+ in_poly (points, n_points, d))
+ {
+ gdouble area = width * height;
+
+ /* check if the new rectangle is larger (in terms of area)
+ * than the currently stored rectangle in r, if yes store
+ * new rectangle to r
+ */
+ if (r->area <= area)
+ {
+ r->a.x = a.x;
+ r->a.y = a.y;
+
+ r->b.x = b.x;
+ r->b.y = b.y;
+
+ r->c.x = c.x;
+ r->c.y = c.y;
+
+ r->d.x = d.x;
+ r->d.y = d.y;
+
+ r->area = area;
+ }
+ }
+}