summaryrefslogtreecommitdiff
path: root/src/sim/sim_linepolyfill.c
diff options
context:
space:
mode:
authorscuri <scuri>2008-10-17 06:10:33 +0000
committerscuri <scuri>2008-10-17 06:10:33 +0000
commit7b52cc13af4e85f1ca2deb6b6c77de9c95ea0dcf (patch)
treed0857278bde2eff784227c57dcaf930346ceb7ac /src/sim/sim_linepolyfill.c
First commit - moving from LuaForge to SourceForge
Diffstat (limited to 'src/sim/sim_linepolyfill.c')
-rw-r--r--src/sim/sim_linepolyfill.c1000
1 files changed, 1000 insertions, 0 deletions
diff --git a/src/sim/sim_linepolyfill.c b/src/sim/sim_linepolyfill.c
new file mode 100644
index 0000000..1a20907
--- /dev/null
+++ b/src/sim/sim_linepolyfill.c
@@ -0,0 +1,1000 @@
+/** \file
+ * \brief Primitives of the Simulation Base Driver
+ *
+ * See Copyright Notice in cd.h
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <memory.h>
+#include <assert.h>
+
+#include "cd.h"
+#include "cd_private.h"
+#include "cd_truetype.h"
+#include "sim.h"
+
+
+/* para estilos de linha usando rotacao de bits */
+static const unsigned short int simLineStyleBitTable[5]=
+{
+ 0xFFFF, /* CD_CONTINUOUS */
+ 0xFF00, /* CD_DASHED */
+ 0x1111, /* CD_DOTTED */
+ 0xFE10, /* CD_DASH_DOT */
+ 0xFF24, /* CD_DASH_DOT_DOT*/
+};
+int simLineStyleNoReset = 0;
+static unsigned short int simLineStyleLastBits = 0;
+
+#define simRotateLineStyle(_x) (((_x) & 0x8000)? ((_x) << 1)|(0x0001): ((_x) << 1))
+
+#define INTENSITYSHIFT 8 /* # of bits by which to shift ErrorAcc to get intensity level */
+
+
+/* Point in Polygon was obtained from:
+ www.geometryalgorithms.com/Archive/algorithm_0103/algorithm_0103.htm
+
+ Copyright 2001, softSurfer (www.softsurfer.com)
+ This code may be freely used and modified for any purpose
+ providing that this copyright notice is included with it.
+ SoftSurfer makes no warranty for this code, and cannot be held
+ liable for any real or imagined damage resulting from its use.
+*/
+
+#define isLeft( _P0, _P1, _x, _y ) ((_P1.x - _P0.x)*(_y - _P0.y) - (_x - _P0.x)*(_P1.y - _P0.y))
+
+int simIsPointInPolyWind(cdPoint* poly, int n, int x, int y)
+{
+ int i, i1,
+ wn = 0; /* the winding number counter */
+
+ for (i = 0; i < n; i++)
+ {
+ i1 = (i+1)%n; /* next point(i+1), next of last(n-1) is first(0) */
+
+ if (poly[i].y <= y)
+ {
+ if (poly[i1].y > y) /* an upward crossing */
+ if (isLeft(poly[i], poly[i1], x, y) > 0) /* P left of edge */
+ ++wn; /* have a valid up intersect */
+ }
+ else
+ {
+ if (poly[i1].y <= y) /* a downward crossing */
+ if (isLeft(poly[i], poly[i1], x, y) < 0) /* P right of edge */
+ --wn; /* have a valid down intersect */
+ }
+ }
+
+ return wn;
+}
+
+static int compare_int(const int* xx1, const int* xx2)
+{
+ return *xx1 - *xx2;
+}
+
+void simAddSegment(simLineSegment* segment, int x1, int y1, int x2, int y2, int *y_max, int *y_min)
+{
+ /* Make sure p2.y > p1.y */
+ if (y1 > y2)
+ {
+ _cdSwapInt(y1, y2);
+ _cdSwapInt(x1, x2);
+ }
+
+ segment->x1 = x1;
+ segment->y1 = y1;
+ segment->x2 = x2;
+ segment->y2 = y2;
+
+ segment->x = x2; /* initial value */
+
+ segment->DeltaY = y2 - y1;
+ segment->DeltaX = x2 - x1;
+ if (segment->DeltaX >= 0)
+ segment->XDir = -1; /* inverted from simLineThin since here is from p2 to p1 */
+ else
+ {
+ segment->XDir = 1;
+ segment->DeltaX = -segment->DeltaX; /* make DeltaX positive */
+ }
+
+ segment->ErrorAcc = 0; /* initialize the line error accumulator to 0 */
+
+ /* Is this an X-major or Y-major line? */
+ if (segment->DeltaY > segment->DeltaX)
+ {
+ if (segment->DeltaY==0) /* do not compute for horizontal segments */
+ return;
+
+ /* Y-major line; calculate 16-bit fixed-point fractional part of a
+ pixel that X advances each time Y advances 1 pixel, truncating the
+ result so that we won't overrun the endpoint along the X axis */
+ segment->ErrorInc = (unsigned short)(((unsigned long)segment->DeltaX << 16) / (unsigned long)segment->DeltaY);
+ }
+ else
+ {
+ if (segment->DeltaX==0) /* do not compute for vertical segments */
+ return;
+
+ /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
+ pixel that Y advances each time X advances 1 pixel, truncating the
+ result to avoid overrunning the endpoint along the X axis */
+ segment->ErrorInc = (unsigned short)(((unsigned long)segment->DeltaY << 16) / (unsigned long)segment->DeltaX);
+ }
+
+ /* also calculates y_max and y_min of the polygon */
+ if (y2 > *y_max)
+ *y_max = y2;
+ if (y1 < *y_min)
+ *y_min = y1;
+}
+
+int simSegmentInc(simLineSegment* segment, cdCanvas* canvas, int y)
+{
+ unsigned short ErrorAccTemp, Weighting;
+
+ if (segment->DeltaY == 0)
+ {
+ /* Horizontal line */
+ while (segment->DeltaX-- != 0)
+ segment->x += segment->XDir;
+ return segment->x;
+ }
+
+ if (segment->DeltaX == 0)
+ {
+ /* Vertical line */
+ segment->DeltaY--;
+ return segment->x;
+ }
+
+ if (segment->DeltaX == segment->DeltaY)
+ {
+ /* Perfect Diagonal line */
+ segment->x += segment->XDir;
+ segment->DeltaY--;
+ return segment->x;
+ }
+
+ /* Is this an X-major or Y-major line? */
+ if (segment->DeltaY > segment->DeltaX)
+ {
+ /* Increment pixels other than the first and last */
+ ErrorAccTemp = segment->ErrorAcc; /* remember currrent accumulated error */
+ segment->ErrorAcc += segment->ErrorInc; /* calculate error for next pixel */
+ if (segment->ErrorAcc <= ErrorAccTemp)
+ {
+ /* The error accumulator turned over, so advance the X coord */
+ segment->x += segment->XDir;
+ }
+
+ Weighting = segment->ErrorAcc >> INTENSITYSHIFT;
+
+ if (Weighting < 128)
+ return segment->x;
+ else
+ return segment->x + segment->XDir;
+ }
+ else
+ {
+ /* Increment all pixels other than the first and last */
+ int hline_end = 0;
+ while (!hline_end)
+ {
+ ErrorAccTemp = segment->ErrorAcc; /* remember currrent accumulated error */
+ segment->ErrorAcc += segment->ErrorInc; /* calculate error for next pixel */
+ if (segment->ErrorAcc <= ErrorAccTemp)
+ {
+ /* The error accumulator turned over, so advance the Y coord */
+ hline_end = 1;
+ }
+
+ segment->x += segment->XDir; /* X-major, so always advance X */
+ }
+
+ return segment->x;
+ }
+}
+
+typedef struct _simIntervalList
+{
+ int* xx;
+ int n, count;
+} simIntervalList;
+
+static int simFillCheckAAPixel(simIntervalList* line_int_list, int x)
+{
+ int i, *xx = line_int_list->xx;
+ for (i = 0; i < line_int_list->n; i+=2)
+ {
+ if (xx[i] <= x && x <= xx[i+1])
+ return 0; /* inside, already drawn, do not draw */
+ }
+ return 1;
+}
+
+static void simPolyAAPixels(cdCanvas *canvas, simIntervalList* line_int_list, int y_min, int y_max, int x1, int y1, int x2, int y2)
+{
+ unsigned short ErrorInc, ErrorAcc;
+ unsigned short ErrorAccTemp, Weighting;
+ int DeltaX, DeltaY, XDir;
+ int no_antialias = !(canvas->simulation->antialias);
+
+ /* Make sure p2.y > p1.y */
+ if (y1 > y2)
+ {
+ _cdSwapInt(y1, y2);
+ _cdSwapInt(x1, x2);
+ }
+
+ DeltaX = x2 - x1;
+ if (DeltaX >= 0)
+ XDir = 1;
+ else
+ {
+ XDir = -1;
+ DeltaX = -DeltaX; /* make DeltaX positive */
+ }
+
+ /* Special-case horizontal, vertical, and diagonal lines, which
+ require no weighting because they go right through the center of
+ every pixel */
+ DeltaY = y2 - y1;
+ if (DeltaY == 0 || DeltaX == 0 || DeltaX == DeltaY) return;
+
+ /* Line is not horizontal, diagonal, or vertical */
+
+ /* start and end pixels are not necessary
+ since they are always drawn in the previous step. */
+
+ ErrorAcc = 0; /* initialize the line error accumulator to 0 */
+
+ /* Is this an X-major or Y-major line? */
+ if (DeltaY > DeltaX)
+ {
+ ErrorInc = (unsigned short)(((unsigned long)DeltaX << 16) / (unsigned long)DeltaY);
+
+ /* Draw all pixels other than the first and last */
+ while (--DeltaY)
+ {
+ ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
+ ErrorAcc += ErrorInc; /* calculate error for next pixel */
+ if (ErrorAcc <= ErrorAccTemp)
+ x1 += XDir;
+
+ y1++; /* Y-major, so always advance Y */
+
+ Weighting = ErrorAcc >> INTENSITYSHIFT;
+
+ if (y1 < y_min || y1 > y_max) continue;
+
+ if (no_antialias)
+ {
+ if (Weighting < 128)
+ {
+ if (simFillCheckAAPixel(line_int_list+(y1-y_min), x1))
+ simFillDrawAAPixel(canvas, x1, y1, 255);
+ }
+ else
+ {
+ if (simFillCheckAAPixel(line_int_list+(y1-y_min), x1 + XDir))
+ simFillDrawAAPixel(canvas, x1 + XDir, y1, 255);
+ }
+ }
+ else
+ {
+ if (simFillCheckAAPixel(line_int_list+(y1-y_min), x1))
+ simFillDrawAAPixel(canvas, x1, y1, 255-Weighting);
+
+ if (simFillCheckAAPixel(line_int_list+(y1-y_min), x1 + XDir))
+ simFillDrawAAPixel(canvas, x1 + XDir, y1, Weighting);
+ }
+ }
+ }
+ else
+ {
+ ErrorInc = (unsigned short)(((unsigned long)DeltaY << 16) / (unsigned long)DeltaX);
+
+ /* Draw all pixels other than the first and last */
+ while (--DeltaX)
+ {
+ ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
+ ErrorAcc += ErrorInc; /* calculate error for next pixel */
+ if (ErrorAcc <= ErrorAccTemp)
+ y1++;
+
+ x1 += XDir; /* X-major, so always advance X */
+
+ Weighting = ErrorAcc >> INTENSITYSHIFT;
+
+ if (y1 < y_min || y1 > y_max) continue;
+
+ if (no_antialias)
+ {
+ if (Weighting < 128)
+ {
+ if (simFillCheckAAPixel(line_int_list+(y1-y_min), x1))
+ simFillDrawAAPixel(canvas, x1, y1, 255);
+ }
+ else
+ {
+ if (y1+1 < y_min || y1+1 > y_max) continue;
+
+ if (simFillCheckAAPixel(line_int_list+(y1+1-y_min), x1))
+ simFillDrawAAPixel(canvas, x1, y1+1, 255);
+ }
+ }
+ else
+ {
+ if (simFillCheckAAPixel(line_int_list+(y1-y_min), x1))
+ simFillDrawAAPixel(canvas, x1, y1, 255-Weighting);
+
+ if (y1+1 < y_min || y1+1 > y_max) continue;
+
+ if (simFillCheckAAPixel(line_int_list+(y1+1-y_min), x1))
+ simFillDrawAAPixel(canvas, x1, y1+1, Weighting);
+ }
+ }
+ }
+}
+
+static void simLineIntervallInit(simIntervalList* line_int_list, int count)
+{
+ line_int_list->xx = malloc(sizeof(int)*count);
+ line_int_list->n = 0;
+ line_int_list->count = count;
+}
+
+static void simLineIntervallAdd(simIntervalList* line_int_list, int x1, int x2)
+{
+ int i = line_int_list->n;
+ line_int_list->xx[i] = x1;
+ line_int_list->xx[i+1] = x2;
+ line_int_list->n += 2;
+}
+
+void simPolyFill(cdSimulation* simulation, cdPoint* poly, int n)
+{
+ simLineSegment *seg_i;
+ simIntervalList* line_int_list, *line_il;
+ int y_max, y_min, i, y, i1, fill_mode, num_lines,
+ inter_count, width, height, *xx;
+
+ simLineSegment *segment = (simLineSegment *)malloc(n*sizeof(simLineSegment));
+
+ width = simulation->canvas->w;
+ height = simulation->canvas->h;
+ fill_mode = simulation->canvas->fill_mode;
+
+ y_max = poly[0].y;
+ y_min = poly[0].y;
+ for(i = 0; i < n; i++)
+ {
+ i1 = (i+1)%n; /* next point(i+1), next of last(n-1) is first(0) */
+ simAddSegment(segment+i, poly[i].x, poly[i].y, poly[i1].x, poly[i1].y, &y_max, &y_min);
+ }
+
+ if (y_min > height-1 || y_max < 0)
+ {
+ free(segment);
+ return;
+ }
+
+ if (y_min < 0)
+ y_min = 0;
+
+ if (y_max > height-1)
+ num_lines = height-y_min;
+ else
+ num_lines = y_max-y_min+1;
+
+ line_int_list = malloc(sizeof(simIntervalList)*num_lines);
+ memset(line_int_list, 0, sizeof(simIntervalList)*num_lines);
+
+ xx = (int*)malloc((n+1)*sizeof(int));
+
+ /* for all horizontal lines between y_max and y_min */
+ for(y = y_max; y >= y_min; y--)
+ {
+ inter_count = 0;
+
+ /* for all segments, calculates the intervals to be filled
+ from the intersection with the horizontal line y. */
+ for(i = 0; i < n; i++)
+ {
+ seg_i = segment + i;
+
+ /* if the minimum Y coordinate of the segment is greater than the current y, then ignore the segment. */
+ /* if it is an horizontal line, then ignore the segment. */
+ if (seg_i->y1 > y ||
+ seg_i->y1 == seg_i->y2)
+ continue;
+
+ if (y == seg_i->y1) /* intersection at the start point (x1,y1) */
+ {
+ int i_next = (i==n-1)? 0: i+1;
+ int i_prev = (i==0)? n-1: i-1;
+ simLineSegment *seg_i_next = segment + i_next;
+ simLineSegment *seg_i_prev = segment + i_prev;
+
+ /* always save at least one intersection point for (y1) */
+
+ xx[inter_count++] = seg_i->x1; /* save the intersection point */
+
+ /* check for missing bottom-corner points (|_|), must duplicate the intersection */
+ if ((seg_i_next->y1 == y && seg_i_next->y2 == seg_i_next->y1) || /* next is an horizontal line */
+ (seg_i_prev->y1 == y && seg_i_prev->y2 == seg_i_prev->y1)) /* previous is an horizontal line */
+ {
+ xx[inter_count++] = seg_i->x1; /* save the intersection point */
+ }
+ }
+ else if ((y > seg_i->y1) && (y < seg_i->y2)) /* intersection inside the segment, do not include y2 */
+ {
+ xx[inter_count++] = simSegmentInc(seg_i, simulation->canvas, y); /* save the intersection point */
+ }
+ else if (y == seg_i->y2) /* intersection at the end point (x2,y2) */
+ {
+ int i_next = (i==n-1)? 0: i+1;
+ int i_prev = (i==0)? n-1: i-1;
+ simLineSegment *seg_i_next = segment + i_next;
+ simLineSegment *seg_i_prev = segment + i_prev;
+
+ /* only save the intersection point for (y2) if not handled by (y1) of another segment */
+
+ /* check for missing top-corner points (^) or (|ŻŻ|) */
+ if ((seg_i_next->y2 == y && seg_i_next->y2 == seg_i_next->y1) || /* next is an horizontal line */
+ (seg_i_prev->y2 == y && seg_i_prev->y2 == seg_i_prev->y1) || /* previous is an horizontal line */
+ (seg_i_next->y2 == y && seg_i_next->x2 == seg_i->x2 && seg_i_next->x1 != seg_i->x1) ||
+ (seg_i_prev->y2 == y && seg_i_prev->x2 == seg_i->x2 && seg_i_prev->x1 != seg_i->x1))
+ {
+ xx[inter_count++] = seg_i->x2; /* save the intersection point */
+ }
+ }
+ }
+
+ /* if outside the canvas, ignore the intervals and */
+ /* continue since the segments where updated. */
+ if (y > height-1 || inter_count == 0)
+ continue;
+
+ /* sort the intervals */
+ qsort(xx, inter_count, sizeof(int), (int (*)(const void*,const void*))compare_int);
+
+ line_il = line_int_list+(y-y_min);
+ simLineIntervallInit(line_il, inter_count*2);
+
+ /* for all intervals, fill the interval */
+ for(i = 0; i < inter_count; i += 2) /* process only pairs */
+ {
+ if (fill_mode == CD_EVENODD)
+ {
+ /* since it fills only pairs of intervals, */
+ /* it is the EVENODD fill rule. */
+ simFillHorizLine(simulation, xx[i], y, xx[i+1]);
+ simLineIntervallAdd(line_il, xx[i], xx[i+1]);
+ }
+ else
+ {
+ simFillHorizLine(simulation, xx[i], y, xx[i+1]);
+ simLineIntervallAdd(line_il, xx[i], xx[i+1]);
+ if ((i+2 < inter_count) && (xx[i+1] < xx[i+2])) /* avoid point intervals */
+ if (simIsPointInPolyWind(poly, n, (xx[i+1]+xx[i+2])/2, y)) /* if the next interval is inside the polygon then fill it */
+ {
+ simFillHorizLine(simulation, xx[i+1], y, xx[i+2]);
+ simLineIntervallAdd(line_il, xx[i+1], xx[i+2]);
+ }
+ }
+ }
+ }
+
+ free(xx);
+ free(segment);
+
+ /* Once the polygon has been filled, now let's draw the
+ * antialiased and incomplete pixels at the edges */
+
+ if (y_max > height-1)
+ y_max = height-1;
+
+ /* Go through all line segments of the poly */
+ for(i = 0; i < n; i++)
+ {
+ i1 = (i+1)%n;
+ simPolyAAPixels(simulation->canvas, line_int_list, y_min, y_max, poly[i].x, poly[i].y, poly[i1].x, poly[i1].y);
+ }
+
+ for (i = 0; i < num_lines; i++)
+ {
+ if (line_int_list[i].xx)
+ free(line_int_list[i].xx);
+ }
+ free(line_int_list);
+}
+
+/*************************************************************************************/
+/*************************************************************************************/
+
+#define _cdLineDrawPixel(_canvas, _x1, _y1, _ls, _fgcolor, _bgcolor) \
+{ \
+ if (_ls & 1) \
+ _canvas->cxPixel(_canvas->ctxcanvas, _x1, _y1, _fgcolor); \
+ else if (canvas->back_opacity == CD_OPAQUE) \
+ _canvas->cxPixel(_canvas->ctxcanvas, _x1, _y1, _bgcolor); \
+}
+
+void simLineThick(cdCanvas* canvas, int x1, int y1, int x2, int y2)
+{
+ const int interior = canvas->interior_style;
+ const int width = canvas->line_width;
+ const int style = canvas->line_style;
+
+ const int dx = x2-x1;
+ const int dy = y2-y1;
+
+ const double len = hypot(dx,dy);
+
+ const double dnx = dx/len;
+ const double dny = dy/len;
+
+ const int w1 = (int)width/2;
+ const int w2 = width-w1;
+
+ const int n1x = cdRound( w1*dny);
+ const int n1y = cdRound(-w1*dnx);
+
+ const int n2x = cdRound(-w2*dny);
+ const int n2y = cdRound( w2*dnx);
+
+ const int p1x = x1 + n1x;
+ const int p1y = y1 + n1y;
+ const int p2x = x1 + n2x;
+ const int p2y = y1 + n2y;
+ const int p3x = p2x + dx;
+ const int p3y = p2y + dy;
+ const int p4x = p1x + dx;
+ const int p4y = p1y + dy;
+
+ cdPoint poly[4];
+
+ cdCanvasLineWidth(canvas, 1);
+ cdCanvasInteriorStyle(canvas, CD_SOLID);
+ cdCanvasLineStyle(canvas, CD_CONTINUOUS);
+
+ poly[0].x = p1x;
+ poly[0].y = p1y;
+ poly[1].x = p2x;
+ poly[1].y = p2y;
+ poly[2].x = p3x;
+ poly[2].y = p3y;
+ poly[3].x = p4x;
+ poly[3].y = p4y;
+
+ simPolyFill(canvas->simulation, poly, 4);
+
+ cdCanvasLineWidth(canvas, width);
+ cdCanvasInteriorStyle(canvas, interior);
+ cdCanvasLineStyle(canvas, style);
+}
+
+void simLineThin(cdCanvas* canvas, int x1, int y1, int x2, int y2)
+{
+ unsigned short ErrorInc, ErrorAcc;
+ unsigned short ErrorAccTemp, Weighting;
+ int DeltaX, DeltaY, XDir;
+ long aa_fgcolor;
+ unsigned char alpha = cdAlpha(canvas->foreground), aa_alpha;
+ int no_antialias = !(canvas->simulation->antialias);
+ unsigned short int ls;
+ long fgcolor = canvas->foreground;
+ long bgcolor = canvas->background;
+
+ if (simLineStyleNoReset == 2)
+ ls = simLineStyleLastBits;
+ else
+ {
+ ls = simLineStyleBitTable[canvas->line_style];
+
+ if (simLineStyleNoReset == 1)
+ simLineStyleNoReset = 2;
+ }
+
+ /* Make sure p2.y > p1.y */
+ if (y1 > y2)
+ {
+ _cdSwapInt(y1, y2);
+ _cdSwapInt(x1, x2);
+ }
+
+ /* Draw the initial pixel, which is always exactly intersected by
+ the line and so needs no weighting */
+ _cdLineDrawPixel(canvas, x1, y1, ls, fgcolor, bgcolor);
+ ls = simRotateLineStyle(ls);
+
+ DeltaX = x2 - x1;
+ if (DeltaX >= 0)
+ XDir = 1;
+ else
+ {
+ XDir = -1;
+ DeltaX = -DeltaX; /* make DeltaX positive */
+ }
+
+ /* Special-case horizontal, vertical, and diagonal lines, which
+ require no weighting because they go right through the center of
+ every pixel */
+ DeltaY = y2 - y1;
+ if (DeltaY == 0)
+ {
+ /* Horizontal line */
+ while (DeltaX-- != 0)
+ {
+ x1 += XDir;
+ _cdLineDrawPixel(canvas, x1, y1, ls, fgcolor, bgcolor);
+ ls = simRotateLineStyle(ls);
+ }
+ simLineStyleLastBits = ls;
+ return;
+ }
+
+ if (DeltaX == 0)
+ {
+ /* Vertical line */
+ do
+ {
+ y1++;
+ _cdLineDrawPixel(canvas, x1, y1, ls, fgcolor, bgcolor);
+ ls = simRotateLineStyle(ls);
+ } while (--DeltaY != 0);
+ simLineStyleLastBits = ls;
+ return;
+ }
+
+ if (DeltaX == DeltaY)
+ {
+ /* Perfect Diagonal line */
+ do
+ {
+ x1 += XDir;
+ y1++;
+ _cdLineDrawPixel(canvas, x1, y1, ls, fgcolor, bgcolor);
+ ls = simRotateLineStyle(ls);
+ } while (--DeltaY != 0);
+ simLineStyleLastBits = ls;
+ return;
+ }
+
+ /* Line is not horizontal, diagonal, or vertical */
+
+ ErrorAcc = 0; /* initialize the line error accumulator to 0 */
+
+ /* Is this an X-major or Y-major line? */
+ if (DeltaY > DeltaX)
+ {
+ /* Y-major line; calculate 16-bit fixed-point fractional part of a
+ pixel that X advances each time Y advances 1 pixel, truncating the
+ result so that we won't overrun the endpoint along the X axis */
+ ErrorInc = (unsigned short)(((unsigned long)DeltaX << 16) / (unsigned long)DeltaY);
+
+ /* Draw all pixels other than the first and last */
+ while (--DeltaY)
+ {
+ ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
+ ErrorAcc += ErrorInc; /* calculate error for next pixel */
+ if (ErrorAcc <= ErrorAccTemp)
+ {
+ /* The error accumulator turned over, so advance the X coord */
+ x1 += XDir;
+ }
+
+ y1++; /* Y-major, so always advance Y */
+
+ Weighting = ErrorAcc >> INTENSITYSHIFT;
+
+ if (no_antialias)
+ {
+ if (Weighting < 128)
+ _cdLineDrawPixel(canvas, x1, y1, ls, fgcolor, bgcolor)
+ else
+ _cdLineDrawPixel(canvas, x1 + XDir, y1, ls, fgcolor, bgcolor)
+ ls = simRotateLineStyle(ls);
+ }
+ else
+ {
+ /* The IntensityBits most significant bits of ErrorAcc give us the
+ intensity weighting for this pixel, and the complement of the
+ weighting for the paired pixel.
+ Combine the Weighting with the existing alpha,
+ When Weighting is zero alpha must be fully preserved. */
+ aa_alpha = ((255-Weighting) * alpha) / 255;
+
+ aa_fgcolor = cdEncodeAlpha(fgcolor, aa_alpha);
+ _cdLineDrawPixel(canvas, x1, y1, ls, aa_fgcolor, bgcolor);
+ aa_fgcolor = cdEncodeAlpha(fgcolor, 255-aa_alpha);
+ _cdLineDrawPixel(canvas, x1 + XDir, y1, ls, aa_fgcolor, bgcolor);
+ ls = simRotateLineStyle(ls);
+ }
+ }
+ /* Draw the final pixel, which is always exactly intersected by the line
+ and so needs no weighting */
+ _cdLineDrawPixel(canvas, x2, y2, ls, fgcolor, bgcolor);
+ ls = simRotateLineStyle(ls);
+ }
+ else
+ {
+ /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
+ pixel that Y advances each time X advances 1 pixel, truncating the
+ result to avoid overrunning the endpoint along the X axis */
+ ErrorInc = (unsigned short)(((unsigned long)DeltaY << 16) / (unsigned long)DeltaX);
+
+ /* Draw all pixels other than the first and last */
+ while (--DeltaX)
+ {
+ ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
+ ErrorAcc += ErrorInc; /* calculate error for next pixel */
+ if (ErrorAcc <= ErrorAccTemp)
+ {
+ /* The error accumulator turned over, so advance the Y coord */
+ y1++;
+ }
+
+ x1 += XDir; /* X-major, so always advance X */
+
+ Weighting = ErrorAcc >> INTENSITYSHIFT;
+
+ if (no_antialias)
+ {
+ if (Weighting < 128)
+ _cdLineDrawPixel(canvas, x1, y1, ls, fgcolor, bgcolor)
+ else
+ _cdLineDrawPixel(canvas, x1, y1+1, ls, fgcolor, bgcolor)
+ ls = simRotateLineStyle(ls);
+ }
+ else
+ {
+ /* The IntensityBits most significant bits of ErrorAcc give us the
+ intensity weighting for this pixel, and the complement of the
+ weighting for the paired pixel.
+ Combine the Weighting with the existing alpha,
+ When Weighting is zero alpha must be fully preserved. */
+ aa_alpha = ((255-Weighting) * alpha) / 255;
+
+ aa_fgcolor = cdEncodeAlpha(fgcolor, aa_alpha);
+ _cdLineDrawPixel(canvas, x1, y1, ls, aa_fgcolor, bgcolor);
+ aa_fgcolor = cdEncodeAlpha(fgcolor, 255-aa_alpha);
+ _cdLineDrawPixel(canvas, x1, y1+1, ls, aa_fgcolor, bgcolor);
+ ls = simRotateLineStyle(ls);
+ }
+ }
+
+ /* Draw the final pixel, which is always exactly intersected by the line
+ and so needs no weighting */
+ _cdLineDrawPixel(canvas, x2, y2, ls, fgcolor, bgcolor);
+ ls = simRotateLineStyle(ls);
+ }
+
+ simLineStyleLastBits = ls;
+}
+
+void simfLineThin(cdCanvas* canvas, double x1, double y1, double x2, double y2, int *last_xi_a, int *last_yi_a, int *last_xi_b, int *last_yi_b)
+{
+ double DeltaX, DeltaY, a, b;
+ long aa_fgcolor;
+ unsigned char alpha = cdAlpha(canvas->foreground), aa_alpha;
+ int no_antialias = !(canvas->simulation->antialias);
+ int yi, xi, update_a = 1, update_b = 1;
+ unsigned short int ls;
+ long fgcolor = canvas->foreground;
+ long bgcolor = canvas->background;
+
+ if (simLineStyleNoReset == 2)
+ ls = simLineStyleLastBits;
+ else
+ {
+ ls = simLineStyleBitTable[canvas->line_style];
+
+ if (simLineStyleNoReset == 1)
+ simLineStyleNoReset = 2;
+ }
+
+ DeltaX = fabs(x2 - x1);
+ DeltaY = fabs(y2 - y1);
+
+ if (DeltaX > 0.0001)
+ {
+ a = (y1-y2)/(x1-x2);
+ b = y1 - a*x1;
+ }
+ else
+ {
+ a = 0;
+ b = x1;
+ }
+
+ /* NOTICE: all the complexity of this function
+ is related to check and update the previous point */
+
+ /* Is this an X-major or Y-major line? */
+ if (DeltaY > DeltaX)
+ {
+ /* Increment in Y */
+ int y1i = _cdRound(y1),
+ y2i = _cdRound(y2);
+ int yi_first = y1i;
+ int yi_last = y2i, xi_last;
+
+ if (y1i > y2i)
+ _cdSwapInt(y1i, y2i);
+
+ for (yi = y1i; yi <= y2i; yi++)
+ {
+ double x;
+ if (a)
+ x = (yi - b)/a;
+ else
+ x = b;
+
+ xi = (int)floor(x);
+
+ /* if at the last pixel, store the return value */
+ if (yi == yi_last)
+ xi_last = xi;
+
+ /* Combine the Weighting with the existing alpha,
+ When Weighting is zero alpha must be fully preserved. */
+ aa_alpha = (int)((1.0-(x - xi)) * alpha);
+
+ if (no_antialias)
+ {
+ if (aa_alpha > 128)
+ _cdLineDrawPixel(canvas, xi, yi, ls, fgcolor, bgcolor)
+ else
+ _cdLineDrawPixel(canvas, xi+1, yi, ls, fgcolor, bgcolor)
+ }
+ else
+ {
+ if (yi == yi_first)
+ {
+ if (yi == yi_last) /* one pixel only */
+ {
+ update_a = 0;
+ update_b = 0;
+ }
+
+ /* if at first, compare with the last two previously drawn */
+ /* if the new is equal to the previous, do NOT draw */
+ if ((xi != *last_xi_a || yi != *last_yi_a) &&
+ (xi != *last_xi_b || yi != *last_yi_b))
+ {
+ aa_fgcolor = cdEncodeAlpha(fgcolor, aa_alpha);
+ _cdLineDrawPixel(canvas, xi, yi, ls, aa_fgcolor, bgcolor);
+
+ if (yi == yi_last) /* one pixel only */
+ update_a = 1;
+ }
+
+ if ((xi+1 != *last_xi_a || yi != *last_yi_a) &&
+ (xi+1 != *last_xi_b || yi != *last_yi_b))
+ {
+ aa_fgcolor = cdEncodeAlpha(fgcolor, 255-aa_alpha);
+ _cdLineDrawPixel(canvas, xi+1, yi, ls, aa_fgcolor, bgcolor);
+
+ if (yi == yi_last) /* one pixel only */
+ update_b = 1;
+ }
+ }
+ else
+ {
+ aa_fgcolor = cdEncodeAlpha(fgcolor, aa_alpha);
+ _cdLineDrawPixel(canvas, xi, yi, ls, aa_fgcolor, bgcolor);
+ aa_fgcolor = cdEncodeAlpha(fgcolor, 255-aa_alpha);
+ _cdLineDrawPixel(canvas, xi+1, yi, ls, aa_fgcolor, bgcolor);
+ }
+ }
+
+ ls = simRotateLineStyle(ls);
+ }
+
+ if (update_a)
+ {
+ *last_xi_a = xi_last;
+ *last_yi_a = yi_last;
+ }
+ if (update_b)
+ {
+ *last_xi_b = xi_last+1;
+ *last_yi_b = yi_last;
+ }
+ }
+ else
+ {
+ /* Increment in X */
+ int x1i = _cdRound(x1),
+ x2i = _cdRound(x2);
+ int xi_first = x1i;
+ int xi_last = x2i, yi_last;
+
+ if (x1i > x2i)
+ _cdSwapInt(x1i, x2i);
+
+ for (xi = x1i; xi <= x2i; xi++)
+ {
+ double y = a*xi + b;
+ yi = (int)floor(y);
+
+ /* if at the last pixel, store the return value */
+ if (xi == xi_last)
+ yi_last = yi;
+
+ /* Combine the Weighting with the existing alpha,
+ When Weighting is zero alpha must be fully preserved. */
+ aa_alpha = (int)((1.0-(y - yi)) * alpha);
+
+ if (no_antialias)
+ {
+ if (aa_alpha > 128)
+ _cdLineDrawPixel(canvas, xi, yi, ls, fgcolor, bgcolor)
+ else
+ _cdLineDrawPixel(canvas, xi, yi+1, ls, fgcolor, bgcolor)
+ }
+ else
+ {
+ if (xi == xi_first)
+ {
+ if (xi == xi_last) /* one pixel only */
+ {
+ update_a = 0;
+ update_b = 0;
+ }
+
+ /* if at first, compare with the last to draw */
+ /* if new is equal to the previous, do NOT draw */
+ if ((xi != *last_xi_a || yi != *last_yi_a) &&
+ (xi != *last_xi_b || yi != *last_yi_b))
+ {
+ aa_fgcolor = cdEncodeAlpha(fgcolor, aa_alpha);
+ _cdLineDrawPixel(canvas, xi, yi, ls, aa_fgcolor, bgcolor);
+
+ if (xi == xi_last) /* one pixel only */
+ update_a = 1;
+ }
+
+ if ((xi != *last_xi_a || yi+1 != *last_yi_a) &&
+ (xi != *last_xi_b || yi+1 != *last_yi_b))
+ {
+ aa_fgcolor = cdEncodeAlpha(fgcolor, 255-aa_alpha);
+ _cdLineDrawPixel(canvas, xi, yi+1, ls, aa_fgcolor, bgcolor);
+
+ if (xi == xi_last) /* one pixel only */
+ update_b = 1;
+ }
+ }
+ else
+ {
+ aa_fgcolor = cdEncodeAlpha(fgcolor, aa_alpha);
+ _cdLineDrawPixel(canvas, xi, yi, ls, aa_fgcolor, bgcolor);
+ aa_fgcolor = cdEncodeAlpha(fgcolor, 255-aa_alpha);
+ _cdLineDrawPixel(canvas, xi, yi+1, ls, aa_fgcolor, bgcolor);
+ }
+ }
+
+ ls = simRotateLineStyle(ls);
+ }
+
+ if (update_a)
+ {
+ *last_xi_a = xi_last;
+ *last_yi_a = yi_last;
+ }
+ if (update_b)
+ {
+ *last_xi_b = xi_last;
+ *last_yi_b = yi_last+1;
+ }
+ }
+
+ simLineStyleLastBits = ls;
+}