From 7c2038e7b55a7344ff003d5dea8dfb28978dcc83 Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 5 Dec 2018 22:53:22 -0300 Subject: [PATCH] Adjust line algorithm depending on each case (fix #1944, #1894, #1395) Related to this bug report: https://community.aseprite.org/t/1045 --- src/app/tools/intertwine.cpp | 17 ++++++++++++++-- src/app/tools/intertwiners.h | 25 ++++++++++++++--------- src/doc/algo.cpp | 39 ++++++++++++++++++++++++++++++------ src/doc/algo.h | 18 ++++++++++++++++- src/doc/primitives.cpp | 2 +- 5 files changed, 82 insertions(+), 19 deletions(-) diff --git a/src/app/tools/intertwine.cpp b/src/app/tools/intertwine.cpp index 73d065e03..2c8d1d54d 100644 --- a/src/app/tools/intertwine.cpp +++ b/src/app/tools/intertwine.cpp @@ -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 diff --git a/src/app/tools/intertwiners.h b/src/app/tools/intertwiners.h index 26dc26f6f..b11763c83 100644 --- a/src/app/tools/intertwiners.h +++ b/src/app/tools/intertwiners.h @@ -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= 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) diff --git a/src/doc/algo.h b/src/doc/algo.h index f66f21641..512ad64d2 100644 --- a/src/doc/algo.h +++ b/src/doc/algo.h @@ -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); diff --git a/src/doc/primitives.cpp b/src/doc/primitives.cpp index 7b415fda4..8774c47ed 100644 --- a/src/doc/primitives.cpp +++ b/src/doc/primitives.cpp @@ -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)