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 (SppPointRec center, SppPointRec point1, SppPointRec point2)
1206 * reflect from X coordinates back to ellipse
1207 * coordinates -- y increasing upwards
1209 a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x);
1210 a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x);
1220 translateBounds (miArcFacePtr b, int x, int y, double fx, double fy)
1228 b->counterClock.x -= fx;
1229 b->counterClock.y -= fy;
1233 miArcJoin (GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pLeft, miArcFacePtr pRight,
1234 int xOrgLeft, int yOrgLeft, double xFtransLeft, double yFtransLeft,
1235 int xOrgRight, int yOrgRight, double xFtransRight, double yFtransRight)
1237 SppPointRec center, corner, otherCorner;
1238 SppPointRec poly[5], e;
1239 SppPointPtr pArcPts;
1242 miArcFaceRec Right, Left;
1245 double xFtrans, yFtrans;
1247 double ae, ac2, ec2, bc2, de;
1250 xOrg = (xOrgRight + xOrgLeft) / 2;
1251 yOrg = (yOrgRight + yOrgLeft) / 2;
1252 xFtrans = (xFtransLeft + xFtransRight) / 2;
1253 yFtrans = (yFtransLeft + yFtransRight) / 2;
1255 translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight,
1256 xFtrans - xFtransRight, yFtrans - yFtransRight);
1258 translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft,
1259 xFtrans - xFtransLeft, yFtrans - yFtransLeft);
1263 if (pRight->clock.x == pLeft->counterClock.x &&
1264 pRight->clock.y == pLeft->counterClock.y)
1266 center = pRight->center;
1267 if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock))
1270 corner = pRight->clock;
1271 otherCorner = pLeft->counterClock;
1273 a = angleBetween (center, pLeft->clock, pRight->counterClock);
1274 corner = pLeft->clock;
1275 otherCorner = pRight->counterClock;
1277 switch (GDK_GC_FBDATA(pGC)->values.join_style) {
1278 case GDK_JOIN_ROUND:
1279 width = (GDK_GC_FBDATA(pGC)->values.line_width ? (double)GDK_GC_FBDATA(pGC)->values.line_width : (double)1);
1281 arc.x = center.x - width/2;
1282 arc.y = center.y - width/2;
1285 arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x);
1287 pArcPts = (SppPointPtr) g_malloc (3 * sizeof (SppPointRec));
1290 pArcPts[0].x = otherCorner.x;
1291 pArcPts[0].y = otherCorner.y;
1292 pArcPts[1].x = center.x;
1293 pArcPts[1].y = center.y;
1294 pArcPts[2].x = corner.x;
1295 pArcPts[2].y = corner.y;
1296 if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) )
1298 /* by drawing with miFillSppPoly and setting the endpoints of the arc
1299 * to be the corners, we assure that the cap will meet up with the
1300 * rest of the line */
1301 miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans);
1305 case GDK_JOIN_MITER:
1307 * don't miter arcs with less than 11 degrees between them
1312 poly[2] = otherCorner;
1313 bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) +
1314 (corner.y - otherCorner.y) * (corner.y - otherCorner.y);
1316 ac2 = (corner.x - center.x) * (corner.x - center.x) +
1317 (corner.y - center.y) * (corner.y - center.y);
1318 ae = sqrt (ac2 - ec2);
1320 e.x = (corner.x + otherCorner.x) / 2;
1321 e.y = (corner.y + otherCorner.y) / 2;
1322 poly[3].x = e.x + de * (e.x - center.x) / ae;
1323 poly[3].y = e.y + de * (e.y - center.y) / ae;
1328 case GDK_JOIN_BEVEL:
1331 poly[2] = otherCorner;
1336 miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans);
1341 miArcCap (GdkDrawable *pDraw, GdkGC *pGC, miArcFacePtr pFace, int end,
1342 int xOrg, int yOrg, double xFtrans, double yFtrans)
1344 SppPointRec corner, otherCorner, center, endPoint, poly[5];
1346 corner = pFace->clock;
1347 otherCorner = pFace->counterClock;
1348 center = pFace->center;
1349 switch (GDK_GC_FBDATA(pGC)->values.cap_style) {
1350 case GDK_CAP_PROJECTING:
1351 poly[0].x = otherCorner.x;
1352 poly[0].y = otherCorner.y;
1353 poly[1].x = corner.x;
1354 poly[1].y = corner.y;
1355 poly[2].x = corner.x -
1356 (center.y - corner.y);
1357 poly[2].y = corner.y +
1358 (center.x - corner.x);
1359 poly[3].x = otherCorner.x -
1360 (otherCorner.y - center.y);
1361 poly[3].y = otherCorner.y +
1362 (otherCorner.x - center.x);
1363 poly[4].x = otherCorner.x;
1364 poly[4].y = otherCorner.y;
1365 miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans);
1369 * miRoundCap just needs these to be unequal.
1372 endPoint.x = endPoint.x + 100;
1373 miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0,
1374 -xOrg, -yOrg, xFtrans, yFtrans);
1381 /* MIROUNDCAP -- a private helper function
1382 * Put Rounded cap on end. pCenter is the center of this end of the line
1383 * pEnd is the center of the other end of the line. pCorner is one of the
1384 * two corners at this end of the line.
1385 * NOTE: pOtherCorner must be counter-clockwise from pCorner.
1388 static void miRoundCap(GdkDrawable *pDraw, GdkGC *pGC, SppPointRec pCenter, SppPointRec pEnd, SppPointRec pCorner,
1389 SppPointRec pOtherCorner, int fLineEnd, int xOrg, int yOrg,
1390 double xFtrans, double yFtrans)
1395 SppPointPtr pArcPts;
1397 width = (GDK_GC_FBDATA(pGC)->values.line_width ? (double)GDK_GC_FBDATA(pGC)->values.line_width : (double)1);
1399 arc.x = pCenter.x - width/2;
1400 arc.y = pCenter.y - width/2;
1403 arc.angle1 = -(miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x));
1404 if(PTISEQUAL(pCenter, pEnd))
1405 arc.angle2 = - 180.0;
1407 arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1;
1409 arc.angle2 += 360.0;
1411 pArcPts = (SppPointPtr) NULL;
1412 if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) )
1414 /* by drawing with miFillSppPoly and setting the endpoints of the arc
1415 * to be the corners, we assure that the cap will meet up with the
1416 * rest of the line */
1417 miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans);
1423 * To avoid inaccuracy at the cardinal points, use trig functions
1424 * which are exact for those angles
1428 #define M_PI 3.14159265358979323846
1431 #define M_PI_2 1.57079632679489661923
1434 # define Dsin(d) ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
1435 # define Dcos(d) ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
1436 # define mod(a,b) ((a) >= 0 ? (a) % (b) : (b) - (-a) % (b))
1443 if (floor (a/90) == a/90) {
1445 switch (mod (i, 4)) {
1452 return cos (a * M_PI / 180.0);
1460 if (floor (a/90) == a/90) {
1462 switch (mod (i, 4)) {
1469 return sin (a * M_PI / 180.0);
1481 return asin(v) * (180.0 / M_PI);
1485 miDatan2 (double dy, double dx)
1491 } else if (dx == 0) {
1495 } else if (fabs (dy) == fabs (dx)) {
1506 return atan2 (dy, dx) * (180.0 / M_PI);
1510 /* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper
1511 * routine for filled arc and line (round cap) code.
1512 * Returns the number of points in the arc. Note that it takes a pointer
1513 * to a pointer to where it should put the points and an index (cpt).
1514 * This procedure allocates the space necessary to fit the arc points.
1515 * Sometimes it's convenient for those points to be at the end of an existing
1516 * array. (For example, if we want to leave a spare point to make sectors
1517 * instead of segments.) So we pass in the g_malloc()ed chunk that contains the
1518 * array and an index saying where we should start stashing the points.
1519 * If there isn't an array already, we just pass in a null pointer and
1520 * count on g_realloc() to handle the null pointer correctly.
1523 miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts)
1525 SppArcPtr parc; /* points to an arc */
1526 int cpt; /* number of points already in arc list */
1527 SppPointPtr *ppPts; /* pointer to pointer to arc-list -- modified */
1530 double st, /* Start Theta, start angle */
1531 et, /* End Theta, offset from start theta */
1532 dt, /* Delta Theta, angle to sweep ellipse */
1533 cdt, /* Cos Delta Theta, actually 2 cos(dt) */
1534 x0, y0, /* the recurrence formula needs two points to start */
1536 x2, y2, /* this will be the new point generated */
1537 xc, yc; /* the center point */
1540 GdkPoint last; /* last point on integer boundaries */
1542 /* The spec says that positive angles indicate counterclockwise motion.
1543 * Given our coordinate system (with 0,0 in the upper left corner),
1544 * the screen appears flipped in Y. The easiest fix is to negate the
1547 st = - parc->angle1;
1549 et = - parc->angle2;
1551 /* Try to get a delta theta that is within 1/2 pixel. Then adjust it
1552 * so that it divides evenly into the total.
1553 * I'm just using cdt 'cause I'm lazy.
1556 if (parc->height > cdt)
1563 dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */
1565 count = abs(count) + 1;
1569 cdt = 2 * miDcos(dt);
1570 if (!(poly = (SppPointPtr) g_realloc((gpointer)*ppPts,
1571 (cpt + count) * sizeof(SppPointRec))))
1575 xc = parc->width/2.0; /* store half width and half height */
1576 yc = parc->height/2.0;
1578 x0 = xc * miDcos(st);
1579 y0 = yc * miDsin(st);
1580 x1 = xc * miDcos(st + dt);
1581 y1 = yc * miDsin(st + dt);
1582 xc += parc->x; /* by adding initial point, these become */
1583 yc += parc->y; /* the center point */
1585 poly[cpt].x = (xc + x0);
1586 poly[cpt].y = (yc + y0);
1587 last.x = ROUNDTOINT( poly[cpt + 1].x = (xc + x1) );
1588 last.y = ROUNDTOINT( poly[cpt + 1].y = (yc + y1) );
1590 for(i = 2; i < count; i++)
1595 poly[cpt + i].x = (xc + x2);
1596 poly[cpt + i].y = (yc + y2);
1601 /* adjust the last point */
1602 if (fabs(parc->angle2) >= 360.0)
1603 poly[cpt +i -1] = poly[0];
1605 poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc);
1606 poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc);
1613 double x0, y0, x1, y1;
1617 # define ADD_REALLOC_STEP 20
1620 addCap (miArcCapPtr *capsp, int *ncapsp, int *sizep, int end, int arcIndex)
1625 if (*ncapsp == *sizep)
1627 newsize = *sizep + ADD_REALLOC_STEP;
1628 cap = (miArcCapPtr) g_realloc (*capsp,
1629 newsize * sizeof (**capsp));
1635 cap = &(*capsp)[*ncapsp];
1637 cap->arcIndex = arcIndex;
1642 addJoin (miArcJoinPtr *joinsp, int *njoinsp, int *sizep, int end0, int index0,
1643 int phase0, int end1, int index1, int phase1)
1648 if (*njoinsp == *sizep)
1650 newsize = *sizep + ADD_REALLOC_STEP;
1651 join = (miArcJoinPtr) g_realloc (*joinsp,
1652 newsize * sizeof (**joinsp));
1658 join = &(*joinsp)[*njoinsp];
1660 join->arcIndex0 = index0;
1661 join->phase0 = phase0;
1663 join->arcIndex1 = index1;
1664 join->phase1 = phase1;
1669 addArc (miArcDataPtr *arcsp, int *narcsp, int *sizep, miArc *xarc)
1674 if (*narcsp == *sizep)
1676 newsize = *sizep + ADD_REALLOC_STEP;
1677 arc = (miArcDataPtr) g_realloc (*arcsp,
1678 newsize * sizeof (**arcsp));
1680 return (miArcDataPtr)NULL;
1684 arc = &(*arcsp)[*narcsp];
1691 miFreeArcs(miPolyArcPtr arcs, GdkGC *pGC)
1695 for (iphase = ((GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH) ? 1 : 0);
1699 if (arcs[iphase].narcs > 0)
1700 g_free(arcs[iphase].arcs);
1701 if (arcs[iphase].njoins > 0)
1702 g_free(arcs[iphase].joins);
1703 if (arcs[iphase].ncaps > 0)
1704 g_free(arcs[iphase].caps);
1710 * map angles to radial distance. This only deals with the first quadrant
1714 * a polygonal approximation to the arc for computing arc lengths
1717 # define dashIndexToAngle(di) ((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1))
1718 # define xAngleToDashIndex(xa) ((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64))
1719 # define dashIndexToXAngle(di) ((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1))
1720 # define dashXAngleStep (((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1)))
1723 computeDashMap (miArc *arcp, dashMap *map)
1726 double a, x, y, prevx = 0.0, prevy = 0.0, dist;
1728 for (di = 0; di < DASH_MAP_SIZE; di++) {
1729 a = dashIndexToAngle (di);
1730 x = ((double) arcp->width / 2.0) * miDcos (a);
1731 y = ((double) arcp->height / 2.0) * miDsin (a);
1735 dist = hypot (x - prevx, y - prevy);
1736 map->map[di] = map->map[di - 1] + dist;
1743 typedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes;
1745 /* this routine is a bit gory */
1748 miComputeArcs (miArc *parcs, int narcs, GdkGC *pGC)
1750 int isDashed, isDoubleDash;
1753 int start, i, j, k = 0, nexti, nextk = 0;
1759 struct arcData *data;
1762 int iphase, prevphase = 0, joinphase;
1766 int iDash = 0, dashRemaining;
1767 int iDashStart = 0, dashRemainingStart = 0, iphaseStart;
1768 int startAngle, spanAngle, endAngle, backwards = 0;
1769 int prevDashAngle, dashAngle;
1772 isDashed = !(GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_SOLID);
1773 isDoubleDash = (GDK_GC_FBDATA(pGC)->values.line_style == GDK_LINE_DOUBLE_DASH);
1774 dashOffset = GDK_GC_FBDATA(pGC)->dash_offset;
1776 data = (struct arcData *) ALLOCATE_LOCAL (narcs * sizeof (struct arcData));
1778 return (miPolyArcPtr)NULL;
1779 arcs = (miPolyArcPtr) g_malloc (sizeof (*arcs) * (isDoubleDash ? 2 : 1));
1782 DEALLOCATE_LOCAL(data);
1783 return (miPolyArcPtr)NULL;
1785 for (i = 0; i < narcs; i++) {
1786 a0 = todeg (parcs[i].angle1);
1787 angle2 = parcs[i].angle2;
1788 if (angle2 > FULLCIRCLE)
1789 angle2 = FULLCIRCLE;
1790 else if (angle2 < -FULLCIRCLE)
1791 angle2 = -FULLCIRCLE;
1792 data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE;
1793 a1 = todeg (parcs[i].angle1 + angle2);
1794 data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0));
1795 data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0));
1796 data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1));
1797 data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1));
1800 for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) {
1801 arcs[iphase].njoins = 0;
1802 arcs[iphase].joins = NULL;
1803 joinSize[iphase] = 0;
1805 arcs[iphase].ncaps = 0;
1806 arcs[iphase].caps = NULL;
1807 capSize[iphase] = 0;
1809 arcs[iphase].narcs = 0;
1810 arcs[iphase].arcs = NULL;
1811 arcSize[iphase] = 0;
1817 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[0];
1818 while (dashOffset > 0) {
1819 if (dashOffset >= dashRemaining) {
1820 dashOffset -= dashRemaining;
1821 iphase = iphase ? 0 : 1;
1823 if (iDash == GDK_GC_FBDATA(pGC)->dash_list_len)
1825 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[iDash];
1827 dashRemaining -= dashOffset;
1832 dashRemainingStart = dashRemaining;
1834 iphaseStart = iphase;
1836 for (i = narcs - 1; i >= 0; i--) {
1840 if (data[i].selfJoin || i == j ||
1841 (UNEQUAL (data[i].x1, data[j].x0) ||
1842 UNEQUAL (data[i].y1, data[j].y0)))
1844 if (iphase == 0 || isDoubleDash)
1845 addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
1846 &capSize[iphase], RIGHT_END, 0);
1863 ** deal with dashed arcs. Use special rules for certain 0 area arcs.
1864 ** Presumably, the other 0 area arcs still aren't done right.
1866 arcTypes arcType = OTHER;
1869 if (parcs[i].height == 0
1870 && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00
1871 && parcs[i].angle2 == 0x2d00)
1872 arcType = HORIZONTAL;
1873 else if (parcs[i].width == 0
1874 && (parcs[i].angle1 % FULLCIRCLE) == 0x1680
1875 && parcs[i].angle2 == 0x2d00)
1877 if (arcType == OTHER) {
1879 * precompute an approximation map
1881 computeDashMap (&parcs[i], &map);
1883 * compute each individual dash segment using the path
1886 startAngle = parcs[i].angle1;
1887 spanAngle = parcs[i].angle2;
1888 if (spanAngle > FULLCIRCLE)
1889 spanAngle = FULLCIRCLE;
1890 else if (spanAngle < -FULLCIRCLE)
1891 spanAngle = -FULLCIRCLE;
1893 startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
1894 if (startAngle >= FULLCIRCLE)
1895 startAngle = startAngle % FULLCIRCLE;
1896 endAngle = startAngle + spanAngle;
1897 backwards = spanAngle < 0;
1900 if (arcType == VERTICAL) {
1901 xarc.angle1 = 0x1680;
1902 startAngle = parcs[i].y;
1903 endAngle = startAngle + parcs[i].height;
1905 xarc.angle1 = 0x2d00;
1906 startAngle = parcs[i].x;
1907 endAngle = startAngle + parcs[i].width;
1910 dashAngle = startAngle;
1911 selfJoin = data[i].selfJoin &&
1912 (iphase == 0 || isDoubleDash);
1914 * add dashed arcs to each bucket
1917 while (dashAngle != endAngle) {
1918 prevDashAngle = dashAngle;
1919 if (arcType == OTHER) {
1920 dashAngle = computeAngleFromPath (prevDashAngle, endAngle,
1921 &map, &dashRemaining, backwards);
1922 /* avoid troubles with huge arcs and small dashes */
1923 if (dashAngle == prevDashAngle) {
1930 thisLength = (dashAngle + dashRemaining <= endAngle) ?
1931 dashRemaining : endAngle - dashAngle;
1932 if (arcType == VERTICAL) {
1934 xarc.height = thisLength;
1937 xarc.width = thisLength;
1939 dashAngle += thisLength;
1940 dashRemaining -= thisLength;
1942 if (iphase == 0 || isDoubleDash) {
1943 if (arcType == OTHER) {
1945 spanAngle = prevDashAngle;
1947 spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE;
1948 if (spanAngle >= FULLCIRCLE)
1949 spanAngle = spanAngle % FULLCIRCLE;
1950 xarc.angle1 = spanAngle;
1951 spanAngle = dashAngle - prevDashAngle;
1953 if (dashAngle > prevDashAngle)
1954 spanAngle = - FULLCIRCLE + spanAngle;
1956 if (dashAngle < prevDashAngle)
1957 spanAngle = FULLCIRCLE + spanAngle;
1959 if (spanAngle > FULLCIRCLE)
1960 spanAngle = FULLCIRCLE;
1961 if (spanAngle < -FULLCIRCLE)
1962 spanAngle = -FULLCIRCLE;
1963 xarc.angle2 = spanAngle;
1965 arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
1966 &arcSize[iphase], &xarc);
1970 * cap each end of an on/off dash
1972 if (!isDoubleDash) {
1973 if (prevDashAngle != startAngle) {
1974 addCap (&arcs[iphase].caps,
1975 &arcs[iphase].ncaps,
1976 &capSize[iphase], RIGHT_END,
1977 arc - arcs[iphase].arcs);
1980 if (dashAngle != endAngle) {
1981 addCap (&arcs[iphase].caps,
1982 &arcs[iphase].ncaps,
1983 &capSize[iphase], LEFT_END,
1984 arc - arcs[iphase].arcs);
1987 arc->cap = arcs[iphase].ncaps;
1988 arc->join = arcs[iphase].njoins;
1991 if (dashAngle == endAngle)
1992 arc->selfJoin = selfJoin;
1995 if (dashRemaining <= 0) {
1997 if (iDash == GDK_GC_FBDATA(pGC)->dash_list_len)
1999 iphase = iphase ? 0:1;
2000 dashRemaining = GDK_GC_FBDATA(pGC)->dash_list[iDash];
2004 * make sure a place exists for the position data when
2005 * drawing a zero-length arc
2007 if (startAngle == endAngle) {
2009 if (!isDoubleDash && iphase == 1)
2011 arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs,
2012 &arcSize[prevphase], &parcs[i]);
2015 arc->join = arcs[prevphase].njoins;
2016 arc->cap = arcs[prevphase].ncaps;
2017 arc->selfJoin = data[i].selfJoin;
2020 arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
2021 &arcSize[iphase], &parcs[i]);
2024 arc->join = arcs[iphase].njoins;
2025 arc->cap = arcs[iphase].ncaps;
2026 arc->selfJoin = data[i].selfJoin;
2029 if (prevphase == 0 || isDoubleDash)
2030 k = arcs[prevphase].narcs - 1;
2031 if (iphase == 0 || isDoubleDash)
2032 nextk = arcs[iphase].narcs;
2033 if (nexti == start) {
2037 iphase = iphaseStart;
2038 dashRemaining = dashRemainingStart;
2041 arcsJoin = narcs > 1 && i != j &&
2042 ISEQUAL (data[i].x1, data[j].x0) &&
2043 ISEQUAL (data[i].y1, data[j].y0) &&
2044 !data[i].selfJoin && !data[j].selfJoin;
2053 (prevphase == 0 || isDoubleDash) &&
2054 (iphase == 0 || isDoubleDash))
2059 joinphase = iphaseStart;
2061 * if the join is right at the dash,
2062 * draw the join in foreground
2063 * This is because the foreground
2064 * arcs are computed second, the results
2065 * of which are needed to draw the join
2067 if (joinphase != prevphase)
2070 if (joinphase == 0 || isDoubleDash) {
2071 addJoin (&arcs[joinphase].joins,
2072 &arcs[joinphase].njoins,
2073 &joinSize[joinphase],
2074 LEFT_END, k, prevphase,
2075 RIGHT_END, nextk, iphase);
2076 arc->join = arcs[prevphase].njoins;
2080 * cap the left end of this arc
2081 * unless it joins itself
2083 if ((prevphase == 0 || isDoubleDash) &&
2086 addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps,
2087 &capSize[prevphase], LEFT_END, k);
2088 arc->cap = arcs[prevphase].ncaps;
2090 if (isDashed && !arcsJoin) {
2092 iphase = iphaseStart;
2093 dashRemaining = dashRemainingStart;
2095 nextk = arcs[iphase].narcs;
2096 if (nexti == start) {
2099 iphase = iphaseStart;
2100 dashRemaining = dashRemainingStart;
2103 * cap the right end of the next arc. If the
2104 * next arc is actually the first arc, only
2105 * cap it if it joins with this arc. This
2106 * case will occur when the final dash segment
2107 * of an on/off dash is off. Of course, this
2108 * cap will be drawn at a strange time, but that
2111 if ((iphase == 0 || isDoubleDash) &&
2112 (nexti != start || (arcsJoin && isDashed)))
2113 addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
2114 &capSize[iphase], RIGHT_END, nextk);
2121 * make sure the last section is rendered
2123 for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++)
2124 if (arcs[iphase].narcs > 0) {
2125 arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1;
2126 arcs[iphase].arcs[arcs[iphase].narcs-1].join =
2127 arcs[iphase].njoins;
2128 arcs[iphase].arcs[arcs[iphase].narcs-1].cap =
2131 DEALLOCATE_LOCAL(data);
2134 miFreeArcs(arcs, pGC);
2135 DEALLOCATE_LOCAL(data);
2136 return (miPolyArcPtr)NULL;
2140 angleToLength (int angle, dashMap *map)
2142 double len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen;
2145 gboolean oddSide = FALSE;
2149 while (angle >= 90 * 64) {
2151 totallen += sidelen;
2157 totallen -= sidelen;
2162 angle = 90 * 64 - angle;
2164 di = xAngleToDashIndex (angle);
2165 excess = angle - dashIndexToXAngle (di);
2169 * linearly interpolate between this point and the next
2172 excesslen = (map->map[di + 1] - map->map[di]) *
2173 ((double) excess) / dashXAngleStep;
2177 totallen += (sidelen - len);
2184 * len is along the arc, but may be more than one rotation
2188 lengthToAngle (double len, dashMap *map)
2190 double sidelen = map->map[DASH_MAP_SIZE - 1];
2191 int angle, angleexcess;
2192 gboolean oddSide = FALSE;
2197 * step around the ellipse, subtracting sidelens and
2198 * adding 90 degrees. oddSide will tell if the
2199 * map should be interpolated in reverse
2203 return 2 * FULLCIRCLE; /* infinity */
2204 while (len >= sidelen) {
2211 return -2 * FULLCIRCLE; /* infinity */
2219 len = sidelen - len;
2221 a1 = DASH_MAP_SIZE - 1;
2223 * binary search for the closest pre-computed length
2225 while (a1 - a0 > 1) {
2227 if (len > map->map[a])
2232 angleexcess = dashIndexToXAngle (a0);
2234 * linearly interpolate to the next point
2236 angleexcess += (len - map->map[a0]) /
2237 (map->map[a0+1] - map->map[a0]) * dashXAngleStep;
2239 angle += (90 * 64) - angleexcess;
2241 angle += angleexcess;
2246 * compute the angle of an ellipse which cooresponds to
2247 * the given path length. Note that the correct solution
2248 * to this problem is an eliptic integral, we'll punt and
2249 * approximate (it's only for dashes anyway). This
2250 * approximation uses a polygon.
2252 * The remaining portion of len is stored in *lenp -
2253 * this will be negative if the arc extends beyond
2254 * len and positive if len extends beyond the arc.
2257 static int computeAngleFromPath(int startAngle, int endAngle, dashMap *map, int *lenp, int backwards)
2258 /* int startAngle, endAngle; *//* normalized absolute angles in *64 degrees */
2269 * flip the problem around to always be
2272 a0 = FULLCIRCLE - a0;
2273 a1 = FULLCIRCLE - a1;
2277 len0 = angleToLength (a0, map);
2278 a = lengthToAngle (len0 + len, map);
2281 len -= angleToLength (a1, map) - len0;
2291 * scan convert wide arcs.
2295 * draw zero width/height arcs
2299 drawZeroArc (GdkDrawable *pDraw, GdkGC *pGC, miArc *tarc, int lw,
2300 miArcFacePtr left, miArcFacePtr right)
2302 double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0, w, h, x, y;
2303 double xmax, ymax, xmin, ymin;
2305 double a, startAngle, endAngle;
2311 if (a1 > FULLCIRCLE)
2313 else if (a1 < -FULLCIRCLE)
2315 w = (double)tarc->width / 2.0;
2316 h = (double)tarc->height / 2.0;
2318 * play in X coordinates right away
2320 startAngle = - ((double) a0 / 64.0);
2321 endAngle = - ((double) (a0 + a1) / 64.0);
2332 if (a == startAngle)
2352 if (a1 < 0) /* clockwise */
2354 if (floor (a / 90.0) == floor (endAngle / 90.0))
2357 a = 90 * (floor (a/90.0) + 1);
2361 if (ceil (a / 90.0) == ceil (endAngle / 90.0))
2364 a = 90 * (ceil (a/90.0) - 1);
2368 if ((x1 - x0) + (y1 - y0) < 0)
2379 right->center.x = x0;
2380 right->center.y = y0;
2381 right->clock.x = x0 - lx;
2382 right->clock.y = y0 - ly;
2383 right->counterClock.x = x0 + lx;
2384 right->counterClock.y = y0 + ly;
2388 left->center.x = x1;
2389 left->center.y = y1;
2390 left->clock.x = x1 + lx;
2391 left->clock.y = y1 + ly;
2392 left->counterClock.x = x1 - lx;
2393 left->counterClock.y = y1 - ly;
2407 if (xmax != xmin && ymax != ymin) {
2408 int minx, maxx, miny, maxy;
2410 minx = ICEIL (xmin + w) + tarc->x;
2411 maxx = ICEIL (xmax + w) + tarc->x;
2412 miny = ICEIL (ymin + h) + tarc->y;
2413 maxy = ICEIL (ymax + h) + tarc->y;
2415 gdk_fb_draw_rectangle(pDraw, pGC, TRUE, minx, miny, maxx - minx, maxy - miny);
2420 * this computes the ellipse y value associated with the
2421 * bottom of the tail.
2425 tailEllipseY (struct arc_def *def, struct accelerators *acc)
2430 if (def->w == def->h)
2432 t = def->l * def->w;
2433 if (def->w > def->h) {
2440 t = 2.0 * def->h * t;
2441 t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2;
2443 acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t);
2447 * inverse functions -- compute edge coordinates
2452 outerXfromXY (double x, double y,
2453 struct arc_def *def, struct accelerators *acc)
2455 return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2459 outerYfromXY (double x, double y,
2460 struct arc_def *def, struct accelerators *acc)
2462 return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2466 innerXfromXY (double x, double y,
2467 struct arc_def *def, struct accelerators *acc)
2469 return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2473 innerYfromXY (double x, double y,
2474 struct arc_def *def, struct accelerators *acc)
2476 return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2480 innerYfromY (double y, struct arc_def *def, struct accelerators *acc)
2484 x = (def->w / def->h) * sqrt (acc->h2 - y*y);
2486 return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
2490 computeLine (double x1, double y1, double x2, double y2, struct line *line)
2495 line->m = (x1 - x2) / (y1 - y2);
2496 line->b = x1 - y1 * line->m;
2502 * compute various accelerators for an ellipse. These
2503 * are simply values that are used repeatedly in
2508 computeAcc (miArc *tarc, int lw, struct arc_def *def, struct accelerators *acc)
2510 def->w = ((double) tarc->width) / 2.0;
2511 def->h = ((double) tarc->height) / 2.0;
2512 def->l = ((double) lw) / 2.0;
2513 acc->h2 = def->h * def->h;
2514 acc->w2 = def->w * def->w;
2515 acc->h4 = acc->h2 * acc->h2;
2516 acc->w4 = acc->w2 * acc->w2;
2517 acc->h2l = acc->h2 * def->l;
2518 acc->w2l = acc->w2 * def->l;
2519 acc->h2mw2 = acc->h2 - acc->w2;
2520 acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0;
2521 acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0;
2522 acc->xorg = tarc->x + (tarc->width >> 1);
2523 acc->yorgu = tarc->y + (tarc->height >> 1);
2524 acc->yorgl = acc->yorgu + (tarc->height & 1);
2525 tailEllipseY (def, acc);
2529 * compute y value bounds of various portions of the arc,
2530 * the outer edge, the ellipse and the inner edge.
2534 computeBound (struct arc_def *def, struct arc_bound *bound,
2535 struct accelerators *acc, miArcFacePtr right, miArcFacePtr left)
2540 struct bound innerx, outerx;
2541 struct bound ellipsex;
2543 bound->ellipse.min = Dsin (def->a0) * def->h;
2544 bound->ellipse.max = Dsin (def->a1) * def->h;
2545 if (def->a0 == 45 && def->w == def->h)
2546 ellipsex.min = bound->ellipse.min;
2548 ellipsex.min = Dcos (def->a0) * def->w;
2549 if (def->a1 == 45 && def->w == def->h)
2550 ellipsex.max = bound->ellipse.max;
2552 ellipsex.max = Dcos (def->a1) * def->w;
2553 bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2554 bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2555 bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2556 bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2558 outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2559 outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2560 innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
2561 innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
2564 * save the line end points for the
2565 * cap code to use. Careful here, these are
2566 * in cartesean coordinates (y increasing upwards)
2567 * while the cap code uses inverted coordinates
2568 * (y increasing downwards)
2572 right->counterClock.y = bound->outer.min;
2573 right->counterClock.x = outerx.min;
2574 right->center.y = bound->ellipse.min;
2575 right->center.x = ellipsex.min;
2576 right->clock.y = bound->inner.min;
2577 right->clock.x = innerx.min;
2581 left->clock.y = bound->outer.max;
2582 left->clock.x = outerx.max;
2583 left->center.y = bound->ellipse.max;
2584 left->center.x = ellipsex.max;
2585 left->counterClock.y = bound->inner.max;
2586 left->counterClock.x = innerx.max;
2589 bound->left.min = bound->inner.max;
2590 bound->left.max = bound->outer.max;
2591 bound->right.min = bound->inner.min;
2592 bound->right.max = bound->outer.min;
2594 computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min,
2596 computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max,
2599 if (bound->inner.min > bound->inner.max) {
2600 t = bound->inner.min;
2601 bound->inner.min = bound->inner.max;
2602 bound->inner.max = t;
2604 tail_y = acc->tail_y;
2605 if (tail_y > bound->ellipse.max)
2606 tail_y = bound->ellipse.max;
2607 else if (tail_y < bound->ellipse.min)
2608 tail_y = bound->ellipse.min;
2609 innerTaily = innerYfromY (tail_y, def, acc);
2610 if (bound->inner.min > innerTaily)
2611 bound->inner.min = innerTaily;
2612 if (bound->inner.max < innerTaily)
2613 bound->inner.max = innerTaily;
2614 bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY);
2615 bound->inneri.max = floor(bound->inner.max - acc->fromIntY);
2616 bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY);
2617 bound->outeri.max = floor(bound->outer.max - acc->fromIntY);
2621 * this section computes the x value of the span at y
2622 * intersected with the specified face of the ellipse.
2624 * this is the min/max X value over the set of normal
2625 * lines to the entire ellipse, the equation of the
2629 * x = ------------ y + ellipse_x (1 - --- )
2632 * compute the derivative with-respect-to ellipse_y and solve
2635 * (w^2 - h^2) ellipse_y^3 + h^4 y
2636 * 0 = - ----------------------------------
2637 * h w ellipse_y^2 sqrt (h^2 - ellipse_y^2)
2640 * ellipse_y = ( ---------- ) ^ (1/3)
2643 * The other two solutions to the equation are imaginary.
2645 * This gives the position on the ellipse which generates
2646 * the normal with the largest/smallest x intersection point.
2648 * Now compute the second derivative to check whether
2649 * the intersection is a minimum or maximum:
2651 * h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2652 * - -------------------------------------------
2653 * w y0^3 (sqrt (h^2 - y^2)) ^ 3
2655 * as we only care about the sign,
2657 * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2659 * or (to use accelerators),
2661 * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2)
2666 * computes the position on the ellipse whose normal line
2667 * intersects the given scan line maximally
2671 hookEllipseY (double scan_y, struct arc_bound *bound,
2672 struct accelerators *acc, int left)
2676 if (acc->h2mw2 == 0) {
2677 if ( (scan_y > 0 && !left) || (scan_y < 0 && left) )
2678 return bound->ellipse.min;
2679 return bound->ellipse.max;
2681 ret = (acc->h4 * scan_y) / (acc->h2mw2);
2685 return -cbrt (-ret);
2689 * computes the X value of the intersection of the
2690 * given scan line with the right side of the lower hook
2694 hookX (double scan_y, struct arc_def *def,
2695 struct arc_bound *bound, struct accelerators *acc, int left)
2697 double ellipse_y, x;
2700 if (def->w != def->h) {
2701 ellipse_y = hookEllipseY (scan_y, bound, acc, left);
2702 if (boundedLe (ellipse_y, bound->ellipse)) {
2704 * compute the value of the second
2707 maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 -
2708 acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2);
2709 if ((left && maxMin > 0) || (!left && maxMin < 0)) {
2711 return def->w + left ? -def->l : def->l;
2712 x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) *
2713 sqrt (acc->h2 - ellipse_y * ellipse_y) /
2714 (def->h * def->w * ellipse_y);
2720 if (acc->left.valid && boundedLe (scan_y, bound->left)) {
2721 x = intersectLine (scan_y, acc->left);
2723 if (acc->right.valid)
2724 x = intersectLine (scan_y, acc->right);
2726 x = def->w - def->l;
2729 if (acc->right.valid && boundedLe (scan_y, bound->right)) {
2730 x = intersectLine (scan_y, acc->right);
2732 if (acc->left.valid)
2733 x = intersectLine (scan_y, acc->left);
2735 x = def->w - def->l;
2742 * generate the set of spans with
2743 * the given y coordinate
2747 arcSpan (int y, int lx, int lw, int rx, int rw,
2748 struct arc_def *def, struct arc_bound *bounds,
2749 struct accelerators *acc, int mask)
2751 int linx, loutx, rinx, routx;
2754 if (boundedLe (y, bounds->inneri)) {
2759 * intersection with left face
2761 x = hookX (y + acc->fromIntY, def, bounds, acc, 1);
2762 if (acc->right.valid &&
2763 boundedLe (y + acc->fromIntY, bounds->right))
2765 altx = intersectLine (y + acc->fromIntY, acc->right);
2769 linx = -ICEIL(acc->fromIntX - x);
2770 rinx = ICEIL(acc->fromIntX + x);
2772 if (boundedLe (y, bounds->outeri)) {
2777 * intersection with right face
2779 x = hookX (y + acc->fromIntY, def, bounds, acc, 0);
2780 if (acc->left.valid &&
2781 boundedLe (y + acc->fromIntY, bounds->left))
2784 x = intersectLine (y + acc->fromIntY, acc->left);
2788 loutx = -ICEIL(acc->fromIntX - x);
2789 routx = ICEIL(acc->fromIntX + x);
2793 newFinalSpan (acc->yorgu - y,
2794 acc->xorg + rinx, acc->xorg + routx);
2796 newFinalSpan (acc->yorgl + y,
2797 acc->xorg + rinx, acc->xorg + routx);
2801 newFinalSpan (acc->yorgu - y,
2802 acc->xorg - loutx, acc->xorg - linx);
2804 newFinalSpan (acc->yorgl + y,
2805 acc->xorg - loutx, acc->xorg - linx);
2810 arcSpan0 (int lx, int lw, int rx, int rw,
2811 struct arc_def *def, struct arc_bound *bounds,
2812 struct accelerators *acc, int mask)
2816 if (boundedLe (0, bounds->inneri) &&
2817 acc->left.valid && boundedLe (0, bounds->left) &&
2820 x = def->w - def->l;
2821 if (acc->left.b < x)
2823 lw = ICEIL(acc->fromIntX - x) - lx;
2825 rx = ICEIL(acc->fromIntX + x);
2828 arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask);
2832 tailSpan (int y, int lw, int rw,
2833 struct arc_def *def, struct arc_bound *bounds,
2834 struct accelerators *acc, int mask)
2836 double yy, xalt, x, lx, rx;
2839 if (boundedLe(y, bounds->outeri))
2840 arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask);
2841 else if (def->w != def->h) {
2842 yy = y + acc->fromIntY;
2843 x = tailX(yy, def, bounds, acc);
2844 if (yy == 0.0 && x == -rw - acc->fromIntX)
2846 if (acc->right.valid && boundedLe (yy, bounds->right)) {
2849 xalt = intersectLine (yy, acc->right);
2850 if (xalt >= -rw - acc->fromIntX && xalt <= rx)
2852 n = ICEIL(acc->fromIntX + lx);
2855 newFinalSpan (acc->yorgu - y,
2856 acc->xorg + n, acc->xorg + lw);
2858 newFinalSpan (acc->yorgl + y,
2859 acc->xorg + n, acc->xorg + lw);
2861 n = ICEIL(acc->fromIntX + rx);
2864 newFinalSpan (acc->yorgu - y,
2865 acc->xorg - rw, acc->xorg + n);
2867 newFinalSpan (acc->yorgl + y,
2868 acc->xorg - rw, acc->xorg + n);
2872 ICEIL(acc->fromIntX - x), 0,
2873 ICEIL(acc->fromIntX + x), 0,
2874 def, bounds, acc, mask);
2879 * create whole arcs out of pieces. This code is
2883 static struct finalSpan **finalSpans = NULL;
2884 static int finalMiny = 0, finalMaxy = -1;
2885 static int finalSize = 0;
2887 static int nspans = 0; /* total spans, not just y coords */
2890 struct finalSpan *next;
2891 int min, max; /* x values */
2894 static struct finalSpan *freeFinalSpans, *tmpFinalSpan;
2896 # define allocFinalSpan() (freeFinalSpans ?\
2897 ((tmpFinalSpan = freeFinalSpans), \
2898 (freeFinalSpans = freeFinalSpans->next), \
2899 (tmpFinalSpan->next = NULL), \
2903 # define SPAN_CHUNK_SIZE 128
2905 struct finalSpanChunk {
2906 struct finalSpan data[SPAN_CHUNK_SIZE];
2907 struct finalSpanChunk *next;
2910 static struct finalSpanChunk *chunks;
2913 realAllocSpan (void)
2915 register struct finalSpanChunk *newChunk;
2916 register struct finalSpan *span;
2919 newChunk = (struct finalSpanChunk *) g_malloc (sizeof (struct finalSpanChunk));
2921 return (struct finalSpan *) NULL;
2922 newChunk->next = chunks;
2924 freeFinalSpans = span = newChunk->data + 1;
2925 for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) {
2926 span->next = span+1;
2930 span = newChunk->data;
2936 disposeFinalSpans (void)
2938 struct finalSpanChunk *chunk, *next;
2940 for (chunk = chunks; chunk; chunk = next) {
2945 freeFinalSpans = NULL;
2951 fillSpans (GdkDrawable *pDrawable, GdkGC *pGC)
2953 register struct finalSpan *span;
2954 register GdkSpan* xSpan;
2956 register struct finalSpan **f;
2962 xSpan = xSpans = (GdkSpan*) ALLOCATE_LOCAL (nspans * sizeof (GdkSpan));
2967 for (spany = finalMiny; spany <= finalMaxy; spany++, f++) {
2968 for (span = *f; span; span=span->next) {
2969 if (span->max <= span->min)
2971 xSpan->x = span->min;
2973 xSpan->width = span->max - span->min;
2979 gdk_fb_fill_spans(pDrawable, pGC, xSpans, i, TRUE);
2981 disposeFinalSpans ();
2983 DEALLOCATE_LOCAL (xSpans);
2990 # define SPAN_REALLOC 100
2992 # define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \
2993 &finalSpans[(y) - finalMiny] : \
2996 static struct finalSpan **
2997 realFindSpan (int y)
2999 struct finalSpan **newSpans;
3000 int newSize, newMiny, newMaxy;
3004 if (y < finalMiny || y > finalMaxy) {
3010 change = finalMiny - y;
3012 change = y - finalMaxy;
3013 if (change >= SPAN_REALLOC)
3014 change += SPAN_REALLOC;
3016 change = SPAN_REALLOC;
3017 newSize = finalSize + change;
3018 newSpans = (struct finalSpan **) g_malloc
3019 (newSize * sizeof (struct finalSpan *));
3021 return (struct finalSpan **)NULL;
3022 newMiny = finalMiny;
3023 newMaxy = finalMaxy;
3025 newMiny = finalMiny - change;
3027 newMaxy = finalMaxy + change;
3029 g_memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *),
3030 (char *) finalSpans,
3031 finalSize * sizeof (struct finalSpan *));
3032 g_free (finalSpans);
3034 if ((i = finalMiny - newMiny) > 0)
3035 memset ((char *)newSpans, 0, i * sizeof (struct finalSpan *));
3036 if ((i = newMaxy - finalMaxy) > 0)
3037 memset ((char *)(newSpans + newSize - i), 0,
3038 i * sizeof (struct finalSpan *));
3039 finalSpans = newSpans;
3040 finalMaxy = newMaxy;
3041 finalMiny = newMiny;
3042 finalSize = newSize;
3044 return &finalSpans[y - finalMiny];
3048 newFinalSpan (int y, register int xmin, register int xmax)
3050 register struct finalSpan *x;
3051 register struct finalSpan **f;
3052 struct finalSpan *oldx;
3053 struct finalSpan *prev;
3061 for (x = *f; x; x=x->next) {
3066 if (x->min <= xmax && xmin <= x->max) {
3068 oldx->min = MIN (x->min, xmin);
3069 oldx->max = MAX (x->max, xmax);
3071 prev->next = x->next;
3076 x->min = MIN (x->min, xmin);
3077 x->max = MAX (x->max, xmax);
3090 x = allocFinalSpan ();
3103 mirrorSppPoint (int quadrant, SppPointPtr sppPoint)
3109 sppPoint->x = -sppPoint->x;
3112 sppPoint->x = -sppPoint->x;
3113 sppPoint->y = -sppPoint->y;
3116 sppPoint->y = -sppPoint->y;
3120 * and translate to X coordinate system
3122 sppPoint->y = -sppPoint->y;
3126 * split an arc into pieces which are scan-converted
3127 * in the first-quadrant and mirrored into position.
3128 * This is necessary as the scan-conversion code can
3129 * only deal with arcs completely contained in the
3134 drawArc (miArc *tarc, int l, int a0, int a1, miArcFacePtr right, miArcFacePtr left)
3135 /* miArcFacePtr right, left; */ /* save end line points */
3138 struct accelerators acc;
3139 int startq, endq, curq;
3140 int rightq, leftq = 0, righta = 0, lefta = 0;
3141 miArcFacePtr passRight, passLeft;
3142 int q0 = 0, q1 = 0, mask;
3146 } band[5], sweep[20];
3147 int bandno, sweepno;
3149 int flipRight = 0, flipLeft = 0;
3151 miArcSpanData *spdata;
3154 spdata = miComputeWideEllipse(l, tarc, &mustFree);
3160 startq = a0 / (90 * 64);
3164 endq = (a1-1) / (90 * 64);
3176 q1 = MIN (a1, 90 * 64);
3179 if (curq == startq && a0 == q0 && rightq < 0) {
3183 if (curq == endq && a1 == q1) {
3192 q0 = 180 * 64 - MIN (a1, 180 * 64);
3196 q1 = 180 * 64 - MAX (a0, 90 * 64);
3197 if (curq == startq && 180 * 64 - a0 == q1) {
3201 if (curq == endq && 180 * 64 - a1 == q0) {
3210 q0 = MAX (a0, 180 * 64) - 180 * 64;
3214 q1 = MIN (a1, 270 * 64) - 180 * 64;
3215 if (curq == startq && a0 - 180*64 == q0) {
3219 if (curq == endq && a1 - 180 * 64 == q1) {
3228 q0 = 360 * 64 - MIN (a1, 360 * 64);
3229 q1 = 360 * 64 - MAX (a0, 270 * 64);
3230 if (curq == startq && 360 * 64 - a0 == q1) {
3234 if (curq == endq && 360 * 64 - a1 == q0) {
3240 band[bandno].a0 = q0;
3241 band[bandno].a1 = q1;
3242 band[bandno].mask = 1 << curq;
3259 * find left-most point
3261 for (i = 0; i < bandno; i++)
3262 if (band[i].a0 <= q0) {
3265 mask = band[i].mask;
3270 * locate next point of change
3272 for (i = 0; i < bandno; i++)
3273 if (!(mask & band[i].mask)) {
3274 if (band[i].a0 == q0) {
3275 if (band[i].a1 < q1)
3277 mask |= band[i].mask;
3278 } else if (band[i].a0 < q1)
3282 * create a new sweep
3284 sweep[sweepno].a0 = q0;
3285 sweep[sweepno].a1 = q1;
3286 sweep[sweepno].mask = mask;
3289 * subtract the sweep from the affected bands
3291 for (i = 0; i < bandno; i++)
3292 if (band[i].a0 == q0) {
3295 * check if this band is empty
3297 if (band[i].a0 == band[i].a1)
3298 band[i].a1 = band[i].a0 = 90 * 64 + 1;
3301 computeAcc (tarc, l, &def, &acc);
3302 for (j = 0; j < sweepno; j++) {
3303 mask = sweep[j].mask;
3304 passRight = passLeft = NULL;
3305 if (mask & (1 << rightq)) {
3306 if (sweep[j].a0 == righta)
3308 else if (sweep[j].a1 == righta) {
3313 if (mask & (1 << leftq)) {
3314 if (sweep[j].a1 == lefta)
3320 else if (sweep[j].a0 == lefta) {
3327 drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask,
3328 passRight, passLeft, spdata);
3331 * when copyEnd is set, both ends of the arc were computed
3332 * at the same time; drawQuadrant only takes one end though,
3333 * so the left end will be the only one holding the data. Copy
3339 * mirror the coordinates generated for the
3343 mirrorSppPoint (rightq, &right->clock);
3344 mirrorSppPoint (rightq, &right->center);
3345 mirrorSppPoint (rightq, &right->counterClock);
3349 temp = right->clock;
3350 right->clock = right->counterClock;
3351 right->counterClock = temp;
3355 mirrorSppPoint (leftq, &left->counterClock);
3356 mirrorSppPoint (leftq, &left->center);
3357 mirrorSppPoint (leftq, &left->clock);
3362 left->clock = left->counterClock;
3363 left->counterClock = temp;
3371 drawQuadrant (struct arc_def *def, struct accelerators *acc,
3372 int a0, int a1, int mask, miArcFacePtr right,
3373 miArcFacePtr left, miArcSpanData *spdata)
3375 struct arc_bound bound;
3381 def->a0 = ((double) a0) / 64.0;
3382 def->a1 = ((double) a1) / 64.0;
3383 computeBound (def, &bound, acc, right, left);
3384 yy = bound.inner.min;
3385 if (bound.outer.min < yy)
3386 yy = bound.outer.min;
3387 miny = ICEIL(yy - acc->fromIntY);
3388 yy = bound.inner.max;
3389 if (bound.outer.max > yy)
3390 yy = bound.outer.max;
3391 maxy = floor(yy - acc->fromIntY);
3393 span = spdata->spans;
3396 if (a1 == 90 * 64 && (mask & 1))
3397 newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1);
3400 for (n = spdata->count1; --n >= 0; )
3406 span->lx, -span->lx, 0, span->lx + span->lw,
3407 def, &bound, acc, mask);
3408 if (span->rw + span->rx)
3409 tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask);
3419 arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc);
3421 for (n = spdata->count2; --n >= 0; )
3426 arcSpan (y, span->lx, span->lw, span->rx, span->rw,
3427 def, &bound, acc, mask);
3431 if (spdata->bot && miny <= y && y <= maxy)
3436 if (span->rw <= 0) {
3437 arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw,
3438 def, &bound, acc, n);
3439 if (span->rw + span->rx)
3440 tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n);
3443 arcSpan0 (span->lx, span->lw, span->rx, span->rw,
3444 def, &bound, acc, n);
3448 yy = y + acc->fromIntY;
3449 if (def->w == def->h) {
3450 xalt = def->w - def->l;
3451 x = -sqrt(xalt * xalt - yy * yy);
3453 x = tailX(yy, def, &bound, acc);
3454 if (acc->left.valid && boundedLe (yy, bound.left)) {
3455 xalt = intersectLine (yy, acc->left);
3459 if (acc->right.valid && boundedLe (yy, bound.right)) {
3460 xalt = intersectLine (yy, acc->right);
3466 ICEIL(acc->fromIntX - x), 0,
3467 ICEIL(acc->fromIntX + x), 0,
3468 def, &bound, acc, mask);