You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3719 lines
84 KiB

/***********************************************************
Copyright 1987, 1998 The Open Group
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Digital not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/
/* Author: Keith Packard and Bob Scheifler */
/* Warning: this code is toxic, do not dally very long here. */
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#if defined(_XOPEN_SOURCE)
#include <math.h>
#else
#define _XOPEN_SOURCE /* to get prototype for hypot on some systems */
#include <math.h>
#undef _XOPEN_SOURCE
#endif
#include <X11/X.h>
#include <X11/Xprotostr.h>
#include "misc.h"
#include "gcstruct.h"
#include "scrnintstr.h"
#include "pixmapstr.h"
#include "windowstr.h"
#include "mifpoly.h"
#include "mi.h"
#include "mifillarc.h"
#include <X11/Xfuncproto.h>
static double miDsin(double a);
static double miDcos(double a);
static double miDasin(double v);
static double miDatan2(double dy, double dx);
double cbrt(double);
#ifdef ICEILTEMPDECL
ICEILTEMPDECL
#endif
/*
* some interesting sematic interpretation of the protocol:
*
* Self intersecting arcs (i.e. those spanning 360 degrees)
* never join with other arcs, and are drawn without caps
* (unless on/off dashed, in which case each dash segment
* is capped, except when the last segment meets the
* first segment, when no caps are drawn)
*
* double dash arcs are drawn in two parts, first the
* odd dashes (drawn in background) then the even dashes
* (drawn in foreground). This means that overlapping
* sections of foreground/background are drawn twice,
* first in background then in foreground. The double-draw
* occurs even when the function uses the destination values
* (e.g. xor mode). This is the same way the wide-line
* code works and should be "fixed".
*
*/
#undef max
#undef min
#if defined (__GNUC__) && !defined (__STRICT_ANSI__)
#define USE_INLINE
#endif
#ifdef USE_INLINE
inline static int max (const int x, const int y)
{
return x>y? x:y;
}
inline static int min (const int x, const int y)
{
return x<y? x:y;
}
#else
static int
max (int x, int y)
{
return x>y? x:y;
}
static int
min (int x, int y)
{
return x<y? x:y;
}
#endif
struct bound {
double min, max;
};
struct ibound {
int min, max;
};
#define boundedLe(value, bounds)\
((bounds).min <= (value) && (value) <= (bounds).max)
struct line {
double m, b;
int valid;
};
#define intersectLine(y,line) (line.m * (y) + line.b)
/*
* these are all y value bounds
*/
struct arc_bound {
struct bound ellipse;
struct bound inner;
struct bound outer;
struct bound right;
struct bound left;
struct ibound inneri;
struct ibound outeri;
};
struct accelerators {
double tail_y;
double h2;
double w2;
double h4;
double w4;
double h2mw2;
double h2l;
double w2l;
double fromIntX;
double fromIntY;
struct line left, right;
int yorgu;
int yorgl;
int xorg;
};
struct arc_def {
double w, h, l;
double a0, a1;
};
# define todeg(xAngle) (((double) (xAngle)) / 64.0)
# define RIGHT_END 0
# define LEFT_END 1
typedef struct _miArcJoin {
int arcIndex0, arcIndex1;
int phase0, phase1;
int end0, end1;
} miArcJoinRec, *miArcJoinPtr;
typedef struct _miArcCap {
int arcIndex;
int end;
} miArcCapRec, *miArcCapPtr;
typedef struct _miArcFace {
SppPointRec clock;
SppPointRec center;
SppPointRec counterClock;
} miArcFaceRec, *miArcFacePtr;
typedef struct _miArcData {
xArc arc;
int render; /* non-zero means render after drawing */
int join; /* related join */
int cap; /* related cap */
int selfJoin; /* final dash meets first dash */
miArcFaceRec bounds[2];
double x0, y0, x1, y1;
} miArcDataRec, *miArcDataPtr;
/*
* This is an entire sequence of arcs, computed and categorized according
* to operation. miDashArcs generates either one or two of these.
*/
typedef struct _miPolyArc {
int narcs;
miArcDataPtr arcs;
int ncaps;
miArcCapPtr caps;
int njoins;
miArcJoinPtr joins;
} miPolyArcRec, *miPolyArcPtr;
#define GCValsFunction 0
#define GCValsForeground 1
#define GCValsBackground 2
#define GCValsLineWidth 3
#define GCValsCapStyle 4
#define GCValsJoinStyle 5
#define GCValsMask (GCFunction | GCForeground | GCBackground | \
GCLineWidth | GCCapStyle | GCJoinStyle)
static CARD32 gcvals[6];
static void fillSpans(DrawablePtr pDrawable, GCPtr pGC);
static void newFinalSpan(int y, register int xmin, register int xmax);
static void drawArc(xArc *tarc, int l, int a0, int a1, miArcFacePtr right,
miArcFacePtr left);
static void drawZeroArc(DrawablePtr pDraw, GCPtr pGC, xArc *tarc, int lw,
miArcFacePtr left, miArcFacePtr right);
static void miArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
double xFtransLeft, double yFtransLeft,
int xOrgRight, int yOrgRight,
double xFtransRight, double yFtransRight);
static void miArcCap(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pFace,
int end, int xOrg, int yOrg, double xFtrans,
double yFtrans);
static void miRoundCap(DrawablePtr pDraw, GCPtr pGC, SppPointRec pCenter,
SppPointRec pEnd, SppPointRec pCorner,
SppPointRec pOtherCorner, int fLineEnd,
int xOrg, int yOrg, double xFtrans, double yFtrans);
static void miFreeArcs(miPolyArcPtr arcs, GCPtr pGC);
static miPolyArcPtr miComputeArcs(xArc *parcs, int narcs, GCPtr pGC);
static int miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts);
# define CUBED_ROOT_2 1.2599210498948732038115849718451499938964
# define CUBED_ROOT_4 1.5874010519681993173435330390930175781250
/*
* draw one segment of the arc using the arc spans generation routines
*/
static void
miArcSegment(
DrawablePtr pDraw,
GCPtr pGC,
xArc tarc,
miArcFacePtr right,
miArcFacePtr left)
{
int l = pGC->lineWidth;
int a0, a1, startAngle, endAngle;
miArcFacePtr temp;
if (!l)
l = 1;
if (tarc.width == 0 || tarc.height == 0) {
drawZeroArc (pDraw, pGC, &tarc, l, left, right);
return;
}
if (pGC->miTranslate) {
tarc.x += pDraw->x;
tarc.y += pDraw->y;
}
a0 = tarc.angle1;
a1 = tarc.angle2;
if (a1 > FULLCIRCLE)
a1 = FULLCIRCLE;
else if (a1 < -FULLCIRCLE)
a1 = -FULLCIRCLE;
if (a1 < 0) {
startAngle = a0 + a1;
endAngle = a0;
temp = right;
right = left;
left = temp;
} else {
startAngle = a0;
endAngle = a0 + a1;
}
/*
* bounds check the two angles
*/
if (startAngle < 0)
startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
if (startAngle >= FULLCIRCLE)
startAngle = startAngle % FULLCIRCLE;
if (endAngle < 0)
endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE;
if (endAngle > FULLCIRCLE)
endAngle = (endAngle-1) % FULLCIRCLE + 1;
if ((startAngle == endAngle) && a1) {
startAngle = 0;
endAngle = FULLCIRCLE;
}
drawArc (&tarc, l, startAngle, endAngle, right, left);
}
/*
Three equations combine to describe the boundaries of the arc
x^2/w^2 + y^2/h^2 = 1 ellipse itself
(X-x)^2 + (Y-y)^2 = r^2 circle at (x, y) on the ellipse
(Y-y) = (X-x)*w^2*y/(h^2*x) normal at (x, y) on the ellipse
These lead to a quartic relating Y and y
y^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2
- (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0
The reducible cubic obtained from this quartic is
z^3 - (3N)z^2 - 2V = 0
where
N = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6
V = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2)
Let
t = z - N
p = -N^2
q = -N^3 - V
Then we get
t^3 + 3pt + 2q = 0
The discriminant of this cubic is
D = q^2 + p^3
When D > 0, a real root is obtained as
z = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D))
When D < 0, a real root is obtained as
z = N - 2m*cos(acos(-q/m^3)/3)
where
m = sqrt(|p|) * sign(q)
Given a real root Z of the cubic, the roots of the quartic are the roots
of the two quadratics
y^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0
where
A = +/- sqrt(8Z + b^2 - 4c)
b, c, d are the cubic, quadratic, and linear coefficients of the quartic
Some experimentation is then required to determine which solutions
correspond to the inner and outer boundaries.
*/
typedef struct {
short lx, lw, rx, rw;
} miArcSpan;
typedef struct {
miArcSpan *spans;
int count1, count2, k;
char top, bot, hole;
} miArcSpanData;
typedef struct {
unsigned long lrustamp;
unsigned short lw;
unsigned short width, height;
miArcSpanData *spdata;
} arcCacheRec;
#define CACHESIZE 25
static void drawQuadrant(struct arc_def *def, struct accelerators *acc,
int a0, int a1, int mask, miArcFacePtr right,
miArcFacePtr left, miArcSpanData *spdata);
static arcCacheRec arcCache[CACHESIZE];
static unsigned long lrustamp;
static arcCacheRec *lastCacheHit = &arcCache[0];
static RESTYPE cacheType;
/*
* External so it can be called when low on memory.
* Call with a zero ID in that case.
*/
/*ARGSUSED*/
int
miFreeArcCache (data, id)
pointer data;
XID id;
{
int k;
arcCacheRec *cent;
if (id)
cacheType = 0;
for (k = CACHESIZE, cent = &arcCache[0]; --k >= 0; cent++)
{
if (cent->spdata)
{
cent->lrustamp = 0;
cent->lw = 0;
free(cent->spdata);
cent->spdata = NULL;
}
}
lrustamp = 0;
return Success;
}
static void
miComputeCircleSpans(
int lw,
xArc *parc,
miArcSpanData *spdata)
{
miArcSpan *span;
int doinner;
int x, y, e;
int xk, yk, xm, ym, dx, dy;
int slw, inslw;
int inx = 0, iny, ine = 0;
int inxk = 0, inyk = 0, inxm = 0, inym = 0;
doinner = -lw;
slw = parc->width - doinner;
dy = parc->height & 1;
dx = 1 - dy;
MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym);
inslw = parc->width + doinner;
if (inslw > 0)
{
spdata->hole = spdata->top;
MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym);
}
else
{
spdata->hole = FALSE;
doinner = -y;
}
spdata->count1 = -doinner - spdata->top;
spdata->count2 = y + doinner;
span = spdata->spans;
while (y)
{
MIFILLARCSTEP(slw);
span->lx = dy - x;
if (++doinner <= 0)
{
span->lw = slw;
span->rx = 0;
span->rw = span->lx + slw;
}
else
{
MIFILLINARCSTEP(inslw);
span->lw = x - inx;
span->rx = dy - inx + inslw;
span->rw = inx - x + slw - inslw;
}
span++;
}
if (spdata->bot)
{
if (spdata->count2)
spdata->count2--;
else
{
if (lw > (int)parc->height)
span[-1].rx = span[-1].rw = -((lw - (int)parc->height) >> 1);
else
span[-1].rw = 0;
spdata->count1--;
}
}
}
static void
miComputeEllipseSpans(
int lw,
xArc *parc,
miArcSpanData *spdata)
{
miArcSpan *span;
double w, h, r, xorg;
double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
double A, T, b, d, x, y, t, inx, outx = 0.0, hepp, hepm;
int flip, solution;
w = (double)parc->width / 2.0;
h = (double)parc->height / 2.0;
r = lw / 2.0;
rs = r * r;
Hs = h * h;
WH = w * w - Hs;
Nk = w * r;
Vk = (Nk * Hs) / (WH + WH);
Hf = Hs * Hs;
Nk = (Hf - Nk * Nk) / WH;
Fk = Hf / WH;
hepp = h + EPSILON;
hepm = h - EPSILON;
K = h + ((lw - 1) >> 1);
span = spdata->spans;
if (parc->width & 1)
xorg = .5;
else
xorg = 0.0;
if (spdata->top)
{
span->lx = 0;
span->lw = 1;
span++;
}
spdata->count1 = 0;
spdata->count2 = 0;
spdata->hole = (spdata->top &&
(int)parc->height * lw <= (int)(parc->width * parc->width) &&
lw < (int)parc->height);
for (; K > 0.0; K -= 1.0)
{
N = (K * K + Nk) / 6.0;
Nc = N * N * N;
Vr = Vk * K;
t = Nc + Vr * Vr;
d = Nc + t;
if (d < 0.0) {
d = Nc;
b = N;
if ( (b < 0.0) == (t < 0.0) )
{
b = -b;
d = -d;
}
Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
if ( (Z < 0.0) == (Vr < 0.0) )
flip = 2;
else
flip = 1;
}
else
{
d = Vr * sqrt(d);
Z = N + cbrt(t + d) + cbrt(t - d);
flip = 0;
}
A = sqrt((Z + Z) - Nk);
T = (Fk - Z) * K / A;
inx = 0.0;
solution = FALSE;
b = -A + K;
d = b * b - 4 * (Z + T);
if (d >= 0)
{
d = sqrt(d);
y = (b + d) / 2;
if ((y >= 0.0) && (y < hepp))
{
solution = TRUE;
if (y > hepm)
y = h;
t = y / h;
x = w * sqrt(1 - (t * t));
t = K - y;
if (rs - (t * t) >= 0)
t = sqrt(rs - (t * t));
else
t = 0;
if (flip == 2)
inx = x - t;
else
outx = x + t;
}
}
b = A + K;
d = b * b - 4 * (Z - T);
/* Because of the large magnitudes involved, we lose enough precision
* that sometimes we end up with a negative value near the axis, when
* it should be positive. This is a workaround.
*/
if (d < 0 && !solution)
d = 0.0;
if (d >= 0) {
d = sqrt(d);
y = (b + d) / 2;
if (y < hepp)
{
if (y > hepm)
y = h;
t = y / h;
x = w * sqrt(1 - (t * t));
t = K - y;
if (rs - (t * t) >= 0)
inx = x - sqrt(rs - (t * t));
else
inx = x;
}
y = (b - d) / 2;
if (y >= 0.0)
{
if (y > hepm)
y = h;
t = y / h;
x = w * sqrt(1 - (t * t));
t = K - y;
if (rs - (t * t) >= 0)
t = sqrt(rs - (t * t));
else
t = 0;
if (flip == 1)
inx = x - t;
else
outx = x + t;
}
}
span->lx = ICEIL(xorg - outx);
if (inx <= 0.0)
{
spdata->count1++;
span->lw = ICEIL(xorg + outx) - span->lx;
span->rx = ICEIL(xorg + inx);
span->rw = -ICEIL(xorg - inx);
}
else
{
spdata->count2++;
span->lw = ICEIL(xorg - inx) - span->lx;
span->rx = ICEIL(xorg + inx);
span->rw = ICEIL(xorg + outx) - span->rx;
}
span++;
}
if (spdata->bot)
{
outx = w + r;
if (r >= h && r <= w)
inx = 0.0;
else if (Nk < 0.0 && -Nk < Hs)
{
inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
if (inx > w - r)
inx = w - r;
}
else
inx = w - r;
span->lx = ICEIL(xorg - outx);
if (inx <= 0.0)
{
span->lw = ICEIL(xorg + outx) - span->lx;
span->rx = ICEIL(xorg + inx);
span->rw = -ICEIL(xorg - inx);
}
else
{
span->lw = ICEIL(xorg - inx) - span->lx;
span->rx = ICEIL(xorg + inx);
span->rw = ICEIL(xorg + outx) - span->rx;
}
}
if (spdata->hole)
{
span = &spdata->spans[spdata->count1];
span->lw = -span->lx;
span->rx = 1;
span->rw = span->lw;
spdata->count1--;
spdata->count2++;
}
}
static double
tailX(
double K,
struct arc_def *def,
struct arc_bound *bounds,
struct accelerators *acc)
{
double w, h, r;
double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
double A, T, b, d, x, y, t, hepp, hepm;
int flip, solution;
double xs[2];
double *xp;
w = def->w;
h = def->h;
r = def->l;
rs = r * r;
Hs = acc->h2;
WH = -acc->h2mw2;
Nk = def->w * r;
Vk = (Nk * Hs) / (WH + WH);
Hf = acc->h4;
Nk = (Hf - Nk * Nk) / WH;
if (K == 0.0) {
if (Nk < 0.0 && -Nk < Hs) {
xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
xs[1] = w - r;
if (acc->left.valid && boundedLe(K, bounds->left) &&
!boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
return xs[1];
if (acc->right.valid && boundedLe(K, bounds->right) &&
!boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
return xs[1];
return xs[0];
}
return w - r;
}
Fk = Hf / WH;
hepp = h + EPSILON;
hepm = h - EPSILON;
N = (K * K + Nk) / 6.0;
Nc = N * N * N;
Vr = Vk * K;
xp = xs;
xs[0] = 0.0;
t = Nc + Vr * Vr;
d = Nc + t;
if (d < 0.0) {
d = Nc;
b = N;
if ( (b < 0.0) == (t < 0.0) )
{
b = -b;
d = -d;
}
Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
if ( (Z < 0.0) == (Vr < 0.0) )
flip = 2;
else
flip = 1;
}
else
{
d = Vr * sqrt(d);
Z = N + cbrt(t + d) + cbrt(t - d);
flip = 0;
}
A = sqrt((Z + Z) - Nk);
T = (Fk - Z) * K / A;
solution = FALSE;
b = -A + K;
d = b * b - 4 * (Z + T);
if (d >= 0 && flip == 2)
{
d = sqrt(d);
y = (b + d) / 2;
if ((y >= 0.0) && (y < hepp))
{
solution = TRUE;
if (y > hepm)
y = h;
t = y / h;
x = w * sqrt(1 - (t * t));
t = K - y;
if (rs - (t * t) >= 0)
t = sqrt(rs - (t * t));
else
t = 0;
*xp++ = x - t;
}
}
b = A + K;
d = b * b - 4 * (Z - T);
/* Because of the large magnitudes involved, we lose enough precision
* that sometimes we end up with a negative value near the axis, when
* it should be positive. This is a workaround.
*/
if (d < 0 && !solution)
d = 0.0;
if (d >= 0) {
d = sqrt(d);
y = (b + d) / 2;
if (y < hepp)
{
if (y > hepm)
y = h;
t = y / h;
x = w * sqrt(1 - (t * t));
t = K - y;
if (rs - (t * t) >= 0)
*xp++ = x - sqrt(rs - (t * t));
else
*xp++ = x;
}
y = (b - d) / 2;
if (y >= 0.0 && flip == 1)
{
if (y > hepm)
y = h;
t = y / h;
x = w * sqrt(1 - (t * t));
t = K - y;
if (rs - (t * t) >= 0)
t = sqrt(rs - (t * t));
else
t = 0;
*xp++ = x - t;
}
}
if (xp > &xs[1]) {
if (acc->left.valid && boundedLe(K, bounds->left) &&
!boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
return xs[1];
if (acc->right.valid && boundedLe(K, bounds->right) &&
!boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
return xs[1];
}
return xs[0];
}
static miArcSpanData *
miComputeWideEllipse(
int lw,
register xArc *parc,
Bool *mustFree)
{
miArcSpanData *spdata;
arcCacheRec *cent, *lruent;
int k;
arcCacheRec fakeent;
if (!lw)
lw = 1;
if (parc->height <= 1500)
{
*mustFree = FALSE;
cent = lastCacheHit;
if (cent->lw == lw &&
cent->width == parc->width && cent->height == parc->height)
{
cent->lrustamp = ++lrustamp;
return cent->spdata;
}
lruent = &arcCache[0];
for (k = CACHESIZE, cent = lruent; --k >= 0; cent++)
{
if (cent->lw == lw &&
cent->width == parc->width && cent->height == parc->height)
{
cent->lrustamp = ++lrustamp;
lastCacheHit = cent;
return cent->spdata;
}
if (cent->lrustamp < lruent->lrustamp)
lruent = cent;
}
if (!cacheType)
{
cacheType = CreateNewResourceType(miFreeArcCache);
(void) AddResource(FakeClientID(0), cacheType, NULL);
}
} else {
lruent = &fakeent;
lruent->spdata = NULL;
*mustFree = TRUE;
}
k = (parc->height >> 1) + ((lw - 1) >> 1);
spdata = lruent->spdata;
if (!spdata || spdata->k != k)
{
if (spdata)
free(spdata);
spdata = (miArcSpanData *)malloc(sizeof(miArcSpanData) +
sizeof(miArcSpan) * (k + 2));
lruent->spdata = spdata;
if (!spdata)
{
lruent->lrustamp = 0;
lruent->lw = 0;
return spdata;
}
spdata->spans = (miArcSpan *)(spdata + 1);
spdata->k = k;
}
spdata->top = !(lw & 1) && !(parc->width & 1);
spdata->bot = !(parc->height & 1);
lruent->lrustamp = ++lrustamp;
lruent->lw = lw;
lruent->width = parc->width;
lruent->height = parc->height;
if (lruent != &fakeent)
lastCacheHit = lruent;
if (parc->width == parc->height)
miComputeCircleSpans(lw, parc, spdata);
else
miComputeEllipseSpans(lw, parc, spdata);
return spdata;
}
static void
miFillWideEllipse(
DrawablePtr pDraw,
GCPtr pGC,
xArc *parc)
{
DDXPointPtr points;
DDXPointPtr pts;
int *widths;
int *wids;
miArcSpanData *spdata;
Bool mustFree;
miArcSpan *span;
int xorg, yorgu, yorgl;
yorgu = parc->height + pGC->lineWidth;
int n = (sizeof(int) * 2) * yorgu;
widths = (int *)ALLOCATE_LOCAL(n + (sizeof(DDXPointRec) * 2) * yorgu);
if (!widths)
return;
points = (DDXPointPtr)((char *)widths + n);
spdata = miComputeWideEllipse((int)pGC->lineWidth, parc, &mustFree);
if (!spdata)
{
DEALLOCATE_LOCAL(widths);
return;
}
pts = points;
wids = widths;
span = spdata->spans;
xorg = parc->x + (parc->width >> 1);
yorgu = parc->y + (parc->height >> 1);
yorgl = yorgu + (parc->height & 1);
if (pGC->miTranslate)
{
xorg += pDraw->x;
yorgu += pDraw->y;
yorgl += pDraw->y;
}
yorgu -= spdata->k;
yorgl += spdata->k;
if (spdata->top)
{
pts->x = xorg;
pts->y = yorgu - 1;
pts++;
*wids++ = 1;
span++;
}
for (n = spdata->count1; --n >= 0; )
{
pts[0].x = xorg + span->lx;
pts[0].y = yorgu;
wids[0] = span->lw;
pts[1].x = pts[0].x;
pts[1].y = yorgl;
wids[1] = wids[0];
yorgu++;
yorgl--;
pts += 2;
wids += 2;
span++;
}
if (spdata->hole)
{
pts[0].x = xorg;
pts[0].y = yorgl;
wids[0] = 1;
pts++;
wids++;
}
for (n = spdata->count2; --n >= 0; )
{
pts[0].x = xorg + span->lx;
pts[0].y = yorgu;
wids[0] = span->lw;
pts[1].x = xorg + span->rx;
pts[1].y = pts[0].y;
wids[1] = span->rw;
pts[2].x = pts[0].x;
pts[2].y = yorgl;
wids[2] = wids[0];
pts[3].x = pts[1].x;
pts[3].y = pts[2].y;
wids[3] = wids[1];
yorgu++;
yorgl--;
pts += 4;
wids += 4;
span++;
}
if (spdata->bot)
{
if (span->rw <= 0)
{
pts[0].x = xorg + span->lx;
pts[0].y = yorgu;
wids[0] = span->lw;
pts++;
wids++;
}
else
{
pts[0].x = xorg + span->lx;
pts[0].y = yorgu;
wids[0] = span->lw;
pts[1].x = xorg + span->rx;
pts[1].y = pts[0].y;
wids[1] = span->rw;
pts += 2;
//wids += 2;
}
}
if (mustFree)
free(spdata);
(*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE);
DEALLOCATE_LOCAL(widths);
}
/*
* miPolyArc strategy:
*
* If arc is zero width and solid, we don't have to worry about the rasterop
* or join styles. For wide solid circles, we use a fast integer algorithm.
* For wide solid ellipses, we use special case floating point code.
* Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then
* draw using pGCTo and pDrawTo. If the raster-op was "tricky," that is,
* if it involves the destination, then we use PushPixels to move the bits
* from the scratch drawable to pDraw. (See the wide line code for a
* fuller explanation of this.)
*/
_X_EXPORT void
miPolyArc(pDraw, pGC, narcs, parcs)
DrawablePtr pDraw;
GCPtr pGC;
int narcs;
xArc *parcs;
{
int i;
xArc *parc;
int xMin, xMax, yMin, yMax;
int pixmapWidth = 0, pixmapHeight = 0;
int xOrg = 0, yOrg = 0;
int width;
Bool fTricky;
DrawablePtr pDrawTo;
CARD32 fg, bg;
GCPtr pGCTo;
miPolyArcPtr polyArcs;
int cap[2], join[2];
int iphase;
int halfWidth;
width = pGC->lineWidth;
if(width == 0 && pGC->lineStyle == LineSolid)
{
for(i = narcs, parc = parcs; --i >= 0; parc++)
miArcSegment( pDraw, pGC, *parc,
(miArcFacePtr) 0, (miArcFacePtr) 0 );
fillSpans (pDraw, pGC);
}
else
{
if ((pGC->lineStyle == LineSolid) && narcs)
{
while (parcs->width && parcs->height &&
(parcs->angle2 >= FULLCIRCLE ||
parcs->angle2 <= -FULLCIRCLE))
{
miFillWideEllipse(pDraw, pGC, parcs);
if (!--narcs)
return;
parcs++;
}
}
/* Set up pDrawTo and pGCTo based on the rasterop */
switch(pGC->alu)
{
case GXclear: /* 0 */
case GXcopy: /* src */
case GXcopyInverted: /* NOT src */
case GXset: /* 1 */
fTricky = FALSE;
pDrawTo = pDraw;
pGCTo = pGC;
break;
default:
fTricky = TRUE;
/* find bounding box around arcs */
xMin = yMin = MAXSHORT;
xMax = yMax = MINSHORT;
for(i = narcs, parc = parcs; --i >= 0; parc++)
{
xMin = min (xMin, parc->x);
yMin = min (yMin, parc->y);
xMax = max (xMax, (parc->x + (int) parc->width));
yMax = max (yMax, (parc->y + (int) parc->height));
}
/* expand box to deal with line widths */
halfWidth = (width + 1)/2;
xMin -= halfWidth;
yMin -= halfWidth;
xMax += halfWidth;
yMax += halfWidth;