1/*
2 * Copyright © 2001 Keith Packard
3 * Copyright 2011 VMWare, Inc. All Rights Reserved.
4 * May partly be based on code that is Copyright © The XFree86 Project Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * Author: Eric Anholt <eric@anholt.net>
27 * Author: Michel Dänzer <michel@tungstengraphics.com>
28 * Author: Thomas Hellstrom <thellstrom@vmware.com>
29 */
30#include "saa.h"
31#include "saa_priv.h"
32
33#ifdef RENDER
34#include <mipict.h>
35
36/**
37 * Same as miCreateAlphaPicture, except it uses
38 * saa_check_poly_fill_rect instead
39 */
40
41static PicturePtr
42saa_create_alpha_picture(ScreenPtr pScreen,
43			 PicturePtr pDst,
44			 PictFormatPtr pPictFormat, CARD16 width, CARD16 height)
45{
46    PixmapPtr pPixmap;
47    PicturePtr pPicture;
48    GCPtr pGC;
49    int error;
50    xRectangle rect;
51
52    if (width > 32767 || height > 32767)
53	return 0;
54
55    if (!pPictFormat) {
56	if (pDst->polyEdge == PolyEdgeSharp)
57	    pPictFormat = PictureMatchFormat(pScreen, 1, PICT_a1);
58	else
59	    pPictFormat = PictureMatchFormat(pScreen, 8, PICT_a8);
60	if (!pPictFormat)
61	    return 0;
62    }
63
64    pPixmap = (*pScreen->CreatePixmap) (pScreen, width, height,
65					pPictFormat->depth, 0);
66    if (!pPixmap)
67	return 0;
68    pGC = GetScratchGC(pPixmap->drawable.depth, pScreen);
69    if (!pGC) {
70	(*pScreen->DestroyPixmap) (pPixmap);
71	return 0;
72    }
73    ValidateGC(&pPixmap->drawable, pGC);
74    rect.x = 0;
75    rect.y = 0;
76    rect.width = width;
77    rect.height = height;
78    saa_check_poly_fill_rect(&pPixmap->drawable, pGC, 1, &rect);
79    FreeScratchGC(pGC);
80    pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat,
81			     0, 0, serverClient, &error);
82    (*pScreen->DestroyPixmap) (pPixmap);
83    return pPicture;
84}
85
86/**
87 * saa_trapezoids is essentially a copy of miTrapezoids that uses
88 * saa_create_alpha_picture instead of miCreateAlphaPicture.
89 *
90 * The problem with miCreateAlphaPicture is that it calls PolyFillRect
91 * to initialize the contents after creating the pixmap, which
92 * causes the pixmap to be moved in for acceleration. The subsequent
93 * call to RasterizeTrapezoid won't be accelerated however, which
94 * forces the pixmap to be moved out again.
95 *
96 */
97static void
98saa_trapezoids(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
99	       PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc,
100	       int ntrap, xTrapezoid * traps)
101{
102    ScreenPtr pScreen = pDst->pDrawable->pScreen;
103    PictureScreenPtr ps = GetPictureScreen(pScreen);
104    BoxRec bounds;
105
106    if (maskFormat) {
107	PicturePtr pPicture;
108	INT16 xDst, yDst;
109	INT16 xRel, yRel;
110	saa_access_t access;
111
112	miTrapezoidBounds(ntrap, traps, &bounds);
113
114	if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
115	    return;
116
117	xDst = traps[0].left.p1.x >> 16;
118	yDst = traps[0].left.p1.y >> 16;
119
120	pPicture = saa_create_alpha_picture(pScreen, pDst, maskFormat,
121					    bounds.x2 - bounds.x1,
122					    bounds.y2 - bounds.y1);
123	if (!pPicture)
124	    return;
125
126	if (saa_pad_write(pPicture->pDrawable, NULL, FALSE, &access)) {
127	    for (; ntrap; ntrap--, traps++)
128		(*ps->RasterizeTrapezoid) (pPicture, traps,
129					   -bounds.x1, -bounds.y1);
130	    saa_fad_write(pPicture->pDrawable, access);
131	}
132
133	xRel = bounds.x1 + xSrc - xDst;
134	yRel = bounds.y1 + ySrc - yDst;
135	CompositePicture(op, pSrc, pPicture, pDst,
136			 xRel, yRel, 0, 0, bounds.x1, bounds.y1,
137			 bounds.x2 - bounds.x1, bounds.y2 - bounds.y1);
138	FreePicture(pPicture, 0);
139    } else {
140	if (pDst->polyEdge == PolyEdgeSharp)
141	    maskFormat = PictureMatchFormat(pScreen, 1, PICT_a1);
142	else
143	    maskFormat = PictureMatchFormat(pScreen, 8, PICT_a8);
144	for (; ntrap; ntrap--, traps++)
145	    saa_trapezoids(op, pSrc, pDst, maskFormat, xSrc, ySrc, 1, traps);
146    }
147}
148
149/**
150 * saa_triangles is essentially a copy of miTriangles that uses
151 * saa_create_alpha_picture instead of miCreateAlphaPicture.
152 *
153 * The problem with miCreateAlphaPicture is that it calls PolyFillRect
154 * to initialize the contents after creating the pixmap, which
155 * causes the pixmap to be moved in for acceleration. The subsequent
156 * call to AddTriangles won't be accelerated however, which forces the pixmap
157 * to be moved out again.
158 */
159static void
160saa_triangles(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
161	      PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc,
162	      int ntri, xTriangle * tris)
163{
164    ScreenPtr pScreen = pDst->pDrawable->pScreen;
165    PictureScreenPtr ps = GetPictureScreen(pScreen);
166    BoxRec bounds;
167
168    if (maskFormat) {
169	PicturePtr pPicture;
170	INT16 xDst, yDst;
171	INT16 xRel, yRel;
172	saa_access_t access;
173
174	miTriangleBounds(ntri, tris, &bounds);
175
176	if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
177	    return;
178
179	xDst = tris[0].p1.x >> 16;
180	yDst = tris[0].p1.y >> 16;
181
182	pPicture = saa_create_alpha_picture(pScreen, pDst, maskFormat,
183					    bounds.x2 - bounds.x1,
184					    bounds.y2 - bounds.y1);
185	if (!pPicture)
186	    return;
187
188	if (saa_pad_write(pPicture->pDrawable, NULL, FALSE, &access)) {
189	    (*ps->AddTriangles) (pPicture, -bounds.x1, -bounds.y1, ntri, tris);
190	    saa_fad_write(pPicture->pDrawable, access);
191	}
192
193	xRel = bounds.x1 + xSrc - xDst;
194	yRel = bounds.y1 + ySrc - yDst;
195	CompositePicture(op, pSrc, pPicture, pDst,
196			 xRel, yRel, 0, 0, bounds.x1, bounds.y1,
197			 bounds.x2 - bounds.x1, bounds.y2 - bounds.y1);
198	FreePicture(pPicture, 0);
199    } else {
200	if (pDst->polyEdge == PolyEdgeSharp)
201	    maskFormat = PictureMatchFormat(pScreen, 1, PICT_a1);
202	else
203	    maskFormat = PictureMatchFormat(pScreen, 8, PICT_a8);
204
205	for (; ntri; ntri--, tris++)
206	    saa_triangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, 1, tris);
207    }
208}
209
210static Bool
211saa_driver_composite(CARD8		op,
212		     PicturePtr	pSrc,
213		     PicturePtr	pMask,
214		     PicturePtr	pDst,
215		     INT16		xSrc,
216		     INT16		ySrc,
217		     INT16		xMask,
218		     INT16		yMask,
219		     INT16		xDst,
220		     INT16		yDst,
221		     CARD16		width,
222		     CARD16		height,
223		     RegionPtr src_reg,
224		     RegionPtr mask_reg,
225		     RegionPtr dst_reg)
226{
227    struct saa_screen_priv *sscreen = saa_screen(pDst->pDrawable->pScreen);
228    BoxPtr pbox;
229    int nbox;
230    int src_off_x, src_off_y, mask_off_x, mask_off_y, dst_off_x, dst_off_y;
231    PixmapPtr src_pix = NULL, mask_pix = NULL, dst_pix;
232    struct saa_driver *driver = sscreen->driver;
233
234    if (!driver->composite_prepare)
235	return FALSE;
236
237    dst_pix = saa_get_pixmap(pDst->pDrawable, &dst_off_x, &dst_off_y);
238    if (saa_pixmap(dst_pix)->auth_loc != saa_loc_driver)
239	return FALSE;
240
241    if (pMask && pMask->pDrawable) {
242	mask_pix = saa_get_pixmap(pMask->pDrawable, &mask_off_x, &mask_off_y);
243	if (saa_pixmap(mask_pix)->auth_loc != saa_loc_driver)
244	    return FALSE;
245    }
246    if (pSrc->pDrawable) {
247	src_pix = saa_get_pixmap(pSrc->pDrawable, &src_off_x, &src_off_y);
248	if (saa_pixmap(src_pix)->auth_loc != saa_loc_driver)
249	    return FALSE;
250    }
251
252    if (!driver->composite_prepare(driver, op, pSrc, pMask, pDst,
253				   src_pix, mask_pix, dst_pix,
254				   src_reg, mask_reg, dst_reg))
255	return FALSE;
256
257    nbox = REGION_NUM_RECTS(dst_reg);
258    pbox = REGION_RECTS(dst_reg);
259
260    xDst += pDst->pDrawable->x + dst_off_x;
261    yDst += pDst->pDrawable->y + dst_off_y;
262
263    if (src_pix) {
264	xSrc += pSrc->pDrawable->x + src_off_x - xDst;
265	ySrc += pSrc->pDrawable->y + src_off_y - yDst;
266    }
267    if (mask_pix) {
268	xMask += pMask->pDrawable->x + mask_off_x - xDst;
269	yMask += pMask->pDrawable->y + mask_off_y - yDst;
270    }
271
272    while (nbox--) {
273	driver->composite(driver,
274			  pbox->x1 + xSrc,
275			  pbox->y1 + ySrc,
276			  pbox->x1 + xMask,
277			  pbox->y1 + yMask,
278			  pbox->x1,
279			  pbox->y1,
280			  pbox->x2 - pbox->x1,
281			  pbox->y2 - pbox->y1);
282	pbox++;
283    }
284
285    driver->composite_done(driver);
286    saa_pixmap_dirty(dst_pix, TRUE, dst_reg);
287
288    return TRUE;
289}
290
291/*
292 * Try to turn a composite operation into an accelerated copy.
293 * We can do that in some special cases for PictOpSrc and PictOpOver.
294 */
295
296static Bool
297saa_copy_composite(CARD8 op,
298		   PicturePtr pSrc,
299		   PicturePtr pMask,
300		   PicturePtr pDst,
301		   INT16 xSrc,
302		   INT16 ySrc,
303		   INT16 xMask,
304		   INT16 yMask,
305		   INT16 xDst, INT16 yDst, CARD16 width, CARD16 height,
306		   RegionPtr dst_region)
307{
308    if (!pSrc->pDrawable || pSrc->transform ||
309	pSrc->repeat || xSrc < 0 || ySrc < 0 ||
310	xSrc + width > pSrc->pDrawable->width ||
311	ySrc + height > pSrc->pDrawable->height)
312	return FALSE;
313
314    if (op == PictOpSrc ||
315	(op == PictOpOver && PICT_FORMAT_A(pSrc->format) == 0 &&
316	 pMask == NULL)) {
317
318	int xoff, yoff;
319	PixmapPtr dst_pix = saa_get_pixmap(pDst->pDrawable, &xoff, &yoff);
320	struct saa_pixmap *dst_spix = saa_pixmap(dst_pix);
321	struct saa_pixmap *src_spix =
322	    saa_pixmap(saa_get_drawable_pixmap(pSrc->pDrawable));
323	int ret;
324
325	if (src_spix->auth_loc != saa_loc_driver ||
326	    dst_spix->auth_loc != saa_loc_driver)
327	    return FALSE;
328
329	src_spix->src_format = pSrc->format;
330	dst_spix->dst_format = pDst->format;
331
332	xDst += pDst->pDrawable->x;
333	yDst += pDst->pDrawable->y;
334	xSrc += pSrc->pDrawable->x;
335	ySrc += pSrc->pDrawable->y;
336
337	/*
338	 * Dst region is in backing pixmap space. We need to
339	 * translate it.
340	 */
341	REGION_TRANSLATE(pScreen, dst_region, -xoff, -yoff);
342	ret = saa_hw_copy_nton(pSrc->pDrawable, pDst->pDrawable, NULL,
343			       REGION_RECTS(dst_region),
344			       REGION_NUM_RECTS(dst_region),
345			       xSrc - xDst, ySrc - yDst, FALSE, FALSE);
346	REGION_TRANSLATE(pScreen, dst_region, xoff, yoff);
347
348	src_spix->src_format = 0;
349	dst_spix->dst_format = 0;
350
351	if (ret)
352	    return TRUE;
353    }
354    return FALSE;
355}
356
357static void
358saa_composite(CARD8 op,
359	      PicturePtr pSrc,
360	      PicturePtr pMask,
361	      PicturePtr pDst,
362	      INT16 xSrc,
363	      INT16 ySrc,
364	      INT16 xMask,
365	      INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
366{
367    ScreenPtr pScreen = pDst->pDrawable->pScreen;
368    RegionRec dst_region;
369    RegionPtr src_region;
370    RegionPtr mask_region;
371
372    REGION_NULL(pScreen, &dst_region);
373    if (!saa_compute_composite_regions(pScreen, pSrc, pMask, pDst,
374				       xSrc, ySrc, xMask, yMask, xDst,
375				       yDst, width, height,
376				       &dst_region, &src_region, &mask_region))
377	goto out;
378
379    if (saa_copy_composite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask,
380			   xDst, yDst, width, height, &dst_region))
381	goto out;
382
383    if (saa_driver_composite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask,
384			     xDst, yDst, width, height, src_region,
385			     mask_region, &dst_region))
386	goto out;
387
388    saa_check_composite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask,
389			xDst, yDst, width, height,
390			src_region, mask_region, &dst_region);
391  out:
392    if (src_region)
393	REGION_UNINIT(pScreen, src_region);
394    if (mask_region && mask_region != src_region)
395	REGION_UNINIT(pScreen, mask_region);
396    REGION_UNINIT(pScreen, &dst_region);
397}
398
399void
400saa_render_setup(ScreenPtr pScreen)
401{
402    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
403    struct saa_screen_priv *sscreen = saa_screen(pScreen);
404
405    if (ps) {
406	saa_wrap(sscreen, ps, Trapezoids, saa_trapezoids);
407	saa_wrap(sscreen, ps, Triangles, saa_triangles);
408	saa_wrap(sscreen, ps, Composite, saa_composite);
409	saa_wrap(sscreen, ps, Glyphs,    miGlyphs);
410	saa_wrap(sscreen, ps, UnrealizeGlyph, miUnrealizeGlyph);
411    }
412}
413
414void
415saa_render_takedown(ScreenPtr pScreen)
416{
417    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
418    struct saa_screen_priv *sscreen = saa_screen(pScreen);
419
420    if (ps) {
421	saa_unwrap(sscreen, ps, Trapezoids);
422	saa_unwrap(sscreen, ps, Triangles);
423	saa_unwrap(sscreen, ps, Composite);
424	saa_unwrap(sscreen, ps, Glyphs);
425	saa_unwrap(sscreen, ps, UnrealizeGlyph);
426    }
427}
428#endif
429