michael@0: /* michael@0: * Copyright © 2004 Keith Packard michael@0: * michael@0: * Permission to use, copy, modify, distribute, and sell this software and its michael@0: * documentation for any purpose is hereby granted without fee, provided that michael@0: * the above copyright notice appear in all copies and that both that michael@0: * copyright notice and this permission notice appear in supporting michael@0: * documentation, and that the name of Keith Packard not be used in michael@0: * advertising or publicity pertaining to distribution of the software without michael@0: * specific, written prior permission. Keith Packard makes no michael@0: * representations about the suitability of this software for any purpose. It michael@0: * is provided "as is" without express or implied warranty. michael@0: * michael@0: * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, michael@0: * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO michael@0: * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR michael@0: * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, michael@0: * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER michael@0: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR michael@0: * PERFORMANCE OF THIS SOFTWARE. michael@0: */ michael@0: michael@0: #ifdef HAVE_CONFIG_H michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: #include "pixman-private.h" michael@0: #include "pixman-accessor.h" michael@0: michael@0: /* michael@0: * Step across a small sample grid gap michael@0: */ michael@0: #define RENDER_EDGE_STEP_SMALL(edge) \ michael@0: { \ michael@0: edge->x += edge->stepx_small; \ michael@0: edge->e += edge->dx_small; \ michael@0: if (edge->e > 0) \ michael@0: { \ michael@0: edge->e -= edge->dy; \ michael@0: edge->x += edge->signdx; \ michael@0: } \ michael@0: } michael@0: michael@0: /* michael@0: * Step across a large sample grid gap michael@0: */ michael@0: #define RENDER_EDGE_STEP_BIG(edge) \ michael@0: { \ michael@0: edge->x += edge->stepx_big; \ michael@0: edge->e += edge->dx_big; \ michael@0: if (edge->e > 0) \ michael@0: { \ michael@0: edge->e -= edge->dy; \ michael@0: edge->x += edge->signdx; \ michael@0: } \ michael@0: } michael@0: michael@0: #ifdef PIXMAN_FB_ACCESSORS michael@0: #define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_accessors michael@0: #else michael@0: #define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_no_accessors michael@0: #endif michael@0: michael@0: /* michael@0: * 4 bit alpha michael@0: */ michael@0: michael@0: #define N_BITS 4 michael@0: #define RASTERIZE_EDGES rasterize_edges_4 michael@0: michael@0: #ifndef WORDS_BIGENDIAN michael@0: #define SHIFT_4(o) ((o) << 2) michael@0: #else michael@0: #define SHIFT_4(o) ((1 - (o)) << 2) michael@0: #endif michael@0: michael@0: #define GET_4(x, o) (((x) >> SHIFT_4 (o)) & 0xf) michael@0: #define PUT_4(x, o, v) \ michael@0: (((x) & ~(0xf << SHIFT_4 (o))) | (((v) & 0xf) << SHIFT_4 (o))) michael@0: michael@0: #define DEFINE_ALPHA(line, x) \ michael@0: uint8_t *__ap = (uint8_t *) line + ((x) >> 1); \ michael@0: int __ao = (x) & 1 michael@0: michael@0: #define STEP_ALPHA ((__ap += __ao), (__ao ^= 1)) michael@0: michael@0: #define ADD_ALPHA(a) \ michael@0: { \ michael@0: uint8_t __o = READ (image, __ap); \ michael@0: uint8_t __a = (a) + GET_4 (__o, __ao); \ michael@0: WRITE (image, __ap, PUT_4 (__o, __ao, __a | (0 - ((__a) >> 4)))); \ michael@0: } michael@0: michael@0: #include "pixman-edge-imp.h" michael@0: michael@0: #undef ADD_ALPHA michael@0: #undef STEP_ALPHA michael@0: #undef DEFINE_ALPHA michael@0: #undef RASTERIZE_EDGES michael@0: #undef N_BITS michael@0: michael@0: michael@0: /* michael@0: * 1 bit alpha michael@0: */ michael@0: michael@0: #define N_BITS 1 michael@0: #define RASTERIZE_EDGES rasterize_edges_1 michael@0: michael@0: #include "pixman-edge-imp.h" michael@0: michael@0: #undef RASTERIZE_EDGES michael@0: #undef N_BITS michael@0: michael@0: /* michael@0: * 8 bit alpha michael@0: */ michael@0: michael@0: static force_inline uint8_t michael@0: clip255 (int x) michael@0: { michael@0: if (x > 255) michael@0: return 255; michael@0: michael@0: return x; michael@0: } michael@0: michael@0: #define ADD_SATURATE_8(buf, val, length) \ michael@0: do \ michael@0: { \ michael@0: int i__ = (length); \ michael@0: uint8_t *buf__ = (buf); \ michael@0: int val__ = (val); \ michael@0: \ michael@0: while (i__--) \ michael@0: { \ michael@0: WRITE (image, (buf__), clip255 (READ (image, (buf__)) + (val__))); \ michael@0: (buf__)++; \ michael@0: } \ michael@0: } while (0) michael@0: michael@0: /* michael@0: * We want to detect the case where we add the same value to a long michael@0: * span of pixels. The triangles on the end are filled in while we michael@0: * count how many sub-pixel scanlines contribute to the middle section. michael@0: * michael@0: * +--------------------------+ michael@0: * fill_height =| \ / michael@0: * +------------------+ michael@0: * |================| michael@0: * fill_start fill_end michael@0: */ michael@0: static void michael@0: rasterize_edges_8 (pixman_image_t *image, michael@0: pixman_edge_t * l, michael@0: pixman_edge_t * r, michael@0: pixman_fixed_t t, michael@0: pixman_fixed_t b) michael@0: { michael@0: pixman_fixed_t y = t; michael@0: uint32_t *line; michael@0: int fill_start = -1, fill_end = -1; michael@0: int fill_size = 0; michael@0: uint32_t *buf = (image)->bits.bits; michael@0: int stride = (image)->bits.rowstride; michael@0: int width = (image)->bits.width; michael@0: michael@0: line = buf + pixman_fixed_to_int (y) * stride; michael@0: michael@0: for (;;) michael@0: { michael@0: uint8_t *ap = (uint8_t *) line; michael@0: pixman_fixed_t lx, rx; michael@0: int lxi, rxi; michael@0: michael@0: /* clip X */ michael@0: lx = l->x; michael@0: if (lx < 0) michael@0: lx = 0; michael@0: michael@0: rx = r->x; michael@0: michael@0: if (pixman_fixed_to_int (rx) >= width) michael@0: { michael@0: /* Use the last pixel of the scanline, covered 100%. michael@0: * We can't use the first pixel following the scanline, michael@0: * because accessing it could result in a buffer overrun. michael@0: */ michael@0: rx = pixman_int_to_fixed (width) - 1; michael@0: } michael@0: michael@0: /* Skip empty (or backwards) sections */ michael@0: if (rx > lx) michael@0: { michael@0: int lxs, rxs; michael@0: michael@0: /* Find pixel bounds for span. */ michael@0: lxi = pixman_fixed_to_int (lx); michael@0: rxi = pixman_fixed_to_int (rx); michael@0: michael@0: /* Sample coverage for edge pixels */ michael@0: lxs = RENDER_SAMPLES_X (lx, 8); michael@0: rxs = RENDER_SAMPLES_X (rx, 8); michael@0: michael@0: /* Add coverage across row */ michael@0: if (lxi == rxi) michael@0: { michael@0: WRITE (image, ap + lxi, michael@0: clip255 (READ (image, ap + lxi) + rxs - lxs)); michael@0: } michael@0: else michael@0: { michael@0: WRITE (image, ap + lxi, michael@0: clip255 (READ (image, ap + lxi) + N_X_FRAC (8) - lxs)); michael@0: michael@0: /* Move forward so that lxi/rxi is the pixel span */ michael@0: lxi++; michael@0: michael@0: /* Don't bother trying to optimize the fill unless michael@0: * the span is longer than 4 pixels. */ michael@0: if (rxi - lxi > 4) michael@0: { michael@0: if (fill_start < 0) michael@0: { michael@0: fill_start = lxi; michael@0: fill_end = rxi; michael@0: fill_size++; michael@0: } michael@0: else michael@0: { michael@0: if (lxi >= fill_end || rxi < fill_start) michael@0: { michael@0: /* We're beyond what we saved, just fill it */ michael@0: ADD_SATURATE_8 (ap + fill_start, michael@0: fill_size * N_X_FRAC (8), michael@0: fill_end - fill_start); michael@0: fill_start = lxi; michael@0: fill_end = rxi; michael@0: fill_size = 1; michael@0: } michael@0: else michael@0: { michael@0: /* Update fill_start */ michael@0: if (lxi > fill_start) michael@0: { michael@0: ADD_SATURATE_8 (ap + fill_start, michael@0: fill_size * N_X_FRAC (8), michael@0: lxi - fill_start); michael@0: fill_start = lxi; michael@0: } michael@0: else if (lxi < fill_start) michael@0: { michael@0: ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), michael@0: fill_start - lxi); michael@0: } michael@0: michael@0: /* Update fill_end */ michael@0: if (rxi < fill_end) michael@0: { michael@0: ADD_SATURATE_8 (ap + rxi, michael@0: fill_size * N_X_FRAC (8), michael@0: fill_end - rxi); michael@0: fill_end = rxi; michael@0: } michael@0: else if (fill_end < rxi) michael@0: { michael@0: ADD_SATURATE_8 (ap + fill_end, michael@0: N_X_FRAC (8), michael@0: rxi - fill_end); michael@0: } michael@0: fill_size++; michael@0: } michael@0: } michael@0: } michael@0: else michael@0: { michael@0: ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), rxi - lxi); michael@0: } michael@0: michael@0: WRITE (image, ap + rxi, clip255 (READ (image, ap + rxi) + rxs)); michael@0: } michael@0: } michael@0: michael@0: if (y == b) michael@0: { michael@0: /* We're done, make sure we clean up any remaining fill. */ michael@0: if (fill_start != fill_end) michael@0: { michael@0: if (fill_size == N_Y_FRAC (8)) michael@0: { michael@0: MEMSET_WRAPPED (image, ap + fill_start, michael@0: 0xff, fill_end - fill_start); michael@0: } michael@0: else michael@0: { michael@0: ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), michael@0: fill_end - fill_start); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: michael@0: if (pixman_fixed_frac (y) != Y_FRAC_LAST (8)) michael@0: { michael@0: RENDER_EDGE_STEP_SMALL (l); michael@0: RENDER_EDGE_STEP_SMALL (r); michael@0: y += STEP_Y_SMALL (8); michael@0: } michael@0: else michael@0: { michael@0: RENDER_EDGE_STEP_BIG (l); michael@0: RENDER_EDGE_STEP_BIG (r); michael@0: y += STEP_Y_BIG (8); michael@0: if (fill_start != fill_end) michael@0: { michael@0: if (fill_size == N_Y_FRAC (8)) michael@0: { michael@0: MEMSET_WRAPPED (image, ap + fill_start, michael@0: 0xff, fill_end - fill_start); michael@0: } michael@0: else michael@0: { michael@0: ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), michael@0: fill_end - fill_start); michael@0: } michael@0: michael@0: fill_start = fill_end = -1; michael@0: fill_size = 0; michael@0: } michael@0: michael@0: line += stride; michael@0: } michael@0: } michael@0: } michael@0: michael@0: #ifndef PIXMAN_FB_ACCESSORS michael@0: static michael@0: #endif michael@0: void michael@0: PIXMAN_RASTERIZE_EDGES (pixman_image_t *image, michael@0: pixman_edge_t * l, michael@0: pixman_edge_t * r, michael@0: pixman_fixed_t t, michael@0: pixman_fixed_t b) michael@0: { michael@0: switch (PIXMAN_FORMAT_BPP (image->bits.format)) michael@0: { michael@0: case 1: michael@0: rasterize_edges_1 (image, l, r, t, b); michael@0: break; michael@0: michael@0: case 4: michael@0: rasterize_edges_4 (image, l, r, t, b); michael@0: break; michael@0: michael@0: case 8: michael@0: rasterize_edges_8 (image, l, r, t, b); michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: #ifndef PIXMAN_FB_ACCESSORS michael@0: michael@0: PIXMAN_EXPORT void michael@0: pixman_rasterize_edges (pixman_image_t *image, michael@0: pixman_edge_t * l, michael@0: pixman_edge_t * r, michael@0: pixman_fixed_t t, michael@0: pixman_fixed_t b) michael@0: { michael@0: return_if_fail (image->type == BITS); michael@0: return_if_fail (PIXMAN_FORMAT_TYPE (image->bits.format) == PIXMAN_TYPE_A); michael@0: michael@0: if (image->bits.read_func || image->bits.write_func) michael@0: pixman_rasterize_edges_accessors (image, l, r, t, b); michael@0: else michael@0: pixman_rasterize_edges_no_accessors (image, l, r, t, b); michael@0: } michael@0: michael@0: #endif