1/*
2 * Copyright © 2004 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Keith Packard makes no
11 * representations about the suitability of this software for any purpose.  It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <string.h>
28
29#include "pixman-private.h"
30#include "pixman-accessor.h"
31
32/*
33 * Step across a small sample grid gap
34 */
35#define RENDER_EDGE_STEP_SMALL(edge)					\
36    {									\
37	edge->x += edge->stepx_small;					\
38	edge->e += edge->dx_small;					\
39	if (edge->e > 0)						\
40	{								\
41	    edge->e -= edge->dy;					\
42	    edge->x += edge->signdx;					\
43	}								\
44    }
45
46/*
47 * Step across a large sample grid gap
48 */
49#define RENDER_EDGE_STEP_BIG(edge)					\
50    {									\
51	edge->x += edge->stepx_big;					\
52	edge->e += edge->dx_big;					\
53	if (edge->e > 0)						\
54	{								\
55	    edge->e -= edge->dy;					\
56	    edge->x += edge->signdx;					\
57	}								\
58    }
59
60#ifdef PIXMAN_FB_ACCESSORS
61#define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_accessors
62#else
63#define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_no_accessors
64#endif
65
66/*
67 * 4 bit alpha
68 */
69
70#define N_BITS  4
71#define RASTERIZE_EDGES rasterize_edges_4
72
73#ifndef WORDS_BIGENDIAN
74#define SHIFT_4(o)      ((o) << 2)
75#else
76#define SHIFT_4(o)      ((1 - (o)) << 2)
77#endif
78
79#define GET_4(x, o)      (((x) >> SHIFT_4 (o)) & 0xf)
80#define PUT_4(x, o, v)							\
81    (((x) & ~(0xf << SHIFT_4 (o))) | (((v) & 0xf) << SHIFT_4 (o)))
82
83#define DEFINE_ALPHA(line, x)						\
84    uint8_t   *__ap = (uint8_t *) line + ((x) >> 1);			\
85    int __ao = (x) & 1
86
87#define STEP_ALPHA      ((__ap += __ao), (__ao ^= 1))
88
89#define ADD_ALPHA(a)							\
90    {									\
91        uint8_t __o = READ (image, __ap);				\
92        uint8_t __a = (a) + GET_4 (__o, __ao);				\
93        WRITE (image, __ap, PUT_4 (__o, __ao, __a | (0 - ((__a) >> 4)))); \
94    }
95
96#include "pixman-edge-imp.h"
97
98#undef ADD_ALPHA
99#undef STEP_ALPHA
100#undef DEFINE_ALPHA
101#undef RASTERIZE_EDGES
102#undef N_BITS
103
104
105/*
106 * 1 bit alpha
107 */
108
109#define N_BITS 1
110#define RASTERIZE_EDGES rasterize_edges_1
111
112#include "pixman-edge-imp.h"
113
114#undef RASTERIZE_EDGES
115#undef N_BITS
116
117/*
118 * 8 bit alpha
119 */
120
121static force_inline uint8_t
122clip255 (int x)
123{
124    if (x > 255)
125	return 255;
126
127    return x;
128}
129
130#define ADD_SATURATE_8(buf, val, length)				\
131    do									\
132    {									\
133        int i__ = (length);						\
134        uint8_t *buf__ = (buf);						\
135        int val__ = (val);						\
136									\
137        while (i__--)							\
138        {								\
139            WRITE (image, (buf__), clip255 (READ (image, (buf__)) + (val__))); \
140            (buf__)++;							\
141	}								\
142    } while (0)
143
144/*
145 * We want to detect the case where we add the same value to a long
146 * span of pixels.  The triangles on the end are filled in while we
147 * count how many sub-pixel scanlines contribute to the middle section.
148 *
149 *                 +--------------------------+
150 *  fill_height =|   \                      /
151 *                     +------------------+
152 *                      |================|
153 *                   fill_start       fill_end
154 */
155static void
156rasterize_edges_8 (pixman_image_t *image,
157                   pixman_edge_t * l,
158                   pixman_edge_t * r,
159                   pixman_fixed_t  t,
160                   pixman_fixed_t  b)
161{
162    pixman_fixed_t y = t;
163    uint32_t  *line;
164    int fill_start = -1, fill_end = -1;
165    int fill_size = 0;
166    uint32_t *buf = (image)->bits.bits;
167    int stride = (image)->bits.rowstride;
168    int width = (image)->bits.width;
169
170    line = buf + pixman_fixed_to_int (y) * stride;
171
172    for (;;)
173    {
174        uint8_t *ap = (uint8_t *) line;
175        pixman_fixed_t lx, rx;
176        int lxi, rxi;
177
178        /* clip X */
179        lx = l->x;
180        if (lx < 0)
181	    lx = 0;
182
183        rx = r->x;
184
185        if (pixman_fixed_to_int (rx) >= width)
186	{
187	    /* Use the last pixel of the scanline, covered 100%.
188	     * We can't use the first pixel following the scanline,
189	     * because accessing it could result in a buffer overrun.
190	     */
191	    rx = pixman_int_to_fixed (width) - 1;
192	}
193
194        /* Skip empty (or backwards) sections */
195        if (rx > lx)
196        {
197            int lxs, rxs;
198
199            /* Find pixel bounds for span. */
200            lxi = pixman_fixed_to_int (lx);
201            rxi = pixman_fixed_to_int (rx);
202
203            /* Sample coverage for edge pixels */
204            lxs = RENDER_SAMPLES_X (lx, 8);
205            rxs = RENDER_SAMPLES_X (rx, 8);
206
207            /* Add coverage across row */
208            if (lxi == rxi)
209            {
210                WRITE (image, ap + lxi,
211		       clip255 (READ (image, ap + lxi) + rxs - lxs));
212	    }
213            else
214            {
215                WRITE (image, ap + lxi,
216		       clip255 (READ (image, ap + lxi) + N_X_FRAC (8) - lxs));
217
218                /* Move forward so that lxi/rxi is the pixel span */
219                lxi++;
220
221                /* Don't bother trying to optimize the fill unless
222		 * the span is longer than 4 pixels. */
223                if (rxi - lxi > 4)
224                {
225                    if (fill_start < 0)
226                    {
227                        fill_start = lxi;
228                        fill_end = rxi;
229                        fill_size++;
230		    }
231                    else
232                    {
233                        if (lxi >= fill_end || rxi < fill_start)
234                        {
235                            /* We're beyond what we saved, just fill it */
236                            ADD_SATURATE_8 (ap + fill_start,
237                                            fill_size * N_X_FRAC (8),
238                                            fill_end - fill_start);
239                            fill_start = lxi;
240                            fill_end = rxi;
241                            fill_size = 1;
242			}
243                        else
244                        {
245                            /* Update fill_start */
246                            if (lxi > fill_start)
247                            {
248                                ADD_SATURATE_8 (ap + fill_start,
249                                                fill_size * N_X_FRAC (8),
250                                                lxi - fill_start);
251                                fill_start = lxi;
252			    }
253                            else if (lxi < fill_start)
254                            {
255                                ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8),
256                                                fill_start - lxi);
257			    }
258
259                            /* Update fill_end */
260                            if (rxi < fill_end)
261                            {
262                                ADD_SATURATE_8 (ap + rxi,
263                                                fill_size * N_X_FRAC (8),
264                                                fill_end - rxi);
265                                fill_end = rxi;
266			    }
267                            else if (fill_end < rxi)
268                            {
269                                ADD_SATURATE_8 (ap + fill_end,
270                                                N_X_FRAC (8),
271                                                rxi - fill_end);
272			    }
273                            fill_size++;
274			}
275		    }
276		}
277                else
278                {
279                    ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), rxi - lxi);
280		}
281
282                WRITE (image, ap + rxi, clip255 (READ (image, ap + rxi) + rxs));
283	    }
284	}
285
286        if (y == b)
287        {
288            /* We're done, make sure we clean up any remaining fill. */
289            if (fill_start != fill_end)
290            {
291                if (fill_size == N_Y_FRAC (8))
292                {
293                    MEMSET_WRAPPED (image, ap + fill_start,
294				    0xff, fill_end - fill_start);
295		}
296                else
297                {
298                    ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8),
299                                    fill_end - fill_start);
300		}
301	    }
302            break;
303	}
304
305        if (pixman_fixed_frac (y) != Y_FRAC_LAST (8))
306        {
307            RENDER_EDGE_STEP_SMALL (l);
308            RENDER_EDGE_STEP_SMALL (r);
309            y += STEP_Y_SMALL (8);
310	}
311        else
312        {
313            RENDER_EDGE_STEP_BIG (l);
314            RENDER_EDGE_STEP_BIG (r);
315            y += STEP_Y_BIG (8);
316            if (fill_start != fill_end)
317            {
318                if (fill_size == N_Y_FRAC (8))
319                {
320                    MEMSET_WRAPPED (image, ap + fill_start,
321				    0xff, fill_end - fill_start);
322		}
323                else
324                {
325                    ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8),
326                                    fill_end - fill_start);
327		}
328
329                fill_start = fill_end = -1;
330                fill_size = 0;
331	    }
332
333            line += stride;
334	}
335    }
336}
337
338#ifndef PIXMAN_FB_ACCESSORS
339static
340#endif
341void
342PIXMAN_RASTERIZE_EDGES (pixman_image_t *image,
343                        pixman_edge_t * l,
344                        pixman_edge_t * r,
345                        pixman_fixed_t  t,
346                        pixman_fixed_t  b)
347{
348    switch (PIXMAN_FORMAT_BPP (image->bits.format))
349    {
350    case 1:
351	rasterize_edges_1 (image, l, r, t, b);
352	break;
353
354    case 4:
355	rasterize_edges_4 (image, l, r, t, b);
356	break;
357
358    case 8:
359	rasterize_edges_8 (image, l, r, t, b);
360	break;
361
362    default:
363        break;
364    }
365}
366
367#ifndef PIXMAN_FB_ACCESSORS
368
369PIXMAN_EXPORT void
370pixman_rasterize_edges (pixman_image_t *image,
371                        pixman_edge_t * l,
372                        pixman_edge_t * r,
373                        pixman_fixed_t  t,
374                        pixman_fixed_t  b)
375{
376    return_if_fail (image->type == BITS);
377    return_if_fail (PIXMAN_FORMAT_TYPE (image->bits.format) == PIXMAN_TYPE_A);
378
379    if (image->bits.read_func || image->bits.write_func)
380	pixman_rasterize_edges_accessors (image, l, r, t, b);
381    else
382	pixman_rasterize_edges_no_accessors (image, l, r, t, b);
383}
384
385#endif
386