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)
840 spdata = (miArcSpanData *)g_malloc(sizeof(miArcSpanData) +
841 sizeof(miArcSpan) * (k + 2));
842 lruent->spdata = spdata;
845 lruent->lrustamp = 0;
849 spdata->spans = (miArcSpan *)(spdata + 1);
852 spdata->top = !(lw & 1) && !(parc->width & 1);
853 spdata->bot = !(parc->height & 1);
854 lruent->lrustamp = ++lrustamp;
856 lruent->width = parc->width;
857 lruent->height = parc->height;
858 if (lruent != &fakeent)
859 lastCacheHit = lruent;
860 if (parc->width == parc->height)
861 miComputeCircleSpans(lw, parc, spdata);
863 miComputeEllipseSpans(lw, parc, spdata);
868 miFillWideEllipse(GdkDrawable *pDraw, GdkGC *pGC, miArc *parc)
871 register GdkSpan* pts;
872 miArcSpanData *spdata;
874 register miArcSpan *span;
875 register int xorg, yorgu, yorgl;
878 yorgu = parc->height + GDK_GC_FBDATA(pGC)->values.line_width;
879 points = ALLOCATE_LOCAL(sizeof(GdkSpan) * yorgu * 2);
880 spdata = miComputeWideEllipse(GDK_GC_FBDATA(pGC)->values.line_width, parc, &mustFree);
883 DEALLOCATE_LOCAL(points);
887 span = spdata->spans;
888 xorg = parc->x + (parc->width >> 1);
889 yorgu = parc->y + (parc->height >> 1);
890 yorgl = yorgu + (parc->height & 1);
901 for (n = spdata->count1; --n >= 0; )
903 pts[0].x = xorg + span->lx;
905 pts[0].width = span->lw;
920 for (n = spdata->count2; --n >= 0; )
922 pts[0].x = xorg + span->lx;
924 pts[0].width = span->lw;
926 pts[1].x = xorg + span->rx;
928 pts[1].width = span->rw;
932 pts[2].width = pts[0].width;
936 pts[3].width = pts[1].width;
947 pts[0].x = xorg + span->lx;
949 pts[0].width = span->lw;
954 pts[0].x = xorg + span->lx;
956 pts[0].width = span->lw;
957 pts[1].x = xorg + span->rx;
959 pts[1].width = span->rw;
966 gdk_fb_fill_spans(pDraw, pGC, points, pts - points, FALSE);
968 DEALLOCATE_LOCAL(points);
972 * miPolyArc strategy:
974 * If arc is zero width and solid, we don't have to worry about the rasterop
975 * or join styles. For wide solid circles, we use a fast integer algorithm.
976 * For wide solid ellipses, we use special case floating point code.
977 * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then
978 * draw using pGCTo and pDrawTo. If the raster-op was "tricky," that is,
979 * if it involves the destination, then we use PushPixels to move the bits
980 * from the scratch drawable to pDraw. (See the wide line code for a
981 * fuller explanation of this.)
985 miPolyArc(GdkDrawable *pDraw, GdkGC *pGC, int narcs, miArc *parcs)
989 int xMin, xMax, yMin, yMax;
990 int pixmapWidth = 0, pixmapHeight = 0;
991 int xOrg = 0, yOrg = 0;
994 GdkDrawable* pDrawTo;
997 miPolyArcPtr polyArcs;
1003 width = GDK_GC_FBDATA(pGC)->values.line_width;
1004 if(width == 0 && GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID)
1006 for(i = narcs, parc = parcs; --i >= 0; parc++)
1007 miArcSegment( pDraw, pGC, *parc,
1008 (miArcFacePtr) 0, (miArcFacePtr) 0 );
1009 fillSpans (pDraw, pGC);
1013 if ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID) && narcs)
1015 while (parcs->width && parcs->height &&
1016 (parcs->angle2 >= FULLCIRCLE ||
1017 parcs->angle2 <= -FULLCIRCLE))
1019 miFillWideEllipse(pDraw, pGC, parcs);
1026 /* Set up pDrawTo and pGCTo based on the rasterop */
1027 switch(GDK_GC_FBDATA(pGC)->alu)
1029 case GDK_CLEAR: /* 0 */
1030 case GDK_COPY: /* src */
1031 case GDK_COPY_INVERT: /* NOT src */
1032 case GDK_SET: /* 1 */
1040 /* find bounding box around arcs */
1041 xMin = yMin = SHRT_MAX;
1042 xMax = yMax = SHRT_MIN;
1044 for(i = narcs, parc = parcs; --i >= 0; parc++)
1046 xMin = MIN (xMin, parc->x);
1047 yMin = MIN (yMin, parc->y);
1048 xMax = MAX (xMax, (parc->x + (int) parc->width));
1049 yMax = MAX (yMax, (parc->y + (int) parc->height));
1052 /* expand box to deal with line widths */
1053 halfWidth = (width + 1)/2;
1059 /* compute pixmap size; limit it to size of drawable */
1060 xOrg = MAX(xMin, 0);
1061 yOrg = MAX(yMin, 0);
1062 pixmapWidth = MIN(xMax, GDK_DRAWABLE_FBDATA(pDraw)->width) - xOrg;
1063 pixmapHeight = MIN(yMax, GDK_DRAWABLE_FBDATA(pDraw)->height) - yOrg;
1065 /* if nothing left, return */
1066 if ( (pixmapWidth <= 0) || (pixmapHeight <= 0) ) return;
1068 for(i = narcs, parc = parcs; --i >= 0; parc++)
1074 /* set up scratch GC */
1075 /* allocate a 1 bit deep pixmap of the appropriate size, and
1077 pDrawTo = gdk_pixmap_new(NULL, pixmapWidth, pixmapHeight, 1);
1081 pGCTo = gdk_gc_new(pDrawTo);
1084 gdk_pixmap_unref(pDrawTo);
1087 gdk_gc_set_function(pGCTo, GDK_COPY);
1088 memset(&gcv.background, 0, sizeof(GdkColor));
1089 gcv.foreground.pixel = 1;
1090 gcv.foreground.red = gcv.foreground.green = gcv.foreground.blue = 1;
1091 gdk_gc_set_foreground(pGCTo, &gcv.foreground);
1092 gdk_gc_set_background(pGCTo, &gcv.background);
1093 gdk_gc_set_line_attributes(pGCTo,
1094 GDK_GC_FBDATA(pGC)->values.line_width,
1095 GDK_GC_FBDATA(pGC)->values.line_style,
1096 GDK_GC_FBDATA(pGC)->values.cap_style,
1097 GDK_GC_FBDATA(pGC)->values.join_style);
1098 gdk_fb_drawable_clear(pDrawTo);
1101 fg = GDK_GC_FBDATA(pGC)->values.foreground;
1102 bg = GDK_GC_FBDATA(pGC)->values.background;
1103 if ((GDK_GC_FBDATA(pGC)->values.fill == GDK_TILED) ||
1104 (GDK_GC_FBDATA(pGC)->values.fill == GDK_OPAQUE_STIPPLED))
1105 bg = fg; /* the protocol sez these don't cause color changes */
1107 polyArcs = miComputeArcs (parcs, narcs, pGC);
1112 gdk_pixmap_unref(pDrawTo);
1113 gdk_gc_unref(pGCTo);
1118 cap[0] = cap[1] = 0;
1119 join[0] = join[1] = 0;
1120 for (iphase = ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) ? 1 : 0);
1125 gdk_gc_set_foreground(pGC, &bg);
1126 else if (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH)
1127 gdk_gc_set_foreground(pGC, &fg);
1128 for (i = 0; i < polyArcs[iphase].narcs; i++) {
1129 miArcDataPtr arcData;
1131 arcData = &polyArcs[iphase].arcs[i];
1132 miArcSegment(pDrawTo, pGCTo, arcData->arc,
1133 &arcData->bounds[RIGHT_END],
1134 &arcData->bounds[LEFT_END]);
1135 if (polyArcs[iphase].arcs[i].render) {
1136 fillSpans (pDrawTo, pGCTo);
1138 * don't cap self-joining arcs
1140 if (polyArcs[iphase].arcs[i].selfJoin &&
1141 cap[iphase] < polyArcs[iphase].arcs[i].cap)
1143 while (cap[iphase] < polyArcs[iphase].arcs[i].cap) {
1145 miArcDataPtr arcData0;
1147 arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex;
1148 end = polyArcs[iphase].caps[cap[iphase]].end;
1149 arcData0 = &polyArcs[iphase].arcs[arcIndex];
1150 miArcCap (pDrawTo, pGCTo,
1151 &arcData0->bounds[end], end,
1152 arcData0->arc.x, arcData0->arc.y,
1153 (double) arcData0->arc.width / 2.0,
1154 (double) arcData0->arc.height / 2.0);
1157 while (join[iphase] < polyArcs[iphase].arcs[i].join) {
1158 int arcIndex0, arcIndex1, end0, end1;
1160 miArcDataPtr arcData0, arcData1;
1163 joinp = &polyArcs[iphase].joins[join[iphase]];
1164 arcIndex0 = joinp->arcIndex0;
1166 arcIndex1 = joinp->arcIndex1;
1168 phase0 = joinp->phase0;
1169 phase1 = joinp->phase1;
1170 arcData0 = &polyArcs[phase0].arcs[arcIndex0];
1171 arcData1 = &polyArcs[phase1].arcs[arcIndex1];
1172 miArcJoin (pDrawTo, pGCTo,
1173 &arcData0->bounds[end0],
1174 &arcData1->bounds[end1],
1175 arcData0->arc.x, arcData0->arc.y,
1176 (double) arcData0->arc.width / 2.0,
1177 (double) arcData0->arc.height / 2.0,
1178 arcData1->arc.x, arcData1->arc.y,
1179 (double) arcData1->arc.width / 2.0,
1180 (double) arcData1->arc.height / 2.0);
1184 gdk_fb_draw_drawable(pDraw, pGC, pDrawTo, 0, 0, xOrg, yOrg, pixmapWidth, pixmapHeight);
1185 gdk_fb_drawable_clear(pDrawTo);
1190 miFreeArcs(polyArcs, pGC);
1194 gdk_pixmap_unref(pDrawTo);
1195 gdk_gc_unref(pGCTo);
1201 angleBetween (center, point1, point2)
1202 SppPointRec center, point1, point2;
1207 * reflect from X coordinates back to ellipse
1208 * coordinates -- y increasing upwards
1210 a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x);
1211 a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x);
1221 translateBounds (b, x, y, fx, fy)
1232 b->counterClock.x -= fx;
1233 b->counterClock.y -= fy;
1237 miArcJoin (GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pLeft, miArcFacePtr pRight,
1238 int xOrgLeft, int yOrgLeft, double xFtransLeft, double yFtransLeft,
1239 int xOrgRight, int yOrgRight, double xFtransRight, double yFtransRight)
1241 SppPointRec center, corner, otherCorner;
1242 SppPointRec poly[5], e;
1243 SppPointPtr pArcPts;
1246 miArcFaceRec Right, Left;
1249 double xFtrans, yFtrans;
1251 double ae, ac2, ec2, bc2, de;
1254 xOrg = (xOrgRight + xOrgLeft) / 2;
1255 yOrg = (yOrgRight + yOrgLeft) / 2;
1256 xFtrans = (xFtransLeft + xFtransRight) / 2;
1257 yFtrans = (yFtransLeft + yFtransRight) / 2;
1259 translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight,
1260 xFtrans - xFtransRight, yFtrans - yFtransRight);
1262 translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft,
1263 xFtrans - xFtransLeft, yFtrans - yFtransLeft);
1267 if (pRight->clock.x == pLeft->counterClock.x &&
1268 pRight->clock.y == pLeft->counterClock.y)
1270 center = pRight->center;
1271 if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock))
1274 corner = pRight->clock;
1275 otherCorner = pLeft->counterClock;
1277 a = angleBetween (center, pLeft->clock, pRight->counterClock);
1278 corner = pLeft->clock;
1279 otherCorner = pRight->counterClock;
1281 switch (GDK_GC_FBDATA(pGC)->values.join_style) {
1282 case GDK_JOIN_ROUND:
1283 width = (GDK_GC_FBDATA(pGC)->values.line_width ? (double)GDK_GC_FBDATA(pGC)->values.line_width : (double)1);
1285 arc.x = center.x - width/2;
1286 arc.y = center.y - width/2;
1289 arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x);
1291 pArcPts = (SppPointPtr) g_malloc (3 * sizeof (SppPointRec));
1294 pArcPts[0].x = otherCorner.x;
1295 pArcPts[0].y = otherCorner.y;
1296 pArcPts[1].x = center.x;
1297 pArcPts[1].y = center.y;
1298 pArcPts[2].x = corner.x;
1299 pArcPts[2].y = corner.y;
1300 if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) )
1302 /* by drawing with miFillSppPoly and setting the endpoints of the arc
1303 * to be the corners, we assure that the cap will meet up with the
1304 * rest of the line */
1305 miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans);
1309 case GDK_JOIN_MITER:
1311 * don't miter arcs with less than 11 degrees between them
1316 poly[2] = otherCorner;
1317 bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) +
1318 (corner.y - otherCorner.y) * (corner.y - otherCorner.y);
1320 ac2 = (corner.x - center.x) * (corner.x - center.x) +
1321 (corner.y - center.y) * (corner.y - center.y);
1322 ae = sqrt (ac2 - ec2);
1324 e.x = (corner.x + otherCorner.x) / 2;
1325 e.y = (corner.y + otherCorner.y) / 2;
1326 poly[3].x = e.x + de * (e.x - center.x) / ae;
1327 poly[3].y = e.y + de * (e.y - center.y) / ae;
1332 case GDK_JOIN_BEVEL:
1335 poly[2] = otherCorner;
1340 miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans);
1345 miArcCap (pDraw, pGC, pFace, end, xOrg, yOrg, xFtrans, yFtrans)
1351 double xFtrans, yFtrans;
1353 SppPointRec corner, otherCorner, center, endPoint, poly[5];
1355 corner = pFace->clock;
1356 otherCorner = pFace->counterClock;
1357 center = pFace->center;
1358 switch (GDK_GC_FBDATA(pGC)->values.cap_style) {
1359 case GDK_CAP_PROJECTING:
1360 poly[0].x = otherCorner.x;
1361 poly[0].y = otherCorner.y;
1362 poly[1].x = corner.x;
1363 poly[1].y = corner.y;
1364 poly[2].x = corner.x -
1365 (center.y - corner.y);
1366 poly[2].y = corner.y +
1367 (center.x - corner.x);
1368 poly[3].x = otherCorner.x -
1369 (otherCorner.y - center.y);
1370 poly[3].y = otherCorner.y +
1371 (otherCorner.x - center.x);
1372 poly[4].x = otherCorner.x;
1373 poly[4].y = otherCorner.y;
1374 miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans);
1378 * miRoundCap just needs these to be unequal.
1381 endPoint.x = endPoint.x + 100;
1382 miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0,
1383 -xOrg, -yOrg, xFtrans, yFtrans);
1390 /* MIROUNDCAP -- a private helper function
1391 * Put Rounded cap on end. pCenter is the center of this end of the line
1392 * pEnd is the center of the other end of the line. pCorner is one of the
1393 * two corners at this end of the line.
1394 * NOTE: pOtherCorner must be counter-clockwise from pCorner.
1397 static void miRoundCap(GdkDrawable *pDraw, GdkGC *pGC, SppPointRec pCenter, SppPointRec pEnd, SppPointRec pCorner,
1398 SppPointRec pOtherCorner, int fLineEnd, int xOrg, int yOrg,
1399 double xFtrans, double yFtrans)
1405 SppPointPtr pArcPts;
1407 width = (GDK_GC_FBDATA(pGC)->values.line_width ? (double)GDK_GC_FBDATA(pGC)->values.line_width : (double)1);
1409 arc.x = pCenter.x - width/2;
1410 arc.y = pCenter.y - width/2;
1413 arc.angle1 = -miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x);
1414 if(PTISEQUAL(pCenter, pEnd))
1415 arc.angle2 = - 180.0;
1417 arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1;
1419 arc.angle2 += 360.0;
1421 pArcPts = (SppPointPtr) NULL;
1422 if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) )
1424 /* by drawing with miFillSppPoly and setting the endpoints of the arc
1425 * to be the corners, we assure that the cap will meet up with the
1426 * rest of the line */
1427 miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans);
1433 * To avoid inaccuracy at the cardinal points, use trig functions
1434 * which are exact for those angles
1438 #define M_PI 3.14159265358979323846
1441 #define M_PI_2 1.57079632679489661923
1444 # define Dsin(d) ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
1445 # define Dcos(d) ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
1446 # define mod(a,b) ((a) >= 0 ? (a) % (b) : (b) - (-a) % (b))
1454 if (floor (a/90) == a/90) {
1456 switch (mod (i, 4)) {
1463 return cos (a * M_PI / 180.0);
1472 if (floor (a/90) == a/90) {
1474 switch (mod (i, 4)) {
1481 return sin (a * M_PI / 180.0);
1494 return asin(v) * (180.0 / M_PI);
1505 } else if (dx == 0) {
1509 } else if (fabs (dy) == fabs (dx)) {
1520 return atan2 (dy, dx) * (180.0 / M_PI);
1524 /* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper
1525 * routine for filled arc and line (round cap) code.
1526 * Returns the number of points in the arc. Note that it takes a pointer
1527 * to a pointer to where it should put the points and an index (cpt).
1528 * This procedure allocates the space necessary to fit the arc points.
1529 * Sometimes it's convenient for those points to be at the end of an existing
1530 * array. (For example, if we want to leave a spare point to make sectors
1531 * instead of segments.) So we pass in the g_malloc()ed chunk that contains the
1532 * array and an index saying where we should start stashing the points.
1533 * If there isn't an array already, we just pass in a null pointer and
1534 * count on g_realloc() to handle the null pointer correctly.
1537 miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts)
1539 SppArcPtr parc; /* points to an arc */
1540 int cpt; /* number of points already in arc list */
1541 SppPointPtr *ppPts; /* pointer to pointer to arc-list -- modified */
1544 double st, /* Start Theta, start angle */
1545 et, /* End Theta, offset from start theta */
1546 dt, /* Delta Theta, angle to sweep ellipse */
1547 cdt, /* Cos Delta Theta, actually 2 cos(dt) */
1548 x0, y0, /* the recurrence formula needs two points to start */
1550 x2, y2, /* this will be the new point generated */
1551 xc, yc; /* the center point */
1554 GdkPoint last; /* last point on integer boundaries */
1556 /* The spec says that positive angles indicate counterclockwise motion.
1557 * Given our coordinate system (with 0,0 in the upper left corner),
1558 * the screen appears flipped in Y. The easiest fix is to negate the
1561 st = - parc->angle1;
1563 et = - parc->angle2;
1565 /* Try to get a delta theta that is within 1/2 pixel. Then adjust it
1566 * so that it divides evenly into the total.
1567 * I'm just using cdt 'cause I'm lazy.
1570 if (parc->height > cdt)
1577 dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */
1579 count = abs(count) + 1;
1583 cdt = 2 * miDcos(dt);
1584 if (!(poly = (SppPointPtr) g_realloc((gpointer)*ppPts,
1585 (cpt + count) * sizeof(SppPointRec))))
1589 xc = parc->width/2.0; /* store half width and half height */
1590 yc = parc->height/2.0;
1592 x0 = xc * miDcos(st);
1593 y0 = yc * miDsin(st);
1594 x1 = xc * miDcos(st + dt);
1595 y1 = yc * miDsin(st + dt);
1596 xc += parc->x; /* by adding initial point, these become */
1597 yc += parc->y; /* the center point */
1599 poly[cpt].x = (xc + x0);
1600 poly[cpt].y = (yc + y0);
1601 last.x = ROUNDTOINT( poly[cpt + 1].x = (xc + x1) );
1602 last.y = ROUNDTOINT( poly[cpt + 1].y = (yc + y1) );
1604 for(i = 2; i < count; i++)
1609 poly[cpt + i].x = (xc + x2);
1610 poly[cpt + i].y = (yc + y2);
1615 /* adjust the last point */
1616 if (fabs(parc->angle2) >= 360.0)
1617 poly[cpt +i -1] = poly[0];
1619 poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc);
1620 poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc);
1627 double x0, y0, x1, y1;
1631 # define ADD_REALLOC_STEP 20
1634 addCap (capsp, ncapsp, sizep, end, arcIndex)
1636 int *ncapsp, *sizep;
1642 if (*ncapsp == *sizep)
1644 newsize = *sizep + ADD_REALLOC_STEP;
1645 cap = (miArcCapPtr) g_realloc (*capsp,
1646 newsize * sizeof (**capsp));
1652 cap = &(*capsp)[*ncapsp];
1654 cap->arcIndex = arcIndex;
1659 addJoin (joinsp, njoinsp, sizep, end0, index0, phase0, end1, index1, phase1)
1660 miArcJoinPtr *joinsp;
1661 int *njoinsp, *sizep;
1662 int end0, index0, phase0, end1, index1, phase1;
1667 if (*njoinsp == *sizep)
1669 newsize = *sizep + ADD_REALLOC_STEP;
1670 join = (miArcJoinPtr) g_realloc (*joinsp,
1671 newsize * sizeof (**joinsp));
1677 join = &(*joinsp)[*njoinsp];
1679 join->arcIndex0 = index0;
1680 join->phase0 = phase0;
1682 join->arcIndex1 = index1;
1683 join->phase1 = phase1;
1688 addArc (arcsp, narcsp, sizep, xarc)
1689 miArcDataPtr *arcsp;
1690 int *narcsp, *sizep;
1696 if (*narcsp == *sizep)
1698 newsize = *sizep + ADD_REALLOC_STEP;
1699 arc = (miArcDataPtr) g_realloc (*arcsp,
1700 newsize * sizeof (**arcsp));
1702 return (miArcDataPtr)NULL;
1706 arc = &(*arcsp)[*narcsp];
1713 miFreeArcs(miPolyArcPtr arcs, GdkGC *pGC)
1717 for (iphase = ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) ? 1 : 0);
1721 if (arcs[iphase].narcs > 0)
1722 g_free(arcs[iphase].arcs);
1723 if (arcs[iphase].njoins > 0)
1724 g_free(arcs[iphase].joins);
1725 if (arcs[iphase].ncaps > 0)
1726 g_free(arcs[iphase].caps);
1732 * map angles to radial distance. This only deals with the first quadrant
1736 * a polygonal approximation to the arc for computing arc lengths
1739 # define dashIndexToAngle(di) ((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1))
1740 # define xAngleToDashIndex(xa) ((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64))
1741 # define dashIndexToXAngle(di) ((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1))
1742 # define dashXAngleStep (((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1)))
1745 computeDashMap (arcp, map)
1750 double a, x, y, prevx = 0.0, prevy = 0.0, dist;
1752 for (di = 0; di < DASH_MAP_SIZE; di++) {
1753 a = dashIndexToAngle (di);
1754 x = ((double) arcp->width / 2.0) * miDcos (a);
1755 y = ((double) arcp->height / 2.0) * miDsin (a);
1759 dist = hypot (x - prevx, y - prevy);
1760 map->map[di] = map->map[di - 1] + dist;
1767 typedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes;
1769 /* this routine is a bit gory */
1772 miComputeArcs (miArc *parcs, int narcs, GdkGC *pGC)
1774 int isDashed, isDoubleDash;
1777 int start, i, j, k = 0, nexti, nextk = 0;
1783 struct arcData *data;
1786 int iphase, prevphase = 0, joinphase;
1790 int iDash = 0, dashRemaining;
1791 int iDashStart = 0, dashRemainingStart = 0, iphaseStart;
1792 int startAngle, spanAngle, endAngle, backwards = 0;
1793 int prevDashAngle, dashAngle;
1796 isDashed = !(GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID);
1797 isDoubleDash = (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH);
1798 dashOffset = GDK_GC_FBDATA(pGC)->dash_offset;
1800 data = (struct arcData *) ALLOCATE_LOCAL (narcs * sizeof (struct arcData));
1802 return (miPolyArcPtr)NULL;
1803 arcs = (miPolyArcPtr) g_malloc (sizeof (*arcs) * (isDoubleDash ? 2 : 1));
1806 DEALLOCATE_LOCAL(data);
1807 return (miPolyArcPtr)NULL;
1809 for (i = 0; i < narcs; i++) {
1810 a0 = todeg (parcs[i].angle1);
1811 angle2 = parcs[i].angle2;
1812 if (angle2 > FULLCIRCLE)
1813 angle2 = FULLCIRCLE;
1814 else if (angle2 < -FULLCIRCLE)
1815 angle2 = -FULLCIRCLE;
1816 data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE;
1817 a1 = todeg (parcs[i].angle1 + angle2);
1818 data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0));
1819 data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0));
1820 data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1));
1821 data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1));
1824 for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) {
1825 arcs[iphase].njoins = 0;
1826 arcs[iphase].joins = 0;
1827 joinSize[iphase] = 0;
1829 arcs[iphase].ncaps = 0;
1830 arcs[iphase].caps = 0;
1831 capSize[iphase] = 0;
1833 arcs[iphase].narcs = 0;
1834 arcs[iphase].arcs = 0;
1835 arcSize[iphase] = 0;
1841 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[0];
1842 while (dashOffset > 0) {
1843 if (dashOffset >= dashRemaining) {
1844 dashOffset -= dashRemaining;
1845 iphase = iphase ? 0 : 1;
1847 if (iDash == GDK_GC_FBDATA(pGC)->dash_list_len)
1849 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[iDash];
1851 dashRemaining -= dashOffset;
1856 dashRemainingStart = dashRemaining;
1858 iphaseStart = iphase;
1860 for (i = narcs - 1; i >= 0; i--) {
1864 if (data[i].selfJoin || i == j ||
1865 (UNEQUAL (data[i].x1, data[j].x0) ||
1866 UNEQUAL (data[i].y1, data[j].y0)))
1868 if (iphase == 0 || isDoubleDash)
1869 addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
1870 &capSize[iphase], RIGHT_END, 0);
1887 ** deal with dashed arcs. Use special rules for certain 0 area arcs.
1888 ** Presumably, the other 0 area arcs still aren't done right.
1890 arcTypes arcType = OTHER;
1893 if (parcs[i].height == 0
1894 && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00
1895 && parcs[i].angle2 == 0x2d00)
1896 arcType = HORIZONTAL;
1897 else if (parcs[i].width == 0
1898 && (parcs[i].angle1 % FULLCIRCLE) == 0x1680
1899 && parcs[i].angle2 == 0x2d00)
1901 if (arcType == OTHER) {
1903 * precompute an approximation map
1905 computeDashMap (&parcs[i], &map);
1907 * compute each individual dash segment using the path
1910 startAngle = parcs[i].angle1;
1911 spanAngle = parcs[i].angle2;
1912 if (spanAngle > FULLCIRCLE)
1913 spanAngle = FULLCIRCLE;
1914 else if (spanAngle < -FULLCIRCLE)
1915 spanAngle = -FULLCIRCLE;
1917 startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
1918 if (startAngle >= FULLCIRCLE)
1919 startAngle = startAngle % FULLCIRCLE;
1920 endAngle = startAngle + spanAngle;
1921 backwards = spanAngle < 0;
1924 if (arcType == VERTICAL) {
1925 xarc.angle1 = 0x1680;
1926 startAngle = parcs[i].y;
1927 endAngle = startAngle + parcs[i].height;
1929 xarc.angle1 = 0x2d00;
1930 startAngle = parcs[i].x;
1931 endAngle = startAngle + parcs[i].width;
1934 dashAngle = startAngle;
1935 selfJoin = data[i].selfJoin &&
1936 (iphase == 0 || isDoubleDash);
1938 * add dashed arcs to each bucket
1941 while (dashAngle != endAngle) {
1942 prevDashAngle = dashAngle;
1943 if (arcType == OTHER) {
1944 dashAngle = computeAngleFromPath (prevDashAngle, endAngle,
1945 &map, &dashRemaining, backwards);
1946 /* avoid troubles with huge arcs and small dashes */
1947 if (dashAngle == prevDashAngle) {
1954 thisLength = (dashAngle + dashRemaining <= endAngle) ?
1955 dashRemaining : endAngle - dashAngle;
1956 if (arcType == VERTICAL) {
1958 xarc.height = thisLength;
1961 xarc.width = thisLength;
1963 dashAngle += thisLength;
1964 dashRemaining -= thisLength;
1966 if (iphase == 0 || isDoubleDash) {
1967 if (arcType == OTHER) {
1969 spanAngle = prevDashAngle;
1971 spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE;
1972 if (spanAngle >= FULLCIRCLE)
1973 spanAngle = spanAngle % FULLCIRCLE;
1974 xarc.angle1 = spanAngle;
1975 spanAngle = dashAngle - prevDashAngle;
1977 if (dashAngle > prevDashAngle)
1978 spanAngle = - FULLCIRCLE + spanAngle;
1980 if (dashAngle < prevDashAngle)
1981 spanAngle = FULLCIRCLE + spanAngle;
1983 if (spanAngle > FULLCIRCLE)
1984 spanAngle = FULLCIRCLE;
1985 if (spanAngle < -FULLCIRCLE)
1986 spanAngle = -FULLCIRCLE;
1987 xarc.angle2 = spanAngle;
1989 arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
1990 &arcSize[iphase], &xarc);
1994 * cap each end of an on/off dash
1996 if (!isDoubleDash) {
1997 if (prevDashAngle != startAngle) {
1998 addCap (&arcs[iphase].caps,
1999 &arcs[iphase].ncaps,
2000 &capSize[iphase], RIGHT_END,
2001 arc - arcs[iphase].arcs);
2004 if (dashAngle != endAngle) {
2005 addCap (&arcs[iphase].caps,
2006 &arcs[iphase].ncaps,
2007 &capSize[iphase], LEFT_END,
2008 arc - arcs[iphase].arcs);
2011 arc->cap = arcs[iphase].ncaps;
2012 arc->join = arcs[iphase].njoins;
2015 if (dashAngle == endAngle)
2016 arc->selfJoin = selfJoin;
2019 if (dashRemaining <= 0) {
2021 if (iDash == GDK_GC_FBDATA(pGC)->dash_list_len)
2023 iphase = iphase ? 0:1;
2024 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[iDash];
2028 * make sure a place exists for the position data when
2029 * drawing a zero-length arc
2031 if (startAngle == endAngle) {
2033 if (!isDoubleDash && iphase == 1)
2035 arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs,
2036 &arcSize[prevphase], &parcs[i]);
2039 arc->join = arcs[prevphase].njoins;
2040 arc->cap = arcs[prevphase].ncaps;
2041 arc->selfJoin = data[i].selfJoin;
2044 arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
2045 &arcSize[iphase], &parcs[i]);
2048 arc->join = arcs[iphase].njoins;
2049 arc->cap = arcs[iphase].ncaps;
2050 arc->selfJoin = data[i].selfJoin;
2053 if (prevphase == 0 || isDoubleDash)
2054 k = arcs[prevphase].narcs - 1;
2055 if (iphase == 0 || isDoubleDash)
2056 nextk = arcs[iphase].narcs;
2057 if (nexti == start) {
2061 iphase = iphaseStart;
2062 dashRemaining = dashRemainingStart;
2065 arcsJoin = narcs > 1 && i != j &&
2066 ISEQUAL (data[i].x1, data[j].x0) &&
2067 ISEQUAL (data[i].y1, data[j].y0) &&
2068 !data[i].selfJoin && !data[j].selfJoin;
2077 (prevphase == 0 || isDoubleDash) &&
2078 (iphase == 0 || isDoubleDash))
2083 joinphase = iphaseStart;
2085 * if the join is right at the dash,
2086 * draw the join in foreground
2087 * This is because the foreground
2088 * arcs are computed second, the results
2089 * of which are needed to draw the join
2091 if (joinphase != prevphase)
2094 if (joinphase == 0 || isDoubleDash) {
2095 addJoin (&arcs[joinphase].joins,
2096 &arcs[joinphase].njoins,
2097 &joinSize[joinphase],
2098 LEFT_END, k, prevphase,
2099 RIGHT_END, nextk, iphase);
2100 arc->join = arcs[prevphase].njoins;
2104 * cap the left end of this arc
2105 * unless it joins itself
2107 if ((prevphase == 0 || isDoubleDash) &&
2110 addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps,
2111 &capSize[prevphase], LEFT_END, k);
2112 arc->cap = arcs[prevphase].ncaps;
2114 if (isDashed && !arcsJoin) {
2116 iphase = iphaseStart;
2117 dashRemaining = dashRemainingStart;
2119 nextk = arcs[iphase].narcs;
2120 if (nexti == start) {
2123 iphase = iphaseStart;
2124 dashRemaining = dashRemainingStart;
2127 * cap the right end of the next arc. If the
2128 * next arc is actually the first arc, only
2129 * cap it if it joins with this arc. This
2130 * case will occur when the final dash segment
2131 * of an on/off dash is off. Of course, this
2132 * cap will be drawn at a strange time, but that
2135 if ((iphase == 0 || isDoubleDash) &&
2136 (nexti != start || (arcsJoin && isDashed)))
2137 addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
2138 &capSize[iphase], RIGHT_END, nextk);
2145 * make sure the last section is rendered
2147 for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++)
2148 if (arcs[iphase].narcs > 0) {
2149 arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1;
2150 arcs[iphase].arcs[arcs[iphase].narcs-1].join =
2151 arcs[iphase].njoins;
2152 arcs[iphase].arcs[arcs[iphase].narcs-1].cap =
2155 DEALLOCATE_LOCAL(data);
2158 miFreeArcs(arcs, pGC);
2159 DEALLOCATE_LOCAL(data);
2160 return (miPolyArcPtr)NULL;
2164 angleToLength (angle, map)
2168 double len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen;
2171 gboolean oddSide = FALSE;
2175 while (angle >= 90 * 64) {
2177 totallen += sidelen;
2183 totallen -= sidelen;
2188 angle = 90 * 64 - angle;
2190 di = xAngleToDashIndex (angle);
2191 excess = angle - dashIndexToXAngle (di);
2195 * linearly interpolate between this point and the next
2198 excesslen = (map->map[di + 1] - map->map[di]) *
2199 ((double) excess) / dashXAngleStep;
2203 totallen += (sidelen - len);
2210 * len is along the arc, but may be more than one rotation
2214 lengthToAngle (len, map)
2218 double sidelen = map->map[DASH_MAP_SIZE - 1];
2219 int angle, angleexcess;
2220 gboolean oddSide = FALSE;
2225 * step around the ellipse, subtracting sidelens and
2226 * adding 90 degrees. oddSide will tell if the
2227 * map should be interpolated in reverse
2231 return 2 * FULLCIRCLE; /* infinity */
2232 while (len >= sidelen) {
2239 return -2 * FULLCIRCLE; /* infinity */
2247 len = sidelen - len;
2249 a1 = DASH_MAP_SIZE - 1;
2251 * binary search for the closest pre-computed length
2253 while (a1 - a0 > 1) {
2255 if (len > map->map[a])
2260 angleexcess = dashIndexToXAngle (a0);
2262 * linearly interpolate to the next point
2264 angleexcess += (len - map->map[a0]) /
2265 (map->map[a0+1] - map->map[a0]) * dashXAngleStep;
2267 angle += (90 * 64) - angleexcess;
2269 angle += angleexcess;
2274 * compute the angle of an ellipse which cooresponds to
2275 * the given path length. Note that the correct solution
2276 * to this problem is an eliptic integral, we'll punt and
2277 * approximate (it's only for dashes anyway). This
2278 * approximation uses a polygon.
2280 * The remaining portion of len is stored in *lenp -
2281 * this will be negative if the arc extends beyond
2282 * len and positive if len extends beyond the arc.
2285 static int computeAngleFromPath(int startAngle, int endAngle, dashMap *map, int *lenp, int backwards)
2286 /* int startAngle, endAngle; *//* normalized absolute angles in *64 degrees */
2297 * flip the problem around to always be
2300 a0 = FULLCIRCLE - a0;
2301 a1 = FULLCIRCLE - a1;
2305 len0 = angleToLength (a0, map);
2306 a = lengthToAngle (len0 + len, map);
2309 len -= angleToLength (a1, map) - len0;
2319 * scan convert wide arcs.
2323 * draw zero width/height arcs
2327 drawZeroArc (pDraw, pGC, tarc, lw, left, right)
2332 miArcFacePtr right, left;
2334 double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0, w, h, x, y;
2335 double xmax, ymax, xmin, ymin;
2337 double a, startAngle, endAngle;
2343 if (a1 > FULLCIRCLE)
2345 else if (a1 < -FULLCIRCLE)
2347 w = (double)tarc->width / 2.0;
2348 h = (double)tarc->height / 2.0;
2350 * play in X coordinates right away
2352 startAngle = - ((double) a0 / 64.0);
2353 endAngle = - ((double) (a0 + a1) / 64.0);
2364 if (a == startAngle)
2384 if (a1 < 0) /* clockwise */
2386 if (floor (a / 90.0) == floor (endAngle / 90.0))
2389 a = 90 * (floor (a/90.0) + 1);
2393 if (ceil (a / 90.0) == ceil (endAngle / 90.0))
2396 a = 90 * (ceil (a/90.0) - 1);
2400 if ((x1 - x0) + (y1 - y0) < 0)
2411 right->center.x = x0;
2412 right->center.y = y0;
2413 right->clock.x = x0 - lx;
2414 right->clock.y = y0 - ly;
2415 right->counterClock.x = x0 + lx;
2416 right->counterClock.y = y0 + ly;
2420 left->center.x = x1;
2421 left->center.y = y1;
2422 left->clock.x = x1 + lx;
2423 left->clock.y = y1 + ly;
2424 left->counterClock.x = x1 - lx;
2425 left->counterClock.y = y1 - ly;
2439 if (xmax != xmin && ymax != ymin) {
2440 int minx, maxx, miny, maxy;
2442 minx = ICEIL (xmin + w) + tarc->x;
2443 maxx = ICEIL (xmax + w) + tarc->x;
2444 miny = ICEIL (ymin + h) + tarc->y;
2445 maxy = ICEIL (ymax + h) + tarc->y;
2447 gdk_fb_draw_rectangle(pDraw, pGC, TRUE, minx, miny, maxx - minx, maxy - miny);
2452 * this computes the ellipse y value associated with the
2453 * bottom of the tail.
2457 tailEllipseY (def, acc)
2458 struct arc_def *def;
2459 struct accelerators *acc;
2464 if (def->w == def->h)
2466 t = def->l * def->w;
2467 if (def->w > def->h) {
2474 t = 2.0 * def->h * t;
2475 t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2;
2477 acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t);
2481 * inverse functions -- compute edge coordinates
2486 outerXfromXY (x, y, def, acc)
2488 struct arc_def *def;
2489 struct accelerators *acc;
2491 return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2495 outerYfromXY (x, y, def, acc)
2497 struct arc_def *def;
2498 struct accelerators *acc;
2500 return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2504 innerXfromXY (x, y, def, acc)
2506 struct arc_def *def;
2507 struct accelerators *acc;
2509 return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2513 innerYfromXY (x, y, def, acc)
2515 struct arc_def *def;
2516 struct accelerators *acc;
2518 return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2522 innerYfromY (y, def, acc)
2524 struct arc_def *def;
2525 struct accelerators *acc;
2529 x = (def->w / def->h) * sqrt (acc->h2 - y*y);
2531 return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2535 computeLine (x1, y1, x2, y2, line)
2536 double x1, y1, x2, y2;
2542 line->m = (x1 - x2) / (y1 - y2);
2543 line->b = x1 - y1 * line->m;
2549 * compute various accelerators for an ellipse. These
2550 * are simply values that are used repeatedly in
2555 computeAcc (tarc, lw, def, acc)
2558 struct arc_def *def;
2559 struct accelerators *acc;
2561 def->w = ((double) tarc->width) / 2.0;
2562 def->h = ((double) tarc->height) / 2.0;
2563 def->l = ((double) lw) / 2.0;
2564 acc->h2 = def->h * def->h;
2565 acc->w2 = def->w * def->w;
2566 acc->h4 = acc->h2 * acc->h2;
2567 acc->w4 = acc->w2 * acc->w2;
2568 acc->h2l = acc->h2 * def->l;
2569 acc->w2l = acc->w2 * def->l;
2570 acc->h2mw2 = acc->h2 - acc->w2;
2571 acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0;
2572 acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0;
2573 acc->xorg = tarc->x + (tarc->width >> 1);
2574 acc->yorgu = tarc->y + (tarc->height >> 1);
2575 acc->yorgl = acc->yorgu + (tarc->height & 1);
2576 tailEllipseY (def, acc);
2580 * compute y value bounds of various portions of the arc,
2581 * the outer edge, the ellipse and the inner edge.
2585 computeBound (def, bound, acc, right, left)
2586 struct arc_def *def;
2587 struct arc_bound *bound;
2588 struct accelerators *acc;
2589 miArcFacePtr right, left;
2594 struct bound innerx, outerx;
2595 struct bound ellipsex;
2597 bound->ellipse.min = Dsin (def->a0) * def->h;
2598 bound->ellipse.max = Dsin (def->a1) * def->h;
2599 if (def->a0 == 45 && def->w == def->h)
2600 ellipsex.min = bound->ellipse.min;
2602 ellipsex.min = Dcos (def->a0) * def->w;
2603 if (def->a1 == 45 && def->w == def->h)
2604 ellipsex.max = bound->ellipse.max;
2606 ellipsex.max = Dcos (def->a1) * def->w;
2607 bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2608 bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2609 bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2610 bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2612 outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2613 outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2614 innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2615 innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2618 * save the line end points for the
2619 * cap code to use. Careful here, these are
2620 * in cartesean coordinates (y increasing upwards)
2621 * while the cap code uses inverted coordinates
2622 * (y increasing downwards)
2626 right->counterClock.y = bound->outer.min;
2627 right->counterClock.x = outerx.min;
2628 right->center.y = bound->ellipse.min;
2629 right->center.x = ellipsex.min;
2630 right->clock.y = bound->inner.min;
2631 right->clock.x = innerx.min;
2635 left->clock.y = bound->outer.max;
2636 left->clock.x = outerx.max;
2637 left->center.y = bound->ellipse.max;
2638 left->center.x = ellipsex.max;
2639 left->counterClock.y = bound->inner.max;
2640 left->counterClock.x = innerx.max;
2643 bound->left.min = bound->inner.max;
2644 bound->left.max = bound->outer.max;
2645 bound->right.min = bound->inner.min;
2646 bound->right.max = bound->outer.min;
2648 computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min,
2650 computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max,
2653 if (bound->inner.min > bound->inner.max) {
2654 t = bound->inner.min;
2655 bound->inner.min = bound->inner.max;
2656 bound->inner.max = t;
2658 tail_y = acc->tail_y;
2659 if (tail_y > bound->ellipse.max)
2660 tail_y = bound->ellipse.max;
2661 else if (tail_y < bound->ellipse.min)
2662 tail_y = bound->ellipse.min;
2663 innerTaily = innerYfromY (tail_y, def, acc);
2664 if (bound->inner.min > innerTaily)
2665 bound->inner.min = innerTaily;
2666 if (bound->inner.max < innerTaily)
2667 bound->inner.max = innerTaily;
2668 bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY);
2669 bound->inneri.max = floor(bound->inner.max - acc->fromIntY);
2670 bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY);
2671 bound->outeri.max = floor(bound->outer.max - acc->fromIntY);
2675 * this section computes the x value of the span at y
2676 * intersected with the specified face of the ellipse.
2678 * this is the min/max X value over the set of normal
2679 * lines to the entire ellipse, the equation of the
2683 * x = ------------ y + ellipse_x (1 - --- )
2686 * compute the derivative with-respect-to ellipse_y and solve
2689 * (w^2 - h^2) ellipse_y^3 + h^4 y
2690 * 0 = - ----------------------------------
2691 * h w ellipse_y^2 sqrt (h^2 - ellipse_y^2)
2694 * ellipse_y = ( ---------- ) ^ (1/3)
2697 * The other two solutions to the equation are imaginary.
2699 * This gives the position on the ellipse which generates
2700 * the normal with the largest/smallest x intersection point.
2702 * Now compute the second derivative to check whether
2703 * the intersection is a minimum or maximum:
2705 * h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2706 * - -------------------------------------------
2707 * w y0^3 (sqrt (h^2 - y^2)) ^ 3
2709 * as we only care about the sign,
2711 * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2713 * or (to use accelerators),
2715 * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2)
2720 * computes the position on the ellipse whose normal line
2721 * intersects the given scan line maximally
2725 hookEllipseY (scan_y, bound, acc, left)
2727 struct arc_bound *bound;
2728 struct accelerators *acc;
2733 if (acc->h2mw2 == 0) {
2734 if ( (scan_y > 0 && !left) || (scan_y < 0 && left) )
2735 return bound->ellipse.min;
2736 return bound->ellipse.max;
2738 ret = (acc->h4 * scan_y) / (acc->h2mw2);
2742 return -cbrt (-ret);
2746 * computes the X value of the intersection of the
2747 * given scan line with the right side of the lower hook
2751 hookX (scan_y, def, bound, acc, left)
2753 struct arc_def *def;
2754 struct arc_bound *bound;
2755 struct accelerators *acc;
2758 double ellipse_y, x;
2761 if (def->w != def->h) {
2762 ellipse_y = hookEllipseY (scan_y, bound, acc, left);
2763 if (boundedLe (ellipse_y, bound->ellipse)) {
2765 * compute the value of the second
2768 maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 -
2769 acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2);
2770 if ((left && maxMin > 0) || (!left && maxMin < 0)) {
2772 return def->w + left ? -def->l : def->l;
2773 x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) *
2774 sqrt (acc->h2 - ellipse_y * ellipse_y) /
2775 (def->h * def->w * ellipse_y);
2781 if (acc->left.valid && boundedLe (scan_y, bound->left)) {
2782 x = intersectLine (scan_y, acc->left);
2784 if (acc->right.valid)
2785 x = intersectLine (scan_y, acc->right);
2787 x = def->w - def->l;
2790 if (acc->right.valid && boundedLe (scan_y, bound->right)) {
2791 x = intersectLine (scan_y, acc->right);
2793 if (acc->left.valid)
2794 x = intersectLine (scan_y, acc->left);
2796 x = def->w - def->l;
2803 * generate the set of spans with
2804 * the given y coordinate
2808 arcSpan (y, lx, lw, rx, rw, def, bounds, acc, mask)
2814 struct arc_def *def;
2815 struct arc_bound *bounds;
2816 struct accelerators *acc;
2819 int linx, loutx, rinx, routx;
2822 if (boundedLe (y, bounds->inneri)) {
2827 * intersection with left face
2829 x = hookX (y + acc->fromIntY, def, bounds, acc, 1);
2830 if (acc->right.valid &&
2831 boundedLe (y + acc->fromIntY, bounds->right))
2833 altx = intersectLine (y + acc->fromIntY, acc->right);
2837 linx = -ICEIL(acc->fromIntX - x);
2838 rinx = ICEIL(acc->fromIntX + x);
2840 if (boundedLe (y, bounds->outeri)) {
2845 * intersection with right face
2847 x = hookX (y + acc->fromIntY, def, bounds, acc, 0);
2848 if (acc->left.valid &&
2849 boundedLe (y + acc->fromIntY, bounds->left))
2852 x = intersectLine (y + acc->fromIntY, acc->left);
2856 loutx = -ICEIL(acc->fromIntX - x);
2857 routx = ICEIL(acc->fromIntX + x);
2861 newFinalSpan (acc->yorgu - y,
2862 acc->xorg + rinx, acc->xorg + routx);
2864 newFinalSpan (acc->yorgl + y,
2865 acc->xorg + rinx, acc->xorg + routx);
2869 newFinalSpan (acc->yorgu - y,
2870 acc->xorg - loutx, acc->xorg - linx);
2872 newFinalSpan (acc->yorgl + y,
2873 acc->xorg - loutx, acc->xorg - linx);
2878 arcSpan0 (lx, lw, rx, rw, def, bounds, acc, mask)
2883 struct arc_def *def;
2884 struct arc_bound *bounds;
2885 struct accelerators *acc;
2890 if (boundedLe (0, bounds->inneri) &&
2891 acc->left.valid && boundedLe (0, bounds->left) &&
2894 x = def->w - def->l;
2895 if (acc->left.b < x)
2897 lw = ICEIL(acc->fromIntX - x) - lx;
2899 rx = ICEIL(acc->fromIntX + x);
2902 arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask);
2906 tailSpan (y, lw, rw, def, bounds, acc, mask)
2910 struct arc_def *def;
2911 struct arc_bound *bounds;
2912 struct accelerators *acc;
2915 double yy, xalt, x, lx, rx;
2918 if (boundedLe(y, bounds->outeri))
2919 arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask);
2920 else if (def->w != def->h) {
2921 yy = y + acc->fromIntY;
2922 x = tailX(yy, def, bounds, acc);
2923 if (yy == 0.0 && x == -rw - acc->fromIntX)
2925 if (acc->right.valid && boundedLe (yy, bounds->right)) {
2928 xalt = intersectLine (yy, acc->right);
2929 if (xalt >= -rw - acc->fromIntX && xalt <= rx)
2931 n = ICEIL(acc->fromIntX + lx);
2934 newFinalSpan (acc->yorgu - y,
2935 acc->xorg + n, acc->xorg + lw);
2937 newFinalSpan (acc->yorgl + y,
2938 acc->xorg + n, acc->xorg + lw);
2940 n = ICEIL(acc->fromIntX + rx);
2943 newFinalSpan (acc->yorgu - y,
2944 acc->xorg - rw, acc->xorg + n);
2946 newFinalSpan (acc->yorgl + y,
2947 acc->xorg - rw, acc->xorg + n);
2951 ICEIL(acc->fromIntX - x), 0,
2952 ICEIL(acc->fromIntX + x), 0,
2953 def, bounds, acc, mask);
2958 * create whole arcs out of pieces. This code is
2962 static struct finalSpan **finalSpans = NULL;
2963 static int finalMiny = 0, finalMaxy = -1;
2964 static int finalSize = 0;
2966 static int nspans = 0; /* total spans, not just y coords */
2969 struct finalSpan *next;
2970 int min, max; /* x values */
2973 static struct finalSpan *freeFinalSpans, *tmpFinalSpan;
2975 # define allocFinalSpan() (freeFinalSpans ?\
2976 ((tmpFinalSpan = freeFinalSpans), \
2977 (freeFinalSpans = freeFinalSpans->next), \
2978 (tmpFinalSpan->next = 0), \
2982 # define SPAN_CHUNK_SIZE 128
2984 struct finalSpanChunk {
2985 struct finalSpan data[SPAN_CHUNK_SIZE];
2986 struct finalSpanChunk *next;
2989 static struct finalSpanChunk *chunks;
2994 register struct finalSpanChunk *newChunk;
2995 register struct finalSpan *span;
2998 newChunk = (struct finalSpanChunk *) g_malloc (sizeof (struct finalSpanChunk));
3000 return (struct finalSpan *) NULL;
3001 newChunk->next = chunks;
3003 freeFinalSpans = span = newChunk->data + 1;
3004 for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) {
3005 span->next = span+1;
3009 span = newChunk->data;
3015 disposeFinalSpans ()
3017 struct finalSpanChunk *chunk, *next;
3019 for (chunk = chunks; chunk; chunk = next) {
3030 fillSpans (pDrawable, pGC)
3031 GdkDrawable* pDrawable;
3034 register struct finalSpan *span;
3035 register GdkSpan* xSpan;
3037 register struct finalSpan **f;
3043 xSpan = xSpans = (GdkSpan*) ALLOCATE_LOCAL (nspans * sizeof (GdkSpan));
3048 for (spany = finalMiny; spany <= finalMaxy; spany++, f++) {
3049 for (span = *f; span; span=span->next) {
3050 if (span->max <= span->min)
3052 xSpan->x = span->min;
3054 xSpan->width = span->max - span->min;
3060 gdk_fb_fill_spans(pDrawable, pGC, xSpans, i, TRUE);
3062 disposeFinalSpans ();
3064 DEALLOCATE_LOCAL (xSpans);
3071 # define SPAN_REALLOC 100
3073 # define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \
3074 &finalSpans[(y) - finalMiny] : \
3077 static struct finalSpan **
3081 struct finalSpan **newSpans;
3082 int newSize, newMiny, newMaxy;
3086 if (y < finalMiny || y > finalMaxy) {
3092 change = finalMiny - y;
3094 change = y - finalMaxy;
3095 if (change >= SPAN_REALLOC)
3096 change += SPAN_REALLOC;
3098 change = SPAN_REALLOC;
3099 newSize = finalSize + change;
3100 newSpans = (struct finalSpan **) g_malloc
3101 (newSize * sizeof (struct finalSpan *));
3103 return (struct finalSpan **)NULL;
3104 newMiny = finalMiny;
3105 newMaxy = finalMaxy;
3107 newMiny = finalMiny - change;
3109 newMaxy = finalMaxy + change;
3111 g_memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *),
3112 (char *) finalSpans,
3113 finalSize * sizeof (struct finalSpan *));
3114 g_free (finalSpans);
3116 if ((i = finalMiny - newMiny) > 0)
3117 memset ((char *)newSpans, 0, i * sizeof (struct finalSpan *));
3118 if ((i = newMaxy - finalMaxy) > 0)
3119 memset ((char *)(newSpans + newSize - i), 0,
3120 i * sizeof (struct finalSpan *));
3121 finalSpans = newSpans;
3122 finalMaxy = newMaxy;
3123 finalMiny = newMiny;
3124 finalSize = newSize;
3126 return &finalSpans[y - finalMiny];
3130 newFinalSpan (y, xmin, xmax)
3132 register int xmin, xmax;
3134 register struct finalSpan *x;
3135 register struct finalSpan **f;
3136 struct finalSpan *oldx;
3137 struct finalSpan *prev;
3145 for (x = *f; x; x=x->next) {
3150 if (x->min <= xmax && xmin <= x->max) {
3152 oldx->min = MIN (x->min, xmin);
3153 oldx->max = MAX (x->max, xmax);
3155 prev->next = x->next;
3160 x->min = MIN (x->min, xmin);
3161 x->max = MAX (x->max, xmax);
3174 x = allocFinalSpan ();
3187 mirrorSppPoint (quadrant, sppPoint)
3189 SppPointPtr sppPoint;
3195 sppPoint->x = -sppPoint->x;
3198 sppPoint->x = -sppPoint->x;
3199 sppPoint->y = -sppPoint->y;
3202 sppPoint->y = -sppPoint->y;
3206 * and translate to X coordinate system
3208 sppPoint->y = -sppPoint->y;
3212 * split an arc into pieces which are scan-converted
3213 * in the first-quadrant and mirrored into position.
3214 * This is necessary as the scan-conversion code can
3215 * only deal with arcs completely contained in the
3220 drawArc (miArc *tarc, int l, int a0, int a1, miArcFacePtr right, miArcFacePtr left)
3221 /* miArcFacePtr right, left; */ /* save end line points */
3224 struct accelerators acc;
3225 int startq, endq, curq;
3226 int rightq, leftq = 0, righta = 0, lefta = 0;
3227 miArcFacePtr passRight, passLeft;
3228 int q0 = 0, q1 = 0, mask;
3232 } band[5], sweep[20];
3233 int bandno, sweepno;
3235 int flipRight = 0, flipLeft = 0;
3237 miArcSpanData *spdata;
3240 spdata = miComputeWideEllipse(l, tarc, &mustFree);
3246 startq = a0 / (90 * 64);
3250 endq = (a1-1) / (90 * 64);
3262 q1 = MIN (a1, 90 * 64);
3265 if (curq == startq && a0 == q0 && rightq < 0) {
3269 if (curq == endq && a1 == q1) {
3278 q0 = 180 * 64 - MIN (a1, 180 * 64);
3282 q1 = 180 * 64 - MAX (a0, 90 * 64);
3283 if (curq == startq && 180 * 64 - a0 == q1) {
3287 if (curq == endq && 180 * 64 - a1 == q0) {
3296 q0 = MAX (a0, 180 * 64) - 180 * 64;
3300 q1 = MIN (a1, 270 * 64) - 180 * 64;
3301 if (curq == startq && a0 - 180*64 == q0) {
3305 if (curq == endq && a1 - 180 * 64 == q1) {
3314 q0 = 360 * 64 - MIN (a1, 360 * 64);
3315 q1 = 360 * 64 - MAX (a0, 270 * 64);
3316 if (curq == startq && 360 * 64 - a0 == q1) {
3320 if (curq == endq && 360 * 64 - a1 == q0) {
3326 band[bandno].a0 = q0;
3327 band[bandno].a1 = q1;
3328 band[bandno].mask = 1 << curq;
3345 * find left-most point
3347 for (i = 0; i < bandno; i++)
3348 if (band[i].a0 <= q0) {
3351 mask = band[i].mask;
3356 * locate next point of change
3358 for (i = 0; i < bandno; i++)
3359 if (!(mask & band[i].mask)) {
3360 if (band[i].a0 == q0) {
3361 if (band[i].a1 < q1)
3363 mask |= band[i].mask;
3364 } else if (band[i].a0 < q1)
3368 * create a new sweep
3370 sweep[sweepno].a0 = q0;
3371 sweep[sweepno].a1 = q1;
3372 sweep[sweepno].mask = mask;
3375 * subtract the sweep from the affected bands
3377 for (i = 0; i < bandno; i++)
3378 if (band[i].a0 == q0) {
3381 * check if this band is empty
3383 if (band[i].a0 == band[i].a1)
3384 band[i].a1 = band[i].a0 = 90 * 64 + 1;
3387 computeAcc (tarc, l, &def, &acc);
3388 for (j = 0; j < sweepno; j++) {
3389 mask = sweep[j].mask;
3390 passRight = passLeft = 0;
3391 if (mask & (1 << rightq)) {
3392 if (sweep[j].a0 == righta)
3394 else if (sweep[j].a1 == righta) {
3399 if (mask & (1 << leftq)) {
3400 if (sweep[j].a1 == lefta)
3406 else if (sweep[j].a0 == lefta) {
3413 drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask,
3414 passRight, passLeft, spdata);
3417 * when copyEnd is set, both ends of the arc were computed
3418 * at the same time; drawQuadrant only takes one end though,
3419 * so the left end will be the only one holding the data. Copy
3425 * mirror the coordinates generated for the
3429 mirrorSppPoint (rightq, &right->clock);
3430 mirrorSppPoint (rightq, &right->center);
3431 mirrorSppPoint (rightq, &right->counterClock);
3435 temp = right->clock;
3436 right->clock = right->counterClock;
3437 right->counterClock = temp;
3441 mirrorSppPoint (leftq, &left->counterClock);
3442 mirrorSppPoint (leftq, &left->center);
3443 mirrorSppPoint (leftq, &left->clock);
3448 left->clock = left->counterClock;
3449 left->counterClock = temp;
3457 drawQuadrant (def, acc, a0, a1, mask, right, left, spdata)
3458 struct arc_def *def;
3459 struct accelerators *acc;
3462 miArcFacePtr right, left;
3463 miArcSpanData *spdata;
3465 struct arc_bound bound;
3471 def->a0 = ((double) a0) / 64.0;
3472 def->a1 = ((double) a1) / 64.0;
3473 computeBound (def, &bound, acc, right, left);
3474 yy = bound.inner.min;
3475 if (bound.outer.min < yy)
3476 yy = bound.outer.min;
3477 miny = ICEIL(yy - acc->fromIntY);
3478 yy = bound.inner.max;
3479 if (bound.outer.max > yy)
3480 yy = bound.outer.max;
3481 maxy = floor(yy - acc->fromIntY);
3483 span = spdata->spans;
3486 if (a1 == 90 * 64 && (mask & 1))
3487 newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1);
3490 for (n = spdata->count1; --n >= 0; )
3496 span->lx, -span->lx, 0, span->lx + span->lw,
3497 def, &bound, acc, mask);
3498 if (span->rw + span->rx)
3499 tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask);
3509 arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc);
3511 for (n = spdata->count2; --n >= 0; )
3516 arcSpan (y, span->lx, span->lw, span->rx, span->rw,
3517 def, &bound, acc, mask);
3521 if (spdata->bot && miny <= y && y <= maxy)
3526 if (span->rw <= 0) {
3527 arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw,
3528 def, &bound, acc, n);
3529 if (span->rw + span->rx)
3530 tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n);
3533 arcSpan0 (span->lx, span->lw, span->rx, span->rw,
3534 def, &bound, acc, n);
3538 yy = y + acc->fromIntY;
3539 if (def->w == def->h) {
3540 xalt = def->w - def->l;
3541 x = -sqrt(xalt * xalt - yy * yy);
3543 x = tailX(yy, def, &bound, acc);
3544 if (acc->left.valid && boundedLe (yy, bound.left)) {
3545 xalt = intersectLine (yy, acc->left);
3549 if (acc->right.valid && boundedLe (yy, bound.right)) {
3550 xalt = intersectLine (yy, acc->right);
3556 ICEIL(acc->fromIntX - x), 0,
3557 ICEIL(acc->fromIntX + x), 0,
3558 def, &bound, acc, mask);