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 
36 static void
fbBresSolid(DrawablePtr pDrawable,GCPtr pGC,int dashOffset,int signdx,int signdy,int axis,int x1,int y1,int e,int e1,int e3,int len)37 fbBresSolid(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 
108 static void
fbBresDash(DrawablePtr pDrawable,GCPtr pGC,int dashOffset,int signdx,int signdy,int axis,int x1,int y1,int e,int e1,int e3,int len)109 fbBresDash(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 
181 static void
fbBresFill(DrawablePtr pDrawable,GCPtr pGC,int dashOffset,int signdx,int signdy,int axis,int x1,int y1,int e,int e1,int e3,int len)182 fbBresFill(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 
209 static void
fbSetFg(DrawablePtr pDrawable,GCPtr pGC,Pixel fg)210 fbSetFg(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 
221 static void
fbBresFillDash(DrawablePtr pDrawable,GCPtr pGC,int dashOffset,int signdx,int signdy,int axis,int x1,int y1,int e,int e1,int e3,int len)222 fbBresFillDash(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 
288 static FbBres *
fbSelectBres(DrawablePtr pDrawable,GCPtr pGC)289 fbSelectBres(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 
337 void
fbSegment(DrawablePtr pDrawable,GCPtr pGC,int x1,int y1,int x2,int y2,Bool drawLast,int * dashOffset)338 fbSegment(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