2021-10-11 18:37:13 -03:00

867 lines
18 KiB
C

#ifndef lint
static char sccsid[] = "@(#)mem_vec.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright 1987-1990 Sun Microsystems, Inc.
*/
#include <sys/types.h>
#include <pixrect/pixrect.h>
#include <pixrect/pr_impl_util.h>
#include <pixrect/memvar.h>
#include <pixrect/mem_rop_impl_ops.h>
/* largest delta we can handle without overflow in clip code */
#define MAXDELTA (32767)
/* types */
typedef u_short PIX1;
typedef u_char PIX8;
typedef u_short PIX16;
typedef u_long PIX32;
typedef IFSPARC(u_long,PIX1) TMP1;
typedef IFSPARC(u_long,PIX8) TMP8;
typedef IFSPARC(u_long,PIX16) TMP16;
/* leftmost bit of specified type */
#define LEFTBIT(type) ((type) (1 << (sizeof (type) * 8 - 1)))
/* reflect flag bits */
#define REFLECT_YMAJOR 1
#define REFLECT_XAXIS 2
/* use generic loop for x-major and y-major vector */
#ifndef GENERIC_XY
#define GENERIC_XY !defined(mc68000)
#endif
/* define LINE_TAG to tag certain lines */
#ifdef LINE_TAG
#undef LINE_TAG
#define LINE_TAG(args) (_line_tag(__LINE__), _line_tag_args args)
#else
#define LINE_TAG(args)
#endif
#define swap(a, b, t) _STMT((t) = (a); (a) = (b); (b) = (t);)
/* align pointer to 32 bit boundary */
#define DST_ALIGN(dp, xbits) \
if ((u_int) (dp) & 2) { \
PTR_INCR(PIX32 *, (dp), -2); \
(xbits) += 16; \
} \
else
/* opcode dispatch macro */
#define CASE_OP(op,depth1,macro,arg1,arg2) _STMT( \
depth1( \
switch (op) { \
case 15: macro(F,IFFALSE,arg1,arg2); break; \
case 5: macro(5,IFFALSE,arg1,arg2); break; \
case 0: macro(0,IFFALSE,arg1,arg2); break; \
} \
, \
if ((op) == 12) \
macro(C,IFFALSE,arg1,arg2); \
else \
switch (op) { \
case 6: macro(6,IFFALSE,arg1,arg2); break; \
case 14: macro(E,IFFALSE,arg1,arg2); break; \
case 8: macro(8,IFFALSE,arg1,arg2); break; \
case 4: macro(4,IFTRUE,arg1,arg2); break; \
case 7: macro(7,IFTRUE,arg1,arg2); break; \
} \
))
/* x/y-major dispatch macro */
#define CASE_XY(op,depth1,macro,arg1,arg2) \
if (reflect & REFLECT_YMAJOR) \
CASE_OP(op,depth1,_CAT(macro,Y),arg1,arg2); \
else \
CASE_OP(op,depth1,_CAT(macro,X),arg1,arg2) \
#if GENERIC_XY
#define CASE_G(op,depth1,macro,arg1,arg2) \
CASE_OP(op,depth1,_CAT(macro,XY),arg1,arg2)
#else
#define CASE_G CASE_XY
#endif
/* point operation macros */
#define MPOINTT(op,usetmp,d,m,k,t) _STMT( \
usetmp((t) = *(d);,) \
*(d) = _CAT(OP_mfill,op)(usetmp((t), *(d)), (m), (k)); )
#define MPOINT(op,d,m,k) \
(*(d) = _CAT(OP_mfill,op)(*(d), (m), (k)))
#define UPOINT(op,d,k) \
(*(d) = _CAT(OP_ufill,op)(*(d), (k)))
/* vertical vectors */
#define VEC_V(op,usetmp,dtype,nomask) _STMT( \
nomask(k = _CAT(OP_ufgen,op)(k), \
k = _CAT(OP_mfgen,op)(m,k); m = _CAT(OP_mfmsk,op)(m)); \
PR_LOOPVP(count, \
nomask(UPOINT(op,d,k), MPOINTT(op,usetmp,d,m,k,t)); \
PTR_INCR(dtype *, d, (int) offset)); )
/* horizontal vectors */
#define VEC_H(op,usetmp,arg1,arg2) _STMT( \
if (lm) { \
t = *d; \
*d++ = _CAT(OP_mrop,op)(t, _CAT(OP_mmsk,op)((PIX32) lm), k); \
} \
if (--count >= 0) { \
if (m == ~0) { \
k = _CAT(OP_ufgen,op)(k); \
PR_LOOPVP(count, UPOINT(op,d,k); d++); \
} \
else { \
k = _CAT(OP_mfgen,op)(m,k); \
m = _CAT(OP_mfmsk,op)(m); \
PR_LOOPVP(count, MPOINTT(op,usetmp,d,m,k,t); d++); \
} \
} \
if (m = (PIX32) rm) { \
k = (PIX32) k0; \
t = *d; \
*d = _CAT(OP_mrop,op)(t, _CAT(OP_mmsk,op)(m), k); \
} )
/* depth 1 x-major */
#define VEC_1X(op,usetmp,arg1,arg2) _STMT( \
do { \
t = *d; \
do { \
if (m == 0) { \
m = (TMP1) mleft; \
*d++ = t; \
t = *d; \
} \
t = _CAT(OP_mfill,op)(t,_CAT(OP_mfmsk,op)(m),dummy); \
m >>= 1; \
} while ((error += dy) <= 0 && LOOP_DECR(count)); \
*d = t; \
PTR_INCR(PIX1 *, d, (int) offset); \
error -= dx; \
} while (--count >= 0); )
/* depth 1 y-major */
#define VEC_1Y(op,usetmp,arg1,arg2) _STMT( \
m = (PIX1) _CAT(OP_mfmsk,op)(m); \
mleft = (INT_T) _CAT(OP_mfmsk,op)((PIX1) mleft); \
do { \
if (m == (PIX1) _CAT(OP_mfmsk,op)(0)) { \
m = (TMP1) mleft; \
d++; \
} \
do { \
*d = _CAT(OP_mfill,op)(*d,m,dummy); \
PTR_INCR(PIX1 *, d, (int) offset); \
} while ((error += dy) <= 0 && LOOP_DECR(count)); \
m >>= 1; \
m |= _CAT(OP_mfmsk,op)(0) ? LEFTBIT(PIX1) : 0; \
error -= dx; \
} while (--count >= 0); )
#if GENERIC_XY
/* depth 8/16/32 generic */
#define VEC_GXY(op,usetmp,dtype,nomask) _STMT( \
nomask(k = _CAT(OP_ufgen,op)(k), \
k = _CAT(OP_mfgen,op)(m,k); m = _CAT(OP_mfmsk,op)(m)); \
do { \
usetmp(dtype t; ,) \
nomask(UPOINT(op,d,k), MPOINTT(op,usetmp,d,m,k,t)); \
if ((error += dy) > 0) { \
PTR_INCR(dtype *, d, (int) min_offset); \
error -= dx; \
} \
PTR_INCR(dtype *, d, (int) maj_offset); \
} while (--count >= 0); )
#else GENERIC_XY
/* depth 8/16/32 x-major */
#define VEC_GX(op,usetmp,dtype,nomask) _STMT( \
nomask(k = _CAT(OP_ufgen,op)(k), \
k = _CAT(OP_mfgen,op)(m,k); m = _CAT(OP_mfmsk,op)(m)); \
do { \
do { \
nomask(UPOINT(op,d,k), MPOINT(op,d,m,k)); \
d++; \
} while ((error += dy) <= 0 && LOOP_DECR(count)); \
PTR_INCR(dtype *, d, (int) offset); \
error -= dx; \
} while (--count >= 0); )
/* depth 8/16/32 y-major */
#define VEC_GY(op,usetmp,dtype,nomask) _STMT( \
nomask(k = _CAT(OP_ufgen,op)(k), \
k = _CAT(OP_mfgen,op)(m,k); m = _CAT(OP_mfmsk,op)(m)); \
do { \
do { \
nomask(UPOINT(op,d,k), MPOINT(op,d,m,k)); \
PTR_INCR(dtype *, d, (int) offset); \
} while ((error += dy) <= 0 && LOOP_DECR(count)); \
d++; \
error -= dx; \
} while (--count >= 0); )
#endif GENERIC_XY
mem_vector(pr, x0, y0, x1, y1, op, color)
Pixrect *pr;
int x0, y0, x1, y1;
int op;
int color;
{
int noclip;
int w, h, startx, starty;
int reflect, initerror;
u_int depth;
u_long planes;
struct mpr_data *prd;
{
register struct mprp_data *mprd;
register int rop, rcolor, rdepth;
mprd = mprp_d(pr);
prd = &mprd->mpr;
rop = op;
noclip = rop & PIX_DONTCLIP;
if ((rcolor = PIXOP_COLOR(rop)) == 0)
rcolor = color;
rop = PIXOP_OP(rop);
if ((rdepth = pr->pr_depth) == 1) {
depth = 0;
if (mprd->mpr.md_flags & MP_REVERSEVIDEO)
rop = PIX_OP_REVERSEDST(rop);
if (rcolor)
rop >>= 2;
else
rop &= 3;
rop |= rop << 2;
}
else {
register int rplanes = ~0, allplanes;
#define INV 16
#define CLR 32
#define SET (CLR | INV)
static char optab[16 * 2] = {
8|CLR, 4|INV, 8|INV, 12|INV,
4, 6|SET, 6, 7,
8, 6|INV, 10, 14|INV,
12, 7|INV, 14, 14|SET,
12|CLR, 4|INV, 8|INV, 12|INV,
4, 6|SET, 6, 7,
8, 6|INV, 10, 14|INV,
12, 7|INV, 14, 12|SET
};
if (mprd->mpr.md_flags & MP_PLANEMASK)
rplanes = mprd->planes;
switch (rdepth) {
case 8:
depth = 1;
allplanes = 0xff;
break;
case 16:
depth = 2;
allplanes = 0xffff;
break;
case 32:
depth = 3;
allplanes = 0xffffffff;
break;
default:
return PIX_ERR;
}
rplanes &= allplanes;
rcolor &= rplanes;
if (rcolor == 0) {
rop &= 3;
rop |= rop << 2;
}
else if (rcolor == rplanes) {
rcolor = 0;
rop >>= 2;
rop |= rop << 2;
}
if (rplanes == allplanes)
rop += 16;
rop = optab[rop];
if (rop & CLR)
rcolor = 0;
if (rop & INV)
rcolor = ~rcolor & rplanes;
rop &= 15;
planes = rplanes;
color = rcolor;
#undef SET
#undef CLR
#undef INV
}
op = rop;
}
/* indentation is wrong here */
{
register int dx, dy;
/* vertical vectors */
if ((dx = x1 - x0) == 0) {
register caddr_t daddr;
register INT_T offset;
{
register int y;
register struct mpr_data *mprd;
if ((y = y0) <= (dy = y1))
dy -= y;
else {
dy = y - dy;
y -= dy;
}
dx = x0;
if (!noclip) {
if ((u_int) dx >= pr->pr_size.x)
return 0;
if (y < 0) {
if ((dy += y) < 0)
return 0;
y = 0;
}
if (y + dy >= pr->pr_size.y) {
if ((dy = pr->pr_size.y - y - 1) < 0)
return 0;
}
}
mprd = prd;
offset = (INT_T) mprd->md_linebytes;
daddr = (caddr_t) mprd->md_image;
dx += mprd->md_offset.x;
y += mprd->md_offset.y;
daddr += pr_product((int) offset, y);
}
{
register LOOP_T count = dy;
switch (depth) {
case 0: {
register PIX1 *d;
register TMP1 m, t;
register TMP1 k; /* dummy */
d = (PIX1 *) daddr + (dx >> 4);
m = LEFTBIT(PIX1) >> (dx &= 15);
CASE_OP(op,IFTRUE,VEC_V,PIX1,IFFALSE);
break;
}
case 1: {
register PIX8 *d;
register TMP8 m, k, t;
d = (PIX8 *) daddr + dx;
k = color;
if ((m = planes) == 255) {
LINE_TAG((d, k, count, offset));
CASE_OP(op,IFFALSE,VEC_V,PIX8,IFTRUE);
}
else
CASE_OP(op,IFFALSE,VEC_V,PIX8,IFFALSE);
break;
}
case 2: {
register PIX16 *d;
register TMP16 m, k, t;
d = (PIX16 *) daddr + dx;
k = color;
m = planes;
CASE_OP(op,IFFALSE,VEC_V,PIX16,IFFALSE);
break;
}
case 3: {
register PIX32 *d;
register PIX32 m, k, t;
d = (PIX32 *) daddr + dx;
k = color;
m = planes;
CASE_OP(op,IFFALSE,VEC_V,PIX32,IFFALSE);
break;
}
} /* switch */
}
return 0;
}
/* horizontal vectors */
if ((dy = y1 - y0) == 0) {
register PIX32 *d, m, k;
register INT_T lm = 0, rm = 0;
{
register int x;
register struct mpr_data *mprd;
if (dx > 0)
x = x0;
else {
dx = -dx;
x = x1;
}
dx++;
dy = y0;
if (!noclip) {
if ((u_int) dy >= pr->pr_size.y)
return 0;
if (x < 0) {
if ((dx += x) <= 0)
return 0;
x = 0;
}
if (x + dx >= pr->pr_size.x) {
if ((dx = pr->pr_size.x - x) <= 0)
return 0;
}
}
mprd = prd;
x += mprd->md_offset.x;
d = (PIX32 *) PTR_ADD(mprd->md_image,
pr_product(mprd->md_linebytes,
dy + mprd->md_offset.y));
k = color;
m = planes;
switch (depth) {
case 0:
m = ~0;
switch (op) {
case 0: op = 12; k = 0; break;
case 5: op = 6; k = ~0; break;
case 15: op = 12; k = ~0; break;
}
break;
case 1:
x <<= 3;
dx <<= 3;
k |= k << 8;
k |= k << 16;
m |= m << 8;
m |= m << 16;
break;
case 2:
x <<= 4;
dx <<= 4;
k |= k << 16;
m |= m << 16;
break;
case 3:
x <<= 5;
dx <<= 5;
break;
}
DST_ALIGN(d, x);
d += x >> 5;
if (x &= 31) {
lm = (INT_T) ((PIX32) ~0 >> x & m);
dx -= 32 - x;
}
if (x = dx & 31) {
rm = (INT_T) (~0 << 32 - x & m);
dx -= x;
}
if (dx < 0) {
lm = (INT_T) ((PIX32) lm & (PIX32) rm);
rm = 0;
dx = 0;
}
dx >>= 5;
}
{
register PIX32 t;
register LOOP_T count = dx;
register INT_T k0 = (INT_T) k;
LINE_TAG((d, k, count));
CASE_OP(op,IFFALSE,VEC_H,0,0);
}
return 0;
}
/*
* Need to normalize the vector so that the following
* algorithm can limit the number of cases to be considered.
* We can always interchange the points in x, so that
* x0, y0 is to the left of x1, y1.
*/
if (dx < 0) {
register int tmp;
dx = -dx;
swap(x0, x1, tmp);
if (dy = -dy)
swap(y0, y1, tmp);
}
/*
* The clipping routine needs to work with a vector which
* increases in y from y0 to y1. We want to change
* y0 and y1 so that there is an increase in y
* from y0 to y1 without affecting the clipping
* and bounds checking that we will do. We accomplish
* this by reflecting the vector around the horizontal
* centerline of pr, and remember that we did this by
* incrementing reflect by 2. The reflection will be undone
* before the vector is drawn.
*/
w = pr->pr_size.x;
h = pr->pr_size.y;
if (dy < 0) {
dy = -dy;
y0 = h - 1 - y0;
y1 = h - 1 - y1;
reflect = REFLECT_XAXIS;
}
else
reflect = 0;
/*
* Can now do bounds check, since the vector increasing in
* x and y can check easily if it has no chance of intersecting
* the destination rectangle: if the vector ends before the
* beginning of the target or begins after the end!
*/
if (!noclip &&
(y1 < 0 || y0 >= h || x1 < 0 || x0 >= w))
return 0;
/*
* One more reflection: we want to assume that dx >= dy.
* So if this is not true, we reflect the vector around
* the diagonal line x = y and remember that we did
* this by adding 1 to reflect.
*/
if (dx < dy) {
register int tmp;
swap(x0, y0, tmp);
swap(x1, y1, tmp);
swap(dx, dy, tmp);
swap(w, h, tmp);
reflect |= REFLECT_YMAJOR;
}
initerror = - (dx >> 1); /* error at x0, y0 */
startx = x0;
starty = y0;
/* not indented here either */
if (!noclip) {
/*
* Begin hard part of clipping.
*
* We have insured that we are clipping on a vector
* which has dx > 0, dy > 0 and dx >= dy. The error is
* the vertical distance from the true line to the approximation
* in units where each pixel is dx by dx. Moving one
* to the right (increasing x by 1) subtracts dy from
* the error. Moving one pixel down (increasing y by 1)
* adds dx to the error.
*
* Bresenham functions by restoring the error to the
* range (-dx,0] whenever the error leaves it. The
* algorithm increases x and increases y when
* it needs to constrain the error.
*/
/*
* If dx or dy are too large, scale to avoid overflow in
* multiplications below.
*/
while (dx > MAXDELTA || dy > MAXDELTA) {
dx >>= 1;
dy >>= 1;
}
/*
* Clip left end to yield start of visible vector.
* If the starting x coordinate is negative, we
* must advance to the first x coordinate which will
* be drawn (x=0). As we advance (-startx) units in the
* x direction, the error increases by (-startx)*dy.
* This means that the error when x=0 will be
* -(dx/2)+(-startx)*dy
* For each y advance in this range, the error is reduced
* by dx, and should be in the range (-dx,0] at the y
* value at x=0. Thus to compute the increment in y we
* should take
* (-(dx/2)+(-startx)*dy+(dx-1))/dx
* where the numerator represents the length of the interval
* [-dx+1,-(dx/2)+(-startx)*dy]
* The number of dx steps by which the error can be reduced
* and stay in this interval is the y increment which would
* result if the vector were drawn over this interval.
*/
if (startx < 0) {
register int tmp;
initerror += (-startx * dy);
startx = 0;
tmp = (initerror + (dx-1)) / dx;
if ((starty += tmp) >= h)
return 0;
initerror -= (tmp * dx);
}
/*
* After having advanced x to be at least 0, advance
* y to be in range. If y is already too large (and can
* only get larger!), just give up. Otherwise, if starty < 0,
* we need to compute the value of x at which y is first 0.
* In advancing y to be zero, the error decreases by (-starty)*dx,
* in the y steps.
*
* Immediately after an advance in y the error is in the range
* (-dx,-dx+dy]. This can be seen by noting that what last
* happened was that the error was in the range (-dy,0],
* the error became positive by adding dy, to be in the range (0,dy],
* and we subtracted dx to get into the range (-dx,-dx+dy].
*
* Thus we need to advance x to cause the error to change
* to be at most (-dx+dy), or, in steps of dy, at most:
* ((-dx+dy)-error)/dy
* which is the number of dy steps in the interval [error,-dx+dy].
*/
/* skip to dst top edge */
if (starty < 0) {
register int tmp;
initerror += (starty * dx);
starty = 0;
tmp = ((-dx+dy)-initerror)/dy;
startx += tmp;
initerror += (tmp * dy);
if (startx >= w)
return 0;
}
/*
* Now clip right end.
*
* If the last x position is outside the rectangle,
* then clip the vector back to within the rectangle
* at x=w-1. The corresponding y value has initerror
* -(dx/2)+((w-1)-x0)*dy
* We need an error in the range (-dx,0], so compute
* the corresponding number of steps in y from the number
* of dy's in the interval:
* (-dx,-(dx/2)+((w-1)-x0)*dy]
* which is:
* [-dx+1,-(dx/2)+((w-1)-x0)*dy]
* or:
* (-(dx/2)+((w-1)-x0)*dy+dx-1)/dx
* Note that:
* dx - (dx/2) != (dx/2)
* (consider dx odd), so we can't simplify much further.
*/
if (x1 >= w) {
x1 = w - 1;
y1 = y0 +
(-(dx>>1) + (((w-1)-x0) * dy) +
dx-1)/dx;
}
/*
* If the last y position is outside the rectangle, then
* clip it back at y=h-1. The corresponding x value
* has error
* -(dx/2)-((h-1)-y0)*dx
* We want the last x value with this y value, which
* will have an error in the range (-dy,0] (so that increasing
* x one more time will make the error > 0.) Thus
* the amount of error allocatable to dy steps is from
* the length of the interval:
* [-(dx/2)-((h-1)-y0)*dx,0]
* that is,
* (0-(-(dx/2)-((h-1)-y0)*dx))/dy
* or:
* ((dx/2)+((h-1)-y0)*dx)/dy
*/
if (y1 >= h) {
y1 = h - 1;
x1 = x0 +
((dx>>1)+(((h-1)-y0) * dx))/dy;
}
} /* clip block */
{
register LOOP_T count = x1 - startx;
register caddr_t daddr;
register INT_T offset;
{
register struct mpr_data *mprd = prd;
register int y;
offset = (INT_T) mprd->md_linebytes;
daddr = (caddr_t) mprd->md_image;
/* major axis is y */
if (reflect & REFLECT_YMAJOR) {
y = startx;
startx = starty + mprd->md_offset.x;
h = w;
}
else {
y = starty;
startx += mprd->md_offset.x;
}
if (reflect & REFLECT_XAXIS) {
y = y - h + 1 - mprd->md_offset.y;
offset = (INT_T) (- (int) offset);
}
else
y += mprd->md_offset.y;
daddr += pr_product((int) offset, y);
}
{
register int error = initerror;
#if GENERIC_XY
#define min_offset offset
register INT_T maj_offset;
{
register int t;
if ((t = depth - 1) >= 0) {
t = 1 << t;
maj_offset = (INT_T) t;
if (reflect & REFLECT_YMAJOR) {
maj_offset = min_offset;
min_offset = (INT_T) t;
}
}
}
#endif GENERIC_XY
switch (depth) {
case 0: {
register PIX1 *d;
register TMP1 m, t;
register INT_T mleft;
d = (PIX1 *) daddr + (startx >> 4);
m = LEFTBIT(PIX1);
mleft = (INT_T) m;
m >>= (startx & 15);
CASE_XY(op,IFTRUE,VEC_1,PIX1,IFFALSE);
break;
}
case 1: {
register PIX8 *d;
register TMP8 m, k;
d = (PIX8 *) daddr + startx;
k = color;
if ((PIX8) (m = planes) == (PIX8) ~0) {
LINE_TAG((d, k, count, error, dx, dy,
maj_offset, min_offset));
CASE_G(op,IFFALSE,VEC_G,PIX8,IFTRUE);
}
else
CASE_G(op,IFFALSE,VEC_G,PIX8,IFFALSE);
break;
}
case 2: {
register PIX16 *d;
register TMP16 m, k;
d = (PIX16 *) daddr + startx;
k = color;
m = planes;
CASE_G(op,IFFALSE,VEC_G,PIX16,IFFALSE);
break;
}
case 3: {
register PIX32 *d;
register PIX32 m, k;
d = (PIX32 *) daddr + startx;
k = color;
m = planes;
CASE_G(op,IFFALSE,VEC_G,PIX32,IFFALSE);
break;
}
} /* switch */
}
}
}
return 0;
}