fbseg.c revision 35c4bbdf
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
282static void
283fbBresSolid24RRop(DrawablePtr pDrawable,
284                  GCPtr pGC,
285                  int dashOffset,
286                  int signdx,
287                  int signdy,
288                  int axis, int x1, int y1, int e, int e1, int e3, int len)
289{
290    FbStip *dst;
291    FbStride dstStride;
292    int dstBpp;
293    int dstXoff, dstYoff;
294    FbGCPrivPtr pPriv = fbGetGCPrivate(pGC);
295    FbStip and = pPriv->and;
296    FbStip xor = pPriv->xor;
297    FbStip leftMask, rightMask;
298    int nl;
299    FbStip *d;
300    int x;
301    int rot;
302    FbStip andT, xorT;
303
304    fbGetStipDrawable(pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
305    dst += ((y1 + dstYoff) * dstStride);
306    x1 = (x1 + dstXoff) * 24;
307    if (signdy < 0)
308        dstStride = -dstStride;
309    signdx *= 24;
310    while (len--) {
311        d = dst + (x1 >> FB_STIP_SHIFT);
312        x = x1 & FB_STIP_MASK;
313        rot = FbFirst24Rot(x);
314        andT = FbRot24Stip(and, rot);
315        xorT = FbRot24Stip(xor, rot);
316        FbMaskStip(x, 24, leftMask, nl, rightMask);
317        if (leftMask) {
318            WRITE(d, FbDoMaskRRop(READ(d), andT, xorT, leftMask));
319            d++;
320            andT = FbNext24Stip(andT);
321            xorT = FbNext24Stip(xorT);
322        }
323        if (rightMask)
324            WRITE(d, FbDoMaskRRop(READ(d), andT, xorT, rightMask));
325        if (axis == X_AXIS) {
326            x1 += signdx;
327            e += e1;
328            if (e >= 0) {
329                e += e3;
330                dst += dstStride;
331            }
332        }
333        else {
334            dst += dstStride;
335            e += e1;
336            if (e >= 0) {
337                e += e3;
338                x1 += signdx;
339            }
340        }
341    }
342
343    fbFinishAccess(pDrawable);
344}
345
346static void
347fbBresDash24RRop(DrawablePtr pDrawable,
348                 GCPtr pGC,
349                 int dashOffset,
350                 int signdx,
351                 int signdy,
352                 int axis, int x1, int y1, int e, int e1, int e3, int len)
353{
354    FbStip *dst;
355    FbStride dstStride;
356    int dstBpp;
357    int dstXoff, dstYoff;
358    FbGCPrivPtr pPriv = fbGetGCPrivate(pGC);
359    FbStip andT, xorT;
360    FbStip fgand = pPriv->and;
361    FbStip fgxor = pPriv->xor;
362    FbStip bgand = pPriv->bgand;
363    FbStip bgxor = pPriv->bgxor;
364    FbStip leftMask, rightMask;
365    int nl;
366    FbStip *d;
367    int x;
368    int rot;
369
370    FbDashDeclare;
371    int dashlen;
372    Bool even;
373    Bool doOdd;
374
375    fbGetStipDrawable(pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
376    doOdd = pGC->lineStyle == LineDoubleDash;
377
378    /* compute current dash position */
379    FbDashInit(pGC, pPriv, dashOffset, dashlen, even);
380
381    dst += ((y1 + dstYoff) * dstStride);
382    x1 = (x1 + dstXoff) * 24;
383    if (signdy < 0)
384        dstStride = -dstStride;
385    signdx *= 24;
386    while (len--) {
387        if (even || doOdd) {
388            if (even) {
389                andT = fgand;
390                xorT = fgxor;
391            }
392            else {
393                andT = bgand;
394                xorT = bgxor;
395            }
396            d = dst + (x1 >> FB_STIP_SHIFT);
397            x = x1 & FB_STIP_MASK;
398            rot = FbFirst24Rot(x);
399            andT = FbRot24Stip(andT, rot);
400            xorT = FbRot24Stip(xorT, rot);
401            FbMaskStip(x, 24, leftMask, nl, rightMask);
402            if (leftMask) {
403                WRITE(d, FbDoMaskRRop(READ(d), andT, xorT, leftMask));
404                d++;
405                andT = FbNext24Stip(andT);
406                xorT = FbNext24Stip(xorT);
407            }
408            if (rightMask)
409                WRITE(d, FbDoMaskRRop(READ(d), andT, xorT, rightMask));
410        }
411        if (axis == X_AXIS) {
412            x1 += signdx;
413            e += e1;
414            if (e >= 0) {
415                e += e3;
416                dst += dstStride;
417            }
418        }
419        else {
420            dst += dstStride;
421            e += e1;
422            if (e >= 0) {
423                e += e3;
424                x1 += signdx;
425            }
426        }
427        FbDashStep(dashlen, even);
428    }
429
430    fbFinishAccess(pDrawable);
431}
432
433/*
434 * For drivers that want to bail drawing some lines, this
435 * function takes care of selecting the appropriate rasterizer
436 * based on the contents of the specified GC.
437 */
438
439static FbBres *
440fbSelectBres(DrawablePtr pDrawable, GCPtr pGC)
441{
442    FbGCPrivPtr pPriv = fbGetGCPrivate(pGC);
443    int dstBpp = pDrawable->bitsPerPixel;
444    FbBres *bres;
445
446    if (pGC->lineStyle == LineSolid) {
447        bres = fbBresFill;
448        if (pGC->fillStyle == FillSolid) {
449            bres = fbBresSolid;
450            if (dstBpp == 24)
451                bres = fbBresSolid24RRop;
452            if (pPriv->and == 0) {
453                switch (dstBpp) {
454                case 8:
455                    bres = fbBresSolid8;
456                    break;
457                case 16:
458                    bres = fbBresSolid16;
459                    break;
460                case 24:
461                    bres = fbBresSolid24;
462                    break;
463                case 32:
464                    bres = fbBresSolid32;
465                    break;
466                }
467            }
468        }
469    }
470    else {
471        bres = fbBresFillDash;
472        if (pGC->fillStyle == FillSolid) {
473            bres = fbBresDash;
474            if (dstBpp == 24)
475                bres = fbBresDash24RRop;
476            if (pPriv->and == 0 &&
477                (pGC->lineStyle == LineOnOffDash || pPriv->bgand == 0)) {
478                switch (dstBpp) {
479                case 8:
480                    bres = fbBresDash8;
481                    break;
482                case 16:
483                    bres = fbBresDash16;
484                    break;
485                case 24:
486                    bres = fbBresDash24;
487                    break;
488                case 32:
489                    bres = fbBresDash32;
490                    break;
491                }
492            }
493        }
494    }
495    return bres;
496}
497
498void
499fbSegment(DrawablePtr pDrawable,
500          GCPtr pGC,
501          int x1, int y1, int x2, int y2, Bool drawLast, int *dashOffset)
502{
503    FbBres *bres;
504    RegionPtr pClip = fbGetCompositeClip(pGC);
505    BoxPtr pBox;
506    int nBox;
507    int adx;                    /* abs values of dx and dy */
508    int ady;
509    int signdx;                 /* sign of dx and dy */
510    int signdy;
511    int e, e1, e2, e3;          /* bresenham error and increments */
512    int len;                    /* length of segment */
513    int axis;                   /* major axis */
514    int octant;
515    int dashoff;
516    int doff;
517    unsigned int bias = miGetZeroLineBias(pDrawable->pScreen);
518    unsigned int oc1;           /* outcode of point 1 */
519    unsigned int oc2;           /* outcode of point 2 */
520
521    nBox = RegionNumRects(pClip);
522    pBox = RegionRects(pClip);
523
524    bres = fbSelectBres(pDrawable, pGC);
525
526    CalcLineDeltas(x1, y1, x2, y2, adx, ady, signdx, signdy, 1, 1, octant);
527
528    if (adx > ady) {
529        axis = X_AXIS;
530        e1 = ady << 1;
531        e2 = e1 - (adx << 1);
532        e = e1 - adx;
533        len = adx;
534    }
535    else {
536        axis = Y_AXIS;
537        e1 = adx << 1;
538        e2 = e1 - (ady << 1);
539        e = e1 - ady;
540        SetYMajorOctant(octant);
541        len = ady;
542    }
543
544    FIXUP_ERROR(e, octant, bias);
545
546    /*
547     * Adjust error terms to compare against zero
548     */
549    e3 = e2 - e1;
550    e = e - e1;
551
552    /* we have bresenham parameters and two points.
553       all we have to do now is clip and draw.
554     */
555
556    if (drawLast)
557        len++;
558    dashoff = *dashOffset;
559    *dashOffset = dashoff + len;
560    while (nBox--) {
561        oc1 = 0;
562        oc2 = 0;
563        OUTCODES(oc1, x1, y1, pBox);
564        OUTCODES(oc2, x2, y2, pBox);
565        if ((oc1 | oc2) == 0) {
566            (*bres) (pDrawable, pGC, dashoff,
567                     signdx, signdy, axis, x1, y1, e, e1, e3, len);
568            break;
569        }
570        else if (oc1 & oc2) {
571            pBox++;
572        }
573        else {
574            int new_x1 = x1, new_y1 = y1, new_x2 = x2, new_y2 = y2;
575            int clip1 = 0, clip2 = 0;
576            int clipdx, clipdy;
577            int err;
578
579            if (miZeroClipLine(pBox->x1, pBox->y1, pBox->x2 - 1,
580                               pBox->y2 - 1,
581                               &new_x1, &new_y1, &new_x2, &new_y2,
582                               adx, ady, &clip1, &clip2,
583                               octant, bias, oc1, oc2) == -1) {
584                pBox++;
585                continue;
586            }
587
588            if (axis == X_AXIS)
589                len = abs(new_x2 - new_x1);
590            else
591                len = abs(new_y2 - new_y1);
592            if (clip2 != 0 || drawLast)
593                len++;
594            if (len) {
595                /* unwind bresenham error term to first point */
596                doff = dashoff;
597                err = e;
598                if (clip1) {
599                    clipdx = abs(new_x1 - x1);
600                    clipdy = abs(new_y1 - y1);
601                    if (axis == X_AXIS) {
602                        doff += clipdx;
603                        err += e3 * clipdy + e1 * clipdx;
604                    }
605                    else {
606                        doff += clipdy;
607                        err += e3 * clipdx + e1 * clipdy;
608                    }
609                }
610                (*bres) (pDrawable, pGC, doff,
611                         signdx, signdy, axis, new_x1, new_y1,
612                         err, e1, e3, len);
613            }
614            pBox++;
615        }
616    }                           /* while (nBox--) */
617}
618