1 /*
2  *
3  * Copyright © 1999 Keith Packard
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation, and that the name of Keith Packard not be used in
10  * advertising or publicity pertaining to distribution of the software without
11  * specific, written prior permission.  Keith Packard makes no
12  * representations about the suitability of this software for any purpose.  It
13  * is provided "as is" without express or implied warranty.
14  *
15  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21  * PERFORMANCE OF THIS SOFTWARE.
22  */
23 
24 #ifdef HAVE_DIX_CONFIG_H
25 #include <dix-config.h>
26 #endif
27 
28 #include "scrnintstr.h"
29 #include "gcstruct.h"
30 #include "pixmapstr.h"
31 #include "windowstr.h"
32 #include "mi.h"
33 #include "picturestr.h"
34 #include "mipict.h"
35 
36 int
miCreatePicture(PicturePtr pPicture)37 miCreatePicture(PicturePtr pPicture)
38 {
39     return Success;
40 }
41 
42 void
miDestroyPicture(PicturePtr pPicture)43 miDestroyPicture(PicturePtr pPicture)
44 {
45     if (pPicture->freeCompClip)
46         RegionDestroy(pPicture->pCompositeClip);
47 }
48 
49 static void
miDestroyPictureClip(PicturePtr pPicture)50 miDestroyPictureClip(PicturePtr pPicture)
51 {
52     if (pPicture->clientClip)
53         RegionDestroy(pPicture->clientClip);
54     pPicture->clientClip = NULL;
55 }
56 
57 static int
miChangePictureClip(PicturePtr pPicture,int type,void * value,int n)58 miChangePictureClip(PicturePtr pPicture, int type, void *value, int n)
59 {
60     ScreenPtr pScreen = pPicture->pDrawable->pScreen;
61     PictureScreenPtr ps = GetPictureScreen(pScreen);
62     RegionPtr clientClip;
63 
64     switch (type) {
65     case CT_PIXMAP:
66         /* convert the pixmap to a region */
67         clientClip = BitmapToRegion(pScreen, (PixmapPtr) value);
68         if (!clientClip)
69             return BadAlloc;
70         (*pScreen->DestroyPixmap) ((PixmapPtr) value);
71         break;
72     case CT_REGION:
73         clientClip = value;
74         break;
75     case CT_NONE:
76         clientClip = 0;
77         break;
78     default:
79         clientClip = RegionFromRects(n, (xRectangle *) value, type);
80         if (!clientClip)
81             return BadAlloc;
82         free(value);
83         break;
84     }
85     (*ps->DestroyPictureClip) (pPicture);
86     pPicture->clientClip = clientClip;
87     pPicture->stateChanges |= CPClipMask;
88     return Success;
89 }
90 
91 static void
miChangePicture(PicturePtr pPicture,Mask mask)92 miChangePicture(PicturePtr pPicture, Mask mask)
93 {
94     return;
95 }
96 
97 static void
miValidatePicture(PicturePtr pPicture,Mask mask)98 miValidatePicture(PicturePtr pPicture, Mask mask)
99 {
100     DrawablePtr pDrawable = pPicture->pDrawable;
101 
102     if ((mask & (CPClipXOrigin | CPClipYOrigin | CPClipMask | CPSubwindowMode))
103         || (pDrawable->serialNumber !=
104             (pPicture->serialNumber & DRAWABLE_SERIAL_BITS))) {
105         if (pDrawable->type == DRAWABLE_WINDOW) {
106             WindowPtr pWin = (WindowPtr) pDrawable;
107             RegionPtr pregWin;
108             Bool freeTmpClip, freeCompClip;
109 
110             if (pPicture->subWindowMode == IncludeInferiors) {
111                 pregWin = NotClippedByChildren(pWin);
112                 freeTmpClip = TRUE;
113             }
114             else {
115                 pregWin = &pWin->clipList;
116                 freeTmpClip = FALSE;
117             }
118             freeCompClip = pPicture->freeCompClip;
119 
120             /*
121              * if there is no client clip, we can get by with just keeping the
122              * pointer we got, and remembering whether or not should destroy
123              * (or maybe re-use) it later.  this way, we avoid unnecessary
124              * copying of regions.  (this wins especially if many clients clip
125              * by children and have no client clip.)
126              */
127             if (!pPicture->clientClip) {
128                 if (freeCompClip)
129                     RegionDestroy(pPicture->pCompositeClip);
130                 pPicture->pCompositeClip = pregWin;
131                 pPicture->freeCompClip = freeTmpClip;
132             }
133             else {
134                 /*
135                  * we need one 'real' region to put into the composite clip. if
136                  * pregWin the current composite clip are real, we can get rid of
137                  * one. if pregWin is real and the current composite clip isn't,
138                  * use pregWin for the composite clip. if the current composite
139                  * clip is real and pregWin isn't, use the current composite
140                  * clip. if neither is real, create a new region.
141                  */
142 
143                 RegionTranslate(pPicture->clientClip,
144                                 pDrawable->x + pPicture->clipOrigin.x,
145                                 pDrawable->y + pPicture->clipOrigin.y);
146 
147                 if (freeCompClip) {
148                     RegionIntersect(pPicture->pCompositeClip,
149                                     pregWin, pPicture->clientClip);
150                     if (freeTmpClip)
151                         RegionDestroy(pregWin);
152                 }
153                 else if (freeTmpClip) {
154                     RegionIntersect(pregWin, pregWin, pPicture->clientClip);
155                     pPicture->pCompositeClip = pregWin;
156                 }
157                 else {
158                     pPicture->pCompositeClip = RegionCreate(NullBox, 0);
159                     RegionIntersect(pPicture->pCompositeClip,
160                                     pregWin, pPicture->clientClip);
161                 }
162                 pPicture->freeCompClip = TRUE;
163                 RegionTranslate(pPicture->clientClip,
164                                 -(pDrawable->x + pPicture->clipOrigin.x),
165                                 -(pDrawable->y + pPicture->clipOrigin.y));
166             }
167         }                       /* end of composite clip for a window */
168         else {
169             BoxRec pixbounds;
170 
171             /* XXX should we translate by drawable.x/y here ? */
172             /* If you want pixmaps in offscreen memory, yes */
173             pixbounds.x1 = pDrawable->x;
174             pixbounds.y1 = pDrawable->y;
175             pixbounds.x2 = pDrawable->x + pDrawable->width;
176             pixbounds.y2 = pDrawable->y + pDrawable->height;
177 
178             if (pPicture->freeCompClip) {
179                 RegionReset(pPicture->pCompositeClip, &pixbounds);
180             }
181             else {
182                 pPicture->freeCompClip = TRUE;
183                 pPicture->pCompositeClip = RegionCreate(&pixbounds, 1);
184             }
185 
186             if (pPicture->clientClip) {
187                 if (pDrawable->x || pDrawable->y) {
188                     RegionTranslate(pPicture->clientClip,
189                                     pDrawable->x + pPicture->clipOrigin.x,
190                                     pDrawable->y + pPicture->clipOrigin.y);
191                     RegionIntersect(pPicture->pCompositeClip,
192                                     pPicture->pCompositeClip,
193                                     pPicture->clientClip);
194                     RegionTranslate(pPicture->clientClip,
195                                     -(pDrawable->x + pPicture->clipOrigin.x),
196                                     -(pDrawable->y + pPicture->clipOrigin.y));
197                 }
198                 else {
199                     RegionTranslate(pPicture->pCompositeClip,
200                                     -pPicture->clipOrigin.x,
201                                     -pPicture->clipOrigin.y);
202                     RegionIntersect(pPicture->pCompositeClip,
203                                     pPicture->pCompositeClip,
204                                     pPicture->clientClip);
205                     RegionTranslate(pPicture->pCompositeClip,
206                                     pPicture->clipOrigin.x,
207                                     pPicture->clipOrigin.y);
208                 }
209             }
210         }                       /* end of composite clip for pixmap */
211     }
212 }
213 
214 static int
miChangePictureTransform(PicturePtr pPicture,PictTransform * transform)215 miChangePictureTransform(PicturePtr pPicture, PictTransform * transform)
216 {
217     return Success;
218 }
219 
220 static int
miChangePictureFilter(PicturePtr pPicture,int filter,xFixed * params,int nparams)221 miChangePictureFilter(PicturePtr pPicture,
222                       int filter, xFixed * params, int nparams)
223 {
224     return Success;
225 }
226 
227 #define BOUND(v)	(INT16) ((v) < MINSHORT ? MINSHORT : (v) > MAXSHORT ? MAXSHORT : (v))
228 
229 static inline pixman_bool_t
miClipPictureReg(pixman_region16_t * pRegion,pixman_region16_t * pClip,int dx,int dy)230 miClipPictureReg(pixman_region16_t * pRegion,
231                  pixman_region16_t * pClip, int dx, int dy)
232 {
233     if (pixman_region_n_rects(pRegion) == 1 &&
234         pixman_region_n_rects(pClip) == 1) {
235         pixman_box16_t *pRbox = pixman_region_rectangles(pRegion, NULL);
236         pixman_box16_t *pCbox = pixman_region_rectangles(pClip, NULL);
237         int v;
238 
239         if (pRbox->x1 < (v = pCbox->x1 + dx))
240             pRbox->x1 = BOUND(v);
241         if (pRbox->x2 > (v = pCbox->x2 + dx))
242             pRbox->x2 = BOUND(v);
243         if (pRbox->y1 < (v = pCbox->y1 + dy))
244             pRbox->y1 = BOUND(v);
245         if (pRbox->y2 > (v = pCbox->y2 + dy))
246             pRbox->y2 = BOUND(v);
247         if (pRbox->x1 >= pRbox->x2 || pRbox->y1 >= pRbox->y2) {
248             pixman_region_init(pRegion);
249         }
250     }
251     else if (!pixman_region_not_empty(pClip))
252         return FALSE;
253     else {
254         if (dx || dy)
255             pixman_region_translate(pRegion, -dx, -dy);
256         if (!pixman_region_intersect(pRegion, pRegion, pClip))
257             return FALSE;
258         if (dx || dy)
259             pixman_region_translate(pRegion, dx, dy);
260     }
261     return pixman_region_not_empty(pRegion);
262 }
263 
264 static inline Bool
miClipPictureSrc(RegionPtr pRegion,PicturePtr pPicture,int dx,int dy)265 miClipPictureSrc(RegionPtr pRegion, PicturePtr pPicture, int dx, int dy)
266 {
267     if (pPicture->clientClip) {
268         Bool result;
269 
270         pixman_region_translate(pPicture->clientClip,
271                                 pPicture->clipOrigin.x + dx,
272                                 pPicture->clipOrigin.y + dy);
273 
274         result = RegionIntersect(pRegion, pRegion, pPicture->clientClip);
275 
276         pixman_region_translate(pPicture->clientClip,
277                                 -(pPicture->clipOrigin.x + dx),
278                                 -(pPicture->clipOrigin.y + dy));
279 
280         if (!result)
281             return FALSE;
282     }
283     return TRUE;
284 }
285 
286 static void
SourceValidateOnePicture(PicturePtr pPicture)287 SourceValidateOnePicture(PicturePtr pPicture)
288 {
289     DrawablePtr pDrawable = pPicture->pDrawable;
290     ScreenPtr pScreen;
291 
292     if (!pDrawable)
293         return;
294 
295     pScreen = pDrawable->pScreen;
296 
297     pScreen->SourceValidate(pDrawable, 0, 0, pDrawable->width,
298                             pDrawable->height, pPicture->subWindowMode);
299 }
300 
301 void
miCompositeSourceValidate(PicturePtr pPicture)302 miCompositeSourceValidate(PicturePtr pPicture)
303 {
304     SourceValidateOnePicture(pPicture);
305     if (pPicture->alphaMap)
306         SourceValidateOnePicture(pPicture->alphaMap);
307 }
308 
309 /*
310  * returns FALSE if the final region is empty.  Indistinguishable from
311  * an allocation failure, but rendering ignores those anyways.
312  */
313 
314 Bool
miComputeCompositeRegion(RegionPtr pRegion,PicturePtr pSrc,PicturePtr pMask,PicturePtr pDst,INT16 xSrc,INT16 ySrc,INT16 xMask,INT16 yMask,INT16 xDst,INT16 yDst,CARD16 width,CARD16 height)315 miComputeCompositeRegion(RegionPtr pRegion,
316                          PicturePtr pSrc,
317                          PicturePtr pMask,
318                          PicturePtr pDst,
319                          INT16 xSrc,
320                          INT16 ySrc,
321                          INT16 xMask,
322                          INT16 yMask,
323                          INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
324 {
325 
326     int v;
327 
328     pRegion->extents.x1 = xDst;
329     v = xDst + width;
330     pRegion->extents.x2 = BOUND(v);
331     pRegion->extents.y1 = yDst;
332     v = yDst + height;
333     pRegion->extents.y2 = BOUND(v);
334     pRegion->data = 0;
335     /* Check for empty operation */
336     if (pRegion->extents.x1 >= pRegion->extents.x2 ||
337         pRegion->extents.y1 >= pRegion->extents.y2) {
338         pixman_region_init(pRegion);
339         return FALSE;
340     }
341     /* clip against dst */
342     if (!miClipPictureReg(pRegion, pDst->pCompositeClip, 0, 0)) {
343         pixman_region_fini(pRegion);
344         return FALSE;
345     }
346     if (pDst->alphaMap) {
347         if (!miClipPictureReg(pRegion, pDst->alphaMap->pCompositeClip,
348                               -pDst->alphaOrigin.x, -pDst->alphaOrigin.y)) {
349             pixman_region_fini(pRegion);
350             return FALSE;
351         }
352     }
353     /* clip against src */
354     if (!miClipPictureSrc(pRegion, pSrc, xDst - xSrc, yDst - ySrc)) {
355         pixman_region_fini(pRegion);
356         return FALSE;
357     }
358     if (pSrc->alphaMap) {
359         if (!miClipPictureSrc(pRegion, pSrc->alphaMap,
360                               xDst - (xSrc - pSrc->alphaOrigin.x),
361                               yDst - (ySrc - pSrc->alphaOrigin.y))) {
362             pixman_region_fini(pRegion);
363             return FALSE;
364         }
365     }
366     /* clip against mask */
367     if (pMask) {
368         if (!miClipPictureSrc(pRegion, pMask, xDst - xMask, yDst - yMask)) {
369             pixman_region_fini(pRegion);
370             return FALSE;
371         }
372         if (pMask->alphaMap) {
373             if (!miClipPictureSrc(pRegion, pMask->alphaMap,
374                                   xDst - (xMask - pMask->alphaOrigin.x),
375                                   yDst - (yMask - pMask->alphaOrigin.y))) {
376                 pixman_region_fini(pRegion);
377                 return FALSE;
378             }
379         }
380     }
381 
382     miCompositeSourceValidate(pSrc);
383     if (pMask)
384         miCompositeSourceValidate(pMask);
385 
386     return TRUE;
387 }
388 
389 void
miRenderColorToPixel(PictFormatPtr format,xRenderColor * color,CARD32 * pixel)390 miRenderColorToPixel(PictFormatPtr format, xRenderColor * color, CARD32 *pixel)
391 {
392     CARD32 r, g, b, a;
393     miIndexedPtr pIndexed;
394 
395     switch (format->type) {
396     case PictTypeDirect:
397         r = color->red >> (16 - Ones(format->direct.redMask));
398         g = color->green >> (16 - Ones(format->direct.greenMask));
399         b = color->blue >> (16 - Ones(format->direct.blueMask));
400         a = color->alpha >> (16 - Ones(format->direct.alphaMask));
401         r = r << format->direct.red;
402         g = g << format->direct.green;
403         b = b << format->direct.blue;
404         a = a << format->direct.alpha;
405         *pixel = r | g | b | a;
406         break;
407     case PictTypeIndexed:
408         pIndexed = (miIndexedPtr) (format->index.devPrivate);
409         if (pIndexed->color) {
410             r = color->red >> 11;
411             g = color->green >> 11;
412             b = color->blue >> 11;
413             *pixel = miIndexToEnt15(pIndexed, (r << 10) | (g << 5) | b);
414         }
415         else {
416             r = color->red >> 8;
417             g = color->green >> 8;
418             b = color->blue >> 8;
419             *pixel = miIndexToEntY24(pIndexed, (r << 16) | (g << 8) | b);
420         }
421         break;
422     }
423 }
424 
425 static CARD16
miFillColor(CARD32 pixel,int bits)426 miFillColor(CARD32 pixel, int bits)
427 {
428     while (bits < 16) {
429         pixel |= pixel << bits;
430         bits <<= 1;
431     }
432     return (CARD16) pixel;
433 }
434 
435 Bool
miIsSolidAlpha(PicturePtr pSrc)436 miIsSolidAlpha(PicturePtr pSrc)
437 {
438     ScreenPtr pScreen;
439     char line[1];
440 
441     if (!pSrc->pDrawable)
442         return FALSE;
443 
444     pScreen = pSrc->pDrawable->pScreen;
445 
446     /* Alpha-only */
447     if (PICT_FORMAT_TYPE(pSrc->format) != PICT_TYPE_A)
448         return FALSE;
449     /* repeat */
450     if (!pSrc->repeat)
451         return FALSE;
452     /* 1x1 */
453     if (pSrc->pDrawable->width != 1 || pSrc->pDrawable->height != 1)
454         return FALSE;
455     line[0] = 1;
456     (*pScreen->GetImage) (pSrc->pDrawable, 0, 0, 1, 1, ZPixmap, ~0L, line);
457     switch (pSrc->pDrawable->bitsPerPixel) {
458     case 1:
459         return (CARD8) line[0] == 1 || (CARD8) line[0] == 0x80;
460     case 4:
461         return (CARD8) line[0] == 0xf || (CARD8) line[0] == 0xf0;
462     case 8:
463         return (CARD8) line[0] == 0xff;
464     default:
465         return FALSE;
466     }
467 }
468 
469 void
miRenderPixelToColor(PictFormatPtr format,CARD32 pixel,xRenderColor * color)470 miRenderPixelToColor(PictFormatPtr format, CARD32 pixel, xRenderColor * color)
471 {
472     CARD32 r, g, b, a;
473     miIndexedPtr pIndexed;
474 
475     switch (format->type) {
476     case PictTypeDirect:
477         r = (pixel >> format->direct.red) & format->direct.redMask;
478         g = (pixel >> format->direct.green) & format->direct.greenMask;
479         b = (pixel >> format->direct.blue) & format->direct.blueMask;
480         a = (pixel >> format->direct.alpha) & format->direct.alphaMask;
481         color->red = miFillColor(r, Ones(format->direct.redMask));
482         color->green = miFillColor(g, Ones(format->direct.greenMask));
483         color->blue = miFillColor(b, Ones(format->direct.blueMask));
484         color->alpha = miFillColor(a, Ones(format->direct.alphaMask));
485         break;
486     case PictTypeIndexed:
487         pIndexed = (miIndexedPtr) (format->index.devPrivate);
488         pixel = pIndexed->rgba[pixel & (MI_MAX_INDEXED - 1)];
489         r = (pixel >> 16) & 0xff;
490         g = (pixel >> 8) & 0xff;
491         b = (pixel) & 0xff;
492         color->red = miFillColor(r, 8);
493         color->green = miFillColor(g, 8);
494         color->blue = miFillColor(b, 8);
495         color->alpha = 0xffff;
496         break;
497     }
498 }
499 
500 static void
miTriStrip(CARD8 op,PicturePtr pSrc,PicturePtr pDst,PictFormatPtr maskFormat,INT16 xSrc,INT16 ySrc,int npoints,xPointFixed * points)501 miTriStrip(CARD8 op,
502            PicturePtr pSrc,
503            PicturePtr pDst,
504            PictFormatPtr maskFormat,
505            INT16 xSrc, INT16 ySrc, int npoints, xPointFixed * points)
506 {
507     xTriangle *tris, *tri;
508     int ntri;
509 
510     ntri = npoints - 2;
511     tris = xallocarray(ntri, sizeof(xTriangle));
512     if (!tris)
513         return;
514 
515     for (tri = tris; npoints >= 3; npoints--, points++, tri++) {
516         tri->p1 = points[0];
517         tri->p2 = points[1];
518         tri->p3 = points[2];
519     }
520     CompositeTriangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntri, tris);
521     free(tris);
522 }
523 
524 static void
miTriFan(CARD8 op,PicturePtr pSrc,PicturePtr pDst,PictFormatPtr maskFormat,INT16 xSrc,INT16 ySrc,int npoints,xPointFixed * points)525 miTriFan(CARD8 op,
526          PicturePtr pSrc,
527          PicturePtr pDst,
528          PictFormatPtr maskFormat,
529          INT16 xSrc, INT16 ySrc, int npoints, xPointFixed * points)
530 {
531     xTriangle *tris, *tri;
532     xPointFixed *first;
533     int ntri;
534 
535     ntri = npoints - 2;
536     tris = xallocarray(ntri, sizeof(xTriangle));
537     if (!tris)
538         return;
539 
540     first = points++;
541     for (tri = tris; npoints >= 3; npoints--, points++, tri++) {
542         tri->p1 = *first;
543         tri->p2 = points[0];
544         tri->p3 = points[1];
545     }
546     CompositeTriangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntri, tris);
547     free(tris);
548 }
549 
550 Bool
miPictureInit(ScreenPtr pScreen,PictFormatPtr formats,int nformats)551 miPictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats)
552 {
553     PictureScreenPtr ps;
554 
555     if (!PictureInit(pScreen, formats, nformats))
556         return FALSE;
557     ps = GetPictureScreen(pScreen);
558     ps->CreatePicture = miCreatePicture;
559     ps->DestroyPicture = miDestroyPicture;
560     ps->ChangePictureClip = miChangePictureClip;
561     ps->DestroyPictureClip = miDestroyPictureClip;
562     ps->ChangePicture = miChangePicture;
563     ps->ValidatePicture = miValidatePicture;
564     ps->InitIndexed = miInitIndexed;
565     ps->CloseIndexed = miCloseIndexed;
566     ps->UpdateIndexed = miUpdateIndexed;
567     ps->ChangePictureTransform = miChangePictureTransform;
568     ps->ChangePictureFilter = miChangePictureFilter;
569     ps->RealizeGlyph = miRealizeGlyph;
570     ps->UnrealizeGlyph = miUnrealizeGlyph;
571 
572     /* MI rendering routines */
573     ps->Composite = 0;          /* requires DDX support */
574     ps->Glyphs = miGlyphs;
575     ps->CompositeRects = miCompositeRects;
576     ps->Trapezoids = 0;
577     ps->Triangles = 0;
578 
579     ps->RasterizeTrapezoid = 0; /* requires DDX support */
580     ps->AddTraps = 0;           /* requires DDX support */
581     ps->AddTriangles = 0;       /* requires DDX support */
582 
583     ps->TriStrip = miTriStrip;  /* converts call to CompositeTriangles */
584     ps->TriFan = miTriFan;
585 
586     return TRUE;
587 }
588