/* * Copyright © 2004 Keith Packard * * 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. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "pixman-private.h" #include "pixman-accessor.h" /* * Step across a small sample grid gap */ #define RENDER_EDGE_STEP_SMALL(edge) \ { \ edge->x += edge->stepx_small; \ edge->e += edge->dx_small; \ if (edge->e > 0) \ { \ edge->e -= edge->dy; \ edge->x += edge->signdx; \ } \ } /* * Step across a large sample grid gap */ #define RENDER_EDGE_STEP_BIG(edge) \ { \ edge->x += edge->stepx_big; \ edge->e += edge->dx_big; \ if (edge->e > 0) \ { \ edge->e -= edge->dy; \ edge->x += edge->signdx; \ } \ } #ifdef PIXMAN_FB_ACCESSORS #define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_accessors #else #define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_no_accessors #endif /* * 4 bit alpha */ #define N_BITS 4 #define RASTERIZE_EDGES rasterize_edges_4 #ifndef WORDS_BIGENDIAN #define SHIFT_4(o) ((o) << 2) #else #define SHIFT_4(o) ((1 - (o)) << 2) #endif #define GET_4(x, o) (((x) >> SHIFT_4 (o)) & 0xf) #define PUT_4(x, o, v) \ (((x) & ~(0xf << SHIFT_4 (o))) | (((v) & 0xf) << SHIFT_4 (o))) #define DEFINE_ALPHA(line, x) \ uint8_t *__ap = (uint8_t *) line + ((x) >> 1); \ int __ao = (x) & 1 #define STEP_ALPHA ((__ap += __ao), (__ao ^= 1)) #define ADD_ALPHA(a) \ { \ uint8_t __o = READ (image, __ap); \ uint8_t __a = (a) + GET_4 (__o, __ao); \ WRITE (image, __ap, PUT_4 (__o, __ao, __a | (0 - ((__a) >> 4)))); \ } #include "pixman-edge-imp.h" #undef ADD_ALPHA #undef STEP_ALPHA #undef DEFINE_ALPHA #undef RASTERIZE_EDGES #undef N_BITS /* * 1 bit alpha */ #define N_BITS 1 #define RASTERIZE_EDGES rasterize_edges_1 #include "pixman-edge-imp.h" #undef RASTERIZE_EDGES #undef N_BITS /* * 8 bit alpha */ static force_inline uint8_t clip255 (int x) { if (x > 255) return 255; return x; } #define ADD_SATURATE_8(buf, val, length) \ do \ { \ int i__ = (length); \ uint8_t *buf__ = (buf); \ int val__ = (val); \ \ while (i__--) \ { \ WRITE (image, (buf__), clip255 (READ (image, (buf__)) + (val__))); \ (buf__)++; \ } \ } while (0) /* * We want to detect the case where we add the same value to a long * span of pixels. The triangles on the end are filled in while we * count how many sub-pixel scanlines contribute to the middle section. * * +--------------------------+ * fill_height =| \ / * +------------------+ * |================| * fill_start fill_end */ static void rasterize_edges_8 (pixman_image_t *image, pixman_edge_t * l, pixman_edge_t * r, pixman_fixed_t t, pixman_fixed_t b) { pixman_fixed_t y = t; uint32_t *line; int fill_start = -1, fill_end = -1; int fill_size = 0; uint32_t *buf = (image)->bits.bits; int stride = (image)->bits.rowstride; int width = (image)->bits.width; line = buf + pixman_fixed_to_int (y) * stride; for (;;) { uint8_t *ap = (uint8_t *) line; pixman_fixed_t lx, rx; int lxi, rxi; /* clip X */ lx = l->x; if (lx < 0) lx = 0; rx = r->x; if (pixman_fixed_to_int (rx) >= width) { /* Use the last pixel of the scanline, covered 100%. * We can't use the first pixel following the scanline, * because accessing it could result in a buffer overrun. */ rx = pixman_int_to_fixed (width) - 1; } /* Skip empty (or backwards) sections */ if (rx > lx) { int lxs, rxs; /* Find pixel bounds for span. */ lxi = pixman_fixed_to_int (lx); rxi = pixman_fixed_to_int (rx); /* Sample coverage for edge pixels */ lxs = RENDER_SAMPLES_X (lx, 8); rxs = RENDER_SAMPLES_X (rx, 8); /* Add coverage across row */ if (lxi == rxi) { WRITE (image, ap + lxi, clip255 (READ (image, ap + lxi) + rxs - lxs)); } else { WRITE (image, ap + lxi, clip255 (READ (image, ap + lxi) + N_X_FRAC (8) - lxs)); /* Move forward so that lxi/rxi is the pixel span */ lxi++; /* Don't bother trying to optimize the fill unless * the span is longer than 4 pixels. */ if (rxi - lxi > 4) { if (fill_start < 0) { fill_start = lxi; fill_end = rxi; fill_size++; } else { if (lxi >= fill_end || rxi < fill_start) { /* We're beyond what we saved, just fill it */ ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), fill_end - fill_start); fill_start = lxi; fill_end = rxi; fill_size = 1; } else { /* Update fill_start */ if (lxi > fill_start) { ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), lxi - fill_start); fill_start = lxi; } else if (lxi < fill_start) { ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), fill_start - lxi); } /* Update fill_end */ if (rxi < fill_end) { ADD_SATURATE_8 (ap + rxi, fill_size * N_X_FRAC (8), fill_end - rxi); fill_end = rxi; } else if (fill_end < rxi) { ADD_SATURATE_8 (ap + fill_end, N_X_FRAC (8), rxi - fill_end); } fill_size++; } } } else { ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), rxi - lxi); } WRITE (image, ap + rxi, clip255 (READ (image, ap + rxi) + rxs)); } } if (y == b) { /* We're done, make sure we clean up any remaining fill. */ if (fill_start != fill_end) { if (fill_size == N_Y_FRAC (8)) { MEMSET_WRAPPED (image, ap + fill_start, 0xff, fill_end - fill_start); } else { ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), fill_end - fill_start); } } break; } if (pixman_fixed_frac (y) != Y_FRAC_LAST (8)) { RENDER_EDGE_STEP_SMALL (l); RENDER_EDGE_STEP_SMALL (r); y += STEP_Y_SMALL (8); } else { RENDER_EDGE_STEP_BIG (l); RENDER_EDGE_STEP_BIG (r); y += STEP_Y_BIG (8); if (fill_start != fill_end) { if (fill_size == N_Y_FRAC (8)) { MEMSET_WRAPPED (image, ap + fill_start, 0xff, fill_end - fill_start); } else { ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), fill_end - fill_start); } fill_start = fill_end = -1; fill_size = 0; } line += stride; } } } #ifndef PIXMAN_FB_ACCESSORS static #endif void PIXMAN_RASTERIZE_EDGES (pixman_image_t *image, pixman_edge_t * l, pixman_edge_t * r, pixman_fixed_t t, pixman_fixed_t b) { switch (PIXMAN_FORMAT_BPP (image->bits.format)) { case 1: rasterize_edges_1 (image, l, r, t, b); break; case 4: rasterize_edges_4 (image, l, r, t, b); break; case 8: rasterize_edges_8 (image, l, r, t, b); break; default: break; } } #ifndef PIXMAN_FB_ACCESSORS PIXMAN_EXPORT void pixman_rasterize_edges (pixman_image_t *image, pixman_edge_t * l, pixman_edge_t * r, pixman_fixed_t t, pixman_fixed_t b) { return_if_fail (image->type == BITS); return_if_fail (PIXMAN_FORMAT_TYPE (image->bits.format) == PIXMAN_TYPE_A); if (image->bits.read_func || image->bits.write_func) pixman_rasterize_edges_accessors (image, l, r, t, b); else pixman_rasterize_edges_no_accessors (image, l, r, t, b); } #endif