Adjust line algorithm depending on each case (fix #1944, #1894, #1395)

Related to this bug report:
https://community.aseprite.org/t/1045
This commit is contained in:
David Capello 2018-12-05 22:53:22 -03:00
parent 8106aeee44
commit 7c2038e7b5
5 changed files with 82 additions and 19 deletions

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -10,6 +11,7 @@
#include "app/tools/intertwine.h"
#include "app/tools/controller.h"
#include "app/tools/point_shape.h"
#include "app/tools/stroke.h"
#include "app/tools/symmetry.h"
@ -52,12 +54,23 @@ void Intertwine::doPointshapePoint(int x, int y, ToolLoop* loop)
void Intertwine::doPointshapeHline(int x1, int y, int x2, ToolLoop* loop)
{
algo_line(x1, y, x2, y, loop, (AlgoPixel)doPointshapePoint);
algo_line_perfect(x1, y, x2, y, loop, (AlgoPixel)doPointshapePoint);
}
void Intertwine::doPointshapeLine(int x1, int y1, int x2, int y2, ToolLoop* loop)
{
algo_line(x1, y1, x2, y2, loop, (AlgoPixel)doPointshapePoint);
if (// When "Snap Angle" in being used or...
(int(loop->getModifiers()) & int(ToolLoopModifiers::kSquareAspect)) ||
// "Snap to Grid" is enabled
(loop->getController()->canSnapToGrid() && loop->getSnapToGrid())) {
// We prefer the perfect pixel lines that matches grid tiles
algo_line_perfect(x1, y1, x2, y2, loop, (AlgoPixel)doPointshapePoint);
}
else {
// In other case we use the regular algorithm that is useful to
// draw continuous lines/strokes.
algo_line_continuous(x1, y1, x2, y2, loop, (AlgoPixel)doPointshapePoint);
}
}
} // namespace tools

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -57,15 +58,15 @@ public:
int x2 = stroke[c+1].x;
int y2 = stroke[c+1].y;
algo_line(x1, y1, x2, y2, loop, (AlgoPixel)doPointshapePoint);
doPointshapeLine(x1, y1, x2, y2, loop);
}
}
// Closed shape (polygon outline)
if (loop->getFilled()) {
algo_line(stroke[0].x, stroke[0].y,
stroke[stroke.size()-1].x,
stroke[stroke.size()-1].y, loop, (AlgoPixel)doPointshapePoint);
doPointshapeLine(stroke[0].x, stroke[0].y,
stroke[stroke.size()-1].x,
stroke[stroke.size()-1].y, loop);
}
}
@ -308,20 +309,22 @@ public:
doPointshapePoint(stroke[c].x, stroke[c].y, loop);
}
else if (stroke.size()-c == 2) {
algo_line(stroke[c].x, stroke[c].y,
stroke[c+1].x, stroke[c+1].y, loop, (AlgoPixel)doPointshapePoint);
doPointshapeLine(stroke[c].x, stroke[c].y,
stroke[c+1].x, stroke[c+1].y, loop);
}
else if (stroke.size()-c == 3) {
algo_spline(stroke[c ].x, stroke[c ].y,
stroke[c+1].x, stroke[c+1].y,
stroke[c+1].x, stroke[c+1].y,
stroke[c+2].x, stroke[c+2].y, loop, (AlgoLine)doPointshapeLine);
stroke[c+2].x, stroke[c+2].y, loop,
(AlgoLine)doPointshapeLine);
}
else {
algo_spline(stroke[c ].x, stroke[c ].y,
stroke[c+1].x, stroke[c+1].y,
stroke[c+2].x, stroke[c+2].y,
stroke[c+3].x, stroke[c+3].y, loop, (AlgoLine)doPointshapeLine);
stroke[c+3].x, stroke[c+3].y, loop,
(AlgoLine)doPointshapeLine);
}
}
}
@ -363,12 +366,16 @@ public:
if (stroke.size() == 0)
return;
else if (stroke.size() == 2) {
doPointshapeLine(stroke[0].x, stroke[0].y,
stroke[1].x, stroke[1].y, loop);
}
else if (m_pts.empty() && stroke.size() == 1) {
m_pts = stroke;
}
else {
for (int c=0; c+1<stroke.size(); ++c) {
algo_line(
algo_line_continuous(
stroke[c].x,
stroke[c].y,
stroke[c+1].x,

View File

@ -1,4 +1,5 @@
// Aseprite Document Library
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -20,7 +21,7 @@
namespace doc {
void algo_line(int x1, int y1, int x2, int y2, void* data, AlgoPixel proc)
void algo_line_perfect(int x1, int y1, int x2, int y2, void* data, AlgoPixel proc)
{
bool yaxis;
@ -64,6 +65,32 @@ void algo_line(int x1, int y1, int x2, int y2, void* data, AlgoPixel proc)
}
}
// Line code based on Alois Zingl work released under the
// MIT license http://members.chello.at/easyfilter/bresenham.html
void algo_line_continuous(int x0, int y0, int x1, int y1, void* data, AlgoPixel proc)
{
int dx = ABS(x1-x0), sx = (x0 < x1 ? 1: -1);
int dy = -ABS(y1-y0), sy = (y0 < y1 ? 1: -1);
int err = dx+dy, e2; // error value e_xy
for (;;) {
proc(x0, y0, data);
e2 = 2*err;
if (e2 >= dy) { // e_xy+e_x > 0
if (x0 == x1)
break;
err += dy;
x0 += sx;
}
if (e2 <= dx) { // e_xy+e_y < 0
if (y0 == y1)
break;
err += dx;
y0 += sy;
}
}
}
/* Additional helper functions for the ellipse-drawing helper function
below corresponding to routines in Bresenham's algorithm. */
namespace {
@ -132,11 +159,11 @@ void algo_ellipse(int x1, int y1, int x2, int y2, void *data, AlgoPixel proc)
rx = ABS(x1 - x2);
ry = ABS(y1 - y2);
if (rx == 1) { algo_line(x2, y1, x2, y2, data, proc); rx--; }
if (rx == 0) { algo_line(x1, y1, x1, y2, data, proc); return; }
if (rx == 1) { algo_line_perfect(x2, y1, x2, y2, data, proc); rx--; }
if (rx == 0) { algo_line_perfect(x1, y1, x1, y2, data, proc); return; }
if (ry == 1) { algo_line(x1, y2, x2, y2, data, proc); ry--; }
if (ry == 0) { algo_line(x1, y1, x2, y1, data, proc); return; }
if (ry == 1) { algo_line_perfect(x1, y2, x2, y2, data, proc); ry--; }
if (ry == 0) { algo_line_perfect(x1, y1, x2, y1, data, proc); return; }
rx /= 2;
ry /= 2;
@ -316,7 +343,7 @@ static void draw_quad_rational_bezier_seg(int x0, int y0,
}
} while (dy <= xy && dx >= xy); // gradient negates -> algorithm fails
}
algo_line(x0, y0, x2, y2, data, proc); // plot remaining needle to end
algo_line_continuous(x0, y0, x2, y2, data, proc); // plot remaining needle to end
}
static void draw_rotated_ellipse_rect(int x0, int y0, int x1, int y1, double zd, void* data, AlgoPixel proc)

View File

@ -1,4 +1,5 @@
// Aseprite Document Library
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -18,7 +19,22 @@ namespace doc {
typedef void (*AlgoPixel)(int x, int y, void *data);
typedef void (*AlgoLine)(int x1, int y1, int x2, int y2, void *data);
void algo_line(int x1, int y1, int x2, int y2, void *data, AlgoPixel proc);
// Useful to create lines with more predictable behavior and perfect
// pixel block of lines where we'll have a number of lines/rows that
// looks the same.
//
// Related to: https://github.com/aseprite/aseprite/issues/1395
void algo_line_perfect(int x1, int y1, int x2, int y2, void* data, AlgoPixel proc);
// Useful to create continuous lines (you can draw from one point to
// another, and continue from that point to another in the same
// angle and the line will look continous).
//
// Related to:
// https://community.aseprite.org/t/1045
// https://github.com/aseprite/aseprite/issues/1894
void algo_line_continuous(int x1, int y1, int x2, int y2, void *data, AlgoPixel proc);
void algo_ellipse(int x1, int y1, int x2, int y2, void *data, AlgoPixel proc);
void algo_ellipsefill(int x1, int y1, int x2, int y2, void *data, AlgoHLine proc);

View File

@ -279,7 +279,7 @@ static void hline_for_image(int x1, int y, int x2, Data* data)
void draw_line(Image* image, int x1, int y1, int x2, int y2, color_t color)
{
Data data = { image, color };
algo_line(x1, y1, x2, y2, &data, (AlgoPixel)pixel_for_image);
algo_line_continuous(x1, y1, x2, y2, &data, (AlgoPixel)pixel_for_image);
}
void draw_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t color)