#ifndef lint static char sccsid[] = "@(#)mem_vec.c 9.2 88/01/19"; #endif /* * Copyright 1986 by Sun Microsystems, Inc. */ /* * Memory pixrect vector. * */ #ifdef KERNEL #include "../h/types.h" #include "../pixrect/pixrect.h" #include "../pixrect/memvar.h" #include "../pixrect/pr_util.h" #else #include <sys/types.h> #include <pixrect/pixrect.h> #include <pixrect/memvar.h> #include <pixrect/pr_util.h> #endif #define swap(a,b,t) {(t) = (a); (a) = (b); (b) = (t);} extern char pr_reversedst[]; /* * The 16 possible rasterops. Given opcode i, the correct function to * execute it is Fi. These are used for 8bit memory pixrects. */ #define F0(d,s) (0) #define F1(d,s) (~((d)|(s))) #define F2(d,s) ((d)&~(s)) #define F3(d,s) (~(s)) #define F4(d,s) (~(d)&(s)) #define F5(d,s) (~(d)) #define F6(d,s) ((d)^(s)) #define F7(d,s) (~((d)&(s))) #define F8(d,s) ((d)&(s)) #define F9(d,s) (~((d)^(s))) #define FA(d,s) (d) #define FB(d,s) ((d)|~(s)) #define FC(d,s) (s) #define FD(d,s) (~(d)|(s)) #define FE(d,s) ((d)|(s)) #define FF(d,s) (~0) /* Rasterops for depth 1, source is all zeros: CAT(SF, (op & 3)) * or all ones: CAT(SF, (op >> 2)) */ #define SF0(d,s) ((d)&~(s)) #define SF1(d,s) ((d)^(s)) #define SF2(d,s) (d) #define SF3(d,s) ((d)|(s)) /* Rasterops for depth 8, source is all zeros: CAT(SF8, (op & 3)) * or all ones: CAT(SF8, (op >> 2)) */ #define SF80(d,s) (0) #define SF81(d,s) (~(d)) #define SF82(d,s) (d) #define SF83(d,s) (~0) /* Macros for rasterops when planes != -1 and source is * all zeros: CAT(PMS, (op & 3)) * or all ones: CAT(PMS, (op >> 2)) */ #define PMS0(d,s) ((d) & ~planes) #define PMS1(d,s) ((d) ^ planes) #define PMS2(d,s) (d) #define PMS3(d,s) ((d) | planes) /* Macros for rasterops when planes != -1 and source is arbitrary */ #define PM0(d,s) ((d) & ~planes) #define PM1(d,s) ((d) ^ (planes & ((d) | ~(s)))) #define PM2(d,s) ((d) & ~(planes & (s))) #define PM3(d,s) ((d) ^ (planes & ((d) ^ ~(s)))) #define PM4(d,s) ((d) ^ (planes & ((d) | (s)))) #define PM5(d,s) ((d) ^ planes) #define PM6(d,s) ((d) ^ (planes & (s))) #define PM7(d,s) ((d) ^ (planes & (~(d) | (s)))) #define PM8(d,s) ((d) & (~planes | (s))) #define PM9(d,s) ((d) ^ (planes & ~(s))) #define PMA(d,s) (d) #define PMB(d,s) ((d) | (planes & ~(s))) #define PMC(d,s) ((d) ^ (planes & ((d) ^ (s)))) #define PMD(d,s) ((d) ^ (planes & ~((d) & (s)))) #define PME(d,s) ((d) | (planes & (s))) #define PMF(d,s) ((d) | planes) /* Macros to mask the ends of depth 1 horizontal vectors */ #define M0(m) (*dst &= ~(m)) #define M1(m) (*dst ^= (m)) #define M2(m) (*dst = *dst&~m | color&m) #define M3(m) (*dst |= (m)) /* Booleans to do special cases for depth, planes, color */ #define depth1(x,y) x #define depth8(x,y) y #define allbits(x,y) x #define arbval(x,y) y /* Macro to execute a macro for an opcode. */ #define CASE_BINARY_OP(op,macro,depth,wrtenab,source)\ switch(op) {\ CASEOP(0,macro,depth,wrtenab,source)\ CASEOP(1,macro,depth,wrtenab,source)\ CASEOP(2,macro,depth,wrtenab,source)\ CASEOP(3,macro,depth,wrtenab,source)\ source(,\ CASEOP(4,macro,depth,wrtenab,source)\ CASEOP(5,macro,depth,wrtenab,source)\ CASEOP(6,macro,depth,wrtenab,source)\ CASEOP(7,macro,depth,wrtenab,source)\ CASEOP(8,macro,depth,wrtenab,source)\ CASEOP(9,macro,depth,wrtenab,source)\ CASEOP(B,macro,depth,wrtenab,source)\ CASEOP(C,macro,depth,wrtenab,source)\ CASEOP(D,macro,depth,wrtenab,source)\ CASEOP(E,macro,depth,wrtenab,source)\ CASEOP(F,macro,depth,wrtenab,source)\ )\ } #define CASEOP(op,macro,depth,wrtenab,source)\ case CAT(0x,op): macro(op,depth,wrtenab,source);\ break; #define VISIBLE(ax,ay)\ (clip == 0 || (u_int)(ax) < size.x && (u_int)(ay) < size.y) #define rolledloop(count,s) {\ register short cnt;\ if ((cnt = (count)-1) >= 0)\ do {s;} while (--cnt != -1); \ } /* Macro to draw points in 1 and 8 bit memory pixrects. This macro * does special cases for depth 1 or 8, plane mask of allones, allzeros * or arbitrary, and source color of allones, allzeros, or arbitrary. * It assumes that op has been adjusted for special source cases. */ #define POINT(op, depth, wrtenab, source)\ depth(*dst =CAT(SF,op)(*dst,color),\ wrtenab(source(*dst =CAT(SF8,op)(*dst,color),\ *dst =CAT(F,op)(*dst,color)),\ source(*dst = CAT(PMS,op)(*dst, color),\ *dst = CAT(PM,op)(*dst, color))\ )) /* Macro to do depth 1 and 8 horizontal vectors. When doing depth 1, * source MUST be allbits. If source is allbits then if source is all * zeros, op = op&3, else if source is all ones op = op>>2. wrtenab has * no effect on depth1. */ #define HORIZ(op,depth,wrtenab,source) {\ depth(CAT(M,op)(mask1); dst++;,)\ wrtenab(source(\ rolledloop(count,((*dst = depth(CAT(SF,op)(*dst,color),\ CAT(SF8,op)(*dst,color))), dst++));,\ rolledloop(count,((*dst = CAT(F,op)(*dst,color)), dst++));),\ source(\ rolledloop(count,((*dst = depth(CAT(SF,op)(*dst,color),\ CAT(PMS,op)(*dst,color))),dst++));,\ rolledloop(count,((*dst = depth(CAT(SF,op)(*dst,color),\ CAT(PM,op)(*dst,color))),dst++));)\ )\ depth(if (count >= 0) {CAT(M,op)(mask2); dst++;},)\ } /* Macro to do depth 1 and 8 vertical vectors. When doing depth 1, * source MUST be allbits. If source is allbits then if source is all * zeros, op = op&3, else if source is all ones op = op>>2. wrtenab has * no effect on depth1. */ #define VERT(op,depth,wrtenab,source) {\ wrtenab(source(\ rolledloop(count,depth(*dst = CAT(SF,op)(*dst,color);\ (u_char *)dst += (int)vrtstep;,\ *dst = CAT(SF8,op)(*dst,color); dst += (int)vrtstep;)),\ rolledloop(count,depth(*dst = CAT(SF,op)(*dst,color);\ (u_char *)dst += (int)vrtstep;,\ *dst = CAT(F,op)(*dst,color); dst += (int)vrtstep;))),\ source(\ rolledloop(count,depth(*dst = CAT(SF,op)(*dst,color);\ (u_char *)dst += (int)vrtstep;,\ *dst = CAT(PMS,op)(*dst,color); dst += (int)vrtstep;)),\ rolledloop(count,depth(*dst = CAT(SF,op)(*dst,color);\ (u_char *)dst += (int)vrtstep;,\ *dst = CAT(PM,op)(*dst,color); dst += (int)vrtstep;)))\ )\ } #define MAJORX(op,depth,wrtenab,source)\ do {\ do {\ POINT(op,depth,wrtenab,source);\ depth(\ if((color >>= 1) == 0) {color = 0x8000; dst++;},\ dst++;)\ } while (((error += dy) <= 0) && (--count != -1));\ error -= dx;\ depth((u_char *)dst += (int)vrtstep;, dst += (int)vrtstep;)\ } while ((count >= 0) && (--count != -1)); #define MAJORY(op,depth,wrtenab,source)\ do {\ do {\ POINT(op,depth,wrtenab,source);\ depth((u_char *)dst += (int)vrtstep;, dst += (int)vrtstep;)\ } while (((error += dy) <= 0) && (--count != -1));\ error -= dx;\ depth(\ if((color >>= 1) == 0) {color = 0x8000; dst++;},\ dst++;)\ } while ((count >= 0) && (--count != -1)); mem_vector (dpr, x0, y0, x1, y1, op, colour) struct pixrect *dpr; { struct mprp_data *mprd = (struct mprp_data *) dpr->pr_data; register dx, dy; register count, error, color; register u_char *vrtstep; struct pr_size size; int xs, ys; /* start position */ int clip, reflect, mprplanes; color = PIX_OPCOLOR(op); if (!color) color = colour; clip = !(op & PIX_DONTCLIP); op = (op >> 1) & 0xF; if (mprd->mpr.md_flags & MP_REVERSEVIDEO) op = (pr_reversedst[op]); if (mprd->mpr.md_flags & MP_PLANEMASK) mprplanes = mprd->planes; else mprplanes = ~0; if ((op == (PIX_DST >> 1)) || (mprplanes == 0)) return (0); dx = x1 - x0; dy = y1 - y0; size = dpr->pr_size; /* * Handle length 1 or 2 vectors by just drawing endpoints. */ if ((u_int) (dx + 1) <= 2 && (u_int) (dy + 1) <= 2) { if (dpr->pr_depth == 1) { register short *dst; op = (color) ? op>>2 : op&3; if (VISIBLE(x0, y0)) { dst = mprd_addr (&mprd->mpr, x0, y0); color = 0x8000 >> mprd_skew (&mprd->mpr, x0, y0); CASE_BINARY_OP(op,POINT,depth1,allbits,allbits) } if ((dx != 0 || dy != 0) && VISIBLE(x1, y1)) { dst = mprd_addr (&mprd->mpr, x1, y1); color = 0x8000 >> mprd_skew (&mprd->mpr, x1, y1); CASE_BINARY_OP(op,POINT,depth1,allbits,allbits) } } else /* 8 bit */ { register u_char *dst; register u_char planes = mprplanes; if (VISIBLE(x0, y0)) { dst = mprd8_addr(&mprd->mpr, x0, y0, dpr->pr_depth); CASE_BINARY_OP(op,POINT,depth8,arbval,arbval) } if ((dx != 0 || dy != 0) && VISIBLE(x1, y1)) { dst = mprd8_addr (&mprd->mpr, x1, y1, dpr->pr_depth); CASE_BINARY_OP(op,POINT,depth8,arbval,arbval) } } 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 pos0 is to the left of pos1. */ if (dx < 0) { /* force vector to scan to right */ dx = -dx; dy = -dy; swap (x0, x1, count); swap (y0, y1, count); } /* * 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 dst, and remember that * we did this by incrementing reflect by 2. The reflection will be undone * before the vector is drawn. */ reflect = 0; if (dy < 0) { dy = -dy; y0 = (size.y - 1) - y0; y1 = (size.y - 1) - y1; reflect += 2; } /* * 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 (clip && (y1 < 0 || y0 >= size.y || x1 < 0 || x0 >= size.x)) return (0); /* * Special case for Horizontal lines */ if (dy == 0) { if (clip) { if (x0 < 0) x0 = 0; if (x1 >= size.x) x1 = size.x - 1; dx = x1 - x0; } if (dpr->pr_depth == 1) { register unsigned long *dst; register unsigned long mask1, mask2; register unsigned long px; /* bit address of start pixel */ op = (color) ? op>>2 : op&3; color = -1; px = ((unsigned long) ( ( (char *)mprd->mpr.md_image + pr_product(y0+mprd->mpr.md_offset.y,mprd->mpr.md_linebytes) )) << 3) + x0 + mprd->mpr.md_offset.x; /* bit address */ mask1 = ((unsigned long) 0xFFFFFFFF) >> (px & 0x1F); mask2 = 0xFFFFFFFF << (0x1F - ((px + dx) & 0x1F)); count = (((px & 0x1F) + dx) >> 5) - 1; if (count < 0) mask1 &= mask2; dst = (unsigned long *) ((px >> 3) & ~0x3); CASE_BINARY_OP(op,HORIZ,depth1,allbits,allbits) } else { /* depth8 code */ register u_char *dst; register u_char planes = mprplanes; count = dx + 1; dst = mprd8_addr (&mprd->mpr, x0, y0, dpr->pr_depth); if (color == 0) { op &= 3; goto hconst; } else if ((color&255) == 255) { op >>= 2; goto hconst; } else { CASE_BINARY_OP(op,HORIZ,depth8,arbval,arbval) return (0); } hconst: CASE_BINARY_OP(op,HORIZ,depth8,arbval,allbits) } return (0); } /* * Special case for vertical lines. */ if (dx == 0) { if (clip) { if (y0 < 0) y0 = 0; if (y1 >= size.y) y1 = size.y - 1; dy = y1 - y0; } vrtstep = (u_char *)mprd->mpr.md_linebytes; if (reflect & 2) y0 = size.y - y1-1; count = dy + 1; if (dpr->pr_depth == 1) { register short *dst = mprd_addr (&mprd->mpr, x0, y0); op = (color) ? op>>2 : op&3; color = 0x8000 >> mprd_skew (&mprd->mpr, x0, y0); CASE_BINARY_OP(op,VERT,depth1,arbval,allbits) } else { /* depth8 code */ register u_char *dst; register u_char planes = mprplanes; dst = mprd8_addr (&mprd->mpr, x0, y0, dpr->pr_depth); CASE_BINARY_OP(op,VERT,depth8,arbval,arbval) } 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) { swap (x0, y0, count); swap (x1, y1, count); swap (dx, dy, count); swap (size.x, size.y, count); reflect += 1; } error = -(dx >> 1); /* error at pos0 */ xs = x0; ys = y0; if (!clip) goto clipdone; /* * 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. */ /* * 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 (-xs) units in the x * direction, the error increases by (-xs)*dy. This means that the * error when x=0 will be * -(dx/2)+(-xs)*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)+(-xs)*dy+(dx-1))/dx * where the numerator represents the length of the interval * [-dx+1,-(dx/2)+(-xs)*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 (xs < 0) { error += -xs * dy; xs = 0; count = (error + (dx - 1)) / dx; ys += count; error -= count * 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 ys < 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 (-ys)*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]. */ if (ys >= size.y) return (0); if (ys < 0) { /* skip to dst top edge */ error += ys * dx; ys = 0; count = ((-dx + dy) - error) / dy; xs += count; error += count * dy; if (xs >= size.x) 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=size.x-1. The corresponding y value * has error -(dx/2)+((size.x-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)+((size.x-1)-x0)*dy] * which is: * [-dx+1,-(dx/2)+((size.x-1)-x0)*dy] * or: * (-(dx/2)+((size.x-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 >= size.x) { x1 = size.x - 1; y1 = y0 + (-(dx >> 1) + (((size.x - 1) - x0) * dy) + dx - 1) / dx; } /* * If the last y position is outside the rectangle, then * clip it back at y=size.y-1. The corresponding x value * has error * -(dx/2)-((size.y-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)-((size.y-1)-y0)*dx,0] * that is, * (0-(-(dx/2)-((size.y-1)-y0)*dx))/dy * or: * ((dx/2)+((size.y-1)-y0)*dx)/dy */ if (y1 >= size.y) { y1 = size.y - 1; x1 = x0 + ((dx >> 1) + (((size.y - 1) - y0) * dx)) / dy; } clipdone: /* * Now have to set up for the Bresenham routines. */ count = x1 - xs; vrtstep = (u_char *)mprd->mpr.md_linebytes; if (reflect & 1) { /* major axis is y */ if (reflect & 2) { /* unreflect major axis (misnamed x) */ xs = ((size.x - 1) - xs); vrtstep = (u_char *)(-mprd->mpr.md_linebytes); } if (dpr->pr_depth == 1) { register short *dst; dst = mprd_addr(&mprd->mpr, ys, xs); op = (color) ? op>>2 : op&3; color = 0x8000 >> mprd_skew (&mprd->mpr, ys, xs); CASE_BINARY_OP(op,MAJORY,depth1,arbval,allbits) } else /* if (dpr->pr_depth == 8) */ { register u_char *dst; register u_char planes = mprplanes; dst = mprd8_addr (&mprd->mpr, ys, xs, dpr->pr_depth); CASE_BINARY_OP(op,MAJORY,depth8,arbval,arbval) } } else { /* major axis is x */ if (reflect & 2) { /* unreflect minor axis */ ys = ((size.y - 1) - ys); vrtstep = (u_char *)(-mprd->mpr.md_linebytes); } if (dpr->pr_depth == 1) { register short *dst; dst = mprd_addr (&mprd->mpr, xs, ys); op = (color) ? op>>2 : op&3; color = 0x8000 >> mprd_skew (&mprd->mpr, xs, ys); CASE_BINARY_OP(op,MAJORX,depth1,arbval,allbits) } else /* if (dpr->pr_depth == 8) */ { register u_char *dst; register u_char planes = mprplanes; dst = mprd8_addr (&mprd->mpr, xs, ys, dpr->pr_depth); CASE_BINARY_OP(op,MAJORX,depth8,arbval,arbval) } } return (0); }