mipict.c revision 35c4bbdf
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
36int
37miCreatePicture(PicturePtr pPicture)
38{
39    return Success;
40}
41
42void
43miDestroyPicture(PicturePtr pPicture)
44{
45    if (pPicture->freeCompClip)
46        RegionDestroy(pPicture->pCompositeClip);
47}
48
49static void
50miDestroyPictureClip(PicturePtr pPicture)
51{
52    if (pPicture->clientClip)
53        RegionDestroy(pPicture->clientClip);
54    pPicture->clientClip = NULL;
55}
56
57static int
58miChangePictureClip(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
91static void
92miChangePicture(PicturePtr pPicture, Mask mask)
93{
94    return;
95}
96
97static void
98miValidatePicture(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
214static int
215miChangePictureTransform(PicturePtr pPicture, PictTransform * transform)
216{
217    return Success;
218}
219
220static int
221miChangePictureFilter(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
229static inline pixman_bool_t
230miClipPictureReg(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
264static inline Bool
265miClipPictureSrc(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
286static void
287SourceValidateOnePicture(PicturePtr pPicture)
288{
289    DrawablePtr pDrawable = pPicture->pDrawable;
290    ScreenPtr pScreen;
291
292    if (!pDrawable)
293        return;
294
295    pScreen = pDrawable->pScreen;
296
297    if (pScreen->SourceValidate) {
298        pScreen->SourceValidate(pDrawable, 0, 0, pDrawable->width,
299                                pDrawable->height, pPicture->subWindowMode);
300    }
301}
302
303void
304miCompositeSourceValidate(PicturePtr pPicture)
305{
306    SourceValidateOnePicture(pPicture);
307    if (pPicture->alphaMap)
308        SourceValidateOnePicture(pPicture->alphaMap);
309}
310
311/*
312 * returns FALSE if the final region is empty.  Indistinguishable from
313 * an allocation failure, but rendering ignores those anyways.
314 */
315
316Bool
317miComputeCompositeRegion(RegionPtr pRegion,
318                         PicturePtr pSrc,
319                         PicturePtr pMask,
320                         PicturePtr pDst,
321                         INT16 xSrc,
322                         INT16 ySrc,
323                         INT16 xMask,
324                         INT16 yMask,
325                         INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
326{
327
328    int v;
329
330    pRegion->extents.x1 = xDst;
331    v = xDst + width;
332    pRegion->extents.x2 = BOUND(v);
333    pRegion->extents.y1 = yDst;
334    v = yDst + height;
335    pRegion->extents.y2 = BOUND(v);
336    pRegion->data = 0;
337    /* Check for empty operation */
338    if (pRegion->extents.x1 >= pRegion->extents.x2 ||
339        pRegion->extents.y1 >= pRegion->extents.y2) {
340        pixman_region_init(pRegion);
341        return FALSE;
342    }
343    /* clip against dst */
344    if (!miClipPictureReg(pRegion, pDst->pCompositeClip, 0, 0)) {
345        pixman_region_fini(pRegion);
346        return FALSE;
347    }
348    if (pDst->alphaMap) {
349        if (!miClipPictureReg(pRegion, pDst->alphaMap->pCompositeClip,
350                              -pDst->alphaOrigin.x, -pDst->alphaOrigin.y)) {
351            pixman_region_fini(pRegion);
352            return FALSE;
353        }
354    }
355    /* clip against src */
356    if (!miClipPictureSrc(pRegion, pSrc, xDst - xSrc, yDst - ySrc)) {
357        pixman_region_fini(pRegion);
358        return FALSE;
359    }
360    if (pSrc->alphaMap) {
361        if (!miClipPictureSrc(pRegion, pSrc->alphaMap,
362                              xDst - (xSrc - pSrc->alphaOrigin.x),
363                              yDst - (ySrc - pSrc->alphaOrigin.y))) {
364            pixman_region_fini(pRegion);
365            return FALSE;
366        }
367    }
368    /* clip against mask */
369    if (pMask) {
370        if (!miClipPictureSrc(pRegion, pMask, xDst - xMask, yDst - yMask)) {
371            pixman_region_fini(pRegion);
372            return FALSE;
373        }
374        if (pMask->alphaMap) {
375            if (!miClipPictureSrc(pRegion, pMask->alphaMap,
376                                  xDst - (xMask - pMask->alphaOrigin.x),
377                                  yDst - (yMask - pMask->alphaOrigin.y))) {
378                pixman_region_fini(pRegion);
379                return FALSE;
380            }
381        }
382    }
383
384    miCompositeSourceValidate(pSrc);
385    if (pMask)
386        miCompositeSourceValidate(pMask);
387
388    return TRUE;
389}
390
391void
392miRenderColorToPixel(PictFormatPtr format, xRenderColor * color, CARD32 *pixel)
393{
394    CARD32 r, g, b, a;
395    miIndexedPtr pIndexed;
396
397    switch (format->type) {
398    case PictTypeDirect:
399        r = color->red >> (16 - Ones(format->direct.redMask));
400        g = color->green >> (16 - Ones(format->direct.greenMask));
401        b = color->blue >> (16 - Ones(format->direct.blueMask));
402        a = color->alpha >> (16 - Ones(format->direct.alphaMask));
403        r = r << format->direct.red;
404        g = g << format->direct.green;
405        b = b << format->direct.blue;
406        a = a << format->direct.alpha;
407        *pixel = r | g | b | a;
408        break;
409    case PictTypeIndexed:
410        pIndexed = (miIndexedPtr) (format->index.devPrivate);
411        if (pIndexed->color) {
412            r = color->red >> 11;
413            g = color->green >> 11;
414            b = color->blue >> 11;
415            *pixel = miIndexToEnt15(pIndexed, (r << 10) | (g << 5) | b);
416        }
417        else {
418            r = color->red >> 8;
419            g = color->green >> 8;
420            b = color->blue >> 8;
421            *pixel = miIndexToEntY24(pIndexed, (r << 16) | (g << 8) | b);
422        }
423        break;
424    }
425}
426
427static CARD16
428miFillColor(CARD32 pixel, int bits)
429{
430    while (bits < 16) {
431        pixel |= pixel << bits;
432        bits <<= 1;
433    }
434    return (CARD16) pixel;
435}
436
437Bool
438miIsSolidAlpha(PicturePtr pSrc)
439{
440    ScreenPtr pScreen;
441    char line[1];
442
443    if (!pSrc->pDrawable)
444        return FALSE;
445
446    pScreen = pSrc->pDrawable->pScreen;
447
448    /* Alpha-only */
449    if (PICT_FORMAT_TYPE(pSrc->format) != PICT_TYPE_A)
450        return FALSE;
451    /* repeat */
452    if (!pSrc->repeat)
453        return FALSE;
454    /* 1x1 */
455    if (pSrc->pDrawable->width != 1 || pSrc->pDrawable->height != 1)
456        return FALSE;
457    line[0] = 1;
458    (*pScreen->GetImage) (pSrc->pDrawable, 0, 0, 1, 1, ZPixmap, ~0L, line);
459    switch (pSrc->pDrawable->bitsPerPixel) {
460    case 1:
461        return (CARD8) line[0] == 1 || (CARD8) line[0] == 0x80;
462    case 4:
463        return (CARD8) line[0] == 0xf || (CARD8) line[0] == 0xf0;
464    case 8:
465        return (CARD8) line[0] == 0xff;
466    default:
467        return FALSE;
468    }
469}
470
471void
472miRenderPixelToColor(PictFormatPtr format, CARD32 pixel, xRenderColor * color)
473{
474    CARD32 r, g, b, a;
475    miIndexedPtr pIndexed;
476
477    switch (format->type) {
478    case PictTypeDirect:
479        r = (pixel >> format->direct.red) & format->direct.redMask;
480        g = (pixel >> format->direct.green) & format->direct.greenMask;
481        b = (pixel >> format->direct.blue) & format->direct.blueMask;
482        a = (pixel >> format->direct.alpha) & format->direct.alphaMask;
483        color->red = miFillColor(r, Ones(format->direct.redMask));
484        color->green = miFillColor(g, Ones(format->direct.greenMask));
485        color->blue = miFillColor(b, Ones(format->direct.blueMask));
486        color->alpha = miFillColor(a, Ones(format->direct.alphaMask));
487        break;
488    case PictTypeIndexed:
489        pIndexed = (miIndexedPtr) (format->index.devPrivate);
490        pixel = pIndexed->rgba[pixel & (MI_MAX_INDEXED - 1)];
491        r = (pixel >> 16) & 0xff;
492        g = (pixel >> 8) & 0xff;
493        b = (pixel) & 0xff;
494        color->red = miFillColor(r, 8);
495        color->green = miFillColor(g, 8);
496        color->blue = miFillColor(b, 8);
497        color->alpha = 0xffff;
498        break;
499    }
500}
501
502static void
503miTriStrip(CARD8 op,
504           PicturePtr pSrc,
505           PicturePtr pDst,
506           PictFormatPtr maskFormat,
507           INT16 xSrc, INT16 ySrc, int npoints, xPointFixed * points)
508{
509    xTriangle *tris, *tri;
510    int ntri;
511
512    ntri = npoints - 2;
513    tris = xallocarray(ntri, sizeof(xTriangle));
514    if (!tris)
515        return;
516
517    for (tri = tris; npoints >= 3; npoints--, points++, tri++) {
518        tri->p1 = points[0];
519        tri->p2 = points[1];
520        tri->p3 = points[2];
521    }
522    CompositeTriangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntri, tris);
523    free(tris);
524}
525
526static void
527miTriFan(CARD8 op,
528         PicturePtr pSrc,
529         PicturePtr pDst,
530         PictFormatPtr maskFormat,
531         INT16 xSrc, INT16 ySrc, int npoints, xPointFixed * points)
532{
533    xTriangle *tris, *tri;
534    xPointFixed *first;
535    int ntri;
536
537    ntri = npoints - 2;
538    tris = xallocarray(ntri, sizeof(xTriangle));
539    if (!tris)
540        return;
541
542    first = points++;
543    for (tri = tris; npoints >= 3; npoints--, points++, tri++) {
544        tri->p1 = *first;
545        tri->p2 = points[0];
546        tri->p3 = points[1];
547    }
548    CompositeTriangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntri, tris);
549    free(tris);
550}
551
552Bool
553miPictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats)
554{
555    PictureScreenPtr ps;
556
557    if (!PictureInit(pScreen, formats, nformats))
558        return FALSE;
559    ps = GetPictureScreen(pScreen);
560    ps->CreatePicture = miCreatePicture;
561    ps->DestroyPicture = miDestroyPicture;
562    ps->ChangePictureClip = miChangePictureClip;
563    ps->DestroyPictureClip = miDestroyPictureClip;
564    ps->ChangePicture = miChangePicture;
565    ps->ValidatePicture = miValidatePicture;
566    ps->InitIndexed = miInitIndexed;
567    ps->CloseIndexed = miCloseIndexed;
568    ps->UpdateIndexed = miUpdateIndexed;
569    ps->ChangePictureTransform = miChangePictureTransform;
570    ps->ChangePictureFilter = miChangePictureFilter;
571    ps->RealizeGlyph = miRealizeGlyph;
572    ps->UnrealizeGlyph = miUnrealizeGlyph;
573
574    /* MI rendering routines */
575    ps->Composite = 0;          /* requires DDX support */
576    ps->Glyphs = miGlyphs;
577    ps->CompositeRects = miCompositeRects;
578    ps->Trapezoids = 0;
579    ps->Triangles = 0;
580
581    ps->RasterizeTrapezoid = 0; /* requires DDX support */
582    ps->AddTraps = 0;           /* requires DDX support */
583    ps->AddTriangles = 0;       /* requires DDX support */
584
585    ps->TriStrip = miTriStrip;  /* converts call to CompositeTriangles */
586    ps->TriFan = miTriFan;
587
588    return TRUE;
589}
590