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
36void
37fbBresSolid (DrawablePtr    pDrawable,
38	     GCPtr	    pGC,
39	     int	    dashOffset,
40	     int	    signdx,
41	     int	    signdy,
42	     int	    axis,
43	     int	    x1,
44	     int	    y1,
45	     int	    e,
46	     int	    e1,
47	     int	    e3,
48	     int	    len)
49{
50    FbStip	*dst;
51    FbStride	dstStride;
52    int		dstBpp;
53    int		dstXoff, dstYoff;
54    FbGCPrivPtr	pPriv = fbGetGCPrivate (pGC);
55    FbStip	and = (FbStip) pPriv->and;
56    FbStip	xor = (FbStip) pPriv->xor;
57    FbStip	mask, mask0;
58    FbStip	bits;
59
60    fbGetStipDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
61    dst += ((y1 + dstYoff) * dstStride);
62    x1 = (x1 + dstXoff) * dstBpp;
63    dst += x1 >> FB_STIP_SHIFT;
64    x1 &= FB_STIP_MASK;
65    mask0 = FbStipMask(0, dstBpp);
66    mask = FbStipRight (mask0, x1);
67    if (signdx < 0)
68	mask0 = FbStipRight (mask0, FB_STIP_UNIT - dstBpp);
69    if (signdy < 0)
70	dstStride = -dstStride;
71    if (axis == X_AXIS)
72    {
73	bits = 0;
74	while (len--)
75	{
76	    bits |= mask;
77	    mask = fbBresShiftMask(mask,signdx,dstBpp);
78	    if (!mask)
79	    {
80		WRITE(dst, FbDoMaskRRop (READ(dst), and, xor, bits));
81		bits = 0;
82		dst += signdx;
83		mask = mask0;
84	    }
85	    e += e1;
86	    if (e >= 0)
87	    {
88		WRITE(dst, FbDoMaskRRop (READ(dst), and, xor, bits));
89		bits = 0;
90		dst += dstStride;
91		e += e3;
92	    }
93	}
94	if (bits)
95	    WRITE(dst, FbDoMaskRRop (READ(dst), and, xor, bits));
96    }
97    else
98    {
99	while (len--)
100	{
101	    WRITE(dst, FbDoMaskRRop (READ(dst), and, xor, mask));
102	    dst += dstStride;
103	    e += e1;
104	    if (e >= 0)
105	    {
106		e += e3;
107		mask = fbBresShiftMask(mask,signdx,dstBpp);
108		if (!mask)
109		{
110		    dst += signdx;
111		    mask = mask0;
112		}
113	    }
114	}
115    }
116
117    fbFinishAccess (pDrawable);
118}
119
120void
121fbBresDash (DrawablePtr	pDrawable,
122	    GCPtr	pGC,
123	    int		dashOffset,
124	    int		signdx,
125	    int		signdy,
126	    int		axis,
127	    int		x1,
128	    int		y1,
129	    int		e,
130	    int		e1,
131	    int		e3,
132	    int		len)
133{
134    FbStip	*dst;
135    FbStride	dstStride;
136    int		dstBpp;
137    int		dstXoff, dstYoff;
138    FbGCPrivPtr	pPriv = fbGetGCPrivate (pGC);
139    FbStip	and = (FbStip) pPriv->and;
140    FbStip	xor = (FbStip) pPriv->xor;
141    FbStip	bgand = (FbStip) pPriv->bgand;
142    FbStip	bgxor = (FbStip) pPriv->bgxor;
143    FbStip	mask, mask0;
144    FbDashDeclare;
145    int		dashlen;
146    Bool	even;
147    Bool	doOdd;
148
149    fbGetStipDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
150    doOdd = pGC->lineStyle == LineDoubleDash;
151
152    FbDashInit (pGC, pPriv, dashOffset, dashlen, even);
153
154    dst += ((y1 + dstYoff) * dstStride);
155    x1 = (x1 + dstXoff) * dstBpp;
156    dst += x1 >> FB_STIP_SHIFT;
157    x1 &= FB_STIP_MASK;
158    mask0 = FbStipMask(0, dstBpp);
159    mask = FbStipRight (mask0, x1);
160    if (signdx < 0)
161	mask0 = FbStipRight (mask0, FB_STIP_UNIT - dstBpp);
162    if (signdy < 0)
163	dstStride = -dstStride;
164    while (len--)
165    {
166	if (even)
167	    WRITE(dst, FbDoMaskRRop (READ(dst), and, xor, mask));
168	else if (doOdd)
169	    WRITE(dst, FbDoMaskRRop (READ(dst), bgand, bgxor, mask));
170	if (axis == X_AXIS)
171	{
172	    mask = fbBresShiftMask(mask,signdx,dstBpp);
173	    if (!mask)
174	    {
175		dst += signdx;
176		mask = mask0;
177	    }
178	    e += e1;
179	    if (e >= 0)
180	    {
181		dst += dstStride;
182		e += e3;
183	    }
184	}
185	else
186	{
187	    dst += dstStride;
188	    e += e1;
189	    if (e >= 0)
190	    {
191		e += e3;
192		mask = fbBresShiftMask(mask,signdx,dstBpp);
193		if (!mask)
194		{
195		    dst += signdx;
196		    mask = mask0;
197		}
198	    }
199	}
200	FbDashStep (dashlen, even);
201    }
202
203    fbFinishAccess (pDrawable);
204}
205
206void
207fbBresFill (DrawablePtr	pDrawable,
208	    GCPtr	pGC,
209	    int		dashOffset,
210	    int		signdx,
211	    int		signdy,
212	    int		axis,
213	    int		x1,
214	    int		y1,
215	    int		e,
216	    int		e1,
217	    int		e3,
218	    int		len)
219{
220    while (len--)
221    {
222	fbFill (pDrawable, pGC, x1, y1, 1, 1);
223	if (axis == X_AXIS)
224	{
225	    x1 += signdx;
226	    e += e1;
227	    if (e >= 0)
228	    {
229		e += e3;
230		y1 += signdy;
231	    }
232	}
233	else
234	{
235	    y1 += signdy;
236	    e += e1;
237	    if (e >= 0)
238	    {
239		e += e3;
240		x1 += signdx;
241	    }
242	}
243    }
244}
245
246static void
247fbSetFg (DrawablePtr	pDrawable,
248	 GCPtr		pGC,
249	 Pixel		fg)
250{
251    if (fg != pGC->fgPixel)
252    {
253	ChangeGCVal val;
254	val.val = fg;
255	ChangeGC (NullClient, pGC, GCForeground, &val);
256	ValidateGC (pDrawable, pGC);
257    }
258}
259
260void
261fbBresFillDash (DrawablePtr pDrawable,
262		GCPtr	    pGC,
263		int	    dashOffset,
264		int	    signdx,
265		int	    signdy,
266		int	    axis,
267		int	    x1,
268		int	    y1,
269		int	    e,
270		int	    e1,
271		int	    e3,
272		int	    len)
273{
274    FbGCPrivPtr	pPriv = fbGetGCPrivate (pGC);
275    FbDashDeclare;
276    int		dashlen;
277    Bool	even;
278    Bool	doOdd;
279    Bool	doBg;
280    Pixel	fg, bg;
281
282    fg = pGC->fgPixel;
283    bg = pGC->bgPixel;
284
285    /* whether to fill the odd dashes */
286    doOdd = pGC->lineStyle == LineDoubleDash;
287    /* whether to switch fg to bg when filling odd dashes */
288    doBg = doOdd && (pGC->fillStyle == FillSolid ||
289		     pGC->fillStyle == FillStippled);
290
291    /* compute current dash position */
292    FbDashInit (pGC, pPriv, dashOffset, dashlen, even);
293
294    while (len--)
295    {
296	if (even || doOdd)
297	{
298	    if (doBg)
299	    {
300		if (even)
301		    fbSetFg (pDrawable, pGC, fg);
302		else
303		    fbSetFg (pDrawable, pGC, bg);
304	    }
305	    fbFill (pDrawable, pGC, x1, y1, 1, 1);
306	}
307	if (axis == X_AXIS)
308	{
309	    x1 += signdx;
310	    e += e1;
311	    if (e >= 0)
312	    {
313		e += e3;
314		y1 += signdy;
315	    }
316	}
317	else
318	{
319	    y1 += signdy;
320	    e += e1;
321	    if (e >= 0)
322	    {
323		e += e3;
324		x1 += signdx;
325	    }
326	}
327	FbDashStep (dashlen, even);
328    }
329    if (doBg)
330	fbSetFg (pDrawable, pGC, fg);
331}
332
333#ifdef FB_24BIT
334static void
335fbBresSolid24RRop (DrawablePtr  pDrawable,
336		   GCPtr	pGC,
337		   int		dashOffset,
338		   int		signdx,
339		   int		signdy,
340		   int		axis,
341		   int		x1,
342		   int		y1,
343		   int		e,
344		   int		e1,
345		   int		e3,
346		   int		len)
347{
348    FbStip	*dst;
349    FbStride	dstStride;
350    int		dstBpp;
351    int		dstXoff, dstYoff;
352    FbGCPrivPtr	pPriv = fbGetGCPrivate (pGC);
353    FbStip	and = pPriv->and;
354    FbStip	xor = pPriv->xor;
355    FbStip	leftMask, rightMask;
356    int		nl;
357    FbStip	*d;
358    int		x;
359    int		rot;
360    FbStip	andT, xorT;
361
362    fbGetStipDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
363    dst += ((y1 + dstYoff) * dstStride);
364    x1 = (x1 + dstXoff) * 24;
365    if (signdy < 0)
366	dstStride = -dstStride;
367    signdx *= 24;
368    while (len--)
369    {
370	d = dst + (x1 >> FB_STIP_SHIFT);
371	x = x1 & FB_STIP_MASK;
372	rot = FbFirst24Rot (x);
373	andT = FbRot24Stip(and,rot);
374	xorT = FbRot24Stip(xor,rot);
375	FbMaskStip (x, 24, leftMask, nl, rightMask);
376	if (leftMask)
377	{
378	    WRITE(d, FbDoMaskRRop (READ(d), andT, xorT, leftMask));
379	    d++;
380	    andT = FbNext24Stip (andT);
381	    xorT = FbNext24Stip (xorT);
382	}
383	if (rightMask)
384	    WRITE(d, FbDoMaskRRop (READ(d), andT, xorT, rightMask));
385	if (axis == X_AXIS)
386	{
387	    x1 += signdx;
388	    e += e1;
389	    if (e >= 0)
390	    {
391		e += e3;
392		dst += dstStride;
393	    }
394	}
395	else
396	{
397	    dst += dstStride;
398	    e += e1;
399	    if (e >= 0)
400	    {
401		e += e3;
402		x1 += signdx;
403	    }
404	}
405    }
406
407    fbFinishAccess (pDrawable);
408}
409
410static void
411fbBresDash24RRop (DrawablePtr	pDrawable,
412		  GCPtr		pGC,
413		  int		dashOffset,
414		  int		signdx,
415		  int		signdy,
416		  int		axis,
417		  int		x1,
418		  int		y1,
419		  int		e,
420		  int		e1,
421		  int		e3,
422		  int		len)
423{
424    FbStip	*dst;
425    FbStride	dstStride;
426    int		dstBpp;
427    int		dstXoff, dstYoff;
428    FbGCPrivPtr	pPriv = fbGetGCPrivate (pGC);
429    FbStip	andT, xorT;
430    FbStip	fgand = pPriv->and;
431    FbStip	fgxor = pPriv->xor;
432    FbStip	bgand = pPriv->bgand;
433    FbStip	bgxor = pPriv->bgxor;
434    FbStip	leftMask, rightMask;
435    int		nl;
436    FbStip	*d;
437    int		x;
438    int		rot;
439    FbDashDeclare;
440    int		dashlen;
441    Bool	even;
442    Bool	doOdd;
443
444    fbGetStipDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
445    doOdd = pGC->lineStyle == LineDoubleDash;
446
447    /* compute current dash position */
448    FbDashInit(pGC, pPriv, dashOffset, dashlen, even);
449
450    dst += ((y1 + dstYoff) * dstStride);
451    x1 = (x1 + dstXoff) * 24;
452    if (signdy < 0)
453	dstStride = -dstStride;
454    signdx *= 24;
455    while (len--)
456    {
457	if (even || doOdd)
458	{
459	    if (even)
460	    {
461		andT = fgand;
462		xorT = fgxor;
463	    }
464	    else
465	    {
466		andT = bgand;
467		xorT = bgxor;
468	    }
469	    d = dst + (x1 >> FB_STIP_SHIFT);
470	    x = x1 & FB_STIP_MASK;
471	    rot = FbFirst24Rot (x);
472	    andT = FbRot24Stip (andT, rot);
473	    xorT = FbRot24Stip (xorT, rot);
474	    FbMaskStip (x, 24, leftMask, nl, rightMask);
475	    if (leftMask)
476	    {
477		WRITE(d, FbDoMaskRRop (READ(d), andT, xorT, leftMask));
478		d++;
479		andT = FbNext24Stip (andT);
480		xorT = FbNext24Stip (xorT);
481	    }
482	    if (rightMask)
483		WRITE(d, FbDoMaskRRop (READ(d), andT, xorT, rightMask));
484	}
485	if (axis == X_AXIS)
486	{
487	    x1 += signdx;
488	    e += e1;
489	    if (e >= 0)
490	    {
491		e += e3;
492		dst += dstStride;
493	    }
494	}
495	else
496	{
497	    dst += dstStride;
498	    e += e1;
499	    if (e >= 0)
500	    {
501		e += e3;
502		x1 += signdx;
503	    }
504	}
505	FbDashStep (dashlen, even);
506    }
507
508    fbFinishAccess (pDrawable);
509}
510#endif
511
512/*
513 * For drivers that want to bail drawing some lines, this
514 * function takes care of selecting the appropriate rasterizer
515 * based on the contents of the specified GC.
516 */
517
518FbBres *
519fbSelectBres (DrawablePtr   pDrawable,
520	      GCPtr	    pGC)
521{
522    FbGCPrivPtr	pPriv = fbGetGCPrivate(pGC);
523    int		dstBpp = pDrawable->bitsPerPixel;
524    FbBres *	bres;
525
526    if (pGC->lineStyle == LineSolid)
527    {
528	bres = fbBresFill;
529	if (pGC->fillStyle == FillSolid)
530	{
531	    bres = fbBresSolid;
532#ifdef FB_24BIT
533	    if (dstBpp == 24)
534		bres = fbBresSolid24RRop;
535#endif
536#ifndef FBNOPIXADDR
537	    if (pPriv->and == 0)
538	    {
539		switch (dstBpp) {
540		case 8:	bres = fbBresSolid8; break;
541		case 16: bres = fbBresSolid16; break;
542#ifdef FB_24BIT
543		case 24: bres = fbBresSolid24; break;
544#endif
545		case 32: bres = fbBresSolid32; break;
546		}
547	    }
548#endif
549	}
550    }
551    else
552    {
553	bres = fbBresFillDash;
554	if (pGC->fillStyle == FillSolid)
555	{
556	    bres = fbBresDash;
557#ifdef FB_24BIT
558	    if (dstBpp == 24)
559		bres = fbBresDash24RRop;
560#endif
561#ifndef FBNOPIXADDR
562	    if (pPriv->and == 0 &&
563		(pGC->lineStyle == LineOnOffDash || pPriv->bgand == 0))
564	    {
565		switch (dstBpp) {
566		case 8:	bres = fbBresDash8; break;
567		case 16: bres = fbBresDash16; break;
568#ifdef FB_24BIT
569		case 24: bres = fbBresDash24; break;
570#endif
571		case 32: bres = fbBresDash32; break;
572		}
573	    }
574#endif
575	}
576    }
577    return bres;
578}
579
580void
581fbBres (DrawablePtr	pDrawable,
582	GCPtr		pGC,
583	int		dashOffset,
584	int		signdx,
585	int		signdy,
586	int		axis,
587	int		x1,
588	int		y1,
589	int		e,
590	int		e1,
591	int		e3,
592	int		len)
593{
594    (*fbSelectBres (pDrawable, pGC)) (pDrawable, pGC, dashOffset,
595				      signdx, signdy, axis, x1, y1,
596				      e, e1, e3, len);
597}
598
599void
600fbSegment (DrawablePtr	pDrawable,
601	   GCPtr	pGC,
602	   int		x1,
603	   int		y1,
604	   int		x2,
605	   int		y2,
606	   Bool		drawLast,
607	   int		*dashOffset)
608{
609    FbBres *	bres;
610    RegionPtr	pClip = fbGetCompositeClip(pGC);
611    BoxPtr	pBox;
612    int		nBox;
613    int		adx;		/* abs values of dx and dy */
614    int		ady;
615    int		signdx;		/* sign of dx and dy */
616    int		signdy;
617    int		e, e1, e2, e3;		/* bresenham error and increments */
618    int		len;			/* length of segment */
619    int		axis;			/* major axis */
620    int		octant;
621    int		dashoff;
622    int		doff;
623    unsigned int bias = miGetZeroLineBias(pDrawable->pScreen);
624    unsigned int oc1;	/* outcode of point 1 */
625    unsigned int oc2;	/* outcode of point 2 */
626
627    nBox = RegionNumRects (pClip);
628    pBox = RegionRects (pClip);
629
630    bres = fbSelectBres (pDrawable, pGC);
631
632    CalcLineDeltas(x1, y1, x2, y2, adx, ady, signdx, signdy,
633		   1, 1, octant);
634
635    if (adx > ady)
636    {
637	axis = X_AXIS;
638	e1 = ady << 1;
639	e2 = e1 - (adx << 1);
640	e = e1 - adx;
641	len = adx;
642    }
643    else
644    {
645	axis = Y_AXIS;
646	e1 = adx << 1;
647	e2 = e1 - (ady << 1);
648	e = e1 - ady;
649	SetYMajorOctant(octant);
650	len = ady;
651    }
652
653    FIXUP_ERROR (e, octant, bias);
654
655    /*
656     * Adjust error terms to compare against zero
657     */
658    e3 = e2 - e1;
659    e = e - e1;
660
661    /* we have bresenham parameters and two points.
662       all we have to do now is clip and draw.
663    */
664
665    if (drawLast)
666	len++;
667    dashoff = *dashOffset;
668    *dashOffset = dashoff + len;
669    while(nBox--)
670    {
671	oc1 = 0;
672	oc2 = 0;
673	OUTCODES(oc1, x1, y1, pBox);
674	OUTCODES(oc2, x2, y2, pBox);
675	if ((oc1 | oc2) == 0)
676	{
677	    (*bres) (pDrawable, pGC, dashoff,
678		     signdx, signdy, axis, x1, y1,
679		     e, e1, e3, len);
680	    break;
681	}
682	else if (oc1 & oc2)
683	{
684	    pBox++;
685	}
686	else
687	{
688	    int new_x1 = x1, new_y1 = y1, new_x2 = x2, new_y2 = y2;
689	    int clip1 = 0, clip2 = 0;
690	    int clipdx, clipdy;
691	    int err;
692
693	    if (miZeroClipLine(pBox->x1, pBox->y1, pBox->x2-1,
694			       pBox->y2-1,
695			       &new_x1, &new_y1, &new_x2, &new_y2,
696			       adx, ady, &clip1, &clip2,
697			       octant, bias, oc1, oc2) == -1)
698	    {
699		pBox++;
700		continue;
701	    }
702
703	    if (axis == X_AXIS)
704		len = abs(new_x2 - new_x1);
705	    else
706		len = abs(new_y2 - new_y1);
707	    if (clip2 != 0 || drawLast)
708		len++;
709	    if (len)
710	    {
711		/* unwind bresenham error term to first point */
712		doff = dashoff;
713		err = e;
714		if (clip1)
715		{
716		    clipdx = abs(new_x1 - x1);
717		    clipdy = abs(new_y1 - y1);
718		    if (axis == X_AXIS)
719		    {
720			doff += clipdx;
721			err  += e3 * clipdy + e1 * clipdx;
722		    }
723		    else
724		    {
725			doff += clipdy;
726			err  += e3 * clipdx + e1 * clipdy;
727		    }
728		}
729		(*bres) (pDrawable, pGC, doff,
730			 signdx, signdy, axis, new_x1, new_y1,
731			 err, e1, e3, len);
732	    }
733	    pBox++;
734	}
735    } /* while (nBox--) */
736}
737