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
31PixmapPtr
32fbCreatePixmapBpp (ScreenPtr pScreen, int width, int height, int depth, int bpp,
33		   unsigned usage_hint)
34{
35    PixmapPtr	pPixmap;
36    size_t	datasize;
37    size_t	paddedWidth;
38    int		adjust;
39    int		base;
40
41    paddedWidth = ((width * bpp + FB_MASK) >> FB_SHIFT) * sizeof (FbBits);
42    if (paddedWidth / 4 > 32767 || height > 32767)
43	return NullPixmap;
44    datasize = height * paddedWidth;
45    base = pScreen->totalPixmapSize;
46    adjust = 0;
47    if (base & 7)
48	adjust = 8 - (base & 7);
49    datasize += adjust;
50#ifdef FB_DEBUG
51    datasize += 2 * paddedWidth;
52#endif
53    pPixmap = AllocatePixmap(pScreen, datasize);
54    if (!pPixmap)
55	return NullPixmap;
56    pPixmap->drawable.type = DRAWABLE_PIXMAP;
57    pPixmap->drawable.class = 0;
58    pPixmap->drawable.pScreen = pScreen;
59    pPixmap->drawable.depth = depth;
60    pPixmap->drawable.bitsPerPixel = bpp;
61    pPixmap->drawable.id = 0;
62    pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
63    pPixmap->drawable.x = 0;
64    pPixmap->drawable.y = 0;
65    pPixmap->drawable.width = width;
66    pPixmap->drawable.height = height;
67    pPixmap->devKind = paddedWidth;
68    pPixmap->refcnt = 1;
69    pPixmap->devPrivate.ptr = (pointer) ((char *)pPixmap + base + adjust);
70
71#ifdef FB_DEBUG
72    pPixmap->devPrivate.ptr = (void *) ((char *) pPixmap->devPrivate.ptr + paddedWidth);
73    fbInitializeDrawable (&pPixmap->drawable);
74#endif
75
76#ifdef COMPOSITE
77    pPixmap->screen_x = 0;
78    pPixmap->screen_y = 0;
79#endif
80
81    pPixmap->usage_hint = usage_hint;
82
83    return pPixmap;
84}
85
86PixmapPtr
87fbCreatePixmap (ScreenPtr pScreen, int width, int height, int depth,
88		unsigned usage_hint)
89{
90    int	bpp;
91    bpp = BitsPerPixel (depth);
92#ifdef FB_SCREEN_PRIVATE
93    if (bpp == 32 && depth <= 24)
94	bpp = fbGetScreenPrivate(pScreen)->pix32bpp;
95#endif
96    return fbCreatePixmapBpp (pScreen, width, height, depth, bpp, usage_hint);
97}
98
99Bool
100fbDestroyPixmap (PixmapPtr pPixmap)
101{
102    if(--pPixmap->refcnt)
103	return TRUE;
104    FreePixmap(pPixmap);
105    return TRUE;
106}
107
108#define ADDRECT(reg,r,fr,rx1,ry1,rx2,ry2)			\
109if (((rx1) < (rx2)) && ((ry1) < (ry2)) &&			\
110    (!((reg)->data->numRects &&					\
111       ((r-1)->y1 == (ry1)) &&					\
112       ((r-1)->y2 == (ry2)) &&					\
113       ((r-1)->x1 <= (rx1)) &&					\
114       ((r-1)->x2 >= (rx2)))))					\
115{								\
116    if ((reg)->data->numRects == (reg)->data->size)		\
117    {								\
118	RegionRectAlloc(reg, 1);					\
119	fr = RegionBoxptr(reg);				\
120	r = fr + (reg)->data->numRects;				\
121    }								\
122    r->x1 = (rx1);						\
123    r->y1 = (ry1);						\
124    r->x2 = (rx2);						\
125    r->y2 = (ry2);						\
126    (reg)->data->numRects++;					\
127    if(r->x1 < (reg)->extents.x1)				\
128	(reg)->extents.x1 = r->x1;				\
129    if(r->x2 > (reg)->extents.x2)				\
130	(reg)->extents.x2 = r->x2;				\
131    r++;							\
132}
133
134/* Convert bitmap clip mask into clipping region.
135 * First, goes through each line and makes boxes by noting the transitions
136 * from 0 to 1 and 1 to 0.
137 * Then it coalesces the current line with the previous if they have boxes
138 * at the same X coordinates.
139 */
140RegionPtr
141fbPixmapToRegion(PixmapPtr pPix)
142{
143    register RegionPtr	pReg;
144    FbBits		*pw, w;
145    register int	ib;
146    int			width, h, base, rx1 = 0, crects;
147    FbBits		*pwLineEnd;
148    int			irectPrevStart, irectLineStart;
149    register BoxPtr	prectO, prectN;
150    BoxPtr		FirstRect, rects, prectLineStart;
151    Bool		fInBox, fSame;
152    register FbBits	mask0 = FB_ALLONES & ~FbScrRight(FB_ALLONES, 1);
153    FbBits		*pwLine;
154    int			nWidth;
155
156    pReg = RegionCreate(NULL, 1);
157    if(!pReg)
158	return NullRegion;
159    FirstRect = RegionBoxptr(pReg);
160    rects = FirstRect;
161
162    fbPrepareAccess(&pPix->drawable);
163
164    pwLine = (FbBits *) pPix->devPrivate.ptr;
165    nWidth = pPix->devKind >> (FB_SHIFT-3);
166
167    width = pPix->drawable.width;
168    pReg->extents.x1 = width - 1;
169    pReg->extents.x2 = 0;
170    irectPrevStart = -1;
171    for(h = 0; h < pPix->drawable.height; h++)
172    {
173	pw = pwLine;
174	pwLine += nWidth;
175	irectLineStart = rects - FirstRect;
176	/* If the Screen left most bit of the word is set, we're starting in
177	 * a box */
178	if(READ(pw) & mask0)
179	{
180	    fInBox = TRUE;
181	    rx1 = 0;
182	}
183	else
184	    fInBox = FALSE;
185	/* Process all words which are fully in the pixmap */
186	pwLineEnd = pw + (width >> FB_SHIFT);
187	for (base = 0; pw < pwLineEnd; base += FB_UNIT)
188	{
189	    w = READ(pw++);
190	    if (fInBox)
191	    {
192		if (!~w)
193		    continue;
194	    }
195	    else
196	    {
197		if (!w)
198		    continue;
199	    }
200	    for(ib = 0; ib < FB_UNIT; ib++)
201	    {
202	        /* If the Screen left most bit of the word is set, we're
203		 * starting a box */
204		if(w & mask0)
205		{
206		    if(!fInBox)
207		    {
208			rx1 = base + ib;
209			/* start new box */
210			fInBox = TRUE;
211		    }
212		}
213		else
214		{
215		    if(fInBox)
216		    {
217			/* end box */
218			ADDRECT(pReg, rects, FirstRect,
219				rx1, h, base + ib, h + 1);
220			fInBox = FALSE;
221		    }
222		}
223		/* Shift the word VISUALLY left one. */
224		w = FbScrLeft(w, 1);
225	    }
226	}
227	if(width & FB_MASK)
228	{
229	    /* Process final partial word on line */
230	    w = READ(pw++);
231	    for(ib = 0; ib < (width & FB_MASK); ib++)
232	    {
233	        /* If the Screen left most bit of the word is set, we're
234		 * starting a box */
235		if(w & mask0)
236		{
237		    if(!fInBox)
238		    {
239			rx1 = base + ib;
240			/* start new box */
241			fInBox = TRUE;
242		    }
243		}
244		else
245		{
246		    if(fInBox)
247		    {
248			/* end box */
249			ADDRECT(pReg, rects, FirstRect,
250				rx1, h, base + ib, h + 1);
251			fInBox = FALSE;
252		    }
253		}
254		/* Shift the word VISUALLY left one. */
255		w = FbScrLeft(w, 1);
256	    }
257	}
258	/* If scanline ended with last bit set, end the box */
259	if(fInBox)
260	{
261	    ADDRECT(pReg, rects, FirstRect,
262		    rx1, h, base + (width & FB_MASK), h + 1);
263	}
264	/* if all rectangles on this line have the same x-coords as
265	 * those on the previous line, then add 1 to all the previous  y2s and
266	 * throw away all the rectangles from this line
267	 */
268	fSame = FALSE;
269	if(irectPrevStart != -1)
270	{
271	    crects = irectLineStart - irectPrevStart;
272	    if(crects == ((rects - FirstRect) - irectLineStart))
273	    {
274	        prectO = FirstRect + irectPrevStart;
275	        prectN = prectLineStart = FirstRect + irectLineStart;
276		fSame = TRUE;
277	        while(prectO < prectLineStart)
278		{
279		    if((prectO->x1 != prectN->x1) || (prectO->x2 != prectN->x2))
280		    {
281			  fSame = FALSE;
282			  break;
283		    }
284		    prectO++;
285		    prectN++;
286		}
287		if (fSame)
288		{
289		    prectO = FirstRect + irectPrevStart;
290		    while(prectO < prectLineStart)
291		    {
292			prectO->y2 += 1;
293			prectO++;
294		    }
295		    rects -= crects;
296		    pReg->data->numRects -= crects;
297		}
298	    }
299	}
300	if(!fSame)
301	    irectPrevStart = irectLineStart;
302    }
303    if (!pReg->data->numRects)
304	pReg->extents.x1 = pReg->extents.x2 = 0;
305    else
306    {
307	pReg->extents.y1 = RegionBoxptr(pReg)->y1;
308	pReg->extents.y2 = RegionEnd(pReg)->y2;
309	if (pReg->data->numRects == 1)
310	{
311	    free(pReg->data);
312	    pReg->data = (RegDataPtr)NULL;
313	}
314    }
315
316    fbFinishAccess(&pPix->drawable);
317#ifdef DEBUG
318    if (!RegionIsValid(pReg))
319	FatalError("Assertion failed file %s, line %d: expr\n", __FILE__, __LINE__);
320#endif
321    return pReg;
322}
323
324#ifdef FB_DEBUG
325
326#ifndef WIN32
327#include <stdio.h>
328#else
329#include <dbg.h>
330#endif
331
332static Bool
333fbValidateBits (FbStip *bits, int stride, FbStip data)
334{
335    while (stride--)
336    {
337	if (*bits != data)
338	{
339#ifdef WIN32
340	    NCD_DEBUG ((DEBUG_FAILURE, "fdValidateBits failed at 0x%x (is 0x%x want 0x%x)",
341			bits, *bits, data));
342#else
343	    fprintf (stderr, "fbValidateBits failed\n");
344#endif
345	    return FALSE;
346	}
347	bits++;
348    }
349}
350
351void
352fbValidateDrawable (DrawablePtr pDrawable)
353{
354    FbStip	*bits, *first, *last;
355    int		stride, bpp;
356    int		xoff, yoff;
357    int		height;
358    Bool	failed;
359
360    if (pDrawable->type != DRAWABLE_PIXMAP)
361	pDrawable = (DrawablePtr) fbGetWindowPixmap(pDrawable);
362    fbGetStipDrawable(pDrawable, bits, stride, bpp, xoff, yoff);
363    first = bits - stride;
364    last = bits + stride * pDrawable->height;
365    if (!fbValidateBits (first, stride, FB_HEAD_BITS) ||
366	!fbValidateBits (last, stride, FB_TAIL_BITS))
367	fbInitializeDrawable(pDrawable);
368    fbFinishAccess (pDrawable);
369}
370
371void
372fbSetBits (FbStip *bits, int stride, FbStip data)
373{
374    while (stride--)
375	*bits++ = data;
376}
377
378void
379fbInitializeDrawable (DrawablePtr pDrawable)
380{
381    FbStip  *bits, *first, *last;
382    int	    stride, bpp;
383    int	    xoff, yoff;
384
385    fbGetStipDrawable(pDrawable, bits, stride, bpp, xoff, yoff);
386    first = bits - stride;
387    last = bits + stride * pDrawable->height;
388    fbSetBits (first, stride, FB_HEAD_BITS);
389    fbSetBits (last, stride, FB_TAIL_BITS);
390    fbFinishAccess (pDrawable);
391}
392#endif /* FB_DEBUG */
393