pixmap.c revision 32414907
1/*
2
3Copyright 1993, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29#ifdef HAVE_DIX_CONFIG_H
30#include <dix-config.h>
31#endif
32
33#include <X11/X.h>
34#include "scrnintstr.h"
35#include "mi.h"
36#include "misc.h"
37#include "os.h"
38#include "windowstr.h"
39#include "resource.h"
40#include "dixstruct.h"
41#include "gcstruct.h"
42#include "servermd.h"
43#include "site.h"
44#include "X11/extensions/render.h"
45#include "picturestr.h"
46#include "randrstr.h"
47/*
48 *  Scratch pixmap management and device independent pixmap allocation
49 *  function.
50 */
51
52/* callable by ddx */
53PixmapPtr
54GetScratchPixmapHeader(ScreenPtr pScreen, int width, int height, int depth,
55                       int bitsPerPixel, int devKind, void *pPixData)
56{
57    PixmapPtr pPixmap = pScreen->pScratchPixmap;
58
59    if (pPixmap)
60        pScreen->pScratchPixmap = NULL;
61    else
62        /* width and height of 0 means don't allocate any pixmap data */
63        pPixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth, 0);
64
65    if (pPixmap) {
66        if ((*pScreen->ModifyPixmapHeader) (pPixmap, width, height, depth,
67                                            bitsPerPixel, devKind, pPixData))
68            return pPixmap;
69        (*pScreen->DestroyPixmap) (pPixmap);
70    }
71    return NullPixmap;
72}
73
74/* callable by ddx */
75void
76FreeScratchPixmapHeader(PixmapPtr pPixmap)
77{
78    if (pPixmap) {
79        ScreenPtr pScreen = pPixmap->drawable.pScreen;
80
81        pPixmap->devPrivate.ptr = NULL; /* lest ddx chases bad ptr */
82        if (pScreen->pScratchPixmap)
83            (*pScreen->DestroyPixmap) (pPixmap);
84        else
85            pScreen->pScratchPixmap = pPixmap;
86    }
87}
88
89Bool
90CreateScratchPixmapsForScreen(ScreenPtr pScreen)
91{
92    unsigned int pixmap_size;
93
94    pixmap_size = sizeof(PixmapRec) + dixScreenSpecificPrivatesSize(pScreen, PRIVATE_PIXMAP);
95    pScreen->totalPixmapSize =
96        BitmapBytePad(pixmap_size * 8);
97
98    /* let it be created on first use */
99    pScreen->pScratchPixmap = NULL;
100    return TRUE;
101}
102
103void
104FreeScratchPixmapsForScreen(ScreenPtr pScreen)
105{
106    FreeScratchPixmapHeader(pScreen->pScratchPixmap);
107}
108
109/* callable by ddx */
110PixmapPtr
111AllocatePixmap(ScreenPtr pScreen, int pixDataSize)
112{
113    PixmapPtr pPixmap;
114
115    assert(pScreen->totalPixmapSize > 0);
116
117    if (pScreen->totalPixmapSize > ((size_t) - 1) - pixDataSize)
118        return NullPixmap;
119
120    pPixmap = calloc(1, pScreen->totalPixmapSize + pixDataSize);
121    if (!pPixmap)
122        return NullPixmap;
123
124    dixInitScreenPrivates(pScreen, pPixmap, pPixmap + 1, PRIVATE_PIXMAP);
125    return pPixmap;
126}
127
128/* callable by ddx */
129void
130FreePixmap(PixmapPtr pPixmap)
131{
132    dixFiniPrivates(pPixmap, PRIVATE_PIXMAP);
133    free(pPixmap);
134}
135
136void PixmapUnshareSlavePixmap(PixmapPtr slave_pixmap)
137{
138     int ihandle = -1;
139     ScreenPtr pScreen = slave_pixmap->drawable.pScreen;
140     pScreen->SetSharedPixmapBacking(slave_pixmap, ((void *)(long)ihandle));
141}
142
143PixmapPtr PixmapShareToSlave(PixmapPtr pixmap, ScreenPtr slave)
144{
145    PixmapPtr spix;
146    int ret;
147    void *handle;
148    ScreenPtr master = pixmap->drawable.pScreen;
149    int depth = pixmap->drawable.depth;
150
151    ret = master->SharePixmapBacking(pixmap, slave, &handle);
152    if (ret == FALSE)
153        return NULL;
154
155    spix = slave->CreatePixmap(slave, 0, 0, depth,
156                               CREATE_PIXMAP_USAGE_SHARED);
157    slave->ModifyPixmapHeader(spix, pixmap->drawable.width,
158                              pixmap->drawable.height, depth, 0,
159                              pixmap->devKind, NULL);
160
161    /* have the slave pixmap take a reference on the master pixmap
162       later we destroy them both at the same time */
163    pixmap->refcnt++;
164
165    spix->master_pixmap = pixmap;
166
167    ret = slave->SetSharedPixmapBacking(spix, handle);
168    if (ret == FALSE) {
169        slave->DestroyPixmap(spix);
170        return NULL;
171    }
172
173    return spix;
174}
175
176static void
177PixmapDirtyDamageDestroy(DamagePtr damage, void *closure)
178{
179    PixmapDirtyUpdatePtr dirty = closure;
180
181    dirty->damage = NULL;
182}
183
184Bool
185PixmapStartDirtyTracking(DrawablePtr src,
186                         PixmapPtr slave_dst,
187                         int x, int y, int dst_x, int dst_y,
188                         Rotation rotation)
189{
190    ScreenPtr screen = src->pScreen;
191    PixmapDirtyUpdatePtr dirty_update;
192    RegionPtr damageregion;
193    RegionRec dstregion;
194    BoxRec box;
195
196    dirty_update = calloc(1, sizeof(PixmapDirtyUpdateRec));
197    if (!dirty_update)
198        return FALSE;
199
200    dirty_update->src = src;
201    dirty_update->slave_dst = slave_dst;
202    dirty_update->x = x;
203    dirty_update->y = y;
204    dirty_update->dst_x = dst_x;
205    dirty_update->dst_y = dst_y;
206    dirty_update->rotation = rotation;
207    dirty_update->damage = DamageCreate(NULL, PixmapDirtyDamageDestroy,
208                                        DamageReportNone, TRUE, screen,
209                                        dirty_update);
210
211    if (rotation != RR_Rotate_0) {
212        RRTransformCompute(x, y,
213                           slave_dst->drawable.width,
214                           slave_dst->drawable.height,
215                           rotation,
216                           NULL,
217                           &dirty_update->transform,
218                           &dirty_update->f_transform,
219                           &dirty_update->f_inverse);
220    }
221    if (!dirty_update->damage) {
222        free(dirty_update);
223        return FALSE;
224    }
225
226    /* Damage destination rectangle so that the destination pixmap contents
227     * will get fully initialized
228     */
229    box.x1 = dirty_update->x;
230    box.y1 = dirty_update->y;
231    if (dirty_update->rotation == RR_Rotate_90 ||
232        dirty_update->rotation == RR_Rotate_270) {
233        box.x2 = dirty_update->x + slave_dst->drawable.height;
234        box.y2 = dirty_update->y + slave_dst->drawable.width;
235    } else {
236        box.x2 = dirty_update->x + slave_dst->drawable.width;
237        box.y2 = dirty_update->y + slave_dst->drawable.height;
238    }
239    RegionInit(&dstregion, &box, 1);
240    damageregion = DamageRegion(dirty_update->damage);
241    RegionUnion(damageregion, damageregion, &dstregion);
242    RegionUninit(&dstregion);
243
244    DamageRegister(src, dirty_update->damage);
245    xorg_list_add(&dirty_update->ent, &screen->pixmap_dirty_list);
246    return TRUE;
247}
248
249Bool
250PixmapStopDirtyTracking(DrawablePtr src, PixmapPtr slave_dst)
251{
252    ScreenPtr screen = src->pScreen;
253    PixmapDirtyUpdatePtr ent, safe;
254
255    xorg_list_for_each_entry_safe(ent, safe, &screen->pixmap_dirty_list, ent) {
256        if (ent->src == src && ent->slave_dst == slave_dst) {
257            if (ent->damage)
258                DamageDestroy(ent->damage);
259            xorg_list_del(&ent->ent);
260            free(ent);
261        }
262    }
263    return TRUE;
264}
265
266static void
267PixmapDirtyCopyArea(PixmapPtr dst,
268                    PixmapDirtyUpdatePtr dirty,
269                    RegionPtr dirty_region)
270{
271    DrawablePtr src = dirty->src;
272    ScreenPtr pScreen = src->pScreen;
273    int n;
274    BoxPtr b;
275    GCPtr pGC;
276
277    n = RegionNumRects(dirty_region);
278    b = RegionRects(dirty_region);
279
280    pGC = GetScratchGC(src->depth, pScreen);
281    if (pScreen->root) {
282        ChangeGCVal subWindowMode;
283
284        subWindowMode.val = IncludeInferiors;
285        ChangeGC(NullClient, pGC, GCSubwindowMode, &subWindowMode);
286    }
287    ValidateGC(&dst->drawable, pGC);
288
289    while (n--) {
290        BoxRec dst_box;
291        int w, h;
292
293        dst_box = *b;
294        w = dst_box.x2 - dst_box.x1;
295        h = dst_box.y2 - dst_box.y1;
296
297        pGC->ops->CopyArea(src, &dst->drawable, pGC,
298                           dirty->x + dst_box.x1, dirty->y + dst_box.y1, w, h,
299                           dirty->dst_x + dst_box.x1,
300                           dirty->dst_y + dst_box.y1);
301        b++;
302    }
303    FreeScratchGC(pGC);
304}
305
306static void
307PixmapDirtyCompositeRotate(PixmapPtr dst_pixmap,
308                           PixmapDirtyUpdatePtr dirty,
309                           RegionPtr dirty_region)
310{
311    ScreenPtr pScreen = dirty->src->pScreen;
312    PictFormatPtr format = PictureWindowFormat(pScreen->root);
313    PicturePtr src, dst;
314    XID include_inferiors = IncludeInferiors;
315    int n = RegionNumRects(dirty_region);
316    BoxPtr b = RegionRects(dirty_region);
317    int error;
318
319    src = CreatePicture(None,
320                        dirty->src,
321                        format,
322                        CPSubwindowMode,
323                        &include_inferiors, serverClient, &error);
324    if (!src)
325        return;
326
327    dst = CreatePicture(None,
328                        &dst_pixmap->drawable,
329                        format, 0L, NULL, serverClient, &error);
330    if (!dst)
331        return;
332
333    error = SetPictureTransform(src, &dirty->transform);
334    if (error)
335        return;
336    while (n--) {
337        BoxRec dst_box;
338
339        dst_box = *b;
340        dst_box.x1 += dirty->x;
341        dst_box.x2 += dirty->x;
342        dst_box.y1 += dirty->y;
343        dst_box.y2 += dirty->y;
344        pixman_f_transform_bounds(&dirty->f_inverse, &dst_box);
345
346        CompositePicture(PictOpSrc,
347                         src, NULL, dst,
348                         dst_box.x1,
349                         dst_box.y1,
350                         0, 0,
351                         dst_box.x1,
352                         dst_box.y1,
353                         dst_box.x2 - dst_box.x1,
354                         dst_box.y2 - dst_box.y1);
355        b++;
356    }
357
358    FreePicture(src, None);
359    FreePicture(dst, None);
360}
361
362/*
363 * this function can possibly be improved and optimised, by clipping
364 * instead of iterating
365 * Drivers are free to implement their own version of this.
366 */
367Bool PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty)
368{
369    ScreenPtr pScreen = dirty->src->pScreen;
370    RegionPtr region = DamageRegion(dirty->damage);
371    PixmapPtr dst;
372    SourceValidateProcPtr SourceValidate;
373    RegionRec pixregion;
374    BoxRec box;
375
376    dst = dirty->slave_dst->master_pixmap;
377    if (!dst)
378        dst = dirty->slave_dst;
379
380    box.x1 = 0;
381    box.y1 = 0;
382    if (dirty->rotation == RR_Rotate_90 ||
383        dirty->rotation == RR_Rotate_270) {
384        box.x2 = dst->drawable.height;
385        box.y2 = dst->drawable.width;
386    } else {
387        box.x2 = dst->drawable.width;
388        box.y2 = dst->drawable.height;
389    }
390    RegionInit(&pixregion, &box, 1);
391
392    /*
393     * SourceValidate is used by the software cursor code
394     * to pull the cursor off of the screen when reading
395     * bits from the frame buffer. Bypassing this function
396     * leaves the software cursor in place
397     */
398    SourceValidate = pScreen->SourceValidate;
399    pScreen->SourceValidate = miSourceValidate;
400
401    RegionTranslate(&pixregion, dirty->x, dirty->y);
402    RegionIntersect(&pixregion, &pixregion, region);
403
404    if (RegionNil(&pixregion)) {
405        RegionUninit(&pixregion);
406        return FALSE;
407    }
408
409    RegionTranslate(&pixregion, -dirty->x, -dirty->y);
410
411    if (!pScreen->root || dirty->rotation == RR_Rotate_0)
412        PixmapDirtyCopyArea(dst, dirty, &pixregion);
413    else
414        PixmapDirtyCompositeRotate(dst, dirty, &pixregion);
415    pScreen->SourceValidate = SourceValidate;
416    return TRUE;
417}
418