1/*
2 * Copyright © 1998 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_DIX_CONFIG_H
24#include <dix-config.h>
25#endif
26
27#include <stdlib.h>
28
29#include "fb.h"
30#include "miline.h"
31
32#define fbBresShiftMask(mask,dir,bpp) ((bpp == FB_STIP_UNIT) ? 0 : \
33					((dir < 0) ? FbStipLeft(mask,bpp) : \
34					 FbStipRight(mask,bpp)))
35
36static void
37fbBresSolid(DrawablePtr pDrawable,
38            GCPtr pGC,
39            int dashOffset,
40            int signdx,
41            int signdy,
42            int axis, int x1, int y1, int e, int e1, int e3, int len)
43{
44    FbStip *dst;
45    FbStride dstStride;
46    int dstBpp;
47    int dstXoff, dstYoff;
48    FbGCPrivPtr pPriv = fbGetGCPrivate(pGC);
49    FbStip and = (FbStip) pPriv->and;
50    FbStip xor = (FbStip) pPriv->xor;
51    FbStip mask, mask0;
52    FbStip bits;
53
54    fbGetStipDrawable(pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
55    dst += ((y1 + dstYoff) * dstStride);
56    x1 = (x1 + dstXoff) * dstBpp;
57    dst += x1 >> FB_STIP_SHIFT;
58    x1 &= FB_STIP_MASK;
59    mask0 = FbStipMask(0, dstBpp);
60    mask = FbStipRight(mask0, x1);
61    if (signdx < 0)
62        mask0 = FbStipRight(mask0, FB_STIP_UNIT - dstBpp);
63    if (signdy < 0)
64        dstStride = -dstStride;
65    if (axis == X_AXIS) {
66        bits = 0;
67        while (len--) {
68            bits |= mask;
69            mask = fbBresShiftMask(mask, signdx, dstBpp);
70            if (!mask) {
71                WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, bits));
72                bits = 0;
73                dst += signdx;
74                mask = mask0;
75            }
76            e += e1;
77            if (e >= 0) {
78                if (bits) {
79                    WRITE(dst, FbDoMaskRRop (READ(dst), and, xor, bits));
80                    bits = 0;
81                }
82                dst += dstStride;
83                e += e3;
84            }
85        }
86        if (bits)
87            WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, bits));
88    }
89    else {
90        while (len--) {
91            WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, mask));
92            dst += dstStride;
93            e += e1;
94            if (e >= 0) {
95                e += e3;
96                mask = fbBresShiftMask(mask, signdx, dstBpp);
97                if (!mask) {
98                    dst += signdx;
99                    mask = mask0;
100                }
101            }
102        }
103    }
104
105    fbFinishAccess(pDrawable);
106}
107
108static void
109fbBresDash(DrawablePtr pDrawable,
110           GCPtr pGC,
111           int dashOffset,
112           int signdx,
113           int signdy, int axis, int x1, int y1, int e, int e1, int e3, int len)
114{
115    FbStip *dst;
116    FbStride dstStride;
117    int dstBpp;
118    int dstXoff, dstYoff;
119    FbGCPrivPtr pPriv = fbGetGCPrivate(pGC);
120    FbStip and = (FbStip) pPriv->and;
121    FbStip xor = (FbStip) pPriv->xor;
122    FbStip bgand = (FbStip) pPriv->bgand;
123    FbStip bgxor = (FbStip) pPriv->bgxor;
124    FbStip mask, mask0;
125
126    FbDashDeclare;
127    int dashlen;
128    Bool even;
129    Bool doOdd;
130
131    fbGetStipDrawable(pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
132    doOdd = pGC->lineStyle == LineDoubleDash;
133
134    FbDashInit(pGC, pPriv, dashOffset, dashlen, even);
135
136    dst += ((y1 + dstYoff) * dstStride);
137    x1 = (x1 + dstXoff) * dstBpp;
138    dst += x1 >> FB_STIP_SHIFT;
139    x1 &= FB_STIP_MASK;
140    mask0 = FbStipMask(0, dstBpp);
141    mask = FbStipRight(mask0, x1);
142    if (signdx < 0)
143        mask0 = FbStipRight(mask0, FB_STIP_UNIT - dstBpp);
144    if (signdy < 0)
145        dstStride = -dstStride;
146    while (len--) {
147        if (even)
148            WRITE(dst, FbDoMaskRRop(READ(dst), and, xor, mask));
149        else if (doOdd)
150            WRITE(dst, FbDoMaskRRop(READ(dst), bgand, bgxor, mask));
151        if (axis == X_AXIS) {
152            mask = fbBresShiftMask(mask, signdx, dstBpp);
153            if (!mask) {
154                dst += signdx;
155                mask = mask0;
156            }
157            e += e1;
158            if (e >= 0) {
159                dst += dstStride;
160                e += e3;
161            }
162        }
163        else {
164            dst += dstStride;
165            e += e1;
166            if (e >= 0) {
167                e += e3;
168                mask = fbBresShiftMask(mask, signdx, dstBpp);
169                if (!mask) {
170                    dst += signdx;
171                    mask = mask0;
172                }
173            }
174        }
175        FbDashStep(dashlen, even);
176    }
177
178    fbFinishAccess(pDrawable);
179}
180
181static void
182fbBresFill(DrawablePtr pDrawable,
183           GCPtr pGC,
184           int dashOffset,
185           int signdx,
186           int signdy, int axis, int x1, int y1, int e, int e1, int e3, int len)
187{
188    while (len--) {
189        fbFill(pDrawable, pGC, x1, y1, 1, 1);
190        if (axis == X_AXIS) {
191            x1 += signdx;
192            e += e1;
193            if (e >= 0) {
194                e += e3;
195                y1 += signdy;
196            }
197        }
198        else {
199            y1 += signdy;
200            e += e1;
201            if (e >= 0) {
202                e += e3;
203                x1 += signdx;
204            }
205        }
206    }
207}
208
209static void
210fbSetFg(DrawablePtr pDrawable, GCPtr pGC, Pixel fg)
211{
212    if (fg != pGC->fgPixel) {
213        ChangeGCVal val;
214
215        val.val = fg;
216        ChangeGC(NullClient, pGC, GCForeground, &val);
217        ValidateGC(pDrawable, pGC);
218    }
219}
220
221static void
222fbBresFillDash(DrawablePtr pDrawable,
223               GCPtr pGC,
224               int dashOffset,
225               int signdx,
226               int signdy,
227               int axis, int x1, int y1, int e, int e1, int e3, int len)
228{
229    FbGCPrivPtr pPriv = fbGetGCPrivate(pGC);
230
231    FbDashDeclare;
232    int dashlen;
233    Bool even;
234    Bool doOdd;
235    Bool doBg;
236    Pixel fg, bg;
237
238    fg = pGC->fgPixel;
239    bg = pGC->bgPixel;
240
241    /* whether to fill the odd dashes */
242    doOdd = pGC->lineStyle == LineDoubleDash;
243    /* whether to switch fg to bg when filling odd dashes */
244    doBg = doOdd && (pGC->fillStyle == FillSolid ||
245                     pGC->fillStyle == FillStippled);
246
247    /* compute current dash position */
248    FbDashInit(pGC, pPriv, dashOffset, dashlen, even);
249
250    while (len--) {
251        if (even || doOdd) {
252            if (doBg) {
253                if (even)
254                    fbSetFg(pDrawable, pGC, fg);
255                else
256                    fbSetFg(pDrawable, pGC, bg);
257            }
258            fbFill(pDrawable, pGC, x1, y1, 1, 1);
259        }
260        if (axis == X_AXIS) {
261            x1 += signdx;
262            e += e1;
263            if (e >= 0) {
264                e += e3;
265                y1 += signdy;
266            }
267        }
268        else {
269            y1 += signdy;
270            e += e1;
271            if (e >= 0) {
272                e += e3;
273                x1 += signdx;
274            }
275        }
276        FbDashStep(dashlen, even);
277    }
278    if (doBg)
279        fbSetFg(pDrawable, pGC, fg);
280}
281
282/*
283 * For drivers that want to bail drawing some lines, this
284 * function takes care of selecting the appropriate rasterizer
285 * based on the contents of the specified GC.
286 */
287
288static FbBres *
289fbSelectBres(DrawablePtr pDrawable, GCPtr pGC)
290{
291    FbGCPrivPtr pPriv = fbGetGCPrivate(pGC);
292    int dstBpp = pDrawable->bitsPerPixel;
293    FbBres *bres;
294
295    if (pGC->lineStyle == LineSolid) {
296        bres = fbBresFill;
297        if (pGC->fillStyle == FillSolid) {
298            bres = fbBresSolid;
299            if (pPriv->and == 0) {
300                switch (dstBpp) {
301                case 8:
302                    bres = fbBresSolid8;
303                    break;
304                case 16:
305                    bres = fbBresSolid16;
306                    break;
307                case 32:
308                    bres = fbBresSolid32;
309                    break;
310                }
311            }
312        }
313    }
314    else {
315        bres = fbBresFillDash;
316        if (pGC->fillStyle == FillSolid) {
317            bres = fbBresDash;
318            if (pPriv->and == 0 &&
319                (pGC->lineStyle == LineOnOffDash || pPriv->bgand == 0)) {
320                switch (dstBpp) {
321                case 8:
322                    bres = fbBresDash8;
323                    break;
324                case 16:
325                    bres = fbBresDash16;
326                    break;
327                case 32:
328                    bres = fbBresDash32;
329                    break;
330                }
331            }
332        }
333    }
334    return bres;
335}
336
337void
338fbSegment(DrawablePtr pDrawable,
339          GCPtr pGC,
340          int x1, int y1, int x2, int y2, Bool drawLast, int *dashOffset)
341{
342    FbBres *bres;
343    RegionPtr pClip = fbGetCompositeClip(pGC);
344    BoxPtr pBox;
345    int nBox;
346    int adx;                    /* abs values of dx and dy */
347    int ady;
348    int signdx;                 /* sign of dx and dy */
349    int signdy;
350    int e, e1, e2, e3;          /* bresenham error and increments */
351    int len;                    /* length of segment */
352    int axis;                   /* major axis */
353    int octant;
354    int dashoff;
355    int doff;
356    unsigned int bias = miGetZeroLineBias(pDrawable->pScreen);
357    unsigned int oc1;           /* outcode of point 1 */
358    unsigned int oc2;           /* outcode of point 2 */
359
360    nBox = RegionNumRects(pClip);
361    pBox = RegionRects(pClip);
362
363    bres = fbSelectBres(pDrawable, pGC);
364
365    CalcLineDeltas(x1, y1, x2, y2, adx, ady, signdx, signdy, 1, 1, octant);
366
367    if (adx > ady) {
368        axis = X_AXIS;
369        e1 = ady << 1;
370        e2 = e1 - (adx << 1);
371        e = e1 - adx;
372        len = adx;
373    }
374    else {
375        axis = Y_AXIS;
376        e1 = adx << 1;
377        e2 = e1 - (ady << 1);
378        e = e1 - ady;
379        SetYMajorOctant(octant);
380        len = ady;
381    }
382
383    FIXUP_ERROR(e, octant, bias);
384
385    /*
386     * Adjust error terms to compare against zero
387     */
388    e3 = e2 - e1;
389    e = e - e1;
390
391    /* we have bresenham parameters and two points.
392       all we have to do now is clip and draw.
393     */
394
395    if (drawLast)
396        len++;
397    dashoff = *dashOffset;
398    *dashOffset = dashoff + len;
399    while (nBox--) {
400        oc1 = 0;
401        oc2 = 0;
402        OUTCODES(oc1, x1, y1, pBox);
403        OUTCODES(oc2, x2, y2, pBox);
404        if ((oc1 | oc2) == 0) {
405            (*bres) (pDrawable, pGC, dashoff,
406                     signdx, signdy, axis, x1, y1, e, e1, e3, len);
407            break;
408        }
409        else if (oc1 & oc2) {
410            pBox++;
411        }
412        else {
413            int new_x1 = x1, new_y1 = y1, new_x2 = x2, new_y2 = y2;
414            int clip1 = 0, clip2 = 0;
415            int clipdx, clipdy;
416            int err;
417
418            if (miZeroClipLine(pBox->x1, pBox->y1, pBox->x2 - 1,
419                               pBox->y2 - 1,
420                               &new_x1, &new_y1, &new_x2, &new_y2,
421                               adx, ady, &clip1, &clip2,
422                               octant, bias, oc1, oc2) == -1) {
423                pBox++;
424                continue;
425            }
426
427            if (axis == X_AXIS)
428                len = abs(new_x2 - new_x1);
429            else
430                len = abs(new_y2 - new_y1);
431            if (clip2 != 0 || drawLast)
432                len++;
433            if (len) {
434                /* unwind bresenham error term to first point */
435                doff = dashoff;
436                err = e;
437                if (clip1) {
438                    clipdx = abs(new_x1 - x1);
439                    clipdy = abs(new_y1 - y1);
440                    if (axis == X_AXIS) {
441                        doff += clipdx;
442                        err += e3 * clipdy + e1 * clipdx;
443                    }
444                    else {
445                        doff += clipdy;
446                        err += e3 * clipdx + e1 * clipdy;
447                    }
448                }
449                (*bres) (pDrawable, pGC, doff,
450                         signdx, signdy, axis, new_x1, new_y1,
451                         err, e1, e3, len);
452            }
453            pBox++;
454        }
455    }                           /* while (nBox--) */
456}
457