1/*
2 * Copyright © 2009 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * 	Zhigang Gong <zhigang.gong@linux.intel.com>
25 *
26 * 	original author is Chris Wilson at sna.
27 *
28 */
29
30#include "glamor_priv.h"
31#include "mipict.h"
32#include "damage.h"
33
34/** @file glamor_compositerects.
35 *
36 * compositeRects acceleration implementation
37 */
38
39static int16_t
40bound(int16_t a, uint16_t b)
41{
42    int v = (int) a + (int) b;
43
44    if (v > MAXSHORT)
45        return MAXSHORT;
46    return v;
47}
48
49static Bool
50_pixman_region_init_clipped_rectangles(pixman_region16_t * region,
51                                       unsigned int num_rects,
52                                       xRectangle *rects,
53                                       int tx, int ty, BoxPtr extents)
54{
55    pixman_box16_t stack_boxes[64], *boxes = stack_boxes;
56    pixman_bool_t ret;
57    unsigned int i, j;
58
59    if (num_rects > ARRAY_SIZE(stack_boxes)) {
60        boxes = xallocarray(num_rects, sizeof(pixman_box16_t));
61        if (boxes == NULL)
62            return FALSE;
63    }
64
65    for (i = j = 0; i < num_rects; i++) {
66        boxes[j].x1 = rects[i].x + tx;
67        if (boxes[j].x1 < extents->x1)
68            boxes[j].x1 = extents->x1;
69
70        boxes[j].y1 = rects[i].y + ty;
71        if (boxes[j].y1 < extents->y1)
72            boxes[j].y1 = extents->y1;
73
74        boxes[j].x2 = bound(rects[i].x + tx, rects[i].width);
75        if (boxes[j].x2 > extents->x2)
76            boxes[j].x2 = extents->x2;
77
78        boxes[j].y2 = bound(rects[i].y + ty, rects[i].height);
79        if (boxes[j].y2 > extents->y2)
80            boxes[j].y2 = extents->y2;
81
82        if (boxes[j].x2 > boxes[j].x1 && boxes[j].y2 > boxes[j].y1)
83            j++;
84    }
85
86    ret = FALSE;
87    if (j)
88        ret = pixman_region_init_rects(region, boxes, j);
89
90    if (boxes != stack_boxes)
91        free(boxes);
92
93    DEBUGF("%s: nrects=%d, region=(%d, %d), (%d, %d) x %d\n",
94           __FUNCTION__, num_rects,
95           region->extents.x1, region->extents.y1,
96           region->extents.x2, region->extents.y2, j);
97    return ret;
98}
99
100void
101glamor_composite_rectangles(CARD8 op,
102                            PicturePtr dst,
103                            xRenderColor * color,
104                            int num_rects, xRectangle *rects)
105{
106    PixmapPtr pixmap;
107    struct glamor_pixmap_private *priv;
108    pixman_region16_t region;
109    pixman_box16_t *boxes;
110    int num_boxes;
111    PicturePtr source = NULL;
112    Bool need_free_region = FALSE;
113
114    DEBUGF("%s(op=%d, %08x x %d [(%d, %d)x(%d, %d) ...])\n",
115           __FUNCTION__, op,
116           (color->alpha >> 8 << 24) |
117           (color->red >> 8 << 16) |
118           (color->green >> 8 << 8) |
119           (color->blue >> 8 << 0),
120           num_rects, rects[0].x, rects[0].y, rects[0].width, rects[0].height);
121
122    if (!num_rects)
123        return;
124
125    if (RegionNil(dst->pCompositeClip)) {
126        DEBUGF("%s: empty clip, skipping\n", __FUNCTION__);
127        return;
128    }
129
130    if ((color->red | color->green | color->blue | color->alpha) <= 0x00ff) {
131        switch (op) {
132        case PictOpOver:
133        case PictOpOutReverse:
134        case PictOpAdd:
135            return;
136        case PictOpInReverse:
137        case PictOpSrc:
138            op = PictOpClear;
139            break;
140        case PictOpAtopReverse:
141            op = PictOpOut;
142            break;
143        case PictOpXor:
144            op = PictOpOverReverse;
145            break;
146        }
147    }
148    if (color->alpha <= 0x00ff) {
149        switch (op) {
150        case PictOpOver:
151        case PictOpOutReverse:
152            return;
153        case PictOpInReverse:
154            op = PictOpClear;
155            break;
156        case PictOpAtopReverse:
157            op = PictOpOut;
158            break;
159        case PictOpXor:
160            op = PictOpOverReverse;
161            break;
162        }
163    }
164    else if (color->alpha >= 0xff00) {
165        switch (op) {
166        case PictOpOver:
167            op = PictOpSrc;
168            break;
169        case PictOpInReverse:
170            return;
171        case PictOpOutReverse:
172            op = PictOpClear;
173            break;
174        case PictOpAtopReverse:
175            op = PictOpOverReverse;
176            break;
177        case PictOpXor:
178            op = PictOpOut;
179            break;
180        }
181    }
182    DEBUGF("%s: converted to op %d\n", __FUNCTION__, op);
183
184    if (!_pixman_region_init_clipped_rectangles(&region,
185                                                num_rects, rects,
186                                                dst->pDrawable->x,
187                                                dst->pDrawable->y,
188                                                &dst->pCompositeClip->extents))
189    {
190        DEBUGF("%s: allocation failed for region\n", __FUNCTION__);
191        return;
192    }
193
194    pixmap = glamor_get_drawable_pixmap(dst->pDrawable);
195    priv = glamor_get_pixmap_private(pixmap);
196
197    if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(priv))
198        goto fallback;
199    if (dst->alphaMap) {
200        DEBUGF("%s: fallback, dst has an alpha-map\n", __FUNCTION__);
201        goto fallback;
202    }
203
204    need_free_region = TRUE;
205
206    DEBUGF("%s: drawable extents (%d, %d),(%d, %d) x %d\n",
207           __FUNCTION__,
208           RegionExtents(&region)->x1, RegionExtents(&region)->y1,
209           RegionExtents(&region)->x2, RegionExtents(&region)->y2,
210           RegionNumRects(&region));
211
212    if (dst->pCompositeClip->data &&
213        (!pixman_region_intersect(&region, &region, dst->pCompositeClip) ||
214         RegionNil(&region))) {
215        DEBUGF("%s: zero-intersection between rectangles and clip\n",
216               __FUNCTION__);
217        pixman_region_fini(&region);
218        return;
219    }
220
221    DEBUGF("%s: clipped extents (%d, %d),(%d, %d) x %d\n",
222           __FUNCTION__,
223           RegionExtents(&region)->x1, RegionExtents(&region)->y1,
224           RegionExtents(&region)->x2, RegionExtents(&region)->y2,
225           RegionNumRects(&region));
226
227    boxes = pixman_region_rectangles(&region, &num_boxes);
228    if (op == PictOpSrc || op == PictOpClear) {
229        CARD32 pixel;
230        int dst_x, dst_y;
231
232        glamor_get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y);
233        pixman_region_translate(&region, dst_x, dst_y);
234
235        DEBUGF("%s: pixmap +(%d, %d) extents (%d, %d),(%d, %d)\n",
236               __FUNCTION__, dst_x, dst_y,
237               RegionExtents(&region)->x1, RegionExtents(&region)->y1,
238               RegionExtents(&region)->x2, RegionExtents(&region)->y2);
239
240        if (op == PictOpClear)
241            pixel = 0;
242        else
243            miRenderColorToPixel(dst->pFormat, color, &pixel);
244        glamor_solid_boxes(pixmap, boxes, num_boxes, pixel);
245
246        goto done;
247    }
248    else {
249        if (_X_LIKELY(glamor_pixmap_priv_is_small(priv))) {
250            int error;
251
252            source = CreateSolidPicture(0, color, &error);
253            if (!source)
254                goto done;
255            if (glamor_composite_clipped_region(op, source,
256                                                NULL, dst,
257                                                NULL, NULL, pixmap,
258                                                &region, 0, 0, 0, 0, 0, 0))
259                goto done;
260        }
261    }
262 fallback:
263    miCompositeRects(op, dst, color, num_rects, rects);
264 done:
265    /* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must
266     * manually append the damaged regions ourselves.
267     */
268    DamageRegionAppend(&pixmap->drawable, &region);
269    DamageRegionProcessPending(&pixmap->drawable);
270
271    if (need_free_region)
272        pixman_region_fini(&region);
273    if (source)
274        FreePicture(source, 0);
275    return;
276}
277