pixmap.c revision 35c4bbdf
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 "misc.h"
36#include "os.h"
37#include "windowstr.h"
38#include "resource.h"
39#include "dixstruct.h"
40#include "gcstruct.h"
41#include "servermd.h"
42#include "site.h"
43#include "X11/extensions/render.h"
44#include "picturestr.h"
45#include "randrstr.h"
46/*
47 *  Scratch pixmap management and device independent pixmap allocation
48 *  function.
49 */
50
51/* callable by ddx */
52PixmapPtr
53GetScratchPixmapHeader(ScreenPtr pScreen, int width, int height, int depth,
54                       int bitsPerPixel, int devKind, void *pPixData)
55{
56    PixmapPtr pPixmap = pScreen->pScratchPixmap;
57
58    if (pPixmap)
59        pScreen->pScratchPixmap = NULL;
60    else
61        /* width and height of 0 means don't allocate any pixmap data */
62        pPixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth, 0);
63
64    if (pPixmap) {
65        if ((*pScreen->ModifyPixmapHeader) (pPixmap, width, height, depth,
66                                            bitsPerPixel, devKind, pPixData))
67            return pPixmap;
68        (*pScreen->DestroyPixmap) (pPixmap);
69    }
70    return NullPixmap;
71}
72
73/* callable by ddx */
74void
75FreeScratchPixmapHeader(PixmapPtr pPixmap)
76{
77    if (pPixmap) {
78        ScreenPtr pScreen = pPixmap->drawable.pScreen;
79
80        pPixmap->devPrivate.ptr = NULL; /* lest ddx chases bad ptr */
81        if (pScreen->pScratchPixmap)
82            (*pScreen->DestroyPixmap) (pPixmap);
83        else
84            pScreen->pScratchPixmap = pPixmap;
85    }
86}
87
88Bool
89CreateScratchPixmapsForScreen(ScreenPtr pScreen)
90{
91    unsigned int pixmap_size;
92
93    pixmap_size = sizeof(PixmapRec) + dixScreenSpecificPrivatesSize(pScreen, PRIVATE_PIXMAP);
94    pScreen->totalPixmapSize =
95        BitmapBytePad(pixmap_size * 8);
96
97    /* let it be created on first use */
98    pScreen->pScratchPixmap = NULL;
99    return TRUE;
100}
101
102void
103FreeScratchPixmapsForScreen(ScreenPtr pScreen)
104{
105    FreeScratchPixmapHeader(pScreen->pScratchPixmap);
106}
107
108/* callable by ddx */
109PixmapPtr
110AllocatePixmap(ScreenPtr pScreen, int pixDataSize)
111{
112    PixmapPtr pPixmap;
113
114    assert(pScreen->totalPixmapSize > 0);
115
116    if (pScreen->totalPixmapSize > ((size_t) - 1) - pixDataSize)
117        return NullPixmap;
118
119    pPixmap = malloc(pScreen->totalPixmapSize + pixDataSize);
120    if (!pPixmap)
121        return NullPixmap;
122
123    dixInitScreenPrivates(pScreen, pPixmap, pPixmap + 1, PRIVATE_PIXMAP);
124    return pPixmap;
125}
126
127/* callable by ddx */
128void
129FreePixmap(PixmapPtr pPixmap)
130{
131    dixFiniPrivates(pPixmap, PRIVATE_PIXMAP);
132    free(pPixmap);
133}
134
135PixmapPtr PixmapShareToSlave(PixmapPtr pixmap, ScreenPtr slave)
136{
137    PixmapPtr spix;
138    int ret;
139    void *handle;
140    ScreenPtr master = pixmap->drawable.pScreen;
141    int depth = pixmap->drawable.depth;
142
143    ret = master->SharePixmapBacking(pixmap, slave, &handle);
144    if (ret == FALSE)
145        return NULL;
146
147    spix = slave->CreatePixmap(slave, 0, 0, depth,
148                               CREATE_PIXMAP_USAGE_SHARED);
149    slave->ModifyPixmapHeader(spix, pixmap->drawable.width,
150                              pixmap->drawable.height, depth, 0,
151                              pixmap->devKind, NULL);
152
153    /* have the slave pixmap take a reference on the master pixmap
154       later we destroy them both at the same time */
155    pixmap->refcnt++;
156
157    spix->master_pixmap = pixmap;
158
159    ret = slave->SetSharedPixmapBacking(spix, handle);
160    if (ret == FALSE) {
161        slave->DestroyPixmap(spix);
162        return NULL;
163    }
164
165    return spix;
166}
167
168Bool
169PixmapStartDirtyTracking(PixmapPtr src,
170                         PixmapPtr slave_dst,
171                         int x, int y, int dst_x, int dst_y,
172                         Rotation rotation)
173{
174    ScreenPtr screen = src->drawable.pScreen;
175    PixmapDirtyUpdatePtr dirty_update;
176    RegionPtr damageregion;
177    RegionRec dstregion;
178    BoxRec box;
179
180    dirty_update = calloc(1, sizeof(PixmapDirtyUpdateRec));
181    if (!dirty_update)
182        return FALSE;
183
184    dirty_update->src = src;
185    dirty_update->slave_dst = slave_dst;
186    dirty_update->x = x;
187    dirty_update->y = y;
188    dirty_update->dst_x = dst_x;
189    dirty_update->dst_y = dst_y;
190    dirty_update->rotation = rotation;
191    dirty_update->damage = DamageCreate(NULL, NULL,
192                                        DamageReportNone,
193                                        TRUE, src->drawable.pScreen,
194                                        src->drawable.pScreen);
195
196    if (rotation != RR_Rotate_0) {
197        RRTransformCompute(x, y,
198                           slave_dst->drawable.width,
199                           slave_dst->drawable.height,
200                           rotation,
201                           NULL,
202                           &dirty_update->transform,
203                           &dirty_update->f_transform,
204                           &dirty_update->f_inverse);
205    }
206    if (!dirty_update->damage) {
207        free(dirty_update);
208        return FALSE;
209    }
210
211    /* Damage destination rectangle so that the destination pixmap contents
212     * will get fully initialized
213     */
214    box.x1 = dirty_update->x;
215    box.y1 = dirty_update->y;
216    if (dirty_update->rotation == RR_Rotate_90 ||
217        dirty_update->rotation == RR_Rotate_270) {
218        box.x2 = dirty_update->x + slave_dst->drawable.height;
219        box.y2 = dirty_update->y + slave_dst->drawable.width;
220    } else {
221        box.x2 = dirty_update->x + slave_dst->drawable.width;
222        box.y2 = dirty_update->y + slave_dst->drawable.height;
223    }
224    RegionInit(&dstregion, &box, 1);
225    damageregion = DamageRegion(dirty_update->damage);
226    RegionUnion(damageregion, damageregion, &dstregion);
227    RegionUninit(&dstregion);
228
229    DamageRegister(&src->drawable, dirty_update->damage);
230    xorg_list_add(&dirty_update->ent, &screen->pixmap_dirty_list);
231    return TRUE;
232}
233
234Bool
235PixmapStopDirtyTracking(PixmapPtr src, PixmapPtr slave_dst)
236{
237    ScreenPtr screen = src->drawable.pScreen;
238    PixmapDirtyUpdatePtr ent, safe;
239
240    xorg_list_for_each_entry_safe(ent, safe, &screen->pixmap_dirty_list, ent) {
241        if (ent->src == src && ent->slave_dst == slave_dst) {
242            DamageDestroy(ent->damage);
243            xorg_list_del(&ent->ent);
244            free(ent);
245        }
246    }
247    return TRUE;
248}
249
250static void
251PixmapDirtyCopyArea(PixmapPtr dst,
252                    PixmapDirtyUpdatePtr dirty,
253                    RegionPtr dirty_region)
254{
255    ScreenPtr pScreen = dirty->src->drawable.pScreen;
256    int n;
257    BoxPtr b;
258    GCPtr pGC;
259
260    n = RegionNumRects(dirty_region);
261    b = RegionRects(dirty_region);
262
263    pGC = GetScratchGC(dirty->src->drawable.depth, pScreen);
264    ValidateGC(&dst->drawable, pGC);
265
266    while (n--) {
267        BoxRec dst_box;
268        int w, h;
269
270        dst_box = *b;
271        w = dst_box.x2 - dst_box.x1;
272        h = dst_box.y2 - dst_box.y1;
273
274        pGC->ops->CopyArea(&dirty->src->drawable, &dst->drawable, pGC,
275                           dirty->x + dst_box.x1, dirty->y + dst_box.y1, w, h,
276                           dirty->dst_x + dst_box.x1,
277                           dirty->dst_y + dst_box.y1);
278        b++;
279    }
280    FreeScratchGC(pGC);
281}
282
283static void
284PixmapDirtyCompositeRotate(PixmapPtr dst_pixmap,
285                           PixmapDirtyUpdatePtr dirty,
286                           RegionPtr dirty_region)
287{
288    ScreenPtr pScreen = dirty->src->drawable.pScreen;
289    PictFormatPtr format = PictureWindowFormat(pScreen->root);
290    PicturePtr src, dst;
291    XID include_inferiors = IncludeInferiors;
292    int n = RegionNumRects(dirty_region);
293    BoxPtr b = RegionRects(dirty_region);
294    int error;
295
296    src = CreatePicture(None,
297                        &dirty->src->drawable,
298                        format,
299                        CPSubwindowMode,
300                        &include_inferiors, serverClient, &error);
301    if (!src)
302        return;
303
304    dst = CreatePicture(None,
305                        &dst_pixmap->drawable,
306                        format, 0L, NULL, serverClient, &error);
307    if (!dst)
308        return;
309
310    error = SetPictureTransform(src, &dirty->transform);
311    if (error)
312        return;
313    while (n--) {
314        BoxRec dst_box;
315
316        dst_box = *b;
317        dst_box.x1 += dirty->x;
318        dst_box.x2 += dirty->x;
319        dst_box.y1 += dirty->y;
320        dst_box.y2 += dirty->y;
321        pixman_f_transform_bounds(&dirty->f_inverse, &dst_box);
322
323        CompositePicture(PictOpSrc,
324                         src, NULL, dst,
325                         dst_box.x1,
326                         dst_box.y1,
327                         0, 0,
328                         dst_box.x1,
329                         dst_box.y1,
330                         dst_box.x2 - dst_box.x1,
331                         dst_box.y2 - dst_box.y1);
332        b++;
333    }
334
335    FreePicture(src, None);
336    FreePicture(dst, None);
337}
338
339/*
340 * this function can possibly be improved and optimised, by clipping
341 * instead of iterating
342 * Drivers are free to implement their own version of this.
343 */
344Bool PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty)
345{
346    ScreenPtr pScreen = dirty->src->drawable.pScreen;
347    RegionPtr region = DamageRegion(dirty->damage);
348    PixmapPtr dst;
349    SourceValidateProcPtr SourceValidate;
350    RegionRec pixregion;
351    BoxRec box;
352
353    dst = dirty->slave_dst->master_pixmap;
354    if (!dst)
355        dst = dirty->slave_dst;
356
357    box.x1 = 0;
358    box.y1 = 0;
359    if (dirty->rotation == RR_Rotate_90 ||
360        dirty->rotation == RR_Rotate_270) {
361        box.x2 = dst->drawable.height;
362        box.y2 = dst->drawable.width;
363    } else {
364        box.x2 = dst->drawable.width;
365        box.y2 = dst->drawable.height;
366    }
367    RegionInit(&pixregion, &box, 1);
368
369    /*
370     * SourceValidate is used by the software cursor code
371     * to pull the cursor off of the screen when reading
372     * bits from the frame buffer. Bypassing this function
373     * leaves the software cursor in place
374     */
375    SourceValidate = pScreen->SourceValidate;
376    pScreen->SourceValidate = NULL;
377
378    RegionTranslate(&pixregion, dirty->x, dirty->y);
379    RegionIntersect(&pixregion, &pixregion, region);
380
381    if (RegionNil(&pixregion)) {
382        RegionUninit(&pixregion);
383        return FALSE;
384    }
385
386    RegionTranslate(&pixregion, -dirty->x, -dirty->y);
387
388    if (!pScreen->root || dirty->rotation == RR_Rotate_0)
389        PixmapDirtyCopyArea(dst, dirty, &pixregion);
390    else
391        PixmapDirtyCompositeRotate(dst, dirty, &pixregion);
392    pScreen->SourceValidate = SourceValidate;
393    return TRUE;
394}
395