1 /* $XFree86: xc/programs/Xserver/mi/miarc.c,v 3.7 1999/12/27 00:39:56 robin Exp $ */
2 /***********************************************************
4 Copyright 1987, 1998 The Open Group
8 The above copyright notice and this permission notice shall be included in
9 all copies or substantial portions of the Software.
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
15 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 Except as contained in this notice, the name of The Open Group shall not be
19 used in advertising or otherwise to promote the sale, use or other dealings
20 in this Software without prior written authorization from The Open Group.
23 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
27 Permission to use, copy, modify, and distribute this software and its
28 documentation for any purpose and without fee is hereby granted,
29 provided that the above copyright notice appear in all copies and that
30 both that copyright notice and this permission notice appear in
31 supporting documentation, and that the name of Digital not be
32 used in advertising or publicity pertaining to distribution of the
33 software without specific, written prior permission.
35 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43 ******************************************************************/
44 /* $TOG: miarc.c /main/91 1998/02/09 14:45:57 kaleb $ */
45 /* Author: Keith Packard and Bob Scheifler */
46 /* Warning: this code is toxic, do not dally very long here. */
48 #define _XOPEN_SOURCE_EXTENDED /* to get prototype for cbrt on some systems */
49 #define _XOPEN_SOURCE /* to get prototype for hypot on some systems */
52 #include <string.h> /* memmove */
58 #include "gdkprivate-fb.h"
60 static double miDsin(double a), miDcos(double a), miDasin(double a), miDatan2(double x, double y);
67 * some interesting sematic interpretation of the protocol:
69 * Self intersecting arcs (i.e. those spanning 360 degrees)
70 * never join with other arcs, and are drawn without caps
71 * (unless on/off dashed, in which case each dash segment
72 * is capped, except when the last segment meets the
73 * first segment, when no caps are drawn)
75 * double dash arcs are drawn in two parts, first the
76 * odd dashes (drawn in background) then the even dashes
77 * (drawn in foreground). This means that overlapping
78 * sections of foreground/background are drawn twice,
79 * first in background then in foreground. The double-draw
80 * occurs even when the function uses the destination values
81 * (e.g. xor mode). This is the same way the wide-line
82 * code works and should be "fixed".
89 #if defined (__GNUC__) && defined (__STDC__) && !defined (__STRICT_ANSI__)
101 #define boundedLe(value, bounds)\
102 ((bounds).min <= (value) && (value) <= (bounds).max)
109 #define intersectLine(y,line) (line.m * (y) + line.b)
112 * these are all y value bounds
116 struct bound ellipse;
121 struct ibound inneri;
122 struct ibound outeri;
125 struct accelerators {
136 struct line left, right;
147 # define todeg(xAngle) (((double) (xAngle)) / 64.0)
152 typedef struct _miArcJoin {
153 int arcIndex0, arcIndex1;
156 } miArcJoinRec, *miArcJoinPtr;
158 typedef struct _miArcCap {
161 } miArcCapRec, *miArcCapPtr;
163 typedef struct _miArcFace {
166 SppPointRec counterClock;
167 } miArcFaceRec, *miArcFacePtr;
169 typedef struct _miArcData {
171 int render; /* non-zero means render after drawing */
172 int join; /* related join */
173 int cap; /* related cap */
174 int selfJoin; /* final dash meets first dash */
175 miArcFaceRec bounds[2];
176 double x0, y0, x1, y1;
177 } miArcDataRec, *miArcDataPtr;
180 * This is an entire sequence of arcs, computed and categorized according
181 * to operation. miDashArcs generates either one or two of these.
184 typedef struct _miPolyArc {
191 } miPolyArcRec, *miPolyArcPtr;
194 short lx, lw, rx, rw;
199 int count1, count2, k;
204 unsigned long lrustamp;
206 unsigned short width, height;
207 miArcSpanData *spdata;
210 # define DASH_MAP_SIZE 91
213 double map[DASH_MAP_SIZE];
216 static void fillSpans(GdkDrawable *pDrawable, GdkGC *pGC);
217 static void newFinalSpan(int y, int xmin, int xmax);
218 static void drawArc (miArc *tarc, int l, int a0, int a1, miArcFacePtr right, miArcFacePtr left);
219 static void drawQuadrant(struct arc_def *def, struct accelerators *acc,
220 int a0, int a1, int mask, miArcFacePtr right, miArcFacePtr left,
221 miArcSpanData *spdata);
222 static void drawZeroArc(GdkDrawable *pDraw, GdkGC *pGC, miArc *tarc, int lw, miArcFacePtr right, miArcFacePtr left);
223 static void miArcJoin(GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pRight, miArcFacePtr pLeft, int xOrgRight, int yOrgRight,
224 double xFtransRight, double yFtransRight,
225 int xOrgLeft, int yOrgLeft,
226 double xFtransLeft, double yFtransLeft);
227 static void miArcCap(GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pFace, int end, int xOrg, int yOrg,
228 double xFtrans, double yFtrans);
229 static void miRoundCap(GdkDrawable *pDraw, GdkGC *pGC, SppPointRec pCenter, SppPointRec pEnd, SppPointRec pCorner,
230 SppPointRec pOtherCorner, int fLineEnd, int xOrg, int yOrg,
231 double xFtrans, double yFtrans);
232 static void miFreeArcs(miPolyArcPtr arcs, GdkGC *pGC);
233 static int computeAngleFromPath(int startAngle, int endAngle, dashMap *map, int *lenp, int backwards);
234 static miPolyArcPtr miComputeArcs (miArc *parcs, int narcs, GdkGC *gc);
235 static int miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts);
237 # define CUBED_ROOT_2 1.2599210498948732038115849718451499938964
238 # define CUBED_ROOT_4 1.5874010519681993173435330390930175781250
241 * draw one segment of the arc using the arc spans generation routines
245 miArcSegment(GdkDrawable *pDraw, GdkGC *pGC, miArc tarc, miArcFacePtr right, miArcFacePtr left)
247 int l = GDK_GC_FBDATA(pGC)->values.line_width;
248 int a0, a1, startAngle, endAngle;
254 if (tarc.width == 0 || tarc.height == 0) {
255 drawZeroArc (pDraw, pGC, &tarc, l, left, right);
263 else if (a1 < -FULLCIRCLE)
266 startAngle = a0 + a1;
276 * bounds check the two angles
279 startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
280 if (startAngle >= FULLCIRCLE)
281 startAngle = startAngle % FULLCIRCLE;
283 endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE;
284 if (endAngle > FULLCIRCLE)
285 endAngle = (endAngle-1) % FULLCIRCLE + 1;
286 if ((startAngle == endAngle) && a1) {
288 endAngle = FULLCIRCLE;
291 drawArc (&tarc, l, startAngle, endAngle, right, left);
296 Three equations combine to describe the boundaries of the arc
298 x^2/w^2 + y^2/h^2 = 1 ellipse itself
299 (X-x)^2 + (Y-y)^2 = r^2 circle at (x, y) on the ellipse
300 (Y-y) = (X-x)*w^2*y/(h^2*x) normal at (x, y) on the ellipse
302 These lead to a quartic relating Y and y
304 y^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2
305 - (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0
307 The reducible cubic obtained from this quartic is
309 z^3 - (3N)z^2 - 2V = 0
313 N = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6
314 V = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2)
326 The discriminant of this cubic is
330 When D > 0, a real root is obtained as
332 z = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D))
334 When D < 0, a real root is obtained as
336 z = N - 2m*cos(acos(-q/m^3)/3)
340 m = sqrt(|p|) * sign(q)
342 Given a real root Z of the cubic, the roots of the quartic are the roots
343 of the two quadratics
345 y^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0
349 A = +/- sqrt(8Z + b^2 - 4c)
350 b, c, d are the cubic, quadratic, and linear coefficients of the quartic
352 Some experimentation is then required to determine which solutions
353 correspond to the inner and outer boundaries.
359 static arcCacheRec arcCache[CACHESIZE];
360 static unsigned long lrustamp;
361 static arcCacheRec *lastCacheHit = &arcCache[0];
364 static RESTYPE cacheType;
367 * External so it can be called when low on memory.
368 * Call with a zero ID in that case.
372 miFreeArcCache (data, id)
382 for (k = CACHESIZE, cent = &arcCache[0]; --k >= 0; cent++)
388 g_free(cent->spdata);
398 miComputeCircleSpans(int lw, miArc *parc, miArcSpanData *spdata)
400 register miArcSpan *span;
402 register int x, y, e;
403 int xk, yk, xm, ym, dx, dy;
404 register int slw, inslw;
405 int inx = 0, iny, ine = 0;
406 int inxk = 0, inyk = 0, inxm = 0, inym = 0;
409 slw = parc->width - doinner;
410 y = parc->height >> 1;
411 dy = parc->height & 1;
413 MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym);
414 inslw = parc->width + doinner;
417 spdata->hole = spdata->top;
418 MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym);
422 spdata->hole = FALSE;
425 spdata->count1 = -doinner - spdata->top;
426 spdata->count2 = y + doinner;
427 span = spdata->spans;
436 span->rw = span->lx + slw;
440 MIFILLINARCSTEP(inslw);
442 span->rx = dy - inx + inslw;
443 span->rw = inx - x + slw - inslw;
453 if (lw > (int)parc->height)
454 span[-1].rx = span[-1].rw = -((lw - (int)parc->height) >> 1);
463 miComputeEllipseSpans(int lw, miArc *parc, miArcSpanData *spdata)
465 register miArcSpan *span;
466 double w, h, r, xorg;
467 double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
468 double A, T, b, d, x, y, t, inx, outx = 0.0, hepp, hepm;
471 w = (double)parc->width / 2.0;
472 h = (double)parc->height / 2.0;
478 Vk = (Nk * Hs) / (WH + WH);
480 Nk = (Hf - Nk * Nk) / WH;
484 K = h + ((lw - 1) >> 1);
485 span = spdata->spans;
498 spdata->hole = (spdata->top &&
499 (int)parc->height * lw <= (int)(parc->width * parc->width) &&
500 lw < (int)parc->height);
501 for (; K > 0.0; K -= 1.0)
503 N = (K * K + Nk) / 6.0;
511 if ( (b < 0.0) == (t < 0.0) )
516 Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
517 if ( (Z < 0.0) == (Vr < 0.0) )
525 Z = N + cbrt(t + d) + cbrt(t - d);
528 A = sqrt((Z + Z) - Nk);
529 T = (Fk - Z) * K / A;
533 d = b * b - 4 * (Z + T);
538 if ((y >= 0.0) && (y < hepp))
544 x = w * sqrt(1 - (t * t));
546 if (rs - (t * t) >= 0)
547 t = sqrt(rs - (t * t));
557 d = b * b - 4 * (Z - T);
558 /* Because of the large magnitudes involved, we lose enough precision
559 * that sometimes we end up with a negative value near the axis, when
560 * it should be positive. This is a workaround.
562 if (d < 0 && !solution)
572 x = w * sqrt(1 - (t * t));
574 if (rs - (t * t) >= 0)
575 inx = x - sqrt(rs - (t * t));
585 x = w * sqrt(1 - (t * t));
587 if (rs - (t * t) >= 0)
588 t = sqrt(rs - (t * t));
597 span->lx = ICEIL(xorg - outx);
601 span->lw = ICEIL(xorg + outx) - span->lx;
602 span->rx = ICEIL(xorg + inx);
603 span->rw = -ICEIL(xorg - inx);
608 span->lw = ICEIL(xorg - inx) - span->lx;
609 span->rx = ICEIL(xorg + inx);
610 span->rw = ICEIL(xorg + outx) - span->rx;
617 if (r >= h && r <= w)
619 else if (Nk < 0.0 && -Nk < Hs)
621 inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
627 span->lx = ICEIL(xorg - outx);
630 span->lw = ICEIL(xorg + outx) - span->lx;
631 span->rx = ICEIL(xorg + inx);
632 span->rw = -ICEIL(xorg - inx);
636 span->lw = ICEIL(xorg - inx) - span->lx;
637 span->rx = ICEIL(xorg + inx);
638 span->rw = ICEIL(xorg + outx) - span->rx;
643 span = &spdata->spans[spdata->count1];
644 span->lw = -span->lx;
653 tailX(double K, struct arc_def *def, struct arc_bound *bounds, struct accelerators *acc)
656 double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
657 double A, T, b, d, x, y, t, hepp, hepm;
669 Vk = (Nk * Hs) / (WH + WH);
671 Nk = (Hf - Nk * Nk) / WH;
673 if (Nk < 0.0 && -Nk < Hs) {
674 xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
676 if (acc->left.valid && boundedLe(K, bounds->left) &&
677 !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
679 if (acc->right.valid && boundedLe(K, bounds->right) &&
680 !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
689 N = (K * K + Nk) / 6.0;
699 if ( (b < 0.0) == (t < 0.0) )
704 Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
705 if ( (Z < 0.0) == (Vr < 0.0) )
713 Z = N + cbrt(t + d) + cbrt(t - d);
716 A = sqrt((Z + Z) - Nk);
717 T = (Fk - Z) * K / A;
720 d = b * b - 4 * (Z + T);
721 if (d >= 0 && flip == 2)
725 if ((y >= 0.0) && (y < hepp))
731 x = w * sqrt(1 - (t * t));
733 if (rs - (t * t) >= 0)
734 t = sqrt(rs - (t * t));
741 d = b * b - 4 * (Z - T);
742 /* Because of the large magnitudes involved, we lose enough precision
743 * that sometimes we end up with a negative value near the axis, when
744 * it should be positive. This is a workaround.
746 if (d < 0 && !solution)
756 x = w * sqrt(1 - (t * t));
758 if (rs - (t * t) >= 0)
759 *xp++ = x - sqrt(rs - (t * t));
764 if (y >= 0.0 && flip == 1)
769 x = w * sqrt(1 - (t * t));
771 if (rs - (t * t) >= 0)
772 t = sqrt(rs - (t * t));
779 if (acc->left.valid && boundedLe(K, bounds->left) &&
780 !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
782 if (acc->right.valid && boundedLe(K, bounds->right) &&
783 !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
789 static miArcSpanData *
790 miComputeWideEllipse(int lw, miArc *parc, gboolean *mustFree)
792 register miArcSpanData *spdata;
793 register arcCacheRec *cent, *lruent;
799 if (parc->height <= 1500)
803 if (cent->lw == lw &&
804 cent->width == parc->width && cent->height == parc->height)
806 cent->lrustamp = ++lrustamp;
809 lruent = &arcCache[0];
810 for (k = CACHESIZE, cent = lruent; --k >= 0; cent++)
812 if (cent->lw == lw &&
813 cent->width == parc->width && cent->height == parc->height)
815 cent->lrustamp = ++lrustamp;
819 if (cent->lrustamp < lruent->lrustamp)
825 cacheType = CreateNewResourceType(miFreeArcCache);
826 (void) AddResource(FakeClientID(0), cacheType, NULL);
831 lruent->spdata = NULL;
834 k = (parc->height >> 1) + ((lw - 1) >> 1);
835 spdata = lruent->spdata;
836 if (!spdata || spdata->k != k)
839 spdata = (miArcSpanData *)g_malloc(sizeof(miArcSpanData) +
840 sizeof(miArcSpan) * (k + 2));
841 lruent->spdata = spdata;
844 lruent->lrustamp = 0;
848 spdata->spans = (miArcSpan *)(spdata + 1);
851 spdata->top = !(lw & 1) && !(parc->width & 1);
852 spdata->bot = !(parc->height & 1);
853 lruent->lrustamp = ++lrustamp;
855 lruent->width = parc->width;
856 lruent->height = parc->height;
857 if (lruent != &fakeent)
858 lastCacheHit = lruent;
859 if (parc->width == parc->height)
860 miComputeCircleSpans(lw, parc, spdata);
862 miComputeEllipseSpans(lw, parc, spdata);
867 miFillWideEllipse(GdkDrawable *pDraw, GdkGC *pGC, miArc *parc)
870 register GdkSpan* pts;
871 miArcSpanData *spdata;
873 register miArcSpan *span;
874 register int xorg, yorgu, yorgl;
877 yorgu = parc->height + GDK_GC_FBDATA(pGC)->values.line_width;
878 points = ALLOCATE_LOCAL(sizeof(GdkSpan) * yorgu * 2);
879 spdata = miComputeWideEllipse(GDK_GC_FBDATA(pGC)->values.line_width, parc, &mustFree);
882 DEALLOCATE_LOCAL(points);
886 span = spdata->spans;
887 xorg = parc->x + (parc->width >> 1);
888 yorgu = parc->y + (parc->height >> 1);
889 yorgl = yorgu + (parc->height & 1);
900 for (n = spdata->count1; --n >= 0; )
902 pts[0].x = xorg + span->lx;
904 pts[0].width = span->lw;
919 for (n = spdata->count2; --n >= 0; )
921 pts[0].x = xorg + span->lx;
923 pts[0].width = span->lw;
925 pts[1].x = xorg + span->rx;
927 pts[1].width = span->rw;
931 pts[2].width = pts[0].width;
935 pts[3].width = pts[1].width;
946 pts[0].x = xorg + span->lx;
948 pts[0].width = span->lw;
953 pts[0].x = xorg + span->lx;
955 pts[0].width = span->lw;
956 pts[1].x = xorg + span->rx;
958 pts[1].width = span->rw;
965 gdk_fb_fill_spans(pDraw, pGC, points, pts - points, FALSE);
967 DEALLOCATE_LOCAL(points);
971 * miPolyArc strategy:
973 * If arc is zero width and solid, we don't have to worry about the rasterop
974 * or join styles. For wide solid circles, we use a fast integer algorithm.
975 * For wide solid ellipses, we use special case floating point code.
976 * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then
977 * draw using pGCTo and pDrawTo. If the raster-op was "tricky," that is,
978 * if it involves the destination, then we use PushPixels to move the bits
979 * from the scratch drawable to pDraw. (See the wide line code for a
980 * fuller explanation of this.)
984 miPolyArc(GdkDrawable *pDraw, GdkGC *pGC, int narcs, miArc *parcs)
988 int xMin, xMax, yMin, yMax;
989 int pixmapWidth = 0, pixmapHeight = 0;
990 int xOrg = 0, yOrg = 0;
993 GdkDrawable* pDrawTo;
996 miPolyArcPtr polyArcs;
1002 width = GDK_GC_FBDATA(pGC)->values.line_width;
1003 if(width == 0 && GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID)
1005 for(i = narcs, parc = parcs; --i >= 0; parc++)
1006 miArcSegment( pDraw, pGC, *parc,
1007 (miArcFacePtr) 0, (miArcFacePtr) 0 );
1008 fillSpans (pDraw, pGC);
1012 if ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID) && narcs)
1014 while (parcs->width && parcs->height &&
1015 (parcs->angle2 >= FULLCIRCLE ||
1016 parcs->angle2 <= -FULLCIRCLE))
1018 miFillWideEllipse(pDraw, pGC, parcs);
1025 /* Set up pDrawTo and pGCTo based on the rasterop */
1026 switch(GDK_GC_FBDATA(pGC)->alu)
1028 case GDK_CLEAR: /* 0 */
1029 case GDK_COPY: /* src */
1030 case GDK_COPY_INVERT: /* NOT src */
1031 case GDK_SET: /* 1 */
1039 /* find bounding box around arcs */
1040 xMin = yMin = SHRT_MAX;
1041 xMax = yMax = SHRT_MIN;
1043 for(i = narcs, parc = parcs; --i >= 0; parc++)
1045 xMin = MIN (xMin, parc->x);
1046 yMin = MIN (yMin, parc->y);
1047 xMax = MAX (xMax, (parc->x + (int) parc->width));
1048 yMax = MAX (yMax, (parc->y + (int) parc->height));
1051 /* expand box to deal with line widths */
1052 halfWidth = (width + 1)/2;
1058 /* compute pixmap size; limit it to size of drawable */
1059 xOrg = MAX(xMin, 0);
1060 yOrg = MAX(yMin, 0);
1061 pixmapWidth = MIN(xMax, GDK_DRAWABLE_FBDATA(pDraw)->width) - xOrg;
1062 pixmapHeight = MIN(yMax, GDK_DRAWABLE_FBDATA(pDraw)->height) - yOrg;
1064 /* if nothing left, return */
1065 if ( (pixmapWidth <= 0) || (pixmapHeight <= 0) ) return;
1067 for(i = narcs, parc = parcs; --i >= 0; parc++)
1073 /* set up scratch GC */
1074 /* allocate a 1 bit deep pixmap of the appropriate size, and
1076 pDrawTo = gdk_pixmap_new(NULL, pixmapWidth, pixmapHeight, 1);
1080 pGCTo = gdk_gc_new(pDrawTo);
1083 gdk_pixmap_unref(pDrawTo);
1086 gdk_gc_set_function(pGCTo, GDK_COPY);
1087 memset(&gcv.background, 0, sizeof(GdkColor));
1088 gcv.foreground.pixel = 1;
1089 gcv.foreground.red = gcv.foreground.green = gcv.foreground.blue = 1;
1090 gdk_gc_set_foreground(pGCTo, &gcv.foreground);
1091 gdk_gc_set_background(pGCTo, &gcv.background);
1092 gdk_gc_set_line_attributes(pGCTo,
1093 GDK_GC_FBDATA(pGC)->values.line_width,
1094 GDK_GC_FBDATA(pGC)->values.line_style,
1095 GDK_GC_FBDATA(pGC)->values.cap_style,
1096 GDK_GC_FBDATA(pGC)->values.join_style);
1097 gdk_fb_drawable_clear(pDrawTo);
1100 fg = GDK_GC_FBDATA(pGC)->values.foreground;
1101 bg = GDK_GC_FBDATA(pGC)->values.background;
1102 if ((GDK_GC_FBDATA(pGC)->values.fill == GDK_TILED) ||
1103 (GDK_GC_FBDATA(pGC)->values.fill == GDK_OPAQUE_STIPPLED))
1104 bg = fg; /* the protocol sez these don't cause color changes */
1106 polyArcs = miComputeArcs (parcs, narcs, pGC);
1111 gdk_pixmap_unref(pDrawTo);
1112 gdk_gc_unref(pGCTo);
1117 cap[0] = cap[1] = 0;
1118 join[0] = join[1] = 0;
1119 for (iphase = ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) ? 1 : 0);
1124 gdk_gc_set_foreground(pGC, &bg);
1125 else if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH)
1126 gdk_gc_set_foreground(pGC, &fg);
1127 for (i = 0; i < polyArcs[iphase].narcs; i++) {
1128 miArcDataPtr arcData;
1130 arcData = &polyArcs[iphase].arcs[i];
1131 miArcSegment(pDrawTo, pGCTo, arcData->arc,
1132 &arcData->bounds[RIGHT_END],
1133 &arcData->bounds[LEFT_END]);
1134 if (polyArcs[iphase].arcs[i].render) {
1135 fillSpans (pDrawTo, pGCTo);
1137 * don't cap self-joining arcs
1139 if (polyArcs[iphase].arcs[i].selfJoin &&
1140 cap[iphase] < polyArcs[iphase].arcs[i].cap)
1142 while (cap[iphase] < polyArcs[iphase].arcs[i].cap) {
1144 miArcDataPtr arcData0;
1146 arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex;
1147 end = polyArcs[iphase].caps[cap[iphase]].end;
1148 arcData0 = &polyArcs[iphase].arcs[arcIndex];
1149 miArcCap (pDrawTo, pGCTo,
1150 &arcData0->bounds[end], end,
1151 arcData0->arc.x, arcData0->arc.y,
1152 (double) arcData0->arc.width / 2.0,
1153 (double) arcData0->arc.height / 2.0);
1156 while (join[iphase] < polyArcs[iphase].arcs[i].join) {
1157 int arcIndex0, arcIndex1, end0, end1;
1159 miArcDataPtr arcData0, arcData1;
1162 joinp = &polyArcs[iphase].joins[join[iphase]];
1163 arcIndex0 = joinp->arcIndex0;
1165 arcIndex1 = joinp->arcIndex1;
1167 phase0 = joinp->phase0;
1168 phase1 = joinp->phase1;
1169 arcData0 = &polyArcs[phase0].arcs[arcIndex0];
1170 arcData1 = &polyArcs[phase1].arcs[arcIndex1];
1171 miArcJoin (pDrawTo, pGCTo,
1172 &arcData0->bounds[end0],
1173 &arcData1->bounds[end1],
1174 arcData0->arc.x, arcData0->arc.y,
1175 (double) arcData0->arc.width / 2.0,
1176 (double) arcData0->arc.height / 2.0,
1177 arcData1->arc.x, arcData1->arc.y,
1178 (double) arcData1->arc.width / 2.0,
1179 (double) arcData1->arc.height / 2.0);
1183 gdk_fb_draw_drawable(pDraw, pGC, pDrawTo, 0, 0, xOrg, yOrg, pixmapWidth, pixmapHeight);
1184 gdk_fb_drawable_clear(pDrawTo);
1189 miFreeArcs(polyArcs, pGC);
1193 gdk_pixmap_unref(pDrawTo);
1194 gdk_gc_unref(pGCTo);
1200 angleBetween (SppPointRec center, SppPointRec point1, SppPointRec point2)
1205 * reflect from X coordinates back to ellipse
1206 * coordinates -- y increasing upwards
1208 a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x);
1209 a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x);
1219 translateBounds (miArcFacePtr b, int x, int y, double fx, double fy)
1227 b->counterClock.x -= fx;
1228 b->counterClock.y -= fy;
1232 miArcJoin (GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pLeft, miArcFacePtr pRight,
1233 int xOrgLeft, int yOrgLeft, double xFtransLeft, double yFtransLeft,
1234 int xOrgRight, int yOrgRight, double xFtransRight, double yFtransRight)
1236 SppPointRec center, corner, otherCorner;
1237 SppPointRec poly[5], e;
1238 SppPointPtr pArcPts;
1241 miArcFaceRec Right, Left;
1244 double xFtrans, yFtrans;
1246 double ae, ac2, ec2, bc2, de;
1249 xOrg = (xOrgRight + xOrgLeft) / 2;
1250 yOrg = (yOrgRight + yOrgLeft) / 2;
1251 xFtrans = (xFtransLeft + xFtransRight) / 2;
1252 yFtrans = (yFtransLeft + yFtransRight) / 2;
1254 translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight,
1255 xFtrans - xFtransRight, yFtrans - yFtransRight);
1257 translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft,
1258 xFtrans - xFtransLeft, yFtrans - yFtransLeft);
1262 if (pRight->clock.x == pLeft->counterClock.x &&
1263 pRight->clock.y == pLeft->counterClock.y)
1265 center = pRight->center;
1266 if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock))
1269 corner = pRight->clock;
1270 otherCorner = pLeft->counterClock;
1272 a = angleBetween (center, pLeft->clock, pRight->counterClock);
1273 corner = pLeft->clock;
1274 otherCorner = pRight->counterClock;
1276 switch (GDK_GC_FBDATA(pGC)->values.join_style) {
1277 case GDK_JOIN_ROUND:
1278 width = (GDK_GC_FBDATA(pGC)->values.line_width ? (double)GDK_GC_FBDATA(pGC)->values.line_width : (double)1);
1280 arc.x = center.x - width/2;
1281 arc.y = center.y - width/2;
1284 arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x);
1286 pArcPts = (SppPointPtr) g_malloc (3 * sizeof (SppPointRec));
1289 pArcPts[0].x = otherCorner.x;
1290 pArcPts[0].y = otherCorner.y;
1291 pArcPts[1].x = center.x;
1292 pArcPts[1].y = center.y;
1293 pArcPts[2].x = corner.x;
1294 pArcPts[2].y = corner.y;
1295 if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) )
1297 /* by drawing with miFillSppPoly and setting the endpoints of the arc
1298 * to be the corners, we assure that the cap will meet up with the
1299 * rest of the line */
1300 miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans);
1304 case GDK_JOIN_MITER:
1306 * don't miter arcs with less than 11 degrees between them
1311 poly[2] = otherCorner;
1312 bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) +
1313 (corner.y - otherCorner.y) * (corner.y - otherCorner.y);
1315 ac2 = (corner.x - center.x) * (corner.x - center.x) +
1316 (corner.y - center.y) * (corner.y - center.y);
1317 ae = sqrt (ac2 - ec2);
1319 e.x = (corner.x + otherCorner.x) / 2;
1320 e.y = (corner.y + otherCorner.y) / 2;
1321 poly[3].x = e.x + de * (e.x - center.x) / ae;
1322 poly[3].y = e.y + de * (e.y - center.y) / ae;
1327 case GDK_JOIN_BEVEL:
1330 poly[2] = otherCorner;
1335 miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans);
1340 miArcCap (GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pFace, int end,
1341 int xOrg, int yOrg, double xFtrans, double yFtrans)
1343 SppPointRec corner, otherCorner, center, endPoint, poly[5];
1345 corner = pFace->clock;
1346 otherCorner = pFace->counterClock;
1347 center = pFace->center;
1348 switch (GDK_GC_FBDATA(pGC)->values.cap_style) {
1349 case GDK_CAP_PROJECTING:
1350 poly[0].x = otherCorner.x;
1351 poly[0].y = otherCorner.y;
1352 poly[1].x = corner.x;
1353 poly[1].y = corner.y;
1354 poly[2].x = corner.x -
1355 (center.y - corner.y);
1356 poly[2].y = corner.y +
1357 (center.x - corner.x);
1358 poly[3].x = otherCorner.x -
1359 (otherCorner.y - center.y);
1360 poly[3].y = otherCorner.y +
1361 (otherCorner.x - center.x);
1362 poly[4].x = otherCorner.x;
1363 poly[4].y = otherCorner.y;
1364 miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans);
1368 * miRoundCap just needs these to be unequal.
1371 endPoint.x = endPoint.x + 100;
1372 miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0,
1373 -xOrg, -yOrg, xFtrans, yFtrans);
1380 /* MIROUNDCAP -- a private helper function
1381 * Put Rounded cap on end. pCenter is the center of this end of the line
1382 * pEnd is the center of the other end of the line. pCorner is one of the
1383 * two corners at this end of the line.
1384 * NOTE: pOtherCorner must be counter-clockwise from pCorner.
1387 static void miRoundCap(GdkDrawable *pDraw, GdkGC *pGC, SppPointRec pCenter, SppPointRec pEnd, SppPointRec pCorner,
1388 SppPointRec pOtherCorner, int fLineEnd, int xOrg, int yOrg,
1389 double xFtrans, double yFtrans)
1394 SppPointPtr pArcPts;
1396 width = (GDK_GC_FBDATA(pGC)->values.line_width ? (double)GDK_GC_FBDATA(pGC)->values.line_width : (double)1);
1398 arc.x = pCenter.x - width/2;
1399 arc.y = pCenter.y - width/2;
1402 arc.angle1 = -(miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x));
1403 if(PTISEQUAL(pCenter, pEnd))
1404 arc.angle2 = - 180.0;
1406 arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1;
1408 arc.angle2 += 360.0;
1410 pArcPts = (SppPointPtr) NULL;
1411 if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) )
1413 /* by drawing with miFillSppPoly and setting the endpoints of the arc
1414 * to be the corners, we assure that the cap will meet up with the
1415 * rest of the line */
1416 miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans);
1422 * To avoid inaccuracy at the cardinal points, use trig functions
1423 * which are exact for those angles
1427 #define M_PI 3.14159265358979323846
1430 #define M_PI_2 1.57079632679489661923
1433 # define Dsin(d) ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
1434 # define Dcos(d) ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
1435 # define mod(a,b) ((a) >= 0 ? (a) % (b) : (b) - (-a) % (b))
1442 if (floor (a/90) == a/90) {
1444 switch (mod (i, 4)) {
1451 return cos (a * M_PI / 180.0);
1459 if (floor (a/90) == a/90) {
1461 switch (mod (i, 4)) {
1468 return sin (a * M_PI / 180.0);
1480 return asin(v) * (180.0 / M_PI);
1484 miDatan2 (double dy, double dx)
1490 } else if (dx == 0) {
1494 } else if (fabs (dy) == fabs (dx)) {
1505 return atan2 (dy, dx) * (180.0 / M_PI);
1509 /* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper
1510 * routine for filled arc and line (round cap) code.
1511 * Returns the number of points in the arc. Note that it takes a pointer
1512 * to a pointer to where it should put the points and an index (cpt).
1513 * This procedure allocates the space necessary to fit the arc points.
1514 * Sometimes it's convenient for those points to be at the end of an existing
1515 * array. (For example, if we want to leave a spare point to make sectors
1516 * instead of segments.) So we pass in the g_malloc()ed chunk that contains the
1517 * array and an index saying where we should start stashing the points.
1518 * If there isn't an array already, we just pass in a null pointer and
1519 * count on g_realloc() to handle the null pointer correctly.
1522 miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts)
1524 SppArcPtr parc; /* points to an arc */
1525 int cpt; /* number of points already in arc list */
1526 SppPointPtr *ppPts; /* pointer to pointer to arc-list -- modified */
1529 double st, /* Start Theta, start angle */
1530 et, /* End Theta, offset from start theta */
1531 dt, /* Delta Theta, angle to sweep ellipse */
1532 cdt, /* Cos Delta Theta, actually 2 cos(dt) */
1533 x0, y0, /* the recurrence formula needs two points to start */
1535 x2, y2, /* this will be the new point generated */
1536 xc, yc; /* the center point */
1539 GdkPoint last; /* last point on integer boundaries */
1541 /* The spec says that positive angles indicate counterclockwise motion.
1542 * Given our coordinate system (with 0,0 in the upper left corner),
1543 * the screen appears flipped in Y. The easiest fix is to negate the
1546 st = - parc->angle1;
1548 et = - parc->angle2;
1550 /* Try to get a delta theta that is within 1/2 pixel. Then adjust it
1551 * so that it divides evenly into the total.
1552 * I'm just using cdt 'cause I'm lazy.
1555 if (parc->height > cdt)
1562 dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */
1564 count = abs(count) + 1;
1568 cdt = 2 * miDcos(dt);
1569 if (!(poly = (SppPointPtr) g_realloc((gpointer)*ppPts,
1570 (cpt + count) * sizeof(SppPointRec))))
1574 xc = parc->width/2.0; /* store half width and half height */
1575 yc = parc->height/2.0;
1577 x0 = xc * miDcos(st);
1578 y0 = yc * miDsin(st);
1579 x1 = xc * miDcos(st + dt);
1580 y1 = yc * miDsin(st + dt);
1581 xc += parc->x; /* by adding initial point, these become */
1582 yc += parc->y; /* the center point */
1584 poly[cpt].x = (xc + x0);
1585 poly[cpt].y = (yc + y0);
1586 last.x = ROUNDTOINT( poly[cpt + 1].x = (xc + x1) );
1587 last.y = ROUNDTOINT( poly[cpt + 1].y = (yc + y1) );
1589 for(i = 2; i < count; i++)
1594 poly[cpt + i].x = (xc + x2);
1595 poly[cpt + i].y = (yc + y2);
1600 /* adjust the last point */
1601 if (fabs(parc->angle2) >= 360.0)
1602 poly[cpt +i -1] = poly[0];
1604 poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc);
1605 poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc);
1612 double x0, y0, x1, y1;
1616 # define ADD_REALLOC_STEP 20
1619 addCap (miArcCapPtr *capsp, int *ncapsp, int *sizep, int end, int arcIndex)
1624 if (*ncapsp == *sizep)
1626 newsize = *sizep + ADD_REALLOC_STEP;
1627 cap = (miArcCapPtr) g_realloc (*capsp,
1628 newsize * sizeof (**capsp));
1634 cap = &(*capsp)[*ncapsp];
1636 cap->arcIndex = arcIndex;
1641 addJoin (miArcJoinPtr *joinsp, int *njoinsp, int *sizep, int end0, int index0,
1642 int phase0, int end1, int index1, int phase1)
1647 if (*njoinsp == *sizep)
1649 newsize = *sizep + ADD_REALLOC_STEP;
1650 join = (miArcJoinPtr) g_realloc (*joinsp,
1651 newsize * sizeof (**joinsp));
1657 join = &(*joinsp)[*njoinsp];
1659 join->arcIndex0 = index0;
1660 join->phase0 = phase0;
1662 join->arcIndex1 = index1;
1663 join->phase1 = phase1;
1668 addArc (miArcDataPtr *arcsp, int *narcsp, int *sizep, miArc *xarc)
1673 if (*narcsp == *sizep)
1675 newsize = *sizep + ADD_REALLOC_STEP;
1676 arc = (miArcDataPtr) g_realloc (*arcsp,
1677 newsize * sizeof (**arcsp));
1679 return (miArcDataPtr)NULL;
1683 arc = &(*arcsp)[*narcsp];
1690 miFreeArcs(miPolyArcPtr arcs, GdkGC *pGC)
1694 for (iphase = ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) ? 1 : 0);
1698 if (arcs[iphase].narcs > 0)
1699 g_free(arcs[iphase].arcs);
1700 if (arcs[iphase].njoins > 0)
1701 g_free(arcs[iphase].joins);
1702 if (arcs[iphase].ncaps > 0)
1703 g_free(arcs[iphase].caps);
1709 * map angles to radial distance. This only deals with the first quadrant
1713 * a polygonal approximation to the arc for computing arc lengths
1716 # define dashIndexToAngle(di) ((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1))
1717 # define xAngleToDashIndex(xa) ((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64))
1718 # define dashIndexToXAngle(di) ((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1))
1719 # define dashXAngleStep (((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1)))
1722 computeDashMap (miArc *arcp, dashMap *map)
1725 double a, x, y, prevx = 0.0, prevy = 0.0, dist;
1727 for (di = 0; di < DASH_MAP_SIZE; di++) {
1728 a = dashIndexToAngle (di);
1729 x = ((double) arcp->width / 2.0) * miDcos (a);
1730 y = ((double) arcp->height / 2.0) * miDsin (a);
1734 dist = hypot (x - prevx, y - prevy);
1735 map->map[di] = map->map[di - 1] + dist;
1742 typedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes;
1744 /* this routine is a bit gory */
1747 miComputeArcs (miArc *parcs, int narcs, GdkGC *pGC)
1749 int isDashed, isDoubleDash;
1752 int start, i, j, k = 0, nexti, nextk = 0;
1758 struct arcData *data;
1761 int iphase, prevphase = 0, joinphase;
1765 int iDash = 0, dashRemaining;
1766 int iDashStart = 0, dashRemainingStart = 0, iphaseStart;
1767 int startAngle, spanAngle, endAngle, backwards = 0;
1768 int prevDashAngle, dashAngle;
1771 isDashed = !(GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID);
1772 isDoubleDash = (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH);
1773 dashOffset = GDK_GC_FBDATA(pGC)->dash_offset;
1775 data = (struct arcData *) ALLOCATE_LOCAL (narcs * sizeof (struct arcData));
1777 return (miPolyArcPtr)NULL;
1778 arcs = (miPolyArcPtr) g_malloc (sizeof (*arcs) * (isDoubleDash ? 2 : 1));
1781 DEALLOCATE_LOCAL(data);
1782 return (miPolyArcPtr)NULL;
1784 for (i = 0; i < narcs; i++) {
1785 a0 = todeg (parcs[i].angle1);
1786 angle2 = parcs[i].angle2;
1787 if (angle2 > FULLCIRCLE)
1788 angle2 = FULLCIRCLE;
1789 else if (angle2 < -FULLCIRCLE)
1790 angle2 = -FULLCIRCLE;
1791 data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE;
1792 a1 = todeg (parcs[i].angle1 + angle2);
1793 data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0));
1794 data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0));
1795 data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1));
1796 data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1));
1799 for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) {
1800 arcs[iphase].njoins = 0;
1801 arcs[iphase].joins = NULL;
1802 joinSize[iphase] = 0;
1804 arcs[iphase].ncaps = 0;
1805 arcs[iphase].caps = NULL;
1806 capSize[iphase] = 0;
1808 arcs[iphase].narcs = 0;
1809 arcs[iphase].arcs = NULL;
1810 arcSize[iphase] = 0;
1816 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[0];
1817 while (dashOffset > 0) {
1818 if (dashOffset >= dashRemaining) {
1819 dashOffset -= dashRemaining;
1820 iphase = iphase ? 0 : 1;
1822 if (iDash == GDK_GC_FBDATA(pGC)->dash_list_len)
1824 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[iDash];
1826 dashRemaining -= dashOffset;
1831 dashRemainingStart = dashRemaining;
1833 iphaseStart = iphase;
1835 for (i = narcs - 1; i >= 0; i--) {
1839 if (data[i].selfJoin || i == j ||
1840 (UNEQUAL (data[i].x1, data[j].x0) ||
1841 UNEQUAL (data[i].y1, data[j].y0)))
1843 if (iphase == 0 || isDoubleDash)
1844 addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
1845 &capSize[iphase], RIGHT_END, 0);
1862 ** deal with dashed arcs. Use special rules for certain 0 area arcs.
1863 ** Presumably, the other 0 area arcs still aren't done right.
1865 arcTypes arcType = OTHER;
1868 if (parcs[i].height == 0
1869 && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00
1870 && parcs[i].angle2 == 0x2d00)
1871 arcType = HORIZONTAL;
1872 else if (parcs[i].width == 0
1873 && (parcs[i].angle1 % FULLCIRCLE) == 0x1680
1874 && parcs[i].angle2 == 0x2d00)
1876 if (arcType == OTHER) {
1878 * precompute an approximation map
1880 computeDashMap (&parcs[i], &map);
1882 * compute each individual dash segment using the path
1885 startAngle = parcs[i].angle1;
1886 spanAngle = parcs[i].angle2;
1887 if (spanAngle > FULLCIRCLE)
1888 spanAngle = FULLCIRCLE;
1889 else if (spanAngle < -FULLCIRCLE)
1890 spanAngle = -FULLCIRCLE;
1892 startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
1893 if (startAngle >= FULLCIRCLE)
1894 startAngle = startAngle % FULLCIRCLE;
1895 endAngle = startAngle + spanAngle;
1896 backwards = spanAngle < 0;
1899 if (arcType == VERTICAL) {
1900 xarc.angle1 = 0x1680;
1901 startAngle = parcs[i].y;
1902 endAngle = startAngle + parcs[i].height;
1904 xarc.angle1 = 0x2d00;
1905 startAngle = parcs[i].x;
1906 endAngle = startAngle + parcs[i].width;
1909 dashAngle = startAngle;
1910 selfJoin = data[i].selfJoin &&
1911 (iphase == 0 || isDoubleDash);
1913 * add dashed arcs to each bucket
1916 while (dashAngle != endAngle) {
1917 prevDashAngle = dashAngle;
1918 if (arcType == OTHER) {
1919 dashAngle = computeAngleFromPath (prevDashAngle, endAngle,
1920 &map, &dashRemaining, backwards);
1921 /* avoid troubles with huge arcs and small dashes */
1922 if (dashAngle == prevDashAngle) {
1929 thisLength = (dashAngle + dashRemaining <= endAngle) ?
1930 dashRemaining : endAngle - dashAngle;
1931 if (arcType == VERTICAL) {
1933 xarc.height = thisLength;
1936 xarc.width = thisLength;
1938 dashAngle += thisLength;
1939 dashRemaining -= thisLength;
1941 if (iphase == 0 || isDoubleDash) {
1942 if (arcType == OTHER) {
1944 spanAngle = prevDashAngle;
1946 spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE;
1947 if (spanAngle >= FULLCIRCLE)
1948 spanAngle = spanAngle % FULLCIRCLE;
1949 xarc.angle1 = spanAngle;
1950 spanAngle = dashAngle - prevDashAngle;
1952 if (dashAngle > prevDashAngle)
1953 spanAngle = - FULLCIRCLE + spanAngle;
1955 if (dashAngle < prevDashAngle)
1956 spanAngle = FULLCIRCLE + spanAngle;
1958 if (spanAngle > FULLCIRCLE)
1959 spanAngle = FULLCIRCLE;
1960 if (spanAngle < -FULLCIRCLE)
1961 spanAngle = -FULLCIRCLE;
1962 xarc.angle2 = spanAngle;
1964 arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
1965 &arcSize[iphase], &xarc);
1969 * cap each end of an on/off dash
1971 if (!isDoubleDash) {
1972 if (prevDashAngle != startAngle) {
1973 addCap (&arcs[iphase].caps,
1974 &arcs[iphase].ncaps,
1975 &capSize[iphase], RIGHT_END,
1976 arc - arcs[iphase].arcs);
1979 if (dashAngle != endAngle) {
1980 addCap (&arcs[iphase].caps,
1981 &arcs[iphase].ncaps,
1982 &capSize[iphase], LEFT_END,
1983 arc - arcs[iphase].arcs);
1986 arc->cap = arcs[iphase].ncaps;
1987 arc->join = arcs[iphase].njoins;
1990 if (dashAngle == endAngle)
1991 arc->selfJoin = selfJoin;
1994 if (dashRemaining <= 0) {
1996 if (iDash == GDK_GC_FBDATA(pGC)->dash_list_len)
1998 iphase = iphase ? 0:1;
1999 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[iDash];
2003 * make sure a place exists for the position data when
2004 * drawing a zero-length arc
2006 if (startAngle == endAngle) {
2008 if (!isDoubleDash && iphase == 1)
2010 arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs,
2011 &arcSize[prevphase], &parcs[i]);
2014 arc->join = arcs[prevphase].njoins;
2015 arc->cap = arcs[prevphase].ncaps;
2016 arc->selfJoin = data[i].selfJoin;
2019 arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
2020 &arcSize[iphase], &parcs[i]);
2023 arc->join = arcs[iphase].njoins;
2024 arc->cap = arcs[iphase].ncaps;
2025 arc->selfJoin = data[i].selfJoin;
2028 if (prevphase == 0 || isDoubleDash)
2029 k = arcs[prevphase].narcs - 1;
2030 if (iphase == 0 || isDoubleDash)
2031 nextk = arcs[iphase].narcs;
2032 if (nexti == start) {
2036 iphase = iphaseStart;
2037 dashRemaining = dashRemainingStart;
2040 arcsJoin = narcs > 1 && i != j &&
2041 ISEQUAL (data[i].x1, data[j].x0) &&
2042 ISEQUAL (data[i].y1, data[j].y0) &&
2043 !data[i].selfJoin && !data[j].selfJoin;
2052 (prevphase == 0 || isDoubleDash) &&
2053 (iphase == 0 || isDoubleDash))
2058 joinphase = iphaseStart;
2060 * if the join is right at the dash,
2061 * draw the join in foreground
2062 * This is because the foreground
2063 * arcs are computed second, the results
2064 * of which are needed to draw the join
2066 if (joinphase != prevphase)
2069 if (joinphase == 0 || isDoubleDash) {
2070 addJoin (&arcs[joinphase].joins,
2071 &arcs[joinphase].njoins,
2072 &joinSize[joinphase],
2073 LEFT_END, k, prevphase,
2074 RIGHT_END, nextk, iphase);
2075 arc->join = arcs[prevphase].njoins;
2079 * cap the left end of this arc
2080 * unless it joins itself
2082 if ((prevphase == 0 || isDoubleDash) &&
2085 addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps,
2086 &capSize[prevphase], LEFT_END, k);
2087 arc->cap = arcs[prevphase].ncaps;
2089 if (isDashed && !arcsJoin) {
2091 iphase = iphaseStart;
2092 dashRemaining = dashRemainingStart;
2094 nextk = arcs[iphase].narcs;
2095 if (nexti == start) {
2098 iphase = iphaseStart;
2099 dashRemaining = dashRemainingStart;
2102 * cap the right end of the next arc. If the
2103 * next arc is actually the first arc, only
2104 * cap it if it joins with this arc. This
2105 * case will occur when the final dash segment
2106 * of an on/off dash is off. Of course, this
2107 * cap will be drawn at a strange time, but that
2110 if ((iphase == 0 || isDoubleDash) &&
2111 (nexti != start || (arcsJoin && isDashed)))
2112 addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
2113 &capSize[iphase], RIGHT_END, nextk);
2120 * make sure the last section is rendered
2122 for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++)
2123 if (arcs[iphase].narcs > 0) {
2124 arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1;
2125 arcs[iphase].arcs[arcs[iphase].narcs-1].join =
2126 arcs[iphase].njoins;
2127 arcs[iphase].arcs[arcs[iphase].narcs-1].cap =
2130 DEALLOCATE_LOCAL(data);
2133 miFreeArcs(arcs, pGC);
2134 DEALLOCATE_LOCAL(data);
2135 return (miPolyArcPtr)NULL;
2139 angleToLength (int angle, dashMap *map)
2141 double len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen;
2144 gboolean oddSide = FALSE;
2148 while (angle >= 90 * 64) {
2150 totallen += sidelen;
2156 totallen -= sidelen;
2161 angle = 90 * 64 - angle;
2163 di = xAngleToDashIndex (angle);
2164 excess = angle - dashIndexToXAngle (di);
2168 * linearly interpolate between this point and the next
2171 excesslen = (map->map[di + 1] - map->map[di]) *
2172 ((double) excess) / dashXAngleStep;
2176 totallen += (sidelen - len);
2183 * len is along the arc, but may be more than one rotation
2187 lengthToAngle (double len, dashMap *map)
2189 double sidelen = map->map[DASH_MAP_SIZE - 1];
2190 int angle, angleexcess;
2191 gboolean oddSide = FALSE;
2196 * step around the ellipse, subtracting sidelens and
2197 * adding 90 degrees. oddSide will tell if the
2198 * map should be interpolated in reverse
2202 return 2 * FULLCIRCLE; /* infinity */
2203 while (len >= sidelen) {
2210 return -2 * FULLCIRCLE; /* infinity */
2218 len = sidelen - len;
2220 a1 = DASH_MAP_SIZE - 1;
2222 * binary search for the closest pre-computed length
2224 while (a1 - a0 > 1) {
2226 if (len > map->map[a])
2231 angleexcess = dashIndexToXAngle (a0);
2233 * linearly interpolate to the next point
2235 angleexcess += (len - map->map[a0]) /
2236 (map->map[a0+1] - map->map[a0]) * dashXAngleStep;
2238 angle += (90 * 64) - angleexcess;
2240 angle += angleexcess;
2245 * compute the angle of an ellipse which cooresponds to
2246 * the given path length. Note that the correct solution
2247 * to this problem is an eliptic integral, we'll punt and
2248 * approximate (it's only for dashes anyway). This
2249 * approximation uses a polygon.
2251 * The remaining portion of len is stored in *lenp -
2252 * this will be negative if the arc extends beyond
2253 * len and positive if len extends beyond the arc.
2256 static int computeAngleFromPath(int startAngle, int endAngle, dashMap *map, int *lenp, int backwards)
2257 /* int startAngle, endAngle; *//* normalized absolute angles in *64 degrees */
2268 * flip the problem around to always be
2271 a0 = FULLCIRCLE - a0;
2272 a1 = FULLCIRCLE - a1;
2276 len0 = angleToLength (a0, map);
2277 a = lengthToAngle (len0 + len, map);
2280 len -= angleToLength (a1, map) - len0;
2290 * scan convert wide arcs.
2294 * draw zero width/height arcs
2298 drawZeroArc (GdkDrawable *pDraw, GdkGC *pGC, miArc *tarc, int lw,
2299 miArcFacePtr left, miArcFacePtr right)
2301 double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0, w, h, x, y;
2302 double xmax, ymax, xmin, ymin;
2304 double a, startAngle, endAngle;
2310 if (a1 > FULLCIRCLE)
2312 else if (a1 < -FULLCIRCLE)
2314 w = (double)tarc->width / 2.0;
2315 h = (double)tarc->height / 2.0;
2317 * play in X coordinates right away
2319 startAngle = - ((double) a0 / 64.0);
2320 endAngle = - ((double) (a0 + a1) / 64.0);
2331 if (a == startAngle)
2351 if (a1 < 0) /* clockwise */
2353 if (floor (a / 90.0) == floor (endAngle / 90.0))
2356 a = 90 * (floor (a/90.0) + 1);
2360 if (ceil (a / 90.0) == ceil (endAngle / 90.0))
2363 a = 90 * (ceil (a/90.0) - 1);
2367 if ((x1 - x0) + (y1 - y0) < 0)
2378 right->center.x = x0;
2379 right->center.y = y0;
2380 right->clock.x = x0 - lx;
2381 right->clock.y = y0 - ly;
2382 right->counterClock.x = x0 + lx;
2383 right->counterClock.y = y0 + ly;
2387 left->center.x = x1;
2388 left->center.y = y1;
2389 left->clock.x = x1 + lx;
2390 left->clock.y = y1 + ly;
2391 left->counterClock.x = x1 - lx;
2392 left->counterClock.y = y1 - ly;
2406 if (xmax != xmin && ymax != ymin) {
2407 int minx, maxx, miny, maxy;
2409 minx = ICEIL (xmin + w) + tarc->x;
2410 maxx = ICEIL (xmax + w) + tarc->x;
2411 miny = ICEIL (ymin + h) + tarc->y;
2412 maxy = ICEIL (ymax + h) + tarc->y;
2414 gdk_fb_draw_rectangle(pDraw, pGC, TRUE, minx, miny, maxx - minx, maxy - miny);
2419 * this computes the ellipse y value associated with the
2420 * bottom of the tail.
2424 tailEllipseY (struct arc_def *def, struct accelerators *acc)
2429 if (def->w == def->h)
2431 t = def->l * def->w;
2432 if (def->w > def->h) {
2439 t = 2.0 * def->h * t;
2440 t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2;
2442 acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t);
2446 * inverse functions -- compute edge coordinates
2451 outerXfromXY (double x, double y,
2452 struct arc_def *def, struct accelerators *acc)
2454 return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2458 outerYfromXY (double x, double y,
2459 struct arc_def *def, struct accelerators *acc)
2461 return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2465 innerXfromXY (double x, double y,
2466 struct arc_def *def, struct accelerators *acc)
2468 return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2472 innerYfromXY (double x, double y,
2473 struct arc_def *def, struct accelerators *acc)
2475 return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2479 innerYfromY (double y, struct arc_def *def, struct accelerators *acc)
2483 x = (def->w / def->h) * sqrt (acc->h2 - y*y);
2485 return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2489 computeLine (double x1, double y1, double x2, double y2, struct line *line)
2494 line->m = (x1 - x2) / (y1 - y2);
2495 line->b = x1 - y1 * line->m;
2501 * compute various accelerators for an ellipse. These
2502 * are simply values that are used repeatedly in
2507 computeAcc (miArc *tarc, int lw, struct arc_def *def, struct accelerators *acc)
2509 def->w = ((double) tarc->width) / 2.0;
2510 def->h = ((double) tarc->height) / 2.0;
2511 def->l = ((double) lw) / 2.0;
2512 acc->h2 = def->h * def->h;
2513 acc->w2 = def->w * def->w;
2514 acc->h4 = acc->h2 * acc->h2;
2515 acc->w4 = acc->w2 * acc->w2;
2516 acc->h2l = acc->h2 * def->l;
2517 acc->w2l = acc->w2 * def->l;
2518 acc->h2mw2 = acc->h2 - acc->w2;
2519 acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0;
2520 acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0;
2521 acc->xorg = tarc->x + (tarc->width >> 1);
2522 acc->yorgu = tarc->y + (tarc->height >> 1);
2523 acc->yorgl = acc->yorgu + (tarc->height & 1);
2524 tailEllipseY (def, acc);
2528 * compute y value bounds of various portions of the arc,
2529 * the outer edge, the ellipse and the inner edge.
2533 computeBound (struct arc_def *def, struct arc_bound *bound,
2534 struct accelerators *acc, miArcFacePtr right, miArcFacePtr left)
2539 struct bound innerx, outerx;
2540 struct bound ellipsex;
2542 bound->ellipse.min = Dsin (def->a0) * def->h;
2543 bound->ellipse.max = Dsin (def->a1) * def->h;
2544 if (def->a0 == 45 && def->w == def->h)
2545 ellipsex.min = bound->ellipse.min;
2547 ellipsex.min = Dcos (def->a0) * def->w;
2548 if (def->a1 == 45 && def->w == def->h)
2549 ellipsex.max = bound->ellipse.max;
2551 ellipsex.max = Dcos (def->a1) * def->w;
2552 bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2553 bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2554 bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2555 bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2557 outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2558 outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2559 innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2560 innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2563 * save the line end points for the
2564 * cap code to use. Careful here, these are
2565 * in cartesean coordinates (y increasing upwards)
2566 * while the cap code uses inverted coordinates
2567 * (y increasing downwards)
2571 right->counterClock.y = bound->outer.min;
2572 right->counterClock.x = outerx.min;
2573 right->center.y = bound->ellipse.min;
2574 right->center.x = ellipsex.min;
2575 right->clock.y = bound->inner.min;
2576 right->clock.x = innerx.min;
2580 left->clock.y = bound->outer.max;
2581 left->clock.x = outerx.max;
2582 left->center.y = bound->ellipse.max;
2583 left->center.x = ellipsex.max;
2584 left->counterClock.y = bound->inner.max;
2585 left->counterClock.x = innerx.max;
2588 bound->left.min = bound->inner.max;
2589 bound->left.max = bound->outer.max;
2590 bound->right.min = bound->inner.min;
2591 bound->right.max = bound->outer.min;
2593 computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min,
2595 computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max,
2598 if (bound->inner.min > bound->inner.max) {
2599 t = bound->inner.min;
2600 bound->inner.min = bound->inner.max;
2601 bound->inner.max = t;
2603 tail_y = acc->tail_y;
2604 if (tail_y > bound->ellipse.max)
2605 tail_y = bound->ellipse.max;
2606 else if (tail_y < bound->ellipse.min)
2607 tail_y = bound->ellipse.min;
2608 innerTaily = innerYfromY (tail_y, def, acc);
2609 if (bound->inner.min > innerTaily)
2610 bound->inner.min = innerTaily;
2611 if (bound->inner.max < innerTaily)
2612 bound->inner.max = innerTaily;
2613 bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY);
2614 bound->inneri.max = floor(bound->inner.max - acc->fromIntY);
2615 bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY);
2616 bound->outeri.max = floor(bound->outer.max - acc->fromIntY);
2620 * this section computes the x value of the span at y
2621 * intersected with the specified face of the ellipse.
2623 * this is the min/max X value over the set of normal
2624 * lines to the entire ellipse, the equation of the
2628 * x = ------------ y + ellipse_x (1 - --- )
2631 * compute the derivative with-respect-to ellipse_y and solve
2634 * (w^2 - h^2) ellipse_y^3 + h^4 y
2635 * 0 = - ----------------------------------
2636 * h w ellipse_y^2 sqrt (h^2 - ellipse_y^2)
2639 * ellipse_y = ( ---------- ) ^ (1/3)
2642 * The other two solutions to the equation are imaginary.
2644 * This gives the position on the ellipse which generates
2645 * the normal with the largest/smallest x intersection point.
2647 * Now compute the second derivative to check whether
2648 * the intersection is a minimum or maximum:
2650 * h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2651 * - -------------------------------------------
2652 * w y0^3 (sqrt (h^2 - y^2)) ^ 3
2654 * as we only care about the sign,
2656 * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2658 * or (to use accelerators),
2660 * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2)
2665 * computes the position on the ellipse whose normal line
2666 * intersects the given scan line maximally
2670 hookEllipseY (double scan_y, struct arc_bound *bound,
2671 struct accelerators *acc, int left)
2675 if (acc->h2mw2 == 0) {
2676 if ( (scan_y > 0 && !left) || (scan_y < 0 && left) )
2677 return bound->ellipse.min;
2678 return bound->ellipse.max;
2680 ret = (acc->h4 * scan_y) / (acc->h2mw2);
2684 return -cbrt (-ret);
2688 * computes the X value of the intersection of the
2689 * given scan line with the right side of the lower hook
2693 hookX (double scan_y, struct arc_def *def,
2694 struct arc_bound *bound, struct accelerators *acc, int left)
2696 double ellipse_y, x;
2699 if (def->w != def->h) {
2700 ellipse_y = hookEllipseY (scan_y, bound, acc, left);
2701 if (boundedLe (ellipse_y, bound->ellipse)) {
2703 * compute the value of the second
2706 maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 -
2707 acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2);
2708 if ((left && maxMin > 0) || (!left && maxMin < 0)) {
2710 return def->w + left ? -def->l : def->l;
2711 x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) *
2712 sqrt (acc->h2 - ellipse_y * ellipse_y) /
2713 (def->h * def->w * ellipse_y);
2719 if (acc->left.valid && boundedLe (scan_y, bound->left)) {
2720 x = intersectLine (scan_y, acc->left);
2722 if (acc->right.valid)
2723 x = intersectLine (scan_y, acc->right);
2725 x = def->w - def->l;
2728 if (acc->right.valid && boundedLe (scan_y, bound->right)) {
2729 x = intersectLine (scan_y, acc->right);
2731 if (acc->left.valid)
2732 x = intersectLine (scan_y, acc->left);
2734 x = def->w - def->l;
2741 * generate the set of spans with
2742 * the given y coordinate
2746 arcSpan (int y, int lx, int lw, int rx, int rw,
2747 struct arc_def *def, struct arc_bound *bounds,
2748 struct accelerators *acc, int mask)
2750 int linx, loutx, rinx, routx;
2753 if (boundedLe (y, bounds->inneri)) {
2758 * intersection with left face
2760 x = hookX (y + acc->fromIntY, def, bounds, acc, 1);
2761 if (acc->right.valid &&
2762 boundedLe (y + acc->fromIntY, bounds->right))
2764 altx = intersectLine (y + acc->fromIntY, acc->right);
2768 linx = -ICEIL(acc->fromIntX - x);
2769 rinx = ICEIL(acc->fromIntX + x);
2771 if (boundedLe (y, bounds->outeri)) {
2776 * intersection with right face
2778 x = hookX (y + acc->fromIntY, def, bounds, acc, 0);
2779 if (acc->left.valid &&
2780 boundedLe (y + acc->fromIntY, bounds->left))
2783 x = intersectLine (y + acc->fromIntY, acc->left);
2787 loutx = -ICEIL(acc->fromIntX - x);
2788 routx = ICEIL(acc->fromIntX + x);
2792 newFinalSpan (acc->yorgu - y,
2793 acc->xorg + rinx, acc->xorg + routx);
2795 newFinalSpan (acc->yorgl + y,
2796 acc->xorg + rinx, acc->xorg + routx);
2800 newFinalSpan (acc->yorgu - y,
2801 acc->xorg - loutx, acc->xorg - linx);
2803 newFinalSpan (acc->yorgl + y,
2804 acc->xorg - loutx, acc->xorg - linx);
2809 arcSpan0 (int lx, int lw, int rx, int rw,
2810 struct arc_def *def, struct arc_bound *bounds,
2811 struct accelerators *acc, int mask)
2815 if (boundedLe (0, bounds->inneri) &&
2816 acc->left.valid && boundedLe (0, bounds->left) &&
2819 x = def->w - def->l;
2820 if (acc->left.b < x)
2822 lw = ICEIL(acc->fromIntX - x) - lx;
2824 rx = ICEIL(acc->fromIntX + x);
2827 arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask);
2831 tailSpan (int y, int lw, int rw,
2832 struct arc_def *def, struct arc_bound *bounds,
2833 struct accelerators *acc, int mask)
2835 double yy, xalt, x, lx, rx;
2838 if (boundedLe(y, bounds->outeri))
2839 arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask);
2840 else if (def->w != def->h) {
2841 yy = y + acc->fromIntY;
2842 x = tailX(yy, def, bounds, acc);
2843 if (yy == 0.0 && x == -rw - acc->fromIntX)
2845 if (acc->right.valid && boundedLe (yy, bounds->right)) {
2848 xalt = intersectLine (yy, acc->right);
2849 if (xalt >= -rw - acc->fromIntX && xalt <= rx)
2851 n = ICEIL(acc->fromIntX + lx);
2854 newFinalSpan (acc->yorgu - y,
2855 acc->xorg + n, acc->xorg + lw);
2857 newFinalSpan (acc->yorgl + y,
2858 acc->xorg + n, acc->xorg + lw);
2860 n = ICEIL(acc->fromIntX + rx);
2863 newFinalSpan (acc->yorgu - y,
2864 acc->xorg - rw, acc->xorg + n);
2866 newFinalSpan (acc->yorgl + y,
2867 acc->xorg - rw, acc->xorg + n);
2871 ICEIL(acc->fromIntX - x), 0,
2872 ICEIL(acc->fromIntX + x), 0,
2873 def, bounds, acc, mask);
2878 * create whole arcs out of pieces. This code is
2882 static struct finalSpan **finalSpans = NULL;
2883 static int finalMiny = 0, finalMaxy = -1;
2884 static int finalSize = 0;
2886 static int nspans = 0; /* total spans, not just y coords */
2889 struct finalSpan *next;
2890 int min, max; /* x values */
2893 static struct finalSpan *freeFinalSpans, *tmpFinalSpan;
2895 # define allocFinalSpan() (freeFinalSpans ?\
2896 ((tmpFinalSpan = freeFinalSpans), \
2897 (freeFinalSpans = freeFinalSpans->next), \
2898 (tmpFinalSpan->next = NULL), \
2902 # define SPAN_CHUNK_SIZE 128
2904 struct finalSpanChunk {
2905 struct finalSpan data[SPAN_CHUNK_SIZE];
2906 struct finalSpanChunk *next;
2909 static struct finalSpanChunk *chunks;
2912 realAllocSpan (void)
2914 register struct finalSpanChunk *newChunk;
2915 register struct finalSpan *span;
2918 newChunk = (struct finalSpanChunk *) g_malloc (sizeof (struct finalSpanChunk));
2920 return (struct finalSpan *) NULL;
2921 newChunk->next = chunks;
2923 freeFinalSpans = span = newChunk->data + 1;
2924 for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) {
2925 span->next = span+1;
2929 span = newChunk->data;
2935 disposeFinalSpans (void)
2937 struct finalSpanChunk *chunk, *next;
2939 for (chunk = chunks; chunk; chunk = next) {
2944 freeFinalSpans = NULL;
2950 fillSpans (GdkDrawable *pDrawable, GdkGC *pGC)
2952 register struct finalSpan *span;
2953 register GdkSpan* xSpan;
2955 register struct finalSpan **f;
2961 xSpan = xSpans = (GdkSpan*) ALLOCATE_LOCAL (nspans * sizeof (GdkSpan));
2966 for (spany = finalMiny; spany <= finalMaxy; spany++, f++) {
2967 for (span = *f; span; span=span->next) {
2968 if (span->max <= span->min)
2970 xSpan->x = span->min;
2972 xSpan->width = span->max - span->min;
2978 gdk_fb_fill_spans(pDrawable, pGC, xSpans, i, TRUE);
2980 disposeFinalSpans ();
2982 DEALLOCATE_LOCAL (xSpans);
2989 # define SPAN_REALLOC 100
2991 # define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \
2992 &finalSpans[(y) - finalMiny] : \
2995 static struct finalSpan **
2996 realFindSpan (int y)
2998 struct finalSpan **newSpans;
2999 int newSize, newMiny, newMaxy;
3003 if (y < finalMiny || y > finalMaxy) {
3009 change = finalMiny - y;
3011 change = y - finalMaxy;
3012 if (change >= SPAN_REALLOC)
3013 change += SPAN_REALLOC;
3015 change = SPAN_REALLOC;
3016 newSize = finalSize + change;
3017 newSpans = (struct finalSpan **) g_malloc
3018 (newSize * sizeof (struct finalSpan *));
3020 return (struct finalSpan **)NULL;
3021 newMiny = finalMiny;
3022 newMaxy = finalMaxy;
3024 newMiny = finalMiny - change;
3026 newMaxy = finalMaxy + change;
3028 g_memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *),
3029 (char *) finalSpans,
3030 finalSize * sizeof (struct finalSpan *));
3031 g_free (finalSpans);
3033 if ((i = finalMiny - newMiny) > 0)
3034 memset ((char *)newSpans, 0, i * sizeof (struct finalSpan *));
3035 if ((i = newMaxy - finalMaxy) > 0)
3036 memset ((char *)(newSpans + newSize - i), 0,
3037 i * sizeof (struct finalSpan *));
3038 finalSpans = newSpans;
3039 finalMaxy = newMaxy;
3040 finalMiny = newMiny;
3041 finalSize = newSize;
3043 return &finalSpans[y - finalMiny];
3047 newFinalSpan (int y, register int xmin, register int xmax)
3049 register struct finalSpan *x;
3050 register struct finalSpan **f;
3051 struct finalSpan *oldx;
3052 struct finalSpan *prev;
3060 for (x = *f; x; x=x->next) {
3065 if (x->min <= xmax && xmin <= x->max) {
3067 oldx->min = MIN (x->min, xmin);
3068 oldx->max = MAX (x->max, xmax);
3070 prev->next = x->next;
3075 x->min = MIN (x->min, xmin);
3076 x->max = MAX (x->max, xmax);
3089 x = allocFinalSpan ();
3102 mirrorSppPoint (int quadrant, SppPointPtr sppPoint)
3108 sppPoint->x = -sppPoint->x;
3111 sppPoint->x = -sppPoint->x;
3112 sppPoint->y = -sppPoint->y;
3115 sppPoint->y = -sppPoint->y;
3119 * and translate to X coordinate system
3121 sppPoint->y = -sppPoint->y;
3125 * split an arc into pieces which are scan-converted
3126 * in the first-quadrant and mirrored into position.
3127 * This is necessary as the scan-conversion code can
3128 * only deal with arcs completely contained in the
3133 drawArc (miArc *tarc, int l, int a0, int a1, miArcFacePtr right, miArcFacePtr left)
3134 /* miArcFacePtr right, left; */ /* save end line points */
3137 struct accelerators acc;
3138 int startq, endq, curq;
3139 int rightq, leftq = 0, righta = 0, lefta = 0;
3140 miArcFacePtr passRight, passLeft;
3141 int q0 = 0, q1 = 0, mask;
3145 } band[5], sweep[20];
3146 int bandno, sweepno;
3148 int flipRight = 0, flipLeft = 0;
3150 miArcSpanData *spdata;
3153 spdata = miComputeWideEllipse(l, tarc, &mustFree);
3159 startq = a0 / (90 * 64);
3163 endq = (a1-1) / (90 * 64);
3175 q1 = MIN (a1, 90 * 64);
3178 if (curq == startq && a0 == q0 && rightq < 0) {
3182 if (curq == endq && a1 == q1) {
3191 q0 = 180 * 64 - MIN (a1, 180 * 64);
3195 q1 = 180 * 64 - MAX (a0, 90 * 64);
3196 if (curq == startq && 180 * 64 - a0 == q1) {
3200 if (curq == endq && 180 * 64 - a1 == q0) {
3209 q0 = MAX (a0, 180 * 64) - 180 * 64;
3213 q1 = MIN (a1, 270 * 64) - 180 * 64;
3214 if (curq == startq && a0 - 180*64 == q0) {
3218 if (curq == endq && a1 - 180 * 64 == q1) {
3227 q0 = 360 * 64 - MIN (a1, 360 * 64);
3228 q1 = 360 * 64 - MAX (a0, 270 * 64);
3229 if (curq == startq && 360 * 64 - a0 == q1) {
3233 if (curq == endq && 360 * 64 - a1 == q0) {
3239 band[bandno].a0 = q0;
3240 band[bandno].a1 = q1;
3241 band[bandno].mask = 1 << curq;
3258 * find left-most point
3260 for (i = 0; i < bandno; i++)
3261 if (band[i].a0 <= q0) {
3264 mask = band[i].mask;
3269 * locate next point of change
3271 for (i = 0; i < bandno; i++)
3272 if (!(mask & band[i].mask)) {
3273 if (band[i].a0 == q0) {
3274 if (band[i].a1 < q1)
3276 mask |= band[i].mask;
3277 } else if (band[i].a0 < q1)
3281 * create a new sweep
3283 sweep[sweepno].a0 = q0;
3284 sweep[sweepno].a1 = q1;
3285 sweep[sweepno].mask = mask;
3288 * subtract the sweep from the affected bands
3290 for (i = 0; i < bandno; i++)
3291 if (band[i].a0 == q0) {
3294 * check if this band is empty
3296 if (band[i].a0 == band[i].a1)
3297 band[i].a1 = band[i].a0 = 90 * 64 + 1;
3300 computeAcc (tarc, l, &def, &acc);
3301 for (j = 0; j < sweepno; j++) {
3302 mask = sweep[j].mask;
3303 passRight = passLeft = NULL;
3304 if (mask & (1 << rightq)) {
3305 if (sweep[j].a0 == righta)
3307 else if (sweep[j].a1 == righta) {
3312 if (mask & (1 << leftq)) {
3313 if (sweep[j].a1 == lefta)
3319 else if (sweep[j].a0 == lefta) {
3326 drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask,
3327 passRight, passLeft, spdata);
3330 * when copyEnd is set, both ends of the arc were computed
3331 * at the same time; drawQuadrant only takes one end though,
3332 * so the left end will be the only one holding the data. Copy
3338 * mirror the coordinates generated for the
3342 mirrorSppPoint (rightq, &right->clock);
3343 mirrorSppPoint (rightq, &right->center);
3344 mirrorSppPoint (rightq, &right->counterClock);
3348 temp = right->clock;
3349 right->clock = right->counterClock;
3350 right->counterClock = temp;
3354 mirrorSppPoint (leftq, &left->counterClock);
3355 mirrorSppPoint (leftq, &left->center);
3356 mirrorSppPoint (leftq, &left->clock);
3361 left->clock = left->counterClock;
3362 left->counterClock = temp;
3370 drawQuadrant (struct arc_def *def, struct accelerators *acc,
3371 int a0, int a1, int mask, miArcFacePtr right,
3372 miArcFacePtr left, miArcSpanData *spdata)
3374 struct arc_bound bound;
3380 def->a0 = ((double) a0) / 64.0;
3381 def->a1 = ((double) a1) / 64.0;
3382 computeBound (def, &bound, acc, right, left);
3383 yy = bound.inner.min;
3384 if (bound.outer.min < yy)
3385 yy = bound.outer.min;
3386 miny = ICEIL(yy - acc->fromIntY);
3387 yy = bound.inner.max;
3388 if (bound.outer.max > yy)
3389 yy = bound.outer.max;
3390 maxy = floor(yy - acc->fromIntY);
3392 span = spdata->spans;
3395 if (a1 == 90 * 64 && (mask & 1))
3396 newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1);
3399 for (n = spdata->count1; --n >= 0; )
3405 span->lx, -span->lx, 0, span->lx + span->lw,
3406 def, &bound, acc, mask);
3407 if (span->rw + span->rx)
3408 tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask);
3418 arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc);
3420 for (n = spdata->count2; --n >= 0; )
3425 arcSpan (y, span->lx, span->lw, span->rx, span->rw,
3426 def, &bound, acc, mask);
3430 if (spdata->bot && miny <= y && y <= maxy)
3435 if (span->rw <= 0) {
3436 arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw,
3437 def, &bound, acc, n);
3438 if (span->rw + span->rx)
3439 tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n);
3442 arcSpan0 (span->lx, span->lw, span->rx, span->rw,
3443 def, &bound, acc, n);
3447 yy = y + acc->fromIntY;
3448 if (def->w == def->h) {
3449 xalt = def->w - def->l;
3450 x = -sqrt(xalt * xalt - yy * yy);
3452 x = tailX(yy, def, &bound, acc);
3453 if (acc->left.valid && boundedLe (yy, bound.left)) {
3454 xalt = intersectLine (yy, acc->left);
3458 if (acc->right.valid && boundedLe (yy, bound.right)) {
3459 xalt = intersectLine (yy, acc->right);
3465 ICEIL(acc->fromIntX - x), 0,
3466 ICEIL(acc->fromIntX + x), 0,
3467 def, &bound, acc, mask);