1 /*****************************************************************************
2 * This file is part of gfxprim library. *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
19 * Copyright (C) 2009 - 2012 Jiri Dluhos <jiri.bluebear.dluhos@gmail.com> *
20 * Copyright (C) 2009 - 2012 Cyril Hrubis <metan@ucw.cz> *
22 *****************************************************************************/
24 #define _ISOC99_SOURCE
31 #include "core/GP_Transform.h"
32 #include "core/GP_GetPutPixel.h"
36 #include "GP_Polygon.h"
38 /* A 2D point specified by GP_Coord coordinates. */
43 /* "almost equality" for float coordinates */
44 #define GP_COORDS_ALMOST_EQUAL(a,b) (fabsf((a)-(b)) < 0.0001f)
47 * Edge state. Every edge proceeds from READY to ACTIVE and then FINISHED.
48 * HORIZONTAL is special (horizontal edges are handled separately).
49 * Numeric values reflect sorting priority (ACTIVE is foremost).
51 #define EDGE_HORIZONTAL 3
52 #define EDGE_FINISHED 2
56 /* Working record about an edge. */
58 int state
; /* edge state */
59 float x
; /* X coordinate of the working point */
60 int y
; /* Y coordinate of the working point */
61 int dy
; /* vertical size */
62 float dxy
; /* dx/dy */
65 /* Initializes the edge structure. */
66 static void GP_InitEdge(struct GP_Edge
*e
, GP_Point start
, GP_Point end
)
68 /* horizontal edges are a special case */
69 if (start
.y
== end
.y
) {
73 e
->dxy
= end
.x
- start
.x
;
74 e
->state
= EDGE_HORIZONTAL
;
78 /* initialize the working point to the top point of the edge */
79 if (start
.y
< end
.y
) {
80 e
->x
= (float) start
.x
;
87 e
->dy
= GP_ABS(end
.y
- start
.y
);
89 e
->dxy
= (float)(end
.x
- start
.x
)/(end
.y
- start
.y
);
90 e
->state
= EDGE_READY
;
92 /* Shorten each edge by one pixel at the bottom. This prevents
93 * every vertex point to be reported as two intersections.
94 * This also means causes all horizontal edges cut by one pixel,
95 * but we will fix this at the end by drawing them separately.
100 /* Type of a callback function to be passed to qsort(). */
101 typedef int (*GP_SortCallback
)(const void *, const void *);
104 * Compares two edges. Used for initial sorting of the edges.
105 * Edges are sorted by Y first, then by X, then by DXY.
106 * Returns -1 if e1<e2, +1 if e1>e2, 0 if e1==e2.
108 static int GP_CompareEdgesInitial(struct GP_Edge
*e1
, struct GP_Edge
*e2
)
110 if (e1
->y
< e2
->y
) return -1;
111 if (e1
->y
> e2
->y
) return 1;
113 if (e1
->x
< e2
->x
) return -1;
114 if (e1
->x
> e2
->x
) return 1;
116 if (e1
->dxy
< e2
->dxy
) return -1;
117 if (e1
->dxy
> e2
->dxy
) return 1;
123 * Compares two edges. Used for in-run sorting.
124 * Edges are sorted by state (ACTIVE < READY < FINISHED), then by X,
126 * Returns -1 if e1<e2, +1 if e1>e2, 0 if e1==e2.
128 static int GP_CompareEdgesRuntime(struct GP_Edge
*e1
, struct GP_Edge
*e2
)
130 if (e1
->state
< e2
->state
) return -1;
131 if (e1
->state
> e2
->state
) return 1;
133 if (e1
->x
< e2
->x
) return -1;
134 if (e1
->x
> e2
->x
) return 1;
136 if (e1
->dxy
< e2
->dxy
) return -1;
137 if (e1
->dxy
> e2
->dxy
) return 1;
142 void GP_FillPolygon_Raw(GP_Pixmap
*pixmap
, unsigned int nvert
,
143 const GP_Coord
*xy
, GP_Pixel pixel
)
149 return; /* not enough vertices */
151 GP_Point
const *vert
= (GP_Point
const *) xy
;
153 /* find first and last scanline */
154 GP_Coord ymin
= INT_MAX
, ymax
= -INT_MAX
;
155 for (i
= 0; i
< nvert
; i
++) {
156 ymax
= GP_MAX(ymax
, vert
[i
].y
);
157 ymin
= GP_MIN(ymin
, vert
[i
].y
);
160 /* build a list of edges */
161 struct GP_Edge edges
[nvert
];
162 unsigned int nedges
= 0; /* number of edges in list */
163 for (i
= 0; i
< nvert
; i
++) {
166 * next vertex index (wraps to 0 at end to connect
167 * the last vertex with the first one)
169 unsigned int nexti
= (i
+1) % nvert
;
171 /* add new edge record */
172 e
= edges
+ nedges
++;
173 GP_InitEdge(e
, vert
[i
], vert
[nexti
]);
177 return; /* not really a polygon */
179 /* initially sort edges by Y, then X */
180 qsort(edges
, nedges
, sizeof(struct GP_Edge
),
181 (GP_SortCallback
) GP_CompareEdgesInitial
);
184 * for each scanline, compute intersections with all edges
185 * and draw a horizontal line segment between the intersections.
190 for (y
= ymin
; y
<= ymax
; y
++) {
192 /* mark edges we have just reached as active */
193 for (i
= 0; i
< nedges
; i
++) {
195 if (e
->state
== EDGE_READY
&& (y
== e
->y
)) {
196 e
->state
= EDGE_ACTIVE
;
199 qsort(edges
, nedges
, sizeof(struct GP_Edge
),
200 (GP_SortCallback
) GP_CompareEdgesRuntime
);
202 /* record intersections with active edges */
204 for (i
= 0; i
< nedges
; i
++) {
206 if (e
->state
== EDGE_ACTIVE
) {
207 inter
[ninter
++] = e
->x
;
211 /* draw each even range between intersections */
212 for (i
= 0; i
< ninter
; i
+= 2) {
213 float start
= inter
[i
];
215 /* odd number of intersections - skip last */
219 float end
= inter
[i
+1];
220 GP_HLine_Raw(pixmap
, start
, end
, y
, pixel
);
223 /* update active edges for next step */
224 for (i
= 0; i
< nedges
; i
++) {
226 if (e
->state
== EDGE_ACTIVE
) {
228 e
->state
= EDGE_FINISHED
;
237 /* finishing touch: draw all horizontal edges that were skipped
240 for (i
= 0; i
< nedges
; i
++) {
242 if (e
->state
== EDGE_HORIZONTAL
) {
243 GP_HLine_Raw(pixmap
, e
->x
, e
->x
+ e
->dxy
, e
->y
,
249 void GP_FillPolygon(GP_Pixmap
*pixmap
, unsigned int vertex_count
,
250 const GP_Coord
*xy
, GP_Pixel pixel
)
253 GP_Coord xy_copy
[2 * vertex_count
];
255 for (i
= 0; i
< vertex_count
; i
++) {
256 unsigned int x
= 2 * i
;
257 unsigned int y
= 2 * i
+ 1;
261 GP_TRANSFORM_POINT(pixmap
, xy_copy
[x
], xy_copy
[y
]);
264 GP_FillPolygon_Raw(pixmap
, vertex_count
, xy_copy
, pixel
);
267 void GP_Polygon_Raw(GP_Pixmap
*pixmap
, unsigned int vertex_count
,
268 const GP_Coord
*xy
, GP_Pixel pixel
)
272 GP_Coord prev_x
= xy
[2 * vertex_count
- 2];
273 GP_Coord prev_y
= xy
[2 * vertex_count
- 1];
275 for (i
= 0; i
< vertex_count
; i
++) {
276 GP_Coord x
= xy
[2 * i
];
277 GP_Coord y
= xy
[2 * i
+ 1];
279 GP_Line_Raw(pixmap
, prev_x
, prev_y
, x
, y
, pixel
);
287 void GP_Polygon(GP_Pixmap
*pixmap
, unsigned int vertex_count
,
288 const GP_Coord
*xy
, GP_Pixel pixel
)
292 GP_Coord prev_x
= xy
[2 * vertex_count
- 2];
293 GP_Coord prev_y
= xy
[2 * vertex_count
- 1];
295 GP_TRANSFORM_POINT(pixmap
, prev_x
, prev_y
);
297 for (i
= 0; i
< vertex_count
; i
++) {
298 GP_Coord x
= xy
[2 * i
];
299 GP_Coord y
= xy
[2 * i
+ 1];
301 GP_TRANSFORM_POINT(pixmap
, x
, y
);
303 GP_Line_Raw(pixmap
, prev_x
, prev_y
, x
, y
, pixel
);