mipict.c revision ed6184df
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    pScreen->SourceValidate(pDrawable, 0, 0, pDrawable->width,
298                            pDrawable->height, pPicture->subWindowMode);
299}
300
301void
302miCompositeSourceValidate(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
314Bool
315miComputeCompositeRegion(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
389void
390miRenderColorToPixel(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
425static CARD16
426miFillColor(CARD32 pixel, int bits)
427{
428    while (bits < 16) {
429        pixel |= pixel << bits;
430        bits <<= 1;
431    }
432    return (CARD16) pixel;
433}
434
435Bool
436miIsSolidAlpha(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
469void
470miRenderPixelToColor(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
500static void
501miTriStrip(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
524static void
525miTriFan(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
550Bool
551miPictureInit(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