/* * Copyright © 1998 Keith Packard * Copyright © 2012 Intel Corporation * * 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, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD 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. */ #include #include "fb.h" #include "fbclip.h" #include #include #include #define FbDashDeclare \ unsigned char *__dash, *__firstDash, *__lastDash #define FbDashInit(gc,pgc,dashOffset,dashlen,even) { \ (even) = TRUE; \ __firstDash = (gc)->dash; \ __lastDash = __firstDash + (gc)->numInDashList; \ (dashOffset) %= (pgc)->dashLength; \ \ __dash = __firstDash; \ while ((dashOffset) >= ((dashlen) = *__dash)) { \ (dashOffset) -= (dashlen); \ (even) = 1-(even); \ if (++__dash == __lastDash) \ __dash = __firstDash; \ } \ (dashlen) -= (dashOffset); \ } #define FbDashNext(dashlen) { \ if (++__dash == __lastDash) \ __dash = __firstDash; \ (dashlen) = *__dash; \ } /* as numInDashList is always even, this case can skip a test */ #define FbDashNextEven(dashlen) { \ (dashlen) = *++__dash; \ } #define FbDashNextOdd(dashlen) FbDashNext(dashlen) #define FbDashStep(dashlen,even) { \ if (!--(dashlen)) { \ FbDashNext(dashlen); \ (even) = 1-(even); \ } \ } #define fbBresShiftMask(mask,dir,bpp) ((bpp == FB_STIP_UNIT) ? 0 : \ ((dir < 0) ? FbStipLeft(mask,bpp) : \ FbStipRight(mask,bpp))) typedef void FbBres(DrawablePtr drawable, GCPtr gc, int dashOffset, int sdx, int sdy, int axis, int x, int y, int e, int e1, int e3, int len); #define BRESSOLID fbBresSolid8 #define BRESSOLIDR fbBresSolidR8 #define BRESDASH fbBresDash8 #define BITS BYTE #define BITS2 CARD16 #define BITS4 CARD32 #include "fbsegbits.h" #undef BRESSOLID #undef BRESSOLIDR #undef BRESDASH #undef BITS #undef BITS2 #undef BITS4 #define BRESSOLID fbBresSolid16 #define BRESSOLIDR fbBresSolidR16 #define BRESDASH fbBresDash16 #define BITS CARD16 #define BITS2 CARD32 #include "fbsegbits.h" #undef BRESSOLID #undef BRESSOLIDR #undef BRESDASH #undef BITS #undef BITS2 #define BRESSOLID fbBresSolid32 #define BRESSOLIDR fbBresSolidR32 #define BRESDASH fbBresDash32 #define BITS CARD32 #include "fbsegbits.h" #undef BRESSOLID #undef BRESSOLIDR #undef BRESDASH #undef BITS static void fbBresSolid(DrawablePtr drawable, GCPtr gc, int dashOffset, int sdx, int sdy, int axis, int x1, int y1, int e, int e1, int e3, int len) { FbStip *dst; FbStride stride; int bpp; int dx, dy; FbGCPrivPtr pgc = fb_gc(gc); FbStip and = (FbStip) pgc->and; FbStip xor = (FbStip) pgc->xor; FbStip mask, mask0; FbStip bits; fbGetStipDrawable(drawable, dst, stride, bpp, dx, dy); dst += ((y1 + dy) * stride); x1 = (x1 + dx) * bpp; dst += x1 >> FB_STIP_SHIFT; x1 &= FB_STIP_MASK; mask0 = FbStipMask(0, bpp); mask = FbStipRight(mask0, x1); if (sdx < 0) mask0 = FbStipRight(mask0, FB_STIP_UNIT - bpp); if (sdy < 0) stride = -stride; if (axis == X_AXIS) { bits = 0; while (len--) { bits |= mask; mask = fbBresShiftMask(mask, sdx, bpp); if (!mask) { WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, bits)); bits = 0; dst += sdx; mask = mask0; } e += e1; if (e >= 0) { WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, bits)); bits = 0; dst += stride; e += e3; } } if (bits) WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, bits)); } else { while (len--) { WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, mask)); dst += stride; e += e1; if (e >= 0) { e += e3; mask = fbBresShiftMask(mask, sdx, bpp); if (!mask) { dst += sdx; mask = mask0; } } } } } static void fbBresDash(DrawablePtr drawable, GCPtr gc, int dashOffset, int sdx, int sdy, int axis, int x1, int y1, int e, int e1, int e3, int len) { FbStip *dst; FbStride stride; int bpp; int dx, dy; FbGCPrivPtr pgc = fb_gc(gc); FbStip and = (FbStip) pgc->and; FbStip xor = (FbStip) pgc->xor; FbStip bgand = (FbStip) pgc->bgand; FbStip bgxor = (FbStip) pgc->bgxor; FbStip mask, mask0; FbDashDeclare; int dashlen; bool even; bool doOdd; fbGetStipDrawable(drawable, dst, stride, bpp, dx, dy); doOdd = gc->lineStyle == LineDoubleDash; FbDashInit(gc, pgc, dashOffset, dashlen, even); dst += ((y1 + dy) * stride); x1 = (x1 + dx) * bpp; dst += x1 >> FB_STIP_SHIFT; x1 &= FB_STIP_MASK; mask0 = FbStipMask(0, bpp); mask = FbStipRight(mask0, x1); if (sdx < 0) mask0 = FbStipRight(mask0, FB_STIP_UNIT - bpp); if (sdy < 0) stride = -stride; while (len--) { if (even) WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, mask)); else if (doOdd) WRITE(dst, FbDoMaskRRop(READ(dst), bgand, bgxor, mask)); if (axis == X_AXIS) { mask = fbBresShiftMask(mask, sdx, bpp); if (!mask) { dst += sdx; mask = mask0; } e += e1; if (e >= 0) { dst += stride; e += e3; } } else { dst += stride; e += e1; if (e >= 0) { e += e3; mask = fbBresShiftMask(mask, sdx, bpp); if (!mask) { dst += sdx; mask = mask0; } } } FbDashStep(dashlen, even); } } static void fbBresFill(DrawablePtr drawable, GCPtr gc, int dashOffset, int sdx, int sdy, int axis, int x1, int y1, int e, int e1, int e3, int len) { while (len--) { fbFill(drawable, gc, x1, y1, 1, 1); if (axis == X_AXIS) { x1 += sdx; e += e1; if (e >= 0) { e += e3; y1 += sdy; } } else { y1 += sdy; e += e1; if (e >= 0) { e += e3; x1 += sdx; } } } } static void fbSetFg(DrawablePtr drawable, GCPtr gc, Pixel fg) { if (fg != gc->fgPixel) { gc->fgPixel = fg; fbValidateGC(gc, GCForeground, drawable); } } static void fbBresFillDash(DrawablePtr drawable, GCPtr gc, int dashOffset, int sdx, int sdy, int axis, int x1, int y1, int e, int e1, int e3, int len) { FbGCPrivPtr pgc = fb_gc(gc); FbDashDeclare; int dashlen; bool even; bool doOdd; bool doBg; Pixel fg, bg; fg = gc->fgPixel; bg = gc->bgPixel; /* whether to fill the odd dashes */ doOdd = gc->lineStyle == LineDoubleDash; /* whether to switch fg to bg when filling odd dashes */ doBg = doOdd && (gc->fillStyle == FillSolid || gc->fillStyle == FillStippled); /* compute current dash position */ FbDashInit(gc, pgc, dashOffset, dashlen, even); while (len--) { if (even || doOdd) { if (doBg) { if (even) fbSetFg(drawable, gc, fg); else fbSetFg(drawable, gc, bg); } fbFill(drawable, gc, x1, y1, 1, 1); } if (axis == X_AXIS) { x1 += sdx; e += e1; if (e >= 0) { e += e3; y1 += sdy; } } else { y1 += sdy; e += e1; if (e >= 0) { e += e3; x1 += sdx; } } FbDashStep(dashlen, even); } if (doBg) fbSetFg(drawable, gc, fg); } static FbBres * fbSelectBres(DrawablePtr drawable, GCPtr gc) { FbGCPrivPtr pgc = fb_gc(gc); int bpp = drawable->bitsPerPixel; FbBres *bres; DBG(("%s: line=%d, fill=%d, and=%lx, bgand=%lx\n", __FUNCTION__, gc->lineStyle, gc->fillStyle, (long)pgc->and, (long)pgc->bgand)); assert(gc->lineWidth == 0); if (gc->lineStyle == LineSolid) { bres = fbBresFill; if (gc->fillStyle == FillSolid) { bres = fbBresSolid; if (pgc->and == 0) { switch (bpp) { case 8: bres = fbBresSolid8; break; case 16: bres = fbBresSolid16; break; case 32: bres = fbBresSolid32; break; } } else { switch (bpp) { case 8: bres = fbBresSolidR8; break; case 16: bres = fbBresSolidR16; break; case 32: bres = fbBresSolidR32; break; } } } } else { bres = fbBresFillDash; if (gc->fillStyle == FillSolid) { bres = fbBresDash; if (pgc->and == 0 && (gc->lineStyle == LineOnOffDash || pgc->bgand == 0)) { switch (bpp) { case 8: bres = fbBresDash8; break; case 16: bres = fbBresDash16; break; case 32: bres = fbBresDash32; break; } } } } return bres; } struct fbSegment { FbBres *bres; bool drawLast; int *dashOffset; int x1, y1, x2, y2; }; static void _fbSegment(DrawablePtr drawable, GCPtr gc, const BoxRec *b, void *_data) { struct fbSegment *data = _data; const unsigned int bias = miGetZeroLineBias(drawable->pScreen); int adx, ady; /* abs values of dx and dy */ int sdx, sdy; /* sign of dx and dy */ int e, e1, e2, e3; /* bresenham error and increments */ int len, axis, octant; int dashoff, doff; unsigned int oc1, oc2; DBG(("%s box=(%d, %d),(%d, %d)\n", __FUNCTION__, b->x1, b->y1, b->x2, b->y2)); CalcLineDeltas(data->x1, data->y1, data->x2, data->y2, adx, ady, sdx, sdy, 1, 1, octant); if (adx > ady) { axis = X_AXIS; e1 = ady << 1; e2 = e1 - (adx << 1); e = e1 - adx; len = adx; } else { axis = Y_AXIS; e1 = adx << 1; e2 = e1 - (ady << 1); e = e1 - ady; SetYMajorOctant(octant); len = ady; } FIXUP_ERROR(e, octant, bias); /* * Adjust error terms to compare against zero */ e3 = e2 - e1; e = e - e1; if (data->drawLast) len++; dashoff = *data->dashOffset; *data->dashOffset = dashoff + len; oc1 = 0; oc2 = 0; OUTCODES(oc1, data->x1, data->y1, b); OUTCODES(oc2, data->x2, data->y2, b); if ((oc1 | oc2) == 0) { data->bres(drawable, gc, dashoff, sdx, sdy, axis, data->x1, data->y1, e, e1, e3, len); } else if (oc1 & oc2) { } else { int new_x1 = data->x1, new_y1 = data->y1; int new_x2 = data->x2, new_y2 = data->y2; int clip1 = 0, clip2 = 0; int clipdx, clipdy; int err; if (miZeroClipLine(b->x1, b->y1, b->x2-1, b->y2-1, &new_x1, &new_y1, &new_x2, &new_y2, adx, ady, &clip1, &clip2, octant, bias, oc1, oc2) == -1) return; if (axis == X_AXIS) len = abs(new_x2 - new_x1); else len = abs(new_y2 - new_y1); if (clip2 != 0 || data->drawLast) len++; if (len) { /* unwind bresenham error term to first point */ doff = dashoff; err = e; if (clip1) { clipdx = abs(new_x1 - data->x1); clipdy = abs(new_y1 - data->y1); if (axis == X_AXIS) { doff += clipdx; err += e3 * clipdy + e1 * clipdx; } else { doff += clipdy; err += e3 * clipdx + e1 * clipdy; } } data->bres(drawable, gc, doff, sdx, sdy, axis, new_x1, new_y1, err, e1, e3, len); } } } void fbSegment(DrawablePtr drawable, GCPtr gc, int x1, int y1, int x2, int y2, bool drawLast, int *dashOffset) { struct fbSegment data; BoxRec box; DBG(("%s (%d, %d), (%d, %d), drawLast?=%d\n", __FUNCTION__, x1, y1, x2, y2, drawLast)); /* simple overestimate of line extents for clipping */ box.x1 = x1 - 1; box.y1 = y1 - 1; box.x2 = x2 + 1; box.y2 = y2 + 1; data.x1 = x1; data.y1 = y1; data.x2 = x2; data.y2 = y2; data.dashOffset = dashOffset; data.drawLast = drawLast; data.bres = fbSelectBres(drawable, gc); fbDrawableRunUnclipped(drawable, gc, &box, _fbSegment, &data); } void fbSegment1(DrawablePtr drawable, GCPtr gc, const BoxRec *b, int x1, int y1, int x2, int y2, bool drawLast, int *dashOffset) { struct fbSegment data; DBG(("%s (%d, %d), (%d, %d), drawLast?=%d\n", __FUNCTION__, x1, y1, x2, y2, drawLast)); data.x1 = x1; data.y1 = y1; data.x2 = x2; data.y2 = y2; data.dashOffset = dashOffset; data.drawLast = drawLast; data.bres = fbSelectBres(drawable, gc); _fbSegment(drawable, gc, b, &data); }