summaryrefslogtreecommitdiff
path: root/fb/fbtrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'fb/fbtrap.c')
-rw-r--r--fb/fbtrap.c1382
1 files changed, 1382 insertions, 0 deletions
diff --git a/fb/fbtrap.c b/fb/fbtrap.c
new file mode 100644
index 000000000..3a44d53ef
--- /dev/null
+++ b/fb/fbtrap.c
@@ -0,0 +1,1382 @@
+/*
+ * $XFree86: xc/programs/Xserver/fb/fbtrap.c,v 1.10 2002/09/27 00:31:24 keithp Exp $
+ *
+ * Copyright © 2000 University of Southern California
+ *
+ * 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 University
+ * of Southern California not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. University of Southern California makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * University of Southern California DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE 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: Carl Worth, USC, Information Sciences Institute */
+
+#include "fb.h"
+
+#ifdef RENDER
+
+#include "picturestr.h"
+#include "mipict.h"
+#include "fbpict.h"
+
+#ifdef DEBUG
+#include <stdio.h>
+#include <assert.h>
+
+#define ASSERT(e) assert(e)
+
+#endif
+
+#ifndef ASSERT
+#define ASSERT(e)
+#endif
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define MAX_AREA 0x80000000
+
+/*
+ * A RationalPoint is an exact position along one of the trapezoid
+ * edges represented by an approximate position (x,y) and two error
+ * terms (ex_dy, ey_dx). The error in X is multiplied by the Y
+ * dimension of the line while the error in Y is multiplied by the
+ * X dimension of the line, allowing an exact measurement of the
+ * distance from (x,y) to the line.
+ *
+ * Generally, while walking an edge, one of ex_dy/ey_dx will be zero
+ * indicating that the position error is held in the other.
+ */
+typedef struct {
+ xFixed x;
+ xFixed ex_dy;
+ xFixed y;
+ xFixed ey_dx;
+} RationalPoint;
+
+/*
+ * Edges are walked both horizontally and vertically
+ * They are walked vertically to get to a particular row
+ * of pixels, and then walked horizontally within that row
+ * to compute pixel coverage.
+ *
+ * Edges are always walked from top to bottom and from
+ * left to right. This means that for lines moving leftwards
+ * from top to bottom, the left to right walking actually moves
+ * backwards along the line with respect to the top to bottom
+ * walking.
+ */
+
+/*
+ * A RationalRow represents the two positions where
+ * an edge intersects a row of pixels. This is used
+ * to walk an edge vertically
+ */
+
+typedef struct {
+ RationalPoint top; /* intersection at top of row */
+ RationalPoint bottom; /* intersection at bottom of row */
+ RationalPoint pixel_top; /* intersection at top of pixel */
+} RationalRow;
+
+/*
+ * A RationalCol represents the two positions where
+ * an edge intersects a column of pixels
+ */
+
+typedef struct {
+ RationalPoint left; /* intersection at left of column */
+ RationalPoint right; /* intersection at right of column */
+} RationalCol;
+
+/*
+ Here are some thoughts on line walking:
+
+ Conditions: c2.x - c1.x = 1
+ r2.y - r1.y = 1
+
+ A B C D E F G H
+ c1\ c1 c2 /c2
+r1 r1 |\ \ r1 r1 / r1/| r1 r1
+\-+---+ \-+---+ +-\-+ +\--+ +--/+ +-/-+ +---+-/ +---+-/
+ \| | `.c1 | |r1\| | \ | | / | |/ | | .' | |/
+c1\ | |`-.|c2 | \c2 | | | | | | c1/ | c1|,_/|c2 | /c2
+ |\ | | `. | |\ | \ | | / | /| | ./ | | /|
+ +-\-+ +---+-\ +---+-\ +--\+ +/--+ /-+---+ /-+---+ +-/-+
+ r2\| r2 r2 r2\ /r2 r2 r2 |/r2
+ \c2 c2 c1 c1/
+
+ Bottom Right Right Bottom Top Top Right Right
+
+State transitions:
+
+A -> C, D E -> E, F
+B -> A, B F -> G, H
+C -> A, B G -> G, H
+D -> C, D H -> E, F
+
+*/
+
+/*
+ * Values for PixelWalk.depart. Top and Bottom can have the same value
+ * as only one mode is possible given a line of either positive or
+ * negative slope. These mark the departure edge while walking
+ * rightwards across columns.
+ */
+
+typedef enum _departure {
+ DepartTop = 0, /* edge exits top of pixel */
+ DepartBottom = 0, /* edge exits bottom of pixel */
+ DepartRight = 1 /* edge exits right edge of pixel */
+} Departure;
+
+/*
+ * PixelWalk
+ *
+ * This structure holds state to walk a single edge down the trapezoid.
+ *
+ * The edge is walked twice -- once by rows and once by columns.
+ * The two intersections of the pixel by the edge are then set
+ * from either the row or column position, depending on which edge
+ * is intersected.
+ *
+ * Note that for lines moving left, walking by rows moves down the
+ * line (increasing y) while walking by columns moves up the line
+ * (decreasing y).
+ */
+typedef struct {
+ xFixed dx;
+ xFixed ey_thresh;
+ xFixed dy;
+ xFixed ex_thresh;
+
+ Departure depart;
+
+ /* slope */
+ xFixed m;
+ xFixed em_dx;
+ xFixed y_correct;
+ xFixed ey_correct;
+
+ /* Inverse slope. Does this have a standard symbol? */
+ xFixed p;
+ xFixed ep_dy;
+ xFixed x_correct;
+ xFixed ex_correct;
+
+ /* Trapezoid bottom, used to limit walking to the last row */
+ xFixed bottom;
+
+ /*
+ * Current edge positions along pixel rows and columns
+ */
+ RationalRow row;
+ RationalCol col;
+
+ /*
+ * The three pixel intersection points, copied from the appropriate
+ * row or column position above
+ */
+ RationalPoint p_pixel_top;
+ RationalPoint p_trap_top;
+ RationalPoint p_trap_bottom;
+} PixelWalk;
+
+#if 0
+#ifdef GCC
+#define INLINE inline
+#endif
+#endif
+
+#ifndef INLINE
+#define INLINE
+#endif
+
+/*
+ * Step 'pt' vertically to 'newy'.
+ */
+static INLINE void
+pixelWalkMovePointToRow (PixelWalk *pw, RationalPoint *pt, xFixed newy)
+{
+ xFixed_32_32 oex;
+ xFixed xoff;
+
+ /* X error of old X position and new Y position */
+ oex = (xFixed_32_32) pw->dx * (newy - pt->y) - pt->ey_dx + pt->ex_dy;
+
+ /* amount to step X by */
+ xoff = oex / pw->dy;
+
+ /* step X */
+ pt->x = pt->x + xoff;
+
+ /* set new X error value for new X position and new Y positition */
+ pt->ex_dy = oex - (xFixed_32_32) pw->dy * xoff;
+
+ /* set new Y position, set Y error to zero */
+ pt->y = newy;
+ pt->ey_dx = 0;
+}
+
+/*
+ * Step 'pt' horizontally to 'newx'
+ */
+static INLINE void
+pixelWalkMovePointToCol (PixelWalk *pw, RationalPoint *pt, xFixed newx)
+{
+ xFixed_32_32 oey;
+ xFixed yoff;
+
+ /* Special case vertical lines to arbitrary y */
+ if (pw->dx == 0)
+ {
+ pt->x = newx;
+ pt->ex_dy = 0;
+ pt->y = 0;
+ pt->ey_dx = 0;
+ }
+ else
+ {
+ /* Y error of old Y position and new X position */
+ oey = (xFixed_32_32) pw->dy * (newx - pt->x) - pt->ex_dy + pt->ey_dx;
+
+ /* amount to step Y by */
+ yoff = oey / pw->dx;
+
+ /* step Y */
+ pt->y = pt->y + yoff;
+
+ /* set new Y error value for new Y position and new X position */
+ pt->ey_dx = oey - (xFixed_32_32) pw->dx * yoff;
+
+ /* set new X position, set X error to zero */
+ pt->x = newx;
+ pt->ex_dy = 0;
+ }
+}
+
+/*
+ * Step the 'row' element of 'pw' vertically
+ * (increasing y) by one whole pixel
+ */
+static INLINE void
+pixelWalkStepRow (PixelWalk *pw)
+{
+ xFixed y_next = xFixedFloor (pw->row.bottom.y) + xFixed1;
+
+ if (y_next > pw->bottom)
+ y_next = pw->bottom;
+
+ /* pw.row.top.y < pw.row.bottom.y */
+ pw->row.top = pw->row.bottom;
+
+ if (y_next - pw->row.bottom.y == xFixed1)
+ {
+ pw->row.pixel_top = pw->row.bottom;
+ pw->row.bottom.y += xFixed1;
+ pw->row.bottom.x += pw->p;
+ pw->row.bottom.ex_dy += pw->ep_dy;
+ if (abs (pw->row.bottom.ex_dy) > pw->ex_thresh)
+ {
+ pw->row.bottom.x += pw->x_correct;
+ pw->row.bottom.ex_dy += pw->ex_correct;
+ }
+ }
+ else
+ {
+ pixelWalkMovePointToRow (pw, &pw->row.pixel_top,
+ xFixedCeil (y_next) - xFixed1);
+ pixelWalkMovePointToRow (pw, &pw->row.bottom, y_next);
+ }
+}
+
+/*
+ * Step the 'col' element of 'pw' horizontally
+ * (increasing x) by one whole pixel
+ */
+static INLINE void
+pixelWalkStepCol (PixelWalk *pw)
+{
+ /* pw.col.p1.x < pw.col.p2.x */
+ /*
+ * Copy the current right point into the left point
+ */
+ pw->col.left = pw->col.right;
+
+ /*
+ * Now incrementally walk across the pixel
+ */
+ pw->col.right.x += xFixed1;
+ pw->col.right.y += pw->m;
+ pw->col.right.ey_dx += pw->em_dx;
+ if (pw->col.right.ey_dx > pw->ey_thresh)
+ {
+ pw->col.right.y += pw->y_correct;
+ pw->col.right.ey_dx += pw->ey_correct;
+ }
+}
+/*
+ * Walk to the nearest edge of the next pixel, filling in both p1 and
+ * p2 as necessary from either the row or col intersections.
+ *
+ * The "next" pixel is defined to be the next pixel intersected by the
+ * line with pixels visited in raster scan order, (for the benefit of
+ * cache performance). For lines with positive slope it is easy to
+ * achieve raster scan order by simply calling StepCol for each pixel
+ * in a given scanline, then calling StepRow once at the end of each
+ * scanline.
+ *
+ * However, for lines of negative slope where the magnitude of dx is
+ * greater than dy, a little more work needs to be done. The pixels of
+ * a particular scanline will be visited by succesive calls to StepCol
+ * as before. This will effectively step "up" the line as we scan from
+ * left to right. But, the call to StepRow at the end of the scan line
+ * will step "down" the line and the column information will be
+ * invalid at that point.
+ *
+ * For now, I fix up the column of all negative slope lines by calling
+ * MovePointToCol at the end of each scanline. However, this is an
+ * extremely expensive operation since it involves a 64-bit multiply
+ * and a 64-bit divide. It would be much better, (at least as long as
+ * abs(dx) is not much greater than dy), to instead step the col
+ * backwards as many times as necessary. Or even better, we could
+ * simply restore col to the position it began at when we started the
+ * scanline, then simply step it backwards once. That would give a
+ * performance benefit for lines with slope of any magnitude.
+ */
+
+static INLINE void
+pixelWalkNextPixel (PixelWalk *pw)
+{
+ if (pw->dx < 0)
+ {
+ /*
+ * left moving lines
+ *
+ * Check which pixel edge we're departing from
+ *
+ * Remember that in this case (dx < 0), the 'row' element of 'pw'
+ * walks down the line while 'col' walks up
+ */
+ if (pw->depart == DepartTop)
+ {
+ /*
+ * The edge departs the row at this pixel, the
+ * next time it gets used will be for the next row
+ *
+ * Step down one row and then recompute the
+ * column values to start the next row of
+ * pixels
+ */
+ pixelWalkStepRow(pw);
+ /*
+ * Set column exit pixel
+ */
+ pixelWalkMovePointToCol(pw, &pw->col.right, xFixedFloor(pw->row.bottom.x));
+ /*
+ * This moves the exit pixel to the entry pixel
+ * and computes the next exit pixel
+ */
+ pixelWalkStepCol(pw);
+ /*
+ * The first pixel on the next row will always
+ * be entered from below, set the lower
+ * intersection of this edge with that pixel
+ */
+ pw->p_trap_bottom = pw->row.bottom;
+ }
+ else /* pw->depart == DepartRight */
+ {
+ /*
+ * easy case -- just move right one pixel
+ */
+ pixelWalkStepCol(pw);
+ /*
+ * Set the lower intersection of the edge with the
+ * pixel -- that's just where the edge entered
+ * the pixel from the left
+ */
+ pw->p_trap_bottom = pw->col.left;
+ }
+ /*
+ * Now compute which edge the pixel
+ * is departing from
+ */
+ if (pw->row.top.x <= pw->col.right.x)
+ {
+ /*
+ * row intersection is left of column intersection,
+ * that means the edge hits the top of the pixel
+ * before it hits the right edge
+ */
+ pw->p_trap_top = pw->row.top;
+ pw->depart = DepartTop;
+ /*
+ * Further check to see whether the edge
+ * leaves the right or top edge of the
+ * whole pixel
+ */
+ if (pw->row.pixel_top.x <= pw->col.right.x)
+ pw->p_pixel_top = pw->row.pixel_top;
+ else
+ pw->p_pixel_top = pw->col.right;
+ }
+ else
+ {
+ /*
+ * Row intersection is right of colum intersection,
+ * that means the edge hits the right edge of the
+ * pixel first
+ */
+ pw->p_trap_top = pw->col.right;
+ pw->p_pixel_top = pw->col.right;
+ pw->depart = DepartRight;
+ }
+ }
+ else
+ {
+ /*
+ * right moving lines
+ *
+ * Check which edge we're departing from
+ *
+ * In the dx >= 0 case, the row and col elements both
+ * walk downwards
+ */
+ if (pw->depart == DepartBottom)
+ {
+ /*
+ * The edge departs the row at this pixel,
+ * the next time it gets used will be for the
+ * next row
+ *
+ * Step down one row and (maybe) over one
+ * column to prepare for the next row
+ */
+ if (pw->row.bottom.x == pw->col.right.x)
+ {
+ /*
+ * right through the corner of the pixel,
+ * adjust the column
+ */
+ pixelWalkStepCol(pw);
+ }
+ pixelWalkStepRow(pw);
+ /*
+ * Set the upper intersection of the edge with
+ * the pixel, the first pixel on the next
+ * row is always entered from the top
+ */
+ pw->p_trap_top = pw->row.top;
+ pw->p_pixel_top = pw->row.pixel_top;
+ }
+ else /* pw->depart == DepartRight */
+ {
+ /*
+ * Easy case -- move right one
+ * pixel
+ */
+ pixelWalkStepCol(pw);
+ /*
+ * Set the upper intersection of the edge
+ * with the pixel, that's along the left
+ * edge of the pixel
+ */
+ pw->p_trap_top = pw->col.left;
+ pw->p_pixel_top = pw->col.left;
+ }
+ /*
+ * Now compute the exit edge and the
+ * lower intersection of the edge with the pixel
+ */
+ if (pw->row.bottom.x <= pw->col.right.x)
+ {
+ /*
+ * Hit the place where the edge leaves
+ * the pixel, the lower intersection is
+ * where the edge hits the bottom
+ */
+ pw->p_trap_bottom = pw->row.bottom;
+ pw->depart = DepartBottom;
+ }
+ else
+ {
+ /*
+ * The edge goes through the
+ * next pixel on the row,
+ * the lower intersection is where the
+ * edge hits the right side of the pixel
+ */
+ pw->p_trap_bottom = pw->col.right;
+ pw->depart = DepartRight;
+ }
+ }
+}
+
+/*
+ * Compute the first pixel intersection points
+ * and the departure type from that pixel
+ */
+static void
+pixelWalkFirstPixel (PixelWalk *pw)
+{
+ if (pw->dx < 0)
+ {
+ if (pw->row.top.x <= pw->col.right.x)
+ {
+ /*
+ * leaving through the top.
+ * upper position is the upper point of
+ * the 'row' element
+ */
+ pw->depart = DepartTop;
+ pw->p_trap_top = pw->row.top;
+ /*
+ * further check for pixel top
+ */
+ if (pw->row.pixel_top.x <= pw->col.right.x)
+ pw->p_pixel_top = pw->row.pixel_top;
+ else
+ pw->p_pixel_top = pw->col.right;
+ }
+ else
+ {
+ /*
+ * leaving through the right side
+ * upper position is the right point of
+ * the 'col' element
+ */
+ pw->depart = DepartRight;
+ pw->p_trap_top = pw->col.right;
+ pw->p_pixel_top = pw->col.right;
+ }
+ /*
+ * Now find the lower pixel intersection point
+ */
+ if (pw->row.bottom.x >= pw->col.left.x)
+ /*
+ * entering through bottom,
+ * lower position is the bottom point of
+ * the 'row' element
+ */
+ pw->p_trap_bottom = pw->row.bottom;
+ else
+ /*
+ * entering through left side,
+ * lower position is the left point of
+ * the 'col' element
+ */
+ pw->p_trap_bottom = pw->col.left;
+ }
+ else
+ {
+ if (pw->row.bottom.x <= pw->col.right.x)
+ {
+ /*
+ * leaving through the bottom (or corner).
+ * lower position is the lower point of
+ * the 'row' element
+ */
+ pw->depart = DepartBottom;
+ pw->p_trap_bottom = pw->row.bottom;
+ }
+ else
+ {
+ /*
+ * leaving through the right side
+ * lower position is the right point of
+ * the 'col' element
+ */
+ pw->depart = DepartRight;
+ pw->p_trap_bottom = pw->col.right;
+ }
+ /*
+ * Now find the upper pixel intersection point
+ */
+ if (pw->row.top.x >= pw->col.left.x)
+ {
+ /*
+ * entering through the top (or corner),
+ * upper position is the top point
+ * of the 'row' element
+ */
+ pw->p_trap_top = pw->row.top;
+ /*
+ * further check for pixel entry
+ */
+ if (pw->row.pixel_top.x >= pw->col.left.x)
+ pw->p_pixel_top = pw->row.pixel_top;
+ else
+ pw->p_pixel_top = pw->col.left;
+ }
+ else
+ {
+ /*
+ * entering through the left side,
+ * upper position is the left point of
+ * the 'col' element
+ */
+ pw->p_trap_top = pw->col.left;
+ pw->p_pixel_top = pw->col.left;
+ }
+ }
+}
+
+static void
+pixelWalkInit (PixelWalk *pw, xLineFixed *line, xFixed top_y, xFixed bottom_y)
+{
+ xFixed_32_32 dy_inc, dx_inc;
+ xFixed next_y;
+ xFixed left_x;
+ xPointFixed *top, *bot;
+
+ next_y = xFixedFloor (top_y) + xFixed1;
+ if (next_y > bottom_y)
+ next_y = bottom_y;
+
+ /*
+ * Orient lines top down
+ */
+ if (line->p1.y < line->p2.y)
+ {
+ top = &line->p1;
+ bot = &line->p2;
+ }
+ else
+ {
+ top = &line->p2;
+ bot = &line->p1;
+ }
+
+ pw->dx = bot->x - top->x;
+ pw->ey_thresh = abs(pw->dx >> 1);
+ pw->dy = bot->y - top->y;
+ pw->ex_thresh = pw->dy >> 1;
+
+ /*
+ * Set step values for walking lines
+ */
+ if (pw->dx < 0)
+ {
+ pw->x_correct = -1;
+ pw->ex_correct = pw->dy;
+ pw->y_correct = -1;
+ pw->ey_correct = pw->dx;
+ }
+ else
+ {
+ pw->x_correct = 1;
+ pw->ex_correct = -pw->dy;
+ pw->y_correct = 1;
+ pw->ey_correct = -pw->dx;
+ }
+
+ pw->bottom = bottom_y;
+
+ /*
+ * Compute Bresenham values for walking edges incrementally
+ */
+ dy_inc = (xFixed_32_32) xFixed1 * pw->dy; /* > 0 */
+ if (pw->dx != 0)
+ {
+ pw->m = dy_inc / pw->dx; /* sign(dx) */
+ pw->em_dx = dy_inc - (xFixed_32_32) pw->m * pw->dx; /* > 0 */
+ }
+ else
+ {
+ /* Vertical line. Setting these to zero prevents us from
+ having to put any conditions in pixelWalkStepCol. */
+ pw->m = 0;
+ pw->em_dx = 0;
+ }
+
+ dx_inc = (xFixed_32_32) xFixed1 * (xFixed_32_32) pw->dx; /* sign(dx) */
+ pw->p = dx_inc / pw->dy; /* sign(dx) */
+ pw->ep_dy = dx_inc - (xFixed_32_32) pw->p * pw->dy; /* sign(dx) */
+
+ /*
+ * Initialize 'row' for walking down rows
+ */
+ pw->row.bottom.x = top->x;
+ pw->row.bottom.ex_dy = 0;
+ pw->row.bottom.y = top->y;
+ pw->row.bottom.ey_dx = 0;
+
+ /*
+ * Initialize 'pixel_top' to be on the line for
+ * the first step
+ */
+ pw->row.pixel_top = pw->row.bottom;
+ /*
+ * Move to the pixel above the 'top_y' coordinate,
+ * first setting 'bottom' and then using StepRow
+ * which moves that to 'top' and computes the next 'bottom'
+ */
+ pixelWalkMovePointToRow(pw, &pw->row.bottom, top_y);
+ pixelWalkStepRow(pw);
+
+ /*
+ * Initialize 'col' for walking across columns
+ */
+ pw->col.right.x = top->x;
+ pw->col.right.ex_dy = 0;
+ pw->col.right.y = top->y;
+ pw->col.right.ey_dx = 0;
+
+ /*
+ * First set the column to the left most
+ * pixel hit by the row
+ */
+ if (pw->dx < 0)
+ left_x = pw->row.bottom.x;
+ else
+ left_x = pw->row.top.x;
+
+ pixelWalkMovePointToCol(pw, &pw->col.right, xFixedFloor (left_x));
+ pixelWalkStepCol(pw);
+
+ /*
+ * Compute first pixel intersections and the
+ * first departure state
+ */
+ pixelWalkFirstPixel (pw);
+}
+
+#define RoundShift(a,b) (((a) + (1 << ((b) - 1))) >> (b))
+#define MaxAlpha(depth) ((1 << (depth)) - 1)
+
+#define AreaAlpha(area, depth) (RoundShift (RoundShift (area, depth) * \
+ MaxAlpha (depth), \
+ (31 - depth)))
+
+/*
+ Pixel coverage from the upper-left corner bounded by one horizontal
+ bottom line (bottom) and one line defined by two points, (x1,y1) and
+ (x2,y2), which intersect the pixel. y1 must be less than y2. There
+ are 8 cases yielding the following area calculations:
+
+ A B C D E F G H
++---+ +---+ +-1-+ +1--+ +--1+ +-1-+ +---+ +---+
+| | 1 | | \| | \ | | / | |/ | | 1 | |
+1 | |`-.| | 2 | | | | | | 2 | |,_/| | 1
+|\ | | 2 | | | \ | | / | | | 2 | | /|
++-2-+ +---+ +---+ +--2+ +2--+ +---+ +---+ +-2-+
+
+A: (1/2 * x2 * (y2 - y1))
+B: (1/2 * x2 * (y2 - y1)) + (bottom - y2) * x2
+C: (1/2 * (x1 + x2) * y2 ) + (bottom - y2) * x2
+D: (1/2 * (x1 + x2) * y2 )
+E: (1/2 * (x1 + x2) * y2 )
+F: (1/2 * x1 * y2 )
+G: (1/2 * x1 * (y2 - y1)) + x1 * y1
+H: (1/2 * (x1 + x2) * (y2 - y1)) + x1 * y1
+
+The union of these calculations is valid for all cases. Namely:
+
+ (1/2 * (x1 + x2) * (y2 - y1)) + (bottom - y2) * x2 + x1 * y1
+
+An exercise for later would perhaps be to optimize the calculations
+for some of the cases above. Specifically, it's possible to eliminate
+multiplications by zero in several cases, leaving a maximum of two
+multiplies per pixel calculation. (This is even more promising now
+that the higher level code actually computes the exact same 8 cases
+as part of its pixel walking).
+
+But, for now, I just want to get something working correctly even if
+slower. So, we'll use the non-optimized general equation.
+
+*/
+
+/* 1.16 * 1.16 -> 1.31 */
+#define AREA_MULT(w, h) ( (xFixed_1_31) (((((xFixed_1_16)w)*((xFixed_1_16)h) + 1) >> 1) | (((xFixed_1_16)w)&((xFixed_1_16)h)&0x10000) << 15))
+
+/* (1.16 + 1.16) / 2 -> 1.16 */
+#define WIDTH_AVG(x1,x2) (((x1) + (x2) + 1) >> 1)
+
+#define SubPixelArea(x1, y1, x2, y2, bottom) \
+(xFixed_1_31) ( \
+ AREA_MULT((x1), (y1)) \
+ + AREA_MULT(WIDTH_AVG((x1), (x2)), (y2) - (y1))\
+ + AREA_MULT((x2), (bottom) - (y2)) \
+)
+
+/*
+static xFixed_1_31
+SubPixelArea (xFixed_1_16 x1,
+ xFixed_1_16 y1,
+ xFixed_1_16 x2,
+ xFixed_1_16 y2,
+ xFixed_1_16 bottom)
+{
+ xFixed_1_16 x_trap;
+ xFixed_1_16 h_top, h_trap, h_bot;
+ xFixed_1_31 area;
+
+ x_trap = WIDTH_AVG(x1,x2);
+ h_top = y1;
+ h_trap = (y2 - y1);
+ h_bot = (bottom - y2);
+
+ area = AREA_MULT(x1, h_top) +
+ AREA_MULT(x_trap, h_trap) +
+ AREA_MULT(x2, h_bot);
+
+ return area;
+}
+*/
+
+#define SubPixelAlpha(x1, y1, x2, y2, bottom, depth) \
+( \
+ AreaAlpha( \
+ SubPixelArea((x1), (y1), (x2), (y2), (bottom)), \
+ (depth) \
+ ) \
+)
+
+/*
+static int
+SubPixelAlpha (xFixed_1_16 x1,
+ xFixed_1_16 y1,
+ xFixed_1_16 x2,
+ xFixed_1_16 y2,
+ xFixed_1_16 bottom,
+ int depth)
+{
+ xFixed_1_31 area;
+
+ area = SubPixelArea(x1, y1, x2, y2, bottom);
+
+ return AreaAlpha(area, depth);
+}
+*/
+
+/* Alpha of a pixel above a given horizontal line */
+#define AlphaAbove(pixel_y, line_y, depth) \
+( \
+ AreaAlpha(AREA_MULT((line_y) - (pixel_y), xFixed1), depth) \
+)
+
+static int
+RectAlpha(xFixed pixel_y, xFixed top, xFixed bottom, int depth)
+{
+ if (depth == 1)
+ return top == pixel_y ? 1 : 0;
+ else
+ return (AlphaAbove (pixel_y, bottom, depth) -
+ AlphaAbove (pixel_y, top, depth));
+}
+
+
+/*
+ * Pixel coverage from the left edge bounded by one horizontal lines,
+ * (top and bottom), as well as one PixelWalk line.
+ */
+static int
+AlphaAboveLeft(RationalPoint *upper,
+ RationalPoint *lower,
+ xFixed bottom,
+ xFixed pixel_x,
+ xFixed pixel_y,
+ int depth)
+{
+ return SubPixelAlpha(upper->x - pixel_x,
+ upper->y - pixel_y,
+ lower->x - pixel_x,
+ lower->y - pixel_y,
+ bottom - pixel_y,
+ depth);
+}
+
+/*
+ Pixel coverage from the left edge bounded by two horizontal lines,
+ (top and bottom), as well as one line two points, p1 and p2, which
+ intersect the pixel. The following condition must be true:
+
+ p2.y > p1.y
+*/
+
+/*
+ lr
+ |\
+ +--|-\-------+
+ | a| b\ |
+ =======|===\========== top
+ | c| d \ |
+ =======|=====\======== bot
+ | | \ |
+ +--|-------\-+
+
+ alpha(d) = alpha(cd) - alpha(c) = alpha(abcd) - alpha(ab) - (alpha(ac) - alpha(c))
+
+ alpha(d) = pixelalpha(top, bot, right) - pixelalpha(top, bot, left)
+
+ pixelalpha(top, bot, line) = subpixelalpha(bot, line) - subpixelalpha(top, line)
+*/
+
+static int
+PixelAlpha(xFixed pixel_x,
+ xFixed pixel_y,
+ xFixed top,
+ xFixed bottom,
+ PixelWalk *pw,
+ int depth)
+{
+ int alpha;
+
+#ifdef DEBUG
+ fprintf(stderr, "alpha (%f, %f) - (%f, %f) = ",
+ (double) pw->p1.x / (1 << 16),
+ (double) pw->p1.y / (1 << 16),
+ (double) pw->p2.x / (1 << 16),
+ (double) pw->p2.y / (1 << 16));
+ fflush(stderr);
+#endif
+
+ /*
+ * Sharp polygons are different, alpha is 1 if the
+ * area includes the pixel origin, else zero, in
+ * the above figure, only 'a' has alpha 1
+ */
+ if (depth == 1)
+ {
+ alpha = 0;
+ if (top == pixel_y && pw->p_pixel_top.x != pixel_x)
+ alpha = 1;
+ }
+ else
+ {
+ alpha = (AlphaAboveLeft(&pw->p_pixel_top, &pw->p_trap_bottom,
+ bottom, pixel_x, pixel_y, depth)
+ - AlphaAboveLeft(&pw->p_pixel_top, &pw->p_trap_top,
+ top, pixel_x, pixel_y, depth));
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "0x%x => %f\n",
+ alpha,
+ (double) alpha / ((1 << depth) -1 ));
+ fflush(stderr);
+#endif
+
+ return alpha;
+}
+
+#define INCREMENT_X_AND_PIXEL \
+{ \
+ pixel_x += xFixed1; \
+ (*mask.over) (&mask); \
+}
+
+/* XXX: What do we really want this prototype to look like? Do we want
+ separate versions for 1, 4, 8, and 16-bit alpha? */
+
+#define saturateAdd(t, a, b) (((t) = (a) + (b)), \
+ ((CARD8) ((t) | (0 - ((t) >> 8)))))
+
+#define addAlpha(mask, depth, alpha, temp) (\
+ (*(mask)->store) ((mask), (alpha == (1 << depth) - 1) ? \
+ 0xff000000 : \
+ (saturateAdd (temp, \
+ alpha << (8 - depth), \
+ (*(mask)->fetch) (mask) >> 24) << 24)) \
+)
+
+void
+fbRasterizeTrapezoid (PicturePtr pMask,
+ xTrapezoid *pTrap,
+ int x_off,
+ int y_off)
+{
+ xTrapezoid trap = *pTrap;
+ int alpha, temp;
+
+ FbCompositeOperand mask;
+
+ int depth = pMask->pDrawable->depth;
+ int max_alpha = (1 << depth) - 1;
+ int buf_width = pMask->pDrawable->width;
+
+ xFixed x_off_fixed = IntToxFixed(x_off);
+ xFixed y_off_fixed = IntToxFixed(y_off);
+ xFixed buf_width_fixed = IntToxFixed(buf_width);
+
+ PixelWalk left, right;
+ xFixed pixel_x, pixel_y;
+ xFixed first_right_x;
+ xFixed y, y_next;
+
+ /* trap.left and trap.right must be non-horizontal */
+ if (trap.left.p1.y == trap.left.p2.y
+ || trap.right.p1.y == trap.right.p2.y) {
+ return;
+ }
+
+ trap.top += y_off_fixed;
+ trap.bottom += y_off_fixed;
+ trap.left.p1.x += x_off_fixed;
+ trap.left.p1.y += y_off_fixed;
+ trap.left.p2.x += x_off_fixed;
+ trap.left.p2.y += y_off_fixed;
+ trap.right.p1.x += x_off_fixed;
+ trap.right.p1.y += y_off_fixed;
+ trap.right.p2.x += x_off_fixed;
+ trap.right.p2.y += y_off_fixed;
+
+#ifdef DEBUG
+ fprintf(stderr, "(top, bottom) = (%f, %f)\n",
+ (double) trap.top / (1 << 16),
+ (double) trap.bottom / (1 << 16));
+#endif
+
+ pixelWalkInit(&left, &trap.left, trap.top, trap.bottom);
+ pixelWalkInit(&right, &trap.right, trap.top, trap.bottom);
+
+ /* XXX: I'd still like to optimize this loop for top and
+ bottom. Only the first row intersects top and only the last
+ row, (which could also be the first row), intersects bottom. So
+ we could eliminate some unnecessary calculations from all other
+ rows. Unfortunately, I haven't found an easy way to do it
+ without bloating the text, (eg. unrolling a couple iterations
+ of the loop). So, for sake of maintenance, I'm putting off this
+ optimization at least until this code is more stable.. */
+
+ if (!fbBuildCompositeOperand (pMask, &mask, 0, xFixedToInt (trap.top), FALSE, FALSE))
+ return;
+
+ for (y = trap.top; y < trap.bottom; y = y_next)
+ {
+ pixel_y = xFixedFloor (y);
+ y_next = pixel_y + xFixed1;
+ if (y_next > trap.bottom)
+ y_next = trap.bottom;
+
+ ASSERT (left.row.top.y == y);
+ ASSERT (left.row.bottom.y == y_next);
+ ASSERT (right.row.top.y == y);
+ ASSERT (right.row.bottom.y == y_next);
+
+ pixel_x = xFixedFloor(left.col.left.x);
+
+ /*
+ * Walk pixels on this row that are left of the
+ * first possibly lit pixel
+ *
+ * pixelWalkNextPixel will change .row.top.y
+ * when the last pixel covered by the edge
+ * is passed
+ */
+
+ first_right_x = xFixedFloor(right.col.left.x);
+ while (right.row.top.y == y && first_right_x < pixel_x)
+ {
+ /* these are empty */
+ pixelWalkNextPixel (&right);
+ /* step over */
+ first_right_x += xFixed1;
+ }
+
+ (*mask.set) (&mask, xFixedToInt (pixel_x), xFixedToInt (y));
+
+ /*
+ * Walk pixels on this row intersected by only trap.left
+ *
+ */
+ while (left.row.top.y == y && pixel_x < first_right_x)
+ {
+ alpha = (RectAlpha (pixel_y, y, y_next, depth)
+ - PixelAlpha(pixel_x, pixel_y, y, y_next, &left, depth));
+
+ if (alpha > 0)
+ {
+ if (0 <= pixel_x && pixel_x < buf_width_fixed)
+ addAlpha (&mask, depth, alpha, temp);
+ }
+
+ /*
+ * Step right
+ */
+ pixelWalkNextPixel(&left);
+ INCREMENT_X_AND_PIXEL;
+ }
+
+ /*
+ * Either pixels are covered by both edges or
+ * there are fully covered pixels on this row
+ */
+ if (pixel_x == first_right_x)
+ {
+ /*
+ * Now walk the pixels on this row intersected
+ * by both edges
+ */
+ while (left.row.top.y == y && right.row.top.y == y)
+ {
+ alpha = (PixelAlpha(pixel_x, pixel_y, y, y_next, &right, depth)
+ - PixelAlpha(pixel_x, pixel_y, y, y_next, &left, depth));
+ if (alpha > 0)
+ {
+ ASSERT (0 <= alpha && alpha <= max_alpha);
+ if (0 <= pixel_x && pixel_x < buf_width_fixed)
+ addAlpha (&mask, depth, alpha, temp);
+ }
+ pixelWalkNextPixel(&left);
+ pixelWalkNextPixel(&right);
+ INCREMENT_X_AND_PIXEL;
+ }
+ /*
+ * If the right edge is now left of the left edge,
+ * the left edge will end up only partially walked,
+ * walk it the rest of the way
+ */
+ while (left.row.top.y == y)
+ pixelWalkNextPixel(&left);
+ }
+ else
+ {
+ /*
+ * Fully covered pixels simply saturate
+ */
+ alpha = RectAlpha (pixel_y, y, y_next, depth);
+ if (alpha == max_alpha)
+ {
+ while (pixel_x < first_right_x)
+ {
+ if (0 <= pixel_x && pixel_x < buf_width_fixed)
+ (*mask.store) (&mask, 0xff000000);
+ INCREMENT_X_AND_PIXEL;
+ }
+ }
+ else
+ {
+ while (pixel_x < first_right_x)
+ {
+ ASSERT (0 <= alpha && alpha <= max_alpha);
+ if (0 <= pixel_x && pixel_x < buf_width_fixed)
+ addAlpha (&mask, depth, alpha, temp);
+ INCREMENT_X_AND_PIXEL;
+ }
+ }
+ }
+
+ /*
+ * Finally, pixels intersected only by trap.right
+ */
+ while (right.row.top.y == y)
+ {
+ alpha = PixelAlpha(pixel_x, pixel_y, y, y_next, &right, depth);
+ if (alpha > 0)
+ {
+ if (0 <= pixel_x && pixel_x < buf_width_fixed)
+ addAlpha (&mask, depth, alpha, temp);
+ }
+ pixelWalkNextPixel(&right);
+ INCREMENT_X_AND_PIXEL;
+ }
+ }
+}
+
+/* Some notes on walking while keeping track of errors in both dimensions:
+
+That's really pretty easy. Your bresenham should be walking sub-pixel
+coordinates rather than pixel coordinates. Now you can calculate the
+sub-pixel Y coordinate for any arbitrary sub-pixel X coordinate (or vice
+versa).
+
+ ey: y error term (distance from current Y sub-pixel to line) * dx
+ ex: x error term (distance from current X sub-pixel to line) * dy
+ dx: difference of X coordinates for line endpoints
+ dy: difference of Y coordinates for line endpoints
+ x: current fixed-point X coordinate
+ y: current fixed-point Y coordinate
+
+One of ey or ex will always be zero, depending on whether the distance to
+the line was measured horizontally or vertically.
+
+In moving from x, y to x1, y1:
+
+ (x1 + e1x/dy) - (x + ex/dy) dx
+ --------------------------- = --
+ (y1 + e1y/dx) - (y + ey/dx) dy
+
+ (x1dy + e1x) - (xdy + ex) = (y1dx + e1y) - (ydx + ey)
+
+ dy(x1 - x) + (e1x - ex) = dx(y1-y) + (e1y - ey)
+
+So, if you know y1 and want to know x1:
+
+ Set e1y to zero and compute the error from x:
+
+ oex = dx(y1 - y) - ey + ex
+
+ Compute the number of whole pixels to get close to the line:
+
+ wx = oex / dy
+
+ Set x1:
+
+ Now compute the e1x:
+
+ e1x = oex - wx * dy
+
+A similar operation moves to a known y1. Note that this computation (in
+general) requires 64 bit arithmetic. I suggest just using the available
+64 bit datatype for now, we can optimize the common cases with a few
+conditionals. There's some cpp code in fb/fb.h that selects a 64 bit type
+for machines that XFree86 builds on; there aren't any machines missing a
+64 bit datatype that I know of.
+*/
+
+/* Here's a large-step Bresenham for jogging my memory.
+
+void large_bresenham_x_major(x1, y1, x2, y2, x_inc)
+{
+ int x, y, dx, dy, m;
+ int em_dx, ey_dx;
+
+ dx = x2 - x1;
+ dy = y2 - y1;
+
+ m = (x_inc * dy) / dx;
+ em_dx = (x_inc * dy) - m * dx;
+
+ x = x1;
+ y = y1;
+ ey = 0;
+
+ set(x,y);
+
+ while (x < x2) {
+ x += x_inc;
+ y += m;
+ ey_dx += em_dx;
+ if (ey_dx > dx_2) {
+ y++;
+ ey_dx -= dx;
+ }
+ set(x,y);
+ }
+}
+
+*/
+
+/* Here are the latest, simplified equations for computing trapezoid
+ coverage of a pixel:
+
+ alpha_from_area(A) = round(2**depth-1 * A)
+
+ alpha(o) = 2**depth-1
+
+ alpha(a) = alpha_from_area(area(a))
+
+ alpha(ab) = alpha_from_area(area(ab))
+
+ alpha(b) = alpha(ab) - alpha (a)
+
+ alpha(abc) = alpha_from_area(area(abc))
+
+ alpha(c) = alpha(abc) - alpha(ab)
+
+ alpha(ad) = alpha_from_area(area(ad))
+
+ alpha (d) = alpha(ad) - alpha (a)
+
+ alpha (abde) = alpha_from_area(area(abde))
+
+ alpha (de) = alpha (abde) - alpha (ab)
+
+ alpha (e) = alpha (de) - alpha (d)
+
+ alpha (abcdef) = alpha_from_area(area(abcdef))
+
+ alpha (def) = alpha (abcdef) - alpha (abc)
+
+ alpha (f) = alpha (def) - alpha (de)
+
+ alpha (adg) = alpha_from_area(area(adg))
+
+ alpha (g) = alpha (adg) - alpha (ad)
+
+ alpha (abdegh) = alpha_from_area(area(abdegh))
+
+ alpha (gh) = alpha (abdegh) - alpha (abde)
+
+ alpha (h) = alpha (gh) - alpha (g)
+
+ alpha (abcdefghi) = alpha_from_area(area(abcdefghi)) =
+ alpha_from_area(area(o)) = alpha_from_area(1) = alpha(o)
+
+ alpha (ghi) = alpha (abcdefghi) - alpha (abcdef)
+
+ alpha (i) = alpha (ghi) - alpha (gh)
+*/
+
+/* Latest thoughts from Keith on implementing area/alpha computations:
+
+*** 1.16 * 1.16 -> 1.31 ***
+#define AREA_MULT(w,h) ((w)&(h) == 0x10000 ? 0x80000000 : (((w)*(h) + 1) >> 1)
+
+*** (1.16 + 1.16) / 2 -> 1.16 ***
+#define WIDTH_AVG(x1,x2) (((x1) + (x2) + 1) >> 1)
+
+xFixed_1_31
+SubpixelArea (xFixed_1_16 x1,
+ xFixed_1_16 x2,
+ xFixed_1_16 y1,
+ xFixed_1_16 y2,
+ xFixed_1_16 bottom)
+ {
+ xFixed_1_16 x_trap;
+ xFixed_1_16 h_top, h_trap, h_bot;
+ xFixed_1_31 area;
+
+ x_trap = WIDTH_AVG(x1,x2);
+ h_top = y1;
+ h_trap = (y2 - y1);
+ h_bot = (bottom - y2);
+
+ area = AREA_MULT(x1, h_top) +
+ AREA_MULT(x_trap, h_trap) +
+ AREA_MULT(x2, h_bot);
+
+ return area;
+ }
+
+To convert this xFixed_1_31 value to alpha using 32 bit arithmetic:
+
+int
+AreaAlpha (xFixed_1_31 area, int depth)
+ {
+ return ((area >> bits) * ((1 << depth) - 1)) >> (31 - depth);
+ }
+
+Avoiding the branch bubble in the AREA_MULT could be done with either:
+
+area = (w * h + 1) >> 1;
+area |= ((area - 1) & 0x80000000);
+
+or
+ #define AREA_MULT(w,h) ((((w)*(h) + 1) >> 1) | ((w)&(h)&0x10000) << 15)
+
+depending on your preference, the first takes one less operation but
+can't be expressed as a macro; the second takes a large constant which may
+require an additional instruction on some processors. The differences
+will be swamped by the cost of the multiply.
+
+*/
+
+#endif /* RENDER */