Reimplement line algorithm (fix #1395)

This commit is contained in:
David Capello 2017-06-22 21:10:44 -03:00
parent ac44c4ab97
commit 88e0596692

View File

@ -1,5 +1,5 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2001-2016 David Capello // Copyright (c) 2001-2017 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information. // Read LICENSE.txt for more information.
@ -8,94 +8,54 @@
#include "config.h" #include "config.h"
#endif #endif
#include <math.h>
#include "base/base.h" #include "base/base.h"
#include "doc/algo.h" #include "doc/algo.h"
#include <algorithm>
#include <cmath>
namespace doc { namespace doc {
// Algorightm from Allegro (allegro/src/gfx.c) void algo_line(int x1, int y1, int x2, int y2, void* data, AlgoPixel proc)
// Adapted for Aseprite by David Capello.
void algo_line(int x1, int y1, int x2, int y2, void *data, AlgoPixel proc)
{ {
int dx = x2-x1; bool yaxis;
int dy = y2-y1;
int i1, i2;
int x, y;
int dd;
/* worker macro */ // If the height if the line is bigger than the width, we'll iterate
#define DO_LINE(pri_sign, pri_c, pri_cond, sec_sign, sec_c, sec_cond) \ // over the y-axis.
{ \ if (ABS(y2-y1) > ABS(x2-x1)) {
if (d##pri_c == 0) { \ std::swap(x1, y1);
proc(x1, y1, data); \ std::swap(x2, y2);
return; \ yaxis = true;
} \
\
i1 = 2 * d##sec_c; \
dd = i1 - (sec_sign (pri_sign d##pri_c)); \
i2 = dd - (sec_sign (pri_sign d##pri_c)); \
\
x = x1; \
y = y1; \
\
while (pri_c pri_cond pri_c##2) { \
proc(x, y, data); \
\
if (dd sec_cond 0) { \
sec_c sec_sign##= 1; \
dd += i2; \
} \
else \
dd += i1; \
\
pri_c pri_sign##= 1; \
} \
} }
else
yaxis = false;
if (dx >= 0) { const int w = ABS(x2-x1)+1;
if (dy >= 0) { const int h = ABS(y2-y1)+1;
if (dx >= dy) { const int dx = SGN(x2-x1);
/* (x1 <= x2) && (y1 <= y2) && (dx >= dy) */ const int dy = SGN(y2-y1);
DO_LINE(+, x, <=, +, y, >=);
} int e = 0;
else { int y = y1;
/* (x1 <= x2) && (y1 <= y2) && (dx < dy) */
DO_LINE(+, y, <=, +, x, >=); // Move one x2 extra pixels to the dx direction so we can use
} // operator!=() instead of operator<(). Here I prefer operator!=()
} // instead of swapping x1 with x2 so the error always start from 0
else { // in the origin (x1,y1).
if (dx >= -dy) { x2 += dx;
/* (x1 <= x2) && (y1 > y2) && (dx >= dy) */
DO_LINE(+, x, <=, -, y, <=); for (int x=x1; x!=x2; x+=dx) {
} if (yaxis)
else { proc(y, x, data);
/* (x1 <= x2) && (y1 > y2) && (dx < dy) */ else
DO_LINE(-, y, >=, +, x, >=); proc(x, y, data);
}
} // The error advances "h/w" per each "x" step. As we're using a
} // integer value for "e", we use "w" as the unit.
else { e += h;
if (dy >= 0) { if (e >= w) {
if (-dx >= dy) { y += dy;
/* (x1 > x2) && (y1 <= y2) && (dx >= dy) */ e -= w;
DO_LINE(-, x, >=, +, y, >=);
}
else {
/* (x1 > x2) && (y1 <= y2) && (dx < dy) */
DO_LINE(+, y, <=, -, x, <=);
}
}
else {
if (-dx >= -dy) {
/* (x1 > x2) && (y1 > y2) && (dx >= dy) */
DO_LINE(-, x, >=, -, y, <=);
}
else {
/* (x1 > x2) && (y1 > y2) && (dx < dy) */
DO_LINE(-, y, >=, -, x, <=);
}
} }
} }
} }