app: add support for subpixel image grids

In particular, this enables grids whose points of intersection
are at the middle of the image's pixels, which is useful for
undistorted painting with odd-sized brushes using tools other than
the pencil.

This commit also changes the grid visibility behavior, so that the
the visibiltiy of horizontal and vertical grid lines (depending on
the zoom level) is independent.
This commit is contained in:
Ell
2017-02-19 18:03:02 -05:00
parent 63f1ec4101
commit 1572bccc9f
4 changed files with 127 additions and 121 deletions

View File

@ -108,14 +108,14 @@ gimp_grid_class_init (GimpGridClass *klass)
"xspacing",
_("Spacing X"),
_("Horizontal spacing of grid lines."),
1.0, GIMP_MAX_IMAGE_SIZE, 10.0,
0.0, GIMP_MAX_IMAGE_SIZE, 10.0,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_YSPACING,
"yspacing",
_("Spacing Y"),
_("Vertical spacing of grid lines."),
1.0, GIMP_MAX_IMAGE_SIZE, 10.0,
0.0, GIMP_MAX_IMAGE_SIZE, 10.0,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_UNIT (object_class, PROP_SPACING_UNIT,
@ -283,9 +283,8 @@ gimp_grid_get_spacing (GimpGrid *grid,
{
g_return_if_fail (GIMP_IS_GRID (grid));
/* FIXME subpixel grid */
if (xspacing) *xspacing = RINT (grid->xspacing);
if (yspacing) *yspacing = RINT (grid->yspacing);
if (xspacing) *xspacing = grid->xspacing;
if (yspacing) *yspacing = grid->yspacing;
}
void
@ -295,9 +294,8 @@ gimp_grid_get_offset (GimpGrid *grid,
{
g_return_if_fail (GIMP_IS_GRID (grid));
/* FIXME subpixel grid */
if (xoffset) *xoffset = RINT (grid->xoffset);
if (yoffset) *yoffset = RINT (grid->yoffset);
if (xoffset) *xoffset = grid->xoffset;
if (yoffset) *yoffset = grid->yoffset;
}
const gchar *

View File

@ -100,21 +100,17 @@ gimp_image_snap_x (GimpImage *image,
GimpGrid *grid = gimp_image_get_grid (image);
gdouble xspacing;
gdouble xoffset;
gdouble i;
gimp_grid_get_spacing (grid, &xspacing, NULL);
gimp_grid_get_offset (grid, &xoffset, NULL);
/* the snap-to-grid part could probably be rewritten */
while (xoffset > xspacing)
xoffset -= xspacing;
for (i = xoffset; i <= gimp_image_get_width (image); i += xspacing)
if (xspacing > 0.0)
{
if (i < 0)
continue;
gdouble nearest;
snapped |= gimp_image_snap_distance (x, i,
nearest = xoffset + RINT ((x - xoffset) / xspacing) * xspacing;
snapped |= gimp_image_snap_distance (x, nearest,
epsilon_x,
&mindist, tx);
}
@ -183,22 +179,19 @@ gimp_image_snap_y (GimpImage *image,
if (snap_to_grid)
{
GimpGrid *grid = gimp_image_get_grid (image);
gdouble yspacing;
gdouble yoffset;
gdouble i;
gdouble yspacing;
gdouble yoffset;
gimp_grid_get_spacing (grid, NULL, &yspacing);
gimp_grid_get_offset (grid, NULL, &yoffset);
while (yoffset > yspacing)
yoffset -= yspacing;
for (i = yoffset; i <= gimp_image_get_height (image); i += yspacing)
if (yspacing > 0.0)
{
if (i < 0)
continue;
gdouble nearest;
snapped |= gimp_image_snap_distance (y, i,
nearest = yoffset + RINT ((y - yoffset) / yspacing) * yspacing;
snapped |= gimp_image_snap_distance (y, nearest,
epsilon_y,
&mindist, ty);
}
@ -291,33 +284,28 @@ gimp_image_snap_point (GimpImage *image,
GimpGrid *grid = gimp_image_get_grid (image);
gdouble xspacing, yspacing;
gdouble xoffset, yoffset;
gdouble i;
gimp_grid_get_spacing (grid, &xspacing, &yspacing);
gimp_grid_get_offset (grid, &xoffset, &yoffset);
while (xoffset > xspacing)
xoffset -= xspacing;
while (yoffset > yspacing)
yoffset -= yspacing;
for (i = xoffset; i <= gimp_image_get_width (image); i += xspacing)
if (xspacing > 0.0)
{
if (i < 0)
continue;
gdouble nearest;
snapped |= gimp_image_snap_distance (x, i,
nearest = xoffset + RINT ((x - xoffset) / xspacing) * xspacing;
snapped |= gimp_image_snap_distance (x, nearest,
epsilon_x,
&mindist_x, tx);
}
for (i = yoffset; i <= gimp_image_get_height (image); i += yspacing)
if (yspacing > 0.0)
{
if (i < 0)
continue;
gdouble nearest;
snapped |= gimp_image_snap_distance (y, i,
nearest = yoffset + RINT ((y - yoffset) / yspacing) * yspacing;
snapped |= gimp_image_snap_distance (y, nearest,
epsilon_y,
&mindist_y, ty);
}

View File

@ -20,6 +20,8 @@
#include "config.h"
#include <math.h>
#include <gegl.h>
#include <gtk/gtk.h>
@ -191,6 +193,7 @@ gimp_canvas_grid_draw (GimpCanvasItem *item,
GimpImage *image = gimp_canvas_item_get_image (item);
gdouble xspacing, yspacing;
gdouble xoffset, yoffset;
gboolean vert, horz;
gdouble x, y;
gdouble dx1, dy1, dx2, dy2;
gint x0, x1, x2, x3;
@ -203,14 +206,16 @@ gimp_canvas_grid_draw (GimpCanvasItem *item,
gimp_grid_get_spacing (private->grid, &xspacing, &yspacing);
gimp_grid_get_offset (private->grid, &xoffset, &yoffset);
g_return_if_fail (xspacing > 0.0 &&
yspacing > 0.0);
g_return_if_fail (xspacing >= 0.0 &&
yspacing >= 0.0);
/* skip grid drawing when the space between grid lines starts
* disappearing, see bug #599267.
*/
if (xspacing * shell->scale_x < 2.0 ||
yspacing * shell->scale_y < 2.0)
vert = (xspacing * shell->scale_x >= 2.0);
horz = (yspacing * shell->scale_y >= 2.0);
if (! vert && ! horz)
return;
cairo_clip_extents (cr, &dx1, &dy1, &dx2, &dy2);
@ -223,84 +228,87 @@ gimp_canvas_grid_draw (GimpCanvasItem *item,
width = gimp_image_get_width (image);
height = gimp_image_get_height (image);
while (xoffset > 0)
xoffset -= xspacing;
while (yoffset > 0)
yoffset -= yspacing;
xoffset = fmod (xoffset, xspacing);
yoffset = fmod (yoffset, yspacing);
switch (gimp_grid_get_style (private->grid))
{
case GIMP_GRID_DOTS:
for (x = xoffset; x <= width; x += xspacing)
if (vert && horz)
{
if (x < 0)
continue;
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
if (x_real < x1 || x_real >= x2)
continue;
for (y = yoffset; y <= height; y += yspacing)
for (x = xoffset; x <= width; x += xspacing)
{
if (y < 0)
if (x < 0)
continue;
gimp_canvas_item_transform_xy (item, x, y, &x_real, &y_real);
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
if (y_real >= y1 && y_real < y2)
if (x_real < x1 || x_real >= x2)
continue;
for (y = yoffset; y <= height; y += yspacing)
{
cairo_move_to (cr, x_real, y_real + 0.5);
cairo_line_to (cr, x_real + 1, y_real + 0.5);
if (y < 0)
continue;
gimp_canvas_item_transform_xy (item, x, y, &x_real, &y_real);
if (y_real >= y1 && y_real < y2)
{
cairo_move_to (cr, x_real, y_real + 0.5);
cairo_line_to (cr, x_real + 1, y_real + 0.5);
}
}
}
}
break;
case GIMP_GRID_INTERSECTIONS:
for (x = xoffset; x <= width; x += xspacing)
if (vert && horz)
{
if (x < 0)
continue;
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
if (x_real + CROSSHAIR < x1 || x_real - CROSSHAIR >= x2)
continue;
for (y = yoffset; y <= height; y += yspacing)
for (x = xoffset; x <= width; x += xspacing)
{
if (y < 0)
if (x < 0)
continue;
gimp_canvas_item_transform_xy (item, x, y, &x_real, &y_real);
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
if (y_real + CROSSHAIR < y1 || y_real - CROSSHAIR >= y2)
if (x_real + CROSSHAIR < x1 || x_real - CROSSHAIR >= x2)
continue;
if (x_real >= x1 && x_real < x2)
for (y = yoffset; y <= height; y += yspacing)
{
cairo_move_to (cr,
x_real + 0.5,
CLAMP (y_real - CROSSHAIR,
y1, y2 - 1));
cairo_line_to (cr,
x_real + 0.5,
CLAMP (y_real + CROSSHAIR,
y1, y2 - 1) + 1);
}
if (y < 0)
continue;
if (y_real >= y1 && y_real < y2)
{
cairo_move_to (cr,
CLAMP (x_real - CROSSHAIR,
x1, x2 - 1),
y_real + 0.5);
cairo_line_to (cr,
CLAMP (x_real + CROSSHAIR,
x1, x2 - 1) + 1,
y_real + 0.5);
gimp_canvas_item_transform_xy (item, x, y, &x_real, &y_real);
if (y_real + CROSSHAIR < y1 || y_real - CROSSHAIR >= y2)
continue;
if (x_real >= x1 && x_real < x2)
{
cairo_move_to (cr,
x_real + 0.5,
CLAMP (y_real - CROSSHAIR,
y1, y2 - 1));
cairo_line_to (cr,
x_real + 0.5,
CLAMP (y_real + CROSSHAIR,
y1, y2 - 1) + 1);
}
if (y_real >= y1 && y_real < y2)
{
cairo_move_to (cr,
CLAMP (x_real - CROSSHAIR,
x1, x2 - 1),
y_real + 0.5);
cairo_line_to (cr,
CLAMP (x_real + CROSSHAIR,
x1, x2 - 1) + 1,
y_real + 0.5);
}
}
}
}
@ -312,31 +320,37 @@ gimp_canvas_grid_draw (GimpCanvasItem *item,
gimp_canvas_item_transform_xy (item, 0, 0, &x0, &y0);
gimp_canvas_item_transform_xy (item, width, height, &x3, &y3);
for (x = xoffset; x < width; x += xspacing)
if (vert)
{
if (x < 0)
continue;
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
if (x_real >= x1 && x_real < x2)
for (x = xoffset; x < width; x += xspacing)
{
cairo_move_to (cr, x_real + 0.5, y0);
cairo_line_to (cr, x_real + 0.5, y3 + 1);
if (x < 0)
continue;
gimp_canvas_item_transform_xy (item, x, 0, &x_real, &y_real);
if (x_real >= x1 && x_real < x2)
{
cairo_move_to (cr, x_real + 0.5, y0);
cairo_line_to (cr, x_real + 0.5, y3 + 1);
}
}
}
for (y = yoffset; y < height; y += yspacing)
if (horz)
{
if (y < 0)
continue;
gimp_canvas_item_transform_xy (item, 0, y, &x_real, &y_real);
if (y_real >= y1 && y_real < y2)
for (y = yoffset; y < height; y += yspacing)
{
cairo_move_to (cr, x0, y_real + 0.5);
cairo_line_to (cr, x3 + 1, y_real + 0.5);
if (y < 0)
continue;
gimp_canvas_item_transform_xy (item, 0, y, &x_real, &y_real);
if (y_real >= y1 && y_real < y2)
{
cairo_move_to (cr, x0, y_real + 0.5);
cairo_line_to (cr, x3 + 1, y_real + 0.5);
}
}
}
break;

View File

@ -192,12 +192,15 @@ gimp_grid_editor_constructed (GObject *object)
gtk_table_set_row_spacings (GTK_TABLE (sizeentry), 2);
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
_("Width"), 0, 1, 0.0);
_("Horizontal"), 0, 1, 0.0);
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
_("Height"), 0, 2, 0.0);
_("Vertical"), 0, 2, 0.0);
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
_("Pixels"), 1, 4, 0.0);
gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 0, 2);
gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 1, 2);
gtk_box_pack_start (GTK_BOX (hbox), sizeentry, FALSE, FALSE, 0);
gtk_widget_show (sizeentry);
@ -224,12 +227,15 @@ gimp_grid_editor_constructed (GObject *object)
gtk_table_set_row_spacings (GTK_TABLE (sizeentry), 2);
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
_("Width"), 0, 1, 0.0);
_("Horizontal"), 0, 1, 0.0);
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
_("Height"), 0, 2, 0.0);
_("Vertical"), 0, 2, 0.0);
gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
_("Pixels"), 1, 4, 0.0);
gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 0, 2);
gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 1, 2);
gtk_box_pack_start (GTK_BOX (hbox), sizeentry, FALSE, FALSE, 0);
gtk_widget_show (sizeentry);