Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ |
2 | /* cairo - a vector graphics library with display and print output |
||
3 | * |
||
4 | * Copyright © 2002 University of Southern California |
||
5 | * Copyright © 2005 Red Hat, Inc. |
||
6 | * Copyright © 2011 Intel Corporation |
||
7 | * |
||
8 | * This library is free software; you can redistribute it and/or |
||
9 | * modify it either under the terms of the GNU Lesser General Public |
||
10 | * License version 2.1 as published by the Free Software Foundation |
||
11 | * (the "LGPL") or, at your option, under the terms of the Mozilla |
||
12 | * Public License Version 1.1 (the "MPL"). If you do not alter this |
||
13 | * notice, a recipient may use your version of this file under either |
||
14 | * the MPL or the LGPL. |
||
15 | * |
||
16 | * You should have received a copy of the LGPL along with this library |
||
17 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
||
18 | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
||
19 | * You should have received a copy of the MPL along with this library |
||
20 | * in the file COPYING-MPL-1.1 |
||
21 | * |
||
22 | * The contents of this file are subject to the Mozilla Public License |
||
23 | * Version 1.1 (the "License"); you may not use this file except in |
||
24 | * compliance with the License. You may obtain a copy of the License at |
||
25 | * http://www.mozilla.org/MPL/ |
||
26 | * |
||
27 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
||
28 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
||
29 | * the specific language governing rights and limitations. |
||
30 | * |
||
31 | * The Original Code is the cairo graphics library. |
||
32 | * |
||
33 | * The Initial Developer of the Original Code is University of Southern |
||
34 | * California. |
||
35 | * |
||
36 | * Contributor(s): |
||
37 | * Carl D. Worth |
||
38 | * Behdad Esfahbod |
||
39 | * Chris Wilson |
||
40 | * Karl Tomlinson |
||
41 | */ |
||
42 | |||
43 | #include "cairoint.h" |
||
44 | |||
45 | #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS |
||
46 | |||
47 | #include "cairo-xlib-private.h" |
||
48 | |||
49 | #include "cairo-compositor-private.h" |
||
50 | #include "cairo-damage-private.h" |
||
51 | #include "cairo-image-surface-private.h" |
||
52 | #include "cairo-list-inline.h" |
||
53 | #include "cairo-pattern-private.h" |
||
54 | #include "cairo-pixman-private.h" |
||
55 | #include "cairo-traps-private.h" |
||
56 | #include "cairo-tristrip-private.h" |
||
57 | |||
58 | static cairo_int_status_t |
||
59 | check_composite (const cairo_composite_rectangles_t *extents) |
||
60 | { |
||
61 | cairo_xlib_display_t *display = ((cairo_xlib_surface_t *)extents->surface)->display; |
||
62 | |||
63 | if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op)) |
||
64 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
65 | |||
66 | return CAIRO_STATUS_SUCCESS; |
||
67 | } |
||
68 | |||
69 | static cairo_int_status_t |
||
70 | acquire (void *abstract_dst) |
||
71 | { |
||
72 | cairo_xlib_surface_t *dst = abstract_dst; |
||
73 | cairo_int_status_t status; |
||
74 | |||
75 | status = _cairo_xlib_display_acquire (dst->base.device, &dst->display); |
||
76 | if (unlikely (status)) |
||
77 | return status; |
||
78 | |||
79 | dst->dpy = dst->display->display; |
||
80 | return CAIRO_STATUS_SUCCESS; |
||
81 | } |
||
82 | |||
83 | static cairo_int_status_t |
||
84 | release (void *abstract_dst) |
||
85 | { |
||
86 | cairo_xlib_surface_t *dst = abstract_dst; |
||
87 | |||
88 | cairo_device_release (&dst->display->base); |
||
89 | dst->dpy = NULL; |
||
90 | |||
91 | return CAIRO_STATUS_SUCCESS; |
||
92 | } |
||
93 | |||
94 | static cairo_int_status_t |
||
95 | set_clip_region (void *_surface, |
||
96 | cairo_region_t *region) |
||
97 | { |
||
98 | cairo_xlib_surface_t *surface = _surface; |
||
99 | |||
100 | _cairo_xlib_surface_ensure_picture (surface); |
||
101 | |||
102 | if (region != NULL) { |
||
103 | XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))]; |
||
104 | XRectangle *rects = stack_rects; |
||
105 | int n_rects, i; |
||
106 | |||
107 | n_rects = cairo_region_num_rectangles (region); |
||
108 | if (n_rects > ARRAY_LENGTH (stack_rects)) { |
||
109 | rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); |
||
110 | if (unlikely (rects == NULL)) |
||
111 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
112 | } |
||
113 | for (i = 0; i < n_rects; i++) { |
||
114 | cairo_rectangle_int_t rect; |
||
115 | |||
116 | cairo_region_get_rectangle (region, i, &rect); |
||
117 | |||
118 | rects[i].x = rect.x; |
||
119 | rects[i].y = rect.y; |
||
120 | rects[i].width = rect.width; |
||
121 | rects[i].height = rect.height; |
||
122 | } |
||
123 | XRenderSetPictureClipRectangles (surface->dpy, |
||
124 | surface->picture, |
||
125 | 0, 0, |
||
126 | rects, n_rects); |
||
127 | if (rects != stack_rects) |
||
128 | free (rects); |
||
129 | } else { |
||
130 | XRenderPictureAttributes pa; |
||
131 | pa.clip_mask = None; |
||
132 | XRenderChangePicture (surface->dpy, |
||
133 | surface->picture, |
||
134 | CPClipMask, &pa); |
||
135 | } |
||
136 | |||
137 | return CAIRO_STATUS_SUCCESS; |
||
138 | } |
||
139 | |||
140 | static cairo_int_status_t |
||
141 | copy_image_boxes (void *_dst, |
||
142 | cairo_image_surface_t *image, |
||
143 | cairo_boxes_t *boxes, |
||
144 | int dx, int dy) |
||
145 | { |
||
146 | cairo_xlib_surface_t *dst = _dst; |
||
147 | struct _cairo_boxes_chunk *chunk; |
||
148 | cairo_int_status_t status; |
||
149 | Pixmap src; |
||
150 | GC gc; |
||
151 | int i, j; |
||
152 | |||
153 | assert (image->depth == dst->depth); |
||
154 | |||
155 | status = acquire (dst); |
||
156 | if (unlikely (status)) |
||
157 | return status; |
||
158 | |||
159 | status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc); |
||
160 | if (unlikely (status)) { |
||
161 | release (dst); |
||
162 | return status; |
||
163 | } |
||
164 | |||
165 | src = _cairo_xlib_shm_surface_get_pixmap (&image->base); |
||
166 | if (boxes->num_boxes == 1) { |
||
167 | int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); |
||
168 | int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); |
||
169 | int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); |
||
170 | int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); |
||
171 | |||
172 | _cairo_xlib_shm_surface_mark_active (&image->base); |
||
173 | XCopyArea (dst->dpy, src, dst->drawable, gc, |
||
174 | x1 + dx, y1 + dy, |
||
175 | x2 - x1, y2 - y1, |
||
176 | x1, y1); |
||
177 | } else { |
||
178 | XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; |
||
179 | XRectangle *rects = stack_rects; |
||
180 | |||
181 | if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { |
||
182 | rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); |
||
183 | if (unlikely (rects == NULL)) |
||
184 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
185 | } |
||
186 | |||
187 | j = 0; |
||
188 | for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
||
189 | for (i = 0; i < chunk->count; i++) { |
||
190 | int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); |
||
191 | int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); |
||
192 | int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); |
||
193 | int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); |
||
194 | |||
195 | if (x2 > x1 && y2 > y1) { |
||
196 | rects[j].x = x1; |
||
197 | rects[j].y = y1; |
||
198 | rects[j].width = x2 - x1; |
||
199 | rects[j].height = y2 - y1; |
||
200 | j++; |
||
201 | } |
||
202 | } |
||
203 | } |
||
204 | |||
205 | XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted); |
||
206 | _cairo_xlib_shm_surface_mark_active (&image->base); |
||
207 | XCopyArea (dst->dpy, src, dst->drawable, gc, |
||
208 | 0, 0, image->width, image->height, -dx, -dy); |
||
209 | XSetClipMask (dst->dpy, gc, None); |
||
210 | |||
211 | if (rects != stack_rects) |
||
212 | free (rects); |
||
213 | } |
||
214 | |||
215 | _cairo_xlib_surface_put_gc (dst->display, dst, gc); |
||
216 | release (dst); |
||
217 | return CAIRO_STATUS_SUCCESS; |
||
218 | } |
||
219 | |||
220 | static cairo_bool_t |
||
221 | boxes_cover_surface (cairo_boxes_t *boxes, |
||
222 | cairo_xlib_surface_t *surface) |
||
223 | { |
||
224 | cairo_box_t *b; |
||
225 | |||
226 | if (boxes->num_boxes != 1) |
||
227 | return FALSE; |
||
228 | |||
229 | b = &boxes->chunks.base[0]; |
||
230 | |||
231 | if (_cairo_fixed_integer_part (b->p1.x) > 0 || |
||
232 | _cairo_fixed_integer_part (b->p1.y) > 0) |
||
233 | return FALSE; |
||
234 | |||
235 | if (_cairo_fixed_integer_part (b->p2.x) < surface->width || |
||
236 | _cairo_fixed_integer_part (b->p2.y) < surface->height) |
||
237 | return FALSE; |
||
238 | |||
239 | return TRUE; |
||
240 | } |
||
241 | |||
242 | static cairo_int_status_t |
||
243 | draw_image_boxes (void *_dst, |
||
244 | cairo_image_surface_t *image, |
||
245 | cairo_boxes_t *boxes, |
||
246 | int dx, int dy) |
||
247 | { |
||
248 | cairo_xlib_surface_t *dst = _dst; |
||
249 | struct _cairo_boxes_chunk *chunk; |
||
250 | cairo_image_surface_t *shm = NULL; |
||
251 | cairo_int_status_t status; |
||
252 | int i; |
||
253 | |||
254 | if (image->base.device == dst->base.device) { |
||
255 | if (image->depth != dst->depth) |
||
256 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
257 | |||
258 | if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) |
||
259 | return copy_image_boxes (dst, image, boxes, dx, dy); |
||
260 | |||
261 | goto draw_image_boxes; |
||
262 | } |
||
263 | |||
264 | if (boxes_cover_surface (boxes, dst)) |
||
265 | shm = (cairo_image_surface_t *) _cairo_xlib_surface_get_shm (dst, TRUE); |
||
266 | if (shm) { |
||
267 | for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
||
268 | for (i = 0; i < chunk->count; i++) { |
||
269 | cairo_box_t *b = &chunk->base[i]; |
||
270 | cairo_rectangle_int_t r; |
||
271 | |||
272 | r.x = _cairo_fixed_integer_part (b->p1.x); |
||
273 | r.y = _cairo_fixed_integer_part (b->p1.y); |
||
274 | r.width = _cairo_fixed_integer_part (b->p2.x) - r.x; |
||
275 | r.height = _cairo_fixed_integer_part (b->p2.y) - r.y; |
||
276 | |||
277 | if (shm->pixman_format != image->pixman_format || |
||
278 | ! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data, |
||
279 | image->stride / sizeof (uint32_t), |
||
280 | shm->stride / sizeof (uint32_t), |
||
281 | PIXMAN_FORMAT_BPP (image->pixman_format), |
||
282 | PIXMAN_FORMAT_BPP (shm->pixman_format), |
||
283 | r.x + dx, r.y + dy, |
||
284 | r.x, r.y, |
||
285 | r.width, r.height)) |
||
286 | { |
||
287 | pixman_image_composite32 (PIXMAN_OP_SRC, |
||
288 | image->pixman_image, NULL, shm->pixman_image, |
||
289 | r.x + dx, r.y + dy, |
||
290 | 0, 0, |
||
291 | r.x, r.y, |
||
292 | r.width, r.height); |
||
293 | } |
||
294 | |||
295 | shm->base.damage = |
||
296 | _cairo_damage_add_rectangle (shm->base.damage, &r); |
||
297 | } |
||
298 | } |
||
299 | dst->base.is_clear = FALSE; |
||
300 | dst->fallback++; |
||
301 | dst->base.serial++; |
||
302 | return CAIRO_INT_STATUS_NOTHING_TO_DO; |
||
303 | } |
||
304 | |||
305 | if (image->depth == dst->depth && |
||
306 | ((cairo_xlib_display_t *)dst->display)->shm) { |
||
307 | cairo_box_t extents; |
||
308 | cairo_rectangle_int_t r; |
||
309 | |||
310 | _cairo_boxes_extents (boxes, &extents); |
||
311 | _cairo_box_round_to_rectangle (&extents, &r); |
||
312 | |||
313 | shm = (cairo_image_surface_t *) |
||
314 | _cairo_xlib_surface_create_shm (dst, image->pixman_format, |
||
315 | r.width, r.height); |
||
316 | if (shm) { |
||
317 | int tx = -r.x, ty = -r.y; |
||
318 | |||
319 | assert (shm->pixman_format == image->pixman_format); |
||
320 | for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
||
321 | for (i = 0; i < chunk->count; i++) { |
||
322 | cairo_box_t *b = &chunk->base[i]; |
||
323 | |||
324 | r.x = _cairo_fixed_integer_part (b->p1.x); |
||
325 | r.y = _cairo_fixed_integer_part (b->p1.y); |
||
326 | r.width = _cairo_fixed_integer_part (b->p2.x) - r.x; |
||
327 | r.height = _cairo_fixed_integer_part (b->p2.y) - r.y; |
||
328 | |||
329 | if (! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data, |
||
330 | image->stride / sizeof (uint32_t), |
||
331 | shm->stride / sizeof (uint32_t), |
||
332 | PIXMAN_FORMAT_BPP (image->pixman_format), |
||
333 | PIXMAN_FORMAT_BPP (shm->pixman_format), |
||
334 | r.x + dx, r.y + dy, |
||
335 | r.x + tx, r.y + ty, |
||
336 | r.width, r.height)) |
||
337 | { |
||
338 | pixman_image_composite32 (PIXMAN_OP_SRC, |
||
339 | image->pixman_image, NULL, shm->pixman_image, |
||
340 | r.x + dx, r.y + dy, |
||
341 | 0, 0, |
||
342 | r.x + tx, r.y + ty, |
||
343 | r.width, r.height); |
||
344 | } |
||
345 | } |
||
346 | } |
||
347 | |||
348 | dx = tx; |
||
349 | dy = ty; |
||
350 | image = shm; |
||
351 | |||
352 | if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) { |
||
353 | status = copy_image_boxes (dst, image, boxes, dx, dy); |
||
354 | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
||
355 | goto out; |
||
356 | } |
||
357 | } |
||
358 | } |
||
359 | |||
360 | draw_image_boxes: |
||
361 | status = CAIRO_STATUS_SUCCESS; |
||
362 | for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
||
363 | for (i = 0; i < chunk->count; i++) { |
||
364 | cairo_box_t *b = &chunk->base[i]; |
||
365 | int x1 = _cairo_fixed_integer_part (b->p1.x); |
||
366 | int y1 = _cairo_fixed_integer_part (b->p1.y); |
||
367 | int x2 = _cairo_fixed_integer_part (b->p2.x); |
||
368 | int y2 = _cairo_fixed_integer_part (b->p2.y); |
||
369 | if (_cairo_xlib_surface_draw_image (dst, image, |
||
370 | x1 + dx, y1 + dy, |
||
371 | x2 - x1, y2 - y1, |
||
372 | x1, y1)) { |
||
373 | status = CAIRO_INT_STATUS_UNSUPPORTED; |
||
374 | goto out; |
||
375 | } |
||
376 | } |
||
377 | } |
||
378 | |||
379 | out: |
||
380 | cairo_surface_destroy (&shm->base); |
||
381 | return status; |
||
382 | } |
||
383 | |||
384 | static cairo_int_status_t |
||
385 | copy_boxes (void *_dst, |
||
386 | cairo_surface_t *_src, |
||
387 | cairo_boxes_t *boxes, |
||
388 | const cairo_rectangle_int_t *extents, |
||
389 | int dx, int dy) |
||
390 | { |
||
391 | cairo_xlib_surface_t *dst = _dst; |
||
392 | cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)_src; |
||
393 | struct _cairo_boxes_chunk *chunk; |
||
394 | cairo_int_status_t status; |
||
395 | GC gc; |
||
396 | Drawable d; |
||
397 | int i, j; |
||
398 | |||
399 | if (! _cairo_xlib_surface_same_screen (dst, src)) |
||
400 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
401 | |||
402 | if (dst->depth != src->depth) |
||
403 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
404 | |||
405 | status = acquire (dst); |
||
406 | if (unlikely (status)) |
||
407 | return status; |
||
408 | |||
409 | status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc); |
||
410 | if (unlikely (status)) { |
||
411 | release (dst); |
||
412 | return status; |
||
413 | } |
||
414 | |||
415 | if (src->fallback && src->shm->damage->dirty) { |
||
416 | assert (src != dst); |
||
417 | d = _cairo_xlib_shm_surface_get_pixmap (src->shm); |
||
418 | assert (d != 0); |
||
419 | } else { |
||
420 | if (! src->owns_pixmap) { |
||
421 | XGCValues gcv; |
||
422 | |||
423 | gcv.subwindow_mode = IncludeInferiors; |
||
424 | XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv); |
||
425 | } |
||
426 | d = src->drawable; |
||
427 | } |
||
428 | |||
429 | if (boxes->num_boxes == 1) { |
||
430 | int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); |
||
431 | int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); |
||
432 | int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); |
||
433 | int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); |
||
434 | |||
435 | XCopyArea (dst->dpy, d, dst->drawable, gc, |
||
436 | x1 + dx, y1 + dy, |
||
437 | x2 - x1, y2 - y1, |
||
438 | x1, y1); |
||
439 | } else { |
||
440 | /* We can only have a single control for subwindow_mode on the |
||
441 | * GC. If we have a Window destination, we need to set ClipByChildren, |
||
442 | * but if we have a Window source, we need IncludeInferiors. If we have |
||
443 | * both a Window destination and source, we must fallback. There is |
||
444 | * no convenient way to detect if a drawable is a Pixmap or Window, |
||
445 | * therefore we can only rely on those surfaces that we created |
||
446 | * ourselves to be Pixmaps, and treat everything else as a potential |
||
447 | * Window. |
||
448 | */ |
||
449 | if (src == dst || (!src->owns_pixmap && !dst->owns_pixmap)) { |
||
450 | for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
||
451 | for (i = 0; i < chunk->count; i++) { |
||
452 | int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); |
||
453 | int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); |
||
454 | int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); |
||
455 | int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); |
||
456 | XCopyArea (dst->dpy, d, dst->drawable, gc, |
||
457 | x1 + dx, y1 + dy, |
||
458 | x2 - x1, y2 - y1, |
||
459 | x1, y1); |
||
460 | } |
||
461 | } |
||
462 | } else { |
||
463 | XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; |
||
464 | XRectangle *rects = stack_rects; |
||
465 | |||
466 | if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { |
||
467 | rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); |
||
468 | if (unlikely (rects == NULL)) |
||
469 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
470 | } |
||
471 | |||
472 | j = 0; |
||
473 | for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
||
474 | for (i = 0; i < chunk->count; i++) { |
||
475 | int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); |
||
476 | int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); |
||
477 | int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); |
||
478 | int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); |
||
479 | |||
480 | rects[j].x = x1; |
||
481 | rects[j].y = y1; |
||
482 | rects[j].width = x2 - x1; |
||
483 | rects[j].height = y2 - y1; |
||
484 | j++; |
||
485 | } |
||
486 | } |
||
487 | assert (j == boxes->num_boxes); |
||
488 | |||
489 | XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted); |
||
490 | |||
491 | XCopyArea (dst->dpy, d, dst->drawable, gc, |
||
492 | extents->x + dx, extents->y + dy, |
||
493 | extents->width, extents->height, |
||
494 | extents->x, extents->y); |
||
495 | |||
496 | XSetClipMask (dst->dpy, gc, None); |
||
497 | |||
498 | if (rects != stack_rects) |
||
499 | free (rects); |
||
500 | } |
||
501 | } |
||
502 | |||
503 | if (src->fallback && src->shm->damage->dirty) { |
||
504 | _cairo_xlib_shm_surface_mark_active (src->shm); |
||
505 | } else if (! src->owns_pixmap) { |
||
506 | XGCValues gcv; |
||
507 | |||
508 | gcv.subwindow_mode = ClipByChildren; |
||
509 | XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv); |
||
510 | } |
||
511 | |||
512 | _cairo_xlib_surface_put_gc (dst->display, dst, gc); |
||
513 | release (dst); |
||
514 | return CAIRO_STATUS_SUCCESS; |
||
515 | } |
||
516 | |||
517 | static int |
||
518 | _render_operator (cairo_operator_t op) |
||
519 | { |
||
520 | switch (op) { |
||
521 | case CAIRO_OPERATOR_CLEAR: |
||
522 | return PictOpClear; |
||
523 | |||
524 | case CAIRO_OPERATOR_SOURCE: |
||
525 | return PictOpSrc; |
||
526 | case CAIRO_OPERATOR_OVER: |
||
527 | return PictOpOver; |
||
528 | case CAIRO_OPERATOR_IN: |
||
529 | return PictOpIn; |
||
530 | case CAIRO_OPERATOR_OUT: |
||
531 | return PictOpOut; |
||
532 | case CAIRO_OPERATOR_ATOP: |
||
533 | return PictOpAtop; |
||
534 | |||
535 | case CAIRO_OPERATOR_DEST: |
||
536 | return PictOpDst; |
||
537 | case CAIRO_OPERATOR_DEST_OVER: |
||
538 | return PictOpOverReverse; |
||
539 | case CAIRO_OPERATOR_DEST_IN: |
||
540 | return PictOpInReverse; |
||
541 | case CAIRO_OPERATOR_DEST_OUT: |
||
542 | return PictOpOutReverse; |
||
543 | case CAIRO_OPERATOR_DEST_ATOP: |
||
544 | return PictOpAtopReverse; |
||
545 | |||
546 | case CAIRO_OPERATOR_XOR: |
||
547 | return PictOpXor; |
||
548 | case CAIRO_OPERATOR_ADD: |
||
549 | return PictOpAdd; |
||
550 | case CAIRO_OPERATOR_SATURATE: |
||
551 | return PictOpSaturate; |
||
552 | |||
553 | case CAIRO_OPERATOR_MULTIPLY: |
||
554 | return PictOpMultiply; |
||
555 | case CAIRO_OPERATOR_SCREEN: |
||
556 | return PictOpScreen; |
||
557 | case CAIRO_OPERATOR_OVERLAY: |
||
558 | return PictOpOverlay; |
||
559 | case CAIRO_OPERATOR_DARKEN: |
||
560 | return PictOpDarken; |
||
561 | case CAIRO_OPERATOR_LIGHTEN: |
||
562 | return PictOpLighten; |
||
563 | case CAIRO_OPERATOR_COLOR_DODGE: |
||
564 | return PictOpColorDodge; |
||
565 | case CAIRO_OPERATOR_COLOR_BURN: |
||
566 | return PictOpColorBurn; |
||
567 | case CAIRO_OPERATOR_HARD_LIGHT: |
||
568 | return PictOpHardLight; |
||
569 | case CAIRO_OPERATOR_SOFT_LIGHT: |
||
570 | return PictOpSoftLight; |
||
571 | case CAIRO_OPERATOR_DIFFERENCE: |
||
572 | return PictOpDifference; |
||
573 | case CAIRO_OPERATOR_EXCLUSION: |
||
574 | return PictOpExclusion; |
||
575 | case CAIRO_OPERATOR_HSL_HUE: |
||
576 | return PictOpHSLHue; |
||
577 | case CAIRO_OPERATOR_HSL_SATURATION: |
||
578 | return PictOpHSLSaturation; |
||
579 | case CAIRO_OPERATOR_HSL_COLOR: |
||
580 | return PictOpHSLColor; |
||
581 | case CAIRO_OPERATOR_HSL_LUMINOSITY: |
||
582 | return PictOpHSLLuminosity; |
||
583 | |||
584 | default: |
||
585 | ASSERT_NOT_REACHED; |
||
586 | return PictOpOver; |
||
587 | } |
||
588 | } |
||
589 | |||
590 | static cairo_bool_t |
||
591 | fill_reduces_to_source (cairo_operator_t op, |
||
592 | const cairo_color_t *color, |
||
593 | cairo_xlib_surface_t *dst) |
||
594 | { |
||
595 | if (dst->base.is_clear || CAIRO_COLOR_IS_OPAQUE (color)) { |
||
596 | if (op == CAIRO_OPERATOR_OVER) |
||
597 | return TRUE; |
||
598 | if (op == CAIRO_OPERATOR_ADD) |
||
599 | return (dst->base.content & CAIRO_CONTENT_COLOR) == 0; |
||
600 | } |
||
601 | |||
602 | return FALSE; |
||
603 | } |
||
604 | |||
605 | static cairo_int_status_t |
||
606 | fill_rectangles (void *abstract_surface, |
||
607 | cairo_operator_t op, |
||
608 | const cairo_color_t *color, |
||
609 | cairo_rectangle_int_t *rects, |
||
610 | int num_rects) |
||
611 | { |
||
612 | cairo_xlib_surface_t *dst = abstract_surface; |
||
613 | XRenderColor render_color; |
||
614 | int i; |
||
615 | |||
616 | //X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); |
||
617 | |||
618 | if (fill_reduces_to_source (op, color, dst)) |
||
619 | op = CAIRO_OPERATOR_SOURCE; |
||
620 | |||
621 | if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { |
||
622 | cairo_int_status_t status; |
||
623 | |||
624 | status = CAIRO_INT_STATUS_UNSUPPORTED; |
||
625 | if (op == CAIRO_OPERATOR_SOURCE) |
||
626 | status = _cairo_xlib_core_fill_rectangles (dst, color, num_rects, rects); |
||
627 | return status; |
||
628 | } |
||
629 | |||
630 | render_color.red = color->red_short; |
||
631 | render_color.green = color->green_short; |
||
632 | render_color.blue = color->blue_short; |
||
633 | render_color.alpha = color->alpha_short; |
||
634 | |||
635 | _cairo_xlib_surface_ensure_picture (dst); |
||
636 | if (num_rects == 1) { |
||
637 | /* Take advantage of the protocol compaction that libXrender performs |
||
638 | * to amalgamate sequences of XRenderFillRectangle(). |
||
639 | */ |
||
640 | XRenderFillRectangle (dst->dpy, |
||
641 | _render_operator (op), |
||
642 | dst->picture, |
||
643 | &render_color, |
||
644 | rects->x, rects->y, |
||
645 | rects->width, rects->height); |
||
646 | } else { |
||
647 | XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; |
||
648 | XRectangle *xrects = stack_xrects; |
||
649 | |||
650 | if (num_rects > ARRAY_LENGTH (stack_xrects)) { |
||
651 | xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); |
||
652 | if (unlikely (xrects == NULL)) |
||
653 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
654 | } |
||
655 | |||
656 | for (i = 0; i < num_rects; i++) { |
||
657 | xrects[i].x = rects[i].x; |
||
658 | xrects[i].y = rects[i].y; |
||
659 | xrects[i].width = rects[i].width; |
||
660 | xrects[i].height = rects[i].height; |
||
661 | } |
||
662 | |||
663 | XRenderFillRectangles (dst->dpy, |
||
664 | _render_operator (op), |
||
665 | dst->picture, |
||
666 | &render_color, xrects, num_rects); |
||
667 | |||
668 | if (xrects != stack_xrects) |
||
669 | free (xrects); |
||
670 | } |
||
671 | |||
672 | return CAIRO_STATUS_SUCCESS; |
||
673 | } |
||
674 | |||
675 | static cairo_int_status_t |
||
676 | fill_boxes (void *abstract_surface, |
||
677 | cairo_operator_t op, |
||
678 | const cairo_color_t *color, |
||
679 | cairo_boxes_t *boxes) |
||
680 | { |
||
681 | cairo_xlib_surface_t *dst = abstract_surface; |
||
682 | XRenderColor render_color; |
||
683 | |||
684 | if (fill_reduces_to_source (op, color, dst)) |
||
685 | op = CAIRO_OPERATOR_SOURCE; |
||
686 | |||
687 | if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { |
||
688 | cairo_int_status_t status; |
||
689 | |||
690 | status = CAIRO_INT_STATUS_UNSUPPORTED; |
||
691 | if (op == CAIRO_OPERATOR_SOURCE) |
||
692 | status = _cairo_xlib_core_fill_boxes (dst, color, boxes); |
||
693 | return status; |
||
694 | } |
||
695 | |||
696 | render_color.red = color->red_short; |
||
697 | render_color.green = color->green_short; |
||
698 | render_color.blue = color->blue_short; |
||
699 | render_color.alpha = color->alpha_short; |
||
700 | |||
701 | _cairo_xlib_surface_ensure_picture (dst); |
||
702 | if (boxes->num_boxes == 1) { |
||
703 | int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); |
||
704 | int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); |
||
705 | int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); |
||
706 | int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); |
||
707 | |||
708 | /* Take advantage of the protocol compaction that libXrender performs |
||
709 | * to amalgamate sequences of XRenderFillRectangle(). |
||
710 | */ |
||
711 | XRenderFillRectangle (dst->dpy, |
||
712 | _render_operator (op), |
||
713 | dst->picture, |
||
714 | &render_color, |
||
715 | x1, y1, |
||
716 | x2 - x1, y2 - y1); |
||
717 | } else { |
||
718 | XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; |
||
719 | XRectangle *xrects = stack_xrects; |
||
720 | struct _cairo_boxes_chunk *chunk; |
||
721 | int i, j; |
||
722 | |||
723 | if (boxes->num_boxes > ARRAY_LENGTH (stack_xrects)) { |
||
724 | xrects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); |
||
725 | if (unlikely (xrects == NULL)) |
||
726 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
727 | } |
||
728 | |||
729 | j = 0; |
||
730 | for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
||
731 | for (i = 0; i < chunk->count; i++) { |
||
732 | int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); |
||
733 | int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); |
||
734 | int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); |
||
735 | int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); |
||
736 | |||
737 | xrects[j].x = x1; |
||
738 | xrects[j].y = y1; |
||
739 | xrects[j].width = x2 - x1; |
||
740 | xrects[j].height = y2 - y1; |
||
741 | j++; |
||
742 | } |
||
743 | } |
||
744 | |||
745 | XRenderFillRectangles (dst->dpy, |
||
746 | _render_operator (op), |
||
747 | dst->picture, |
||
748 | &render_color, xrects, j); |
||
749 | |||
750 | if (xrects != stack_xrects) |
||
751 | free (xrects); |
||
752 | } |
||
753 | |||
754 | return CAIRO_STATUS_SUCCESS; |
||
755 | } |
||
756 | |||
757 | #if 0 |
||
758 | check_composite () |
||
759 | operation = _categorize_composite_operation (dst, op, src_pattern, |
||
760 | mask_pattern != NULL); |
||
761 | if (operation == DO_UNSUPPORTED) |
||
762 | return UNSUPPORTED ("unsupported operation"); |
||
763 | |||
764 | //X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable)); |
||
765 | |||
766 | operation = _recategorize_composite_operation (dst, op, src, &src_attr, |
||
767 | mask_pattern != NULL); |
||
768 | if (operation == DO_UNSUPPORTED) { |
||
769 | status = UNSUPPORTED ("unsupported operation"); |
||
770 | goto BAIL; |
||
771 | } |
||
772 | #endif |
||
773 | |||
774 | static cairo_int_status_t |
||
775 | composite (void *abstract_dst, |
||
776 | cairo_operator_t op, |
||
777 | cairo_surface_t *abstract_src, |
||
778 | cairo_surface_t *abstract_mask, |
||
779 | int src_x, |
||
780 | int src_y, |
||
781 | int mask_x, |
||
782 | int mask_y, |
||
783 | int dst_x, |
||
784 | int dst_y, |
||
785 | unsigned int width, |
||
786 | unsigned int height) |
||
787 | { |
||
788 | cairo_xlib_surface_t *dst = abstract_dst; |
||
789 | cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; |
||
790 | |||
791 | op = _render_operator (op); |
||
792 | |||
793 | _cairo_xlib_surface_ensure_picture (dst); |
||
794 | if (abstract_mask) { |
||
795 | cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask; |
||
796 | |||
797 | XRenderComposite (dst->dpy, op, |
||
798 | src->picture, mask->picture, dst->picture, |
||
799 | src_x, src_y, |
||
800 | mask_x, mask_y, |
||
801 | dst_x, dst_y, |
||
802 | width, height); |
||
803 | } else { |
||
804 | XRenderComposite (dst->dpy, op, |
||
805 | src->picture, 0, dst->picture, |
||
806 | src_x, src_y, |
||
807 | 0, 0, |
||
808 | dst_x, dst_y, |
||
809 | width, height); |
||
810 | } |
||
811 | |||
812 | return CAIRO_STATUS_SUCCESS; |
||
813 | } |
||
814 | |||
815 | static cairo_int_status_t |
||
816 | lerp (void *abstract_dst, |
||
817 | cairo_surface_t *abstract_src, |
||
818 | cairo_surface_t *abstract_mask, |
||
819 | int src_x, |
||
820 | int src_y, |
||
821 | int mask_x, |
||
822 | int mask_y, |
||
823 | int dst_x, |
||
824 | int dst_y, |
||
825 | unsigned int width, |
||
826 | unsigned int height) |
||
827 | { |
||
828 | cairo_xlib_surface_t *dst = abstract_dst; |
||
829 | cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; |
||
830 | cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask; |
||
831 | |||
832 | _cairo_xlib_surface_ensure_picture (dst); |
||
833 | XRenderComposite (dst->dpy, PictOpOutReverse, |
||
834 | mask->picture, None, dst->picture, |
||
835 | mask_x, mask_y, |
||
836 | 0, 0, |
||
837 | dst_x, dst_y, |
||
838 | width, height); |
||
839 | XRenderComposite (dst->dpy, PictOpAdd, |
||
840 | src->picture, mask->picture, dst->picture, |
||
841 | src_x, src_y, |
||
842 | mask_x, mask_y, |
||
843 | dst_x, dst_y, |
||
844 | width, height); |
||
845 | |||
846 | return CAIRO_STATUS_SUCCESS; |
||
847 | } |
||
848 | |||
849 | static cairo_int_status_t |
||
850 | composite_boxes (void *abstract_dst, |
||
851 | cairo_operator_t op, |
||
852 | cairo_surface_t *abstract_src, |
||
853 | cairo_surface_t *abstract_mask, |
||
854 | int src_x, |
||
855 | int src_y, |
||
856 | int mask_x, |
||
857 | int mask_y, |
||
858 | int dst_x, |
||
859 | int dst_y, |
||
860 | cairo_boxes_t *boxes, |
||
861 | const cairo_rectangle_int_t *extents) |
||
862 | { |
||
863 | cairo_xlib_surface_t *dst = abstract_dst; |
||
864 | Picture src = ((cairo_xlib_source_t *)abstract_src)->picture; |
||
865 | Picture mask = abstract_mask ? ((cairo_xlib_source_t *)abstract_mask)->picture : 0; |
||
866 | XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; |
||
867 | XRectangle *rects = stack_rects; |
||
868 | struct _cairo_boxes_chunk *chunk; |
||
869 | int i, j; |
||
870 | |||
871 | op = _render_operator (op); |
||
872 | _cairo_xlib_surface_ensure_picture (dst); |
||
873 | if (boxes->num_boxes == 1) { |
||
874 | int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); |
||
875 | int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); |
||
876 | int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); |
||
877 | int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); |
||
878 | |||
879 | XRenderComposite (dst->dpy, op, |
||
880 | src, mask, dst->picture, |
||
881 | x1 + src_x, y1 + src_y, |
||
882 | x1 + mask_x, y1 + mask_y, |
||
883 | x1 - dst_x, y1 - dst_y, |
||
884 | x2 - x1, y2 - y1); |
||
885 | return CAIRO_STATUS_SUCCESS; |
||
886 | } |
||
887 | |||
888 | if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { |
||
889 | rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); |
||
890 | if (unlikely (rects == NULL)) |
||
891 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
892 | } |
||
893 | |||
894 | j = 0; |
||
895 | for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
||
896 | for (i = 0; i < chunk->count; i++) { |
||
897 | int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); |
||
898 | int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); |
||
899 | int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); |
||
900 | int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); |
||
901 | |||
902 | rects[j].x = x1 - dst_x; |
||
903 | rects[j].y = y1 - dst_y; |
||
904 | rects[j].width = x2 - x1; |
||
905 | rects[j].height = y2 - y1; |
||
906 | j++; |
||
907 | } |
||
908 | } |
||
909 | assert (j == boxes->num_boxes); |
||
910 | |||
911 | XRenderSetPictureClipRectangles (dst->dpy, |
||
912 | dst->picture, |
||
913 | 0, 0, |
||
914 | rects, j); |
||
915 | if (rects != stack_rects) |
||
916 | free (rects); |
||
917 | |||
918 | XRenderComposite (dst->dpy, op, |
||
919 | src, mask, dst->picture, |
||
920 | extents->x + src_x, extents->y + src_y, |
||
921 | extents->x + mask_x, extents->y + mask_y, |
||
922 | extents->x - dst_x, extents->y - dst_y, |
||
923 | extents->width, extents->height); |
||
924 | |||
925 | set_clip_region (dst, NULL); |
||
926 | |||
927 | return CAIRO_STATUS_SUCCESS; |
||
928 | } |
||
929 | |||
930 | /* font rendering */ |
||
931 | |||
932 | void |
||
933 | _cairo_xlib_font_close (cairo_xlib_font_t *priv) |
||
934 | { |
||
935 | cairo_xlib_display_t *display = (cairo_xlib_display_t *)priv->base.key; |
||
936 | int i; |
||
937 | |||
938 | /* XXX All I really want is to do is zap my glyphs... */ |
||
939 | _cairo_scaled_font_reset_cache (priv->font); |
||
940 | |||
941 | for (i = 0; i < NUM_GLYPHSETS; i++) { |
||
942 | cairo_xlib_font_glyphset_t *info; |
||
943 | |||
944 | info = &priv->glyphset[i]; |
||
945 | if (info->glyphset) |
||
946 | XRenderFreeGlyphSet (display->display, info->glyphset); |
||
947 | } |
||
948 | |||
949 | /* XXX locking */ |
||
950 | cairo_list_del (&priv->link); |
||
951 | cairo_list_del (&priv->base.link); |
||
952 | free (priv); |
||
953 | } |
||
954 | |||
955 | static void |
||
956 | _cairo_xlib_font_fini (cairo_scaled_font_private_t *abstract_private, |
||
957 | cairo_scaled_font_t *font) |
||
958 | { |
||
959 | cairo_xlib_font_t *priv = (cairo_xlib_font_t *) abstract_private; |
||
960 | cairo_status_t status; |
||
961 | cairo_xlib_display_t *display; |
||
962 | int i; |
||
963 | |||
964 | cairo_list_del (&priv->base.link); |
||
965 | cairo_list_del (&priv->link); |
||
966 | |||
967 | status = _cairo_xlib_display_acquire (priv->device, &display); |
||
968 | if (status) |
||
969 | goto BAIL; |
||
970 | |||
971 | for (i = 0; i < NUM_GLYPHSETS; i++) { |
||
972 | cairo_xlib_font_glyphset_t *info; |
||
973 | |||
974 | info = &priv->glyphset[i]; |
||
975 | if (info->glyphset) |
||
976 | XRenderFreeGlyphSet (display->display, info->glyphset); |
||
977 | } |
||
978 | |||
979 | cairo_device_release (&display->base); |
||
980 | BAIL: |
||
981 | cairo_device_destroy (&display->base); |
||
982 | free (priv); |
||
983 | } |
||
984 | |||
985 | static cairo_xlib_font_t * |
||
986 | _cairo_xlib_font_create (cairo_xlib_display_t *display, |
||
987 | cairo_scaled_font_t *font) |
||
988 | { |
||
989 | cairo_xlib_font_t *priv; |
||
990 | int i; |
||
991 | |||
992 | priv = malloc (sizeof (cairo_xlib_font_t)); |
||
993 | if (unlikely (priv == NULL)) |
||
994 | return NULL; |
||
995 | |||
996 | _cairo_scaled_font_attach_private (font, &priv->base, display, |
||
997 | _cairo_xlib_font_fini); |
||
998 | |||
999 | priv->device = cairo_device_reference (&display->base); |
||
1000 | priv->font = font; |
||
1001 | cairo_list_add (&priv->link, &display->fonts); |
||
1002 | |||
1003 | for (i = 0; i < NUM_GLYPHSETS; i++) { |
||
1004 | cairo_xlib_font_glyphset_t *info = &priv->glyphset[i]; |
||
1005 | switch (i) { |
||
1006 | case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break; |
||
1007 | case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break; |
||
1008 | case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break; |
||
1009 | default: ASSERT_NOT_REACHED; break; |
||
1010 | } |
||
1011 | info->xrender_format = NULL; |
||
1012 | info->glyphset = None; |
||
1013 | info->to_free.count = 0; |
||
1014 | } |
||
1015 | |||
1016 | return priv; |
||
1017 | } |
||
1018 | |||
1019 | static int |
||
1020 | _cairo_xlib_get_glyphset_index_for_format (cairo_format_t format) |
||
1021 | { |
||
1022 | if (format == CAIRO_FORMAT_A8) |
||
1023 | return GLYPHSET_INDEX_A8; |
||
1024 | if (format == CAIRO_FORMAT_A1) |
||
1025 | return GLYPHSET_INDEX_A1; |
||
1026 | |||
1027 | assert (format == CAIRO_FORMAT_ARGB32); |
||
1028 | return GLYPHSET_INDEX_ARGB32; |
||
1029 | } |
||
1030 | |||
1031 | static inline cairo_xlib_font_t * |
||
1032 | _cairo_xlib_font_get (const cairo_xlib_display_t *display, |
||
1033 | cairo_scaled_font_t *font) |
||
1034 | { |
||
1035 | return (cairo_xlib_font_t *)_cairo_scaled_font_find_private (font, display); |
||
1036 | } |
||
1037 | |||
1038 | typedef struct { |
||
1039 | cairo_scaled_glyph_private_t base; |
||
1040 | |||
1041 | |||
1042 | cairo_xlib_font_glyphset_t *glyphset; |
||
1043 | } cairo_xlib_glyph_private_t; |
||
1044 | |||
1045 | static void |
||
1046 | _cairo_xlib_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, |
||
1047 | cairo_scaled_glyph_t *glyph, |
||
1048 | cairo_scaled_font_t *font) |
||
1049 | { |
||
1050 | cairo_xlib_glyph_private_t *priv = (cairo_xlib_glyph_private_t *)glyph_private; |
||
1051 | |||
1052 | if (! font->finished) { |
||
1053 | cairo_xlib_font_t *font_private; |
||
1054 | struct _cairo_xlib_font_glyphset_free_glyphs *to_free; |
||
1055 | cairo_xlib_font_glyphset_t *info; |
||
1056 | |||
1057 | font_private = _cairo_xlib_font_get (glyph_private->key, font); |
||
1058 | assert (font_private); |
||
1059 | |||
1060 | info = priv->glyphset; |
||
1061 | to_free = &info->to_free; |
||
1062 | if (to_free->count == ARRAY_LENGTH (to_free->indices)) { |
||
1063 | cairo_xlib_display_t *display; |
||
1064 | |||
1065 | if (_cairo_xlib_display_acquire (font_private->device, |
||
1066 | &display) == CAIRO_STATUS_SUCCESS) { |
||
1067 | XRenderFreeGlyphs (display->display, |
||
1068 | info->glyphset, |
||
1069 | to_free->indices, |
||
1070 | to_free->count); |
||
1071 | cairo_device_release (&display->base); |
||
1072 | } |
||
1073 | |||
1074 | to_free->count = 0; |
||
1075 | } |
||
1076 | |||
1077 | to_free->indices[to_free->count++] = |
||
1078 | _cairo_scaled_glyph_index (glyph); |
||
1079 | } |
||
1080 | |||
1081 | cairo_list_del (&glyph_private->link); |
||
1082 | free (glyph_private); |
||
1083 | } |
||
1084 | |||
1085 | static cairo_status_t |
||
1086 | _cairo_xlib_glyph_attach (cairo_xlib_display_t *display, |
||
1087 | cairo_scaled_glyph_t *glyph, |
||
1088 | cairo_xlib_font_glyphset_t *info) |
||
1089 | { |
||
1090 | cairo_xlib_glyph_private_t *priv; |
||
1091 | |||
1092 | priv = malloc (sizeof (*priv)); |
||
1093 | if (unlikely (priv == NULL)) |
||
1094 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1095 | |||
1096 | _cairo_scaled_glyph_attach_private (glyph, &priv->base, display, |
||
1097 | _cairo_xlib_glyph_fini); |
||
1098 | priv->glyphset = info; |
||
1099 | |||
1100 | glyph->dev_private = info; |
||
1101 | glyph->dev_private_key = display; |
||
1102 | return CAIRO_STATUS_SUCCESS; |
||
1103 | } |
||
1104 | |||
1105 | static cairo_xlib_font_glyphset_t * |
||
1106 | _cairo_xlib_font_get_glyphset_info_for_format (cairo_xlib_display_t *display, |
||
1107 | cairo_scaled_font_t *font, |
||
1108 | cairo_format_t format) |
||
1109 | { |
||
1110 | cairo_xlib_font_t *priv; |
||
1111 | cairo_xlib_font_glyphset_t *info; |
||
1112 | int glyphset_index; |
||
1113 | |||
1114 | glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format); |
||
1115 | |||
1116 | priv = _cairo_xlib_font_get (display, font); |
||
1117 | if (priv == NULL) { |
||
1118 | priv = _cairo_xlib_font_create (display, font); |
||
1119 | if (priv == NULL) |
||
1120 | return NULL; |
||
1121 | } |
||
1122 | |||
1123 | info = &priv->glyphset[glyphset_index]; |
||
1124 | if (info->glyphset == None) { |
||
1125 | info->xrender_format = |
||
1126 | _cairo_xlib_display_get_xrender_format (display, info->format); |
||
1127 | info->glyphset = XRenderCreateGlyphSet (display->display, |
||
1128 | info->xrender_format); |
||
1129 | } |
||
1130 | |||
1131 | return info; |
||
1132 | } |
||
1133 | |||
1134 | static cairo_bool_t |
||
1135 | has_pending_free_glyph (cairo_xlib_font_glyphset_t *info, |
||
1136 | unsigned long glyph_index) |
||
1137 | { |
||
1138 | struct _cairo_xlib_font_glyphset_free_glyphs *to_free; |
||
1139 | int i; |
||
1140 | |||
1141 | to_free = &info->to_free; |
||
1142 | for (i = 0; i < to_free->count; i++) { |
||
1143 | if (to_free->indices[i] == glyph_index) { |
||
1144 | to_free->count--; |
||
1145 | memmove (&to_free->indices[i], |
||
1146 | &to_free->indices[i+1], |
||
1147 | (to_free->count - i) * sizeof (to_free->indices[0])); |
||
1148 | return TRUE; |
||
1149 | } |
||
1150 | } |
||
1151 | |||
1152 | return FALSE; |
||
1153 | } |
||
1154 | |||
1155 | static cairo_xlib_font_glyphset_t * |
||
1156 | find_pending_free_glyph (cairo_xlib_display_t *display, |
||
1157 | cairo_scaled_font_t *font, |
||
1158 | unsigned long glyph_index, |
||
1159 | cairo_image_surface_t *surface) |
||
1160 | { |
||
1161 | cairo_xlib_font_t *priv; |
||
1162 | int i; |
||
1163 | |||
1164 | priv = _cairo_xlib_font_get (display, font); |
||
1165 | if (priv == NULL) |
||
1166 | return NULL; |
||
1167 | |||
1168 | if (surface != NULL) { |
||
1169 | i = _cairo_xlib_get_glyphset_index_for_format (surface->format); |
||
1170 | if (has_pending_free_glyph (&priv->glyphset[i], glyph_index)) |
||
1171 | return &priv->glyphset[i]; |
||
1172 | } else { |
||
1173 | for (i = 0; i < NUM_GLYPHSETS; i++) { |
||
1174 | if (has_pending_free_glyph (&priv->glyphset[i], glyph_index)) |
||
1175 | return &priv->glyphset[i]; |
||
1176 | } |
||
1177 | } |
||
1178 | |||
1179 | return NULL; |
||
1180 | } |
||
1181 | |||
1182 | static cairo_status_t |
||
1183 | _cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display, |
||
1184 | cairo_scaled_font_t *font, |
||
1185 | cairo_scaled_glyph_t **pscaled_glyph) |
||
1186 | { |
||
1187 | XGlyphInfo glyph_info; |
||
1188 | unsigned long glyph_index; |
||
1189 | unsigned char *data; |
||
1190 | cairo_status_t status = CAIRO_STATUS_SUCCESS; |
||
1191 | cairo_scaled_glyph_t *glyph = *pscaled_glyph; |
||
1192 | cairo_image_surface_t *glyph_surface = glyph->surface; |
||
1193 | cairo_bool_t already_had_glyph_surface; |
||
1194 | cairo_xlib_font_glyphset_t *info; |
||
1195 | |||
1196 | glyph_index = _cairo_scaled_glyph_index (glyph); |
||
1197 | |||
1198 | /* check to see if we have a pending XRenderFreeGlyph for this glyph */ |
||
1199 | info = find_pending_free_glyph (display, font, glyph_index, glyph_surface); |
||
1200 | if (info != NULL) |
||
1201 | return _cairo_xlib_glyph_attach (display, glyph, info); |
||
1202 | |||
1203 | if (glyph_surface == NULL) { |
||
1204 | status = _cairo_scaled_glyph_lookup (font, |
||
1205 | glyph_index, |
||
1206 | CAIRO_SCALED_GLYPH_INFO_METRICS | |
||
1207 | CAIRO_SCALED_GLYPH_INFO_SURFACE, |
||
1208 | pscaled_glyph); |
||
1209 | if (unlikely (status)) |
||
1210 | return status; |
||
1211 | |||
1212 | glyph = *pscaled_glyph; |
||
1213 | glyph_surface = glyph->surface; |
||
1214 | already_had_glyph_surface = FALSE; |
||
1215 | } else { |
||
1216 | already_had_glyph_surface = TRUE; |
||
1217 | } |
||
1218 | |||
1219 | info = _cairo_xlib_font_get_glyphset_info_for_format (display, font, |
||
1220 | glyph_surface->format); |
||
1221 | |||
1222 | #if 0 |
||
1223 | /* If the glyph surface has zero height or width, we create |
||
1224 | * a clear 1x1 surface, to avoid various X server bugs. |
||
1225 | */ |
||
1226 | if (glyph_surface->width == 0 || glyph_surface->height == 0) { |
||
1227 | cairo_surface_t *tmp_surface; |
||
1228 | |||
1229 | tmp_surface = cairo_image_surface_create (info->format, 1, 1); |
||
1230 | status = tmp_surface->status; |
||
1231 | if (unlikely (status)) |
||
1232 | goto BAIL; |
||
1233 | |||
1234 | tmp_surface->device_transform = glyph_surface->base.device_transform; |
||
1235 | tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; |
||
1236 | |||
1237 | glyph_surface = (cairo_image_surface_t *) tmp_surface; |
||
1238 | } |
||
1239 | #endif |
||
1240 | |||
1241 | /* If the glyph format does not match the font format, then we |
||
1242 | * create a temporary surface for the glyph image with the font's |
||
1243 | * format. |
||
1244 | */ |
||
1245 | if (glyph_surface->format != info->format) { |
||
1246 | cairo_surface_pattern_t pattern; |
||
1247 | cairo_surface_t *tmp_surface; |
||
1248 | |||
1249 | tmp_surface = cairo_image_surface_create (info->format, |
||
1250 | glyph_surface->width, |
||
1251 | glyph_surface->height); |
||
1252 | status = tmp_surface->status; |
||
1253 | if (unlikely (status)) |
||
1254 | goto BAIL; |
||
1255 | |||
1256 | tmp_surface->device_transform = glyph_surface->base.device_transform; |
||
1257 | tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; |
||
1258 | |||
1259 | _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); |
||
1260 | status = _cairo_surface_paint (tmp_surface, |
||
1261 | CAIRO_OPERATOR_SOURCE, &pattern.base, |
||
1262 | NULL); |
||
1263 | _cairo_pattern_fini (&pattern.base); |
||
1264 | |||
1265 | glyph_surface = (cairo_image_surface_t *) tmp_surface; |
||
1266 | |||
1267 | if (unlikely (status)) |
||
1268 | goto BAIL; |
||
1269 | } |
||
1270 | |||
1271 | /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ |
||
1272 | glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); |
||
1273 | glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); |
||
1274 | glyph_info.width = glyph_surface->width; |
||
1275 | glyph_info.height = glyph_surface->height; |
||
1276 | glyph_info.xOff = glyph->x_advance; |
||
1277 | glyph_info.yOff = glyph->y_advance; |
||
1278 | |||
1279 | data = glyph_surface->data; |
||
1280 | |||
1281 | /* flip formats around */ |
||
1282 | switch (_cairo_xlib_get_glyphset_index_for_format (glyph->surface->format)) { |
||
1283 | case GLYPHSET_INDEX_A1: |
||
1284 | /* local bitmaps are always stored with bit == byte */ |
||
1285 | if (_cairo_is_little_endian() != (BitmapBitOrder (display->display) == LSBFirst)) { |
||
1286 | int c = glyph_surface->stride * glyph_surface->height; |
||
1287 | unsigned char *d; |
||
1288 | unsigned char *new, *n; |
||
1289 | |||
1290 | new = malloc (c); |
||
1291 | if (!new) { |
||
1292 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1293 | goto BAIL; |
||
1294 | } |
||
1295 | n = new; |
||
1296 | d = data; |
||
1297 | do { |
||
1298 | char b = *d++; |
||
1299 | b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); |
||
1300 | b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); |
||
1301 | b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); |
||
1302 | *n++ = b; |
||
1303 | } while (--c); |
||
1304 | data = new; |
||
1305 | } |
||
1306 | break; |
||
1307 | case GLYPHSET_INDEX_A8: |
||
1308 | break; |
||
1309 | case GLYPHSET_INDEX_ARGB32: |
||
1310 | if (_cairo_is_little_endian() != (ImageByteOrder (display->display) == LSBFirst)) { |
||
1311 | unsigned int c = glyph_surface->stride * glyph_surface->height / 4; |
||
1312 | const uint32_t *d; |
||
1313 | uint32_t *new, *n; |
||
1314 | |||
1315 | new = malloc (4 * c); |
||
1316 | if (unlikely (new == NULL)) { |
||
1317 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1318 | goto BAIL; |
||
1319 | } |
||
1320 | n = new; |
||
1321 | d = (uint32_t *) data; |
||
1322 | do { |
||
1323 | *n++ = bswap_32 (*d); |
||
1324 | d++; |
||
1325 | } while (--c); |
||
1326 | data = (uint8_t *) new; |
||
1327 | } |
||
1328 | break; |
||
1329 | default: |
||
1330 | ASSERT_NOT_REACHED; |
||
1331 | break; |
||
1332 | } |
||
1333 | /* XXX assume X server wants pixman padding. Xft assumes this as well */ |
||
1334 | |||
1335 | XRenderAddGlyphs (display->display, info->glyphset, |
||
1336 | &glyph_index, &glyph_info, 1, |
||
1337 | (char *) data, |
||
1338 | glyph_surface->stride * glyph_surface->height); |
||
1339 | |||
1340 | if (data != glyph_surface->data) |
||
1341 | free (data); |
||
1342 | |||
1343 | status = _cairo_xlib_glyph_attach (display, glyph, info); |
||
1344 | |||
1345 | BAIL: |
||
1346 | if (glyph_surface != glyph->surface) |
||
1347 | cairo_surface_destroy (&glyph_surface->base); |
||
1348 | |||
1349 | /* if the scaled glyph didn't already have a surface attached |
||
1350 | * to it, release the created surface now that we have it |
||
1351 | * uploaded to the X server. If the surface has already been |
||
1352 | * there (eg. because image backend requested it), leave it in |
||
1353 | * the cache |
||
1354 | */ |
||
1355 | if (!already_had_glyph_surface) |
||
1356 | _cairo_scaled_glyph_set_surface (glyph, font, NULL); |
||
1357 | |||
1358 | return status; |
||
1359 | } |
||
1360 | |||
1361 | typedef void (*cairo_xrender_composite_text_func_t) |
||
1362 | (Display *dpy, |
||
1363 | int op, |
||
1364 | Picture src, |
||
1365 | Picture dst, |
||
1366 | _Xconst XRenderPictFormat *maskFormat, |
||
1367 | int xSrc, |
||
1368 | int ySrc, |
||
1369 | int xDst, |
||
1370 | int yDst, |
||
1371 | _Xconst XGlyphElt8 *elts, |
||
1372 | int nelt); |
||
1373 | |||
1374 | /* Build a struct of the same size of #cairo_glyph_t that can be used both as |
||
1375 | * an input glyph with double coordinates, and as "working" glyph with |
||
1376 | * integer from-current-point offsets. */ |
||
1377 | typedef union { |
||
1378 | cairo_glyph_t d; |
||
1379 | unsigned long index; |
||
1380 | struct { |
||
1381 | unsigned long index; |
||
1382 | int x; |
||
1383 | int y; |
||
1384 | } i; |
||
1385 | } cairo_xlib_glyph_t; |
||
1386 | |||
1387 | /* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */ |
||
1388 | COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t)); |
||
1389 | |||
1390 | /* Start a new element for the first glyph, |
||
1391 | * or for any glyph that has unexpected position, |
||
1392 | * or if current element has too many glyphs |
||
1393 | * (Xrender limits each element to 252 glyphs, we limit them to 128) |
||
1394 | * |
||
1395 | * These same conditions need to be mirrored between |
||
1396 | * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks |
||
1397 | */ |
||
1398 | #define _start_new_glyph_elt(count, glyph) \ |
||
1399 | (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) |
||
1400 | |||
1401 | static cairo_status_t |
||
1402 | _emit_glyphs_chunk (cairo_xlib_display_t *display, |
||
1403 | cairo_xlib_surface_t *dst, |
||
1404 | int dst_x, int dst_y, |
||
1405 | cairo_xlib_glyph_t *glyphs, |
||
1406 | int num_glyphs, |
||
1407 | cairo_scaled_font_t *font, |
||
1408 | cairo_bool_t use_mask, |
||
1409 | cairo_operator_t op, |
||
1410 | cairo_xlib_source_t *src, |
||
1411 | int src_x, int src_y, |
||
1412 | /* info for this chunk */ |
||
1413 | int num_elts, |
||
1414 | int width, |
||
1415 | cairo_xlib_font_glyphset_t *info) |
||
1416 | { |
||
1417 | /* Which XRenderCompositeText function to use */ |
||
1418 | cairo_xrender_composite_text_func_t composite_text_func; |
||
1419 | int size; |
||
1420 | |||
1421 | /* Element buffer stuff */ |
||
1422 | XGlyphElt8 *elts; |
||
1423 | XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)]; |
||
1424 | |||
1425 | /* Reuse the input glyph array for output char generation */ |
||
1426 | char *char8 = (char *) glyphs; |
||
1427 | unsigned short *char16 = (unsigned short *) glyphs; |
||
1428 | unsigned int *char32 = (unsigned int *) glyphs; |
||
1429 | |||
1430 | int i; |
||
1431 | int nelt; /* Element index */ |
||
1432 | int n; /* Num output glyphs in current element */ |
||
1433 | int j; /* Num output glyphs so far */ |
||
1434 | |||
1435 | switch (width) { |
||
1436 | case 1: |
||
1437 | /* don't cast the 8-variant, to catch possible mismatches */ |
||
1438 | composite_text_func = XRenderCompositeText8; |
||
1439 | size = sizeof (char); |
||
1440 | break; |
||
1441 | case 2: |
||
1442 | composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16; |
||
1443 | size = sizeof (unsigned short); |
||
1444 | break; |
||
1445 | default: |
||
1446 | case 4: |
||
1447 | composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32; |
||
1448 | size = sizeof (unsigned int); |
||
1449 | } |
||
1450 | |||
1451 | /* Allocate element array */ |
||
1452 | if (num_elts <= ARRAY_LENGTH (stack_elts)) { |
||
1453 | elts = stack_elts; |
||
1454 | } else { |
||
1455 | elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8)); |
||
1456 | if (unlikely (elts == NULL)) |
||
1457 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1458 | } |
||
1459 | |||
1460 | /* Fill them in */ |
||
1461 | nelt = 0; |
||
1462 | n = 0; |
||
1463 | j = 0; |
||
1464 | for (i = 0; i < num_glyphs; i++) { |
||
1465 | /* Start a new element for first output glyph, |
||
1466 | * or for any glyph that has unexpected position, |
||
1467 | * or if current element has too many glyphs. |
||
1468 | * |
||
1469 | * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs() |
||
1470 | */ |
||
1471 | if (_start_new_glyph_elt (j, &glyphs[i])) { |
||
1472 | if (j) { |
||
1473 | elts[nelt].nchars = n; |
||
1474 | nelt++; |
||
1475 | n = 0; |
||
1476 | } |
||
1477 | elts[nelt].chars = char8 + size * j; |
||
1478 | elts[nelt].glyphset = info->glyphset; |
||
1479 | elts[nelt].xOff = glyphs[i].i.x; |
||
1480 | elts[nelt].yOff = glyphs[i].i.y; |
||
1481 | } |
||
1482 | |||
1483 | switch (width) { |
||
1484 | case 1: char8 [j] = (char) glyphs[i].index; break; |
||
1485 | case 2: char16[j] = (unsigned short) glyphs[i].index; break; |
||
1486 | default: |
||
1487 | case 4: char32[j] = (unsigned int) glyphs[i].index; break; |
||
1488 | } |
||
1489 | |||
1490 | n++; |
||
1491 | j++; |
||
1492 | } |
||
1493 | |||
1494 | if (n) { |
||
1495 | elts[nelt].nchars = n; |
||
1496 | nelt++; |
||
1497 | } |
||
1498 | |||
1499 | /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the |
||
1500 | * expected number of xGlyphElts. */ |
||
1501 | assert (nelt == num_elts); |
||
1502 | |||
1503 | composite_text_func (display->display, op, |
||
1504 | src->picture, |
||
1505 | dst->picture, |
||
1506 | use_mask ? info->xrender_format : NULL, |
||
1507 | src_x + elts[0].xOff + dst_x, |
||
1508 | src_y + elts[0].yOff + dst_y, |
||
1509 | elts[0].xOff, elts[0].yOff, |
||
1510 | (XGlyphElt8 *) elts, nelt); |
||
1511 | |||
1512 | if (elts != stack_elts) |
||
1513 | free (elts); |
||
1514 | |||
1515 | return CAIRO_STATUS_SUCCESS; |
||
1516 | } |
||
1517 | |||
1518 | static cairo_int_status_t |
||
1519 | check_composite_glyphs (const cairo_composite_rectangles_t *extents, |
||
1520 | cairo_scaled_font_t *font, |
||
1521 | cairo_glyph_t *glyphs, |
||
1522 | int *num_glyphs) |
||
1523 | { |
||
1524 | cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface; |
||
1525 | cairo_xlib_display_t *display = dst->display; |
||
1526 | int max_request_size, size; |
||
1527 | |||
1528 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
||
1529 | |||
1530 | if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op)) |
||
1531 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
1532 | |||
1533 | /* The glyph coordinates must be representable in an int16_t. |
||
1534 | * When possible, they will be expressed as an offset from the |
||
1535 | * previous glyph, otherwise they will be an offset from the |
||
1536 | * surface origin. If we can't guarantee this to be possible, |
||
1537 | * fallback. |
||
1538 | */ |
||
1539 | if (extents->bounded.x + extents->bounded.width > INT16_MAX || |
||
1540 | extents->bounded.y + extents->bounded.height> INT16_MAX || |
||
1541 | extents->bounded.x < INT16_MIN || |
||
1542 | extents->bounded.y < INT16_MIN) |
||
1543 | { |
||
1544 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
1545 | } |
||
1546 | |||
1547 | /* Approximate the size of the largest glyph and fallback if we can not |
||
1548 | * upload it to the xserver. |
||
1549 | */ |
||
1550 | size = ceil (font->max_scale); |
||
1551 | size = 4 * size * size; |
||
1552 | max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display) |
||
1553 | : XMaxRequestSize (display->display)) * 4 - |
||
1554 | sz_xRenderAddGlyphsReq - |
||
1555 | sz_xGlyphInfo - |
||
1556 | 8; |
||
1557 | if (size >= max_request_size) |
||
1558 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
1559 | |||
1560 | return CAIRO_STATUS_SUCCESS; |
||
1561 | } |
||
1562 | |||
1563 | /* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have |
||
1564 | * enough room for padding */ |
||
1565 | #define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) |
||
1566 | |||
1567 | static cairo_int_status_t |
||
1568 | composite_glyphs (void *surface, |
||
1569 | cairo_operator_t op, |
||
1570 | cairo_surface_t *_src, |
||
1571 | int src_x, |
||
1572 | int src_y, |
||
1573 | int dst_x, |
||
1574 | int dst_y, |
||
1575 | cairo_composite_glyphs_info_t *info) |
||
1576 | { |
||
1577 | cairo_xlib_surface_t *dst = surface; |
||
1578 | cairo_xlib_glyph_t *glyphs = (cairo_xlib_glyph_t *)info->glyphs; |
||
1579 | cairo_xlib_source_t *src = (cairo_xlib_source_t *)_src; |
||
1580 | cairo_xlib_display_t *display = dst->display; |
||
1581 | cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; |
||
1582 | cairo_scaled_glyph_t *glyph; |
||
1583 | cairo_fixed_t x = dst_x, y = dst_y; |
||
1584 | cairo_xlib_font_glyphset_t *glyphset = NULL, *this_glyphset_info; |
||
1585 | |||
1586 | unsigned long max_index = 0; |
||
1587 | int width = 1; |
||
1588 | int num_elts = 0; |
||
1589 | int num_out_glyphs = 0; |
||
1590 | int num_glyphs = info->num_glyphs; |
||
1591 | |||
1592 | int max_request_size = XMaxRequestSize (display->display) * 4 |
||
1593 | - MAX (sz_xRenderCompositeGlyphs8Req, |
||
1594 | MAX(sz_xRenderCompositeGlyphs16Req, |
||
1595 | sz_xRenderCompositeGlyphs32Req)); |
||
1596 | int request_size = 0; |
||
1597 | int i; |
||
1598 | |||
1599 | op = _render_operator (op), |
||
1600 | _cairo_xlib_surface_ensure_picture (dst); |
||
1601 | for (i = 0; i < num_glyphs; i++) { |
||
1602 | int this_x, this_y; |
||
1603 | int old_width; |
||
1604 | |||
1605 | status = _cairo_scaled_glyph_lookup (info->font, |
||
1606 | glyphs[i].index, |
||
1607 | CAIRO_SCALED_GLYPH_INFO_METRICS, |
||
1608 | &glyph); |
||
1609 | if (unlikely (status)) |
||
1610 | return status; |
||
1611 | |||
1612 | this_x = _cairo_lround (glyphs[i].d.x); |
||
1613 | this_y = _cairo_lround (glyphs[i].d.y); |
||
1614 | |||
1615 | /* Send unsent glyphs to the server */ |
||
1616 | if (glyph->dev_private_key != display) { |
||
1617 | status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph); |
||
1618 | if (unlikely (status)) |
||
1619 | return status; |
||
1620 | } |
||
1621 | |||
1622 | this_glyphset_info = glyph->dev_private; |
||
1623 | if (!glyphset) |
||
1624 | glyphset = this_glyphset_info; |
||
1625 | |||
1626 | /* The invariant here is that we can always flush the glyphs |
||
1627 | * accumulated before this one, using old_width, and they |
||
1628 | * would fit in the request. |
||
1629 | */ |
||
1630 | old_width = width; |
||
1631 | |||
1632 | /* Update max glyph index */ |
||
1633 | if (glyphs[i].index > max_index) { |
||
1634 | max_index = glyphs[i].index; |
||
1635 | if (max_index >= 65536) |
||
1636 | width = 4; |
||
1637 | else if (max_index >= 256) |
||
1638 | width = 2; |
||
1639 | if (width != old_width) |
||
1640 | request_size += (width - old_width) * num_out_glyphs; |
||
1641 | } |
||
1642 | |||
1643 | /* If we will pass the max request size by adding this glyph, |
||
1644 | * flush current glyphs. Note that we account for a |
||
1645 | * possible element being added below. |
||
1646 | * |
||
1647 | * Also flush if changing glyphsets, as Xrender limits one mask |
||
1648 | * format per request, so we can either break up, or use a |
||
1649 | * wide-enough mask format. We do the former. One reason to |
||
1650 | * prefer the latter is the fact that Xserver ADDs all glyphs |
||
1651 | * to the mask first, and then composes that to final surface, |
||
1652 | * though it's not a big deal. |
||
1653 | * |
||
1654 | * If the glyph has a coordinate which cannot be represented |
||
1655 | * as a 16-bit offset from the previous glyph, flush the |
||
1656 | * current chunk. The current glyph will be the first one in |
||
1657 | * the next chunk, thus its coordinates will be an offset from |
||
1658 | * the destination origin. This offset is guaranteed to be |
||
1659 | * representable as 16-bit offset (otherwise we would have |
||
1660 | * fallen back). |
||
1661 | */ |
||
1662 | if (request_size + width > max_request_size - _cairo_sz_xGlyphElt || |
||
1663 | this_x - x > INT16_MAX || this_x - x < INT16_MIN || |
||
1664 | this_y - y > INT16_MAX || this_y - y < INT16_MIN || |
||
1665 | (this_glyphset_info != glyphset)) { |
||
1666 | status = _emit_glyphs_chunk (display, dst, dst_x, dst_y, |
||
1667 | glyphs, i, info->font, info->use_mask, |
||
1668 | op, src, src_x, src_y, |
||
1669 | num_elts, old_width, glyphset); |
||
1670 | if (unlikely (status)) |
||
1671 | return status; |
||
1672 | |||
1673 | glyphs += i; |
||
1674 | num_glyphs -= i; |
||
1675 | i = 0; |
||
1676 | max_index = glyphs[i].index; |
||
1677 | width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; |
||
1678 | request_size = 0; |
||
1679 | num_elts = 0; |
||
1680 | num_out_glyphs = 0; |
||
1681 | x = y = 0; |
||
1682 | glyphset = this_glyphset_info; |
||
1683 | } |
||
1684 | |||
1685 | /* Convert absolute glyph position to relative-to-current-point |
||
1686 | * position */ |
||
1687 | glyphs[i].i.x = this_x - x; |
||
1688 | glyphs[i].i.y = this_y - y; |
||
1689 | |||
1690 | /* Start a new element for the first glyph, |
||
1691 | * or for any glyph that has unexpected position, |
||
1692 | * or if current element has too many glyphs. |
||
1693 | * |
||
1694 | * These same conditions are mirrored in _emit_glyphs_chunk(). |
||
1695 | */ |
||
1696 | if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) { |
||
1697 | num_elts++; |
||
1698 | request_size += _cairo_sz_xGlyphElt; |
||
1699 | } |
||
1700 | |||
1701 | /* adjust current-position */ |
||
1702 | x = this_x + glyph->x_advance; |
||
1703 | y = this_y + glyph->y_advance; |
||
1704 | |||
1705 | num_out_glyphs++; |
||
1706 | request_size += width; |
||
1707 | } |
||
1708 | |||
1709 | if (num_elts) { |
||
1710 | status = _emit_glyphs_chunk (display, dst, dst_x, dst_y, |
||
1711 | glyphs, i, info->font, info->use_mask, |
||
1712 | op, src, src_x, src_y, |
||
1713 | num_elts, width, glyphset); |
||
1714 | } |
||
1715 | |||
1716 | return status; |
||
1717 | } |
||
1718 | |||
1719 | const cairo_compositor_t * |
||
1720 | _cairo_xlib_mask_compositor_get (void) |
||
1721 | { |
||
1722 | static cairo_mask_compositor_t compositor; |
||
1723 | |||
1724 | if (compositor.base.delegate == NULL) { |
||
1725 | _cairo_mask_compositor_init (&compositor, |
||
1726 | _cairo_xlib_fallback_compositor_get ()); |
||
1727 | |||
1728 | compositor.acquire = acquire; |
||
1729 | compositor.release = release; |
||
1730 | compositor.set_clip_region = set_clip_region; |
||
1731 | compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern; |
||
1732 | compositor.draw_image_boxes = draw_image_boxes; |
||
1733 | compositor.fill_rectangles = fill_rectangles; |
||
1734 | compositor.fill_boxes = fill_boxes; |
||
1735 | compositor.copy_boxes = copy_boxes; |
||
1736 | compositor.check_composite = check_composite; |
||
1737 | compositor.composite = composite; |
||
1738 | //compositor.check_composite_boxes = check_composite_boxes; |
||
1739 | compositor.composite_boxes = composite_boxes; |
||
1740 | compositor.check_composite_glyphs = check_composite_glyphs; |
||
1741 | compositor.composite_glyphs = composite_glyphs; |
||
1742 | } |
||
1743 | |||
1744 | return &compositor.base; |
||
1745 | } |
||
1746 | |||
1747 | #define CAIRO_FIXED_16_16_MIN -32768 |
||
1748 | #define CAIRO_FIXED_16_16_MAX 32767 |
||
1749 | |||
1750 | static cairo_bool_t |
||
1751 | line_exceeds_16_16 (const cairo_line_t *line) |
||
1752 | { |
||
1753 | return |
||
1754 | line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || |
||
1755 | line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || |
||
1756 | line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || |
||
1757 | line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || |
||
1758 | line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || |
||
1759 | line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || |
||
1760 | line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || |
||
1761 | line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX); |
||
1762 | } |
||
1763 | |||
1764 | static void |
||
1765 | project_line_x_onto_16_16 (const cairo_line_t *line, |
||
1766 | cairo_fixed_t top, |
||
1767 | cairo_fixed_t bottom, |
||
1768 | XLineFixed *out) |
||
1769 | { |
||
1770 | cairo_point_double_t p1, p2; |
||
1771 | double m; |
||
1772 | |||
1773 | p1.x = _cairo_fixed_to_double (line->p1.x); |
||
1774 | p1.y = _cairo_fixed_to_double (line->p1.y); |
||
1775 | |||
1776 | p2.x = _cairo_fixed_to_double (line->p2.x); |
||
1777 | p2.y = _cairo_fixed_to_double (line->p2.y); |
||
1778 | |||
1779 | m = (p2.x - p1.x) / (p2.y - p1.y); |
||
1780 | out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); |
||
1781 | out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); |
||
1782 | } |
||
1783 | #if 0 |
||
1784 | static cairo_int_status_T |
||
1785 | check_composite_trapezoids () |
||
1786 | { |
||
1787 | operation = _categorize_composite_operation (dst, op, pattern, TRUE); |
||
1788 | if (operation == DO_UNSUPPORTED) |
||
1789 | return UNSUPPORTED ("unsupported operation"); |
||
1790 | |||
1791 | operation = _recategorize_composite_operation (dst, op, src, |
||
1792 | &attributes, TRUE); |
||
1793 | if (operation == DO_UNSUPPORTED) { |
||
1794 | status = UNSUPPORTED ("unsupported operation"); |
||
1795 | goto BAIL; |
||
1796 | } |
||
1797 | |||
1798 | } |
||
1799 | #endif |
||
1800 | |||
1801 | static cairo_int_status_t |
||
1802 | composite_traps (void *abstract_dst, |
||
1803 | cairo_operator_t op, |
||
1804 | cairo_surface_t *abstract_src, |
||
1805 | int src_x, |
||
1806 | int src_y, |
||
1807 | int dst_x, |
||
1808 | int dst_y, |
||
1809 | const cairo_rectangle_int_t *extents, |
||
1810 | cairo_antialias_t antialias, |
||
1811 | cairo_traps_t *traps) |
||
1812 | { |
||
1813 | cairo_xlib_surface_t *dst = abstract_dst; |
||
1814 | cairo_xlib_display_t *display = dst->display; |
||
1815 | cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; |
||
1816 | XRenderPictFormat *pict_format; |
||
1817 | XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; |
||
1818 | XTrapezoid *xtraps = xtraps_stack; |
||
1819 | int dx, dy; |
||
1820 | int i; |
||
1821 | |||
1822 | //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); |
||
1823 | |||
1824 | if (dst->base.is_clear && |
||
1825 | (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) |
||
1826 | { |
||
1827 | op = CAIRO_OPERATOR_SOURCE; |
||
1828 | } |
||
1829 | |||
1830 | pict_format = |
||
1831 | _cairo_xlib_display_get_xrender_format (display, |
||
1832 | antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8); |
||
1833 | |||
1834 | if (traps->num_traps > ARRAY_LENGTH (xtraps_stack)) { |
||
1835 | xtraps = _cairo_malloc_ab (traps->num_traps, sizeof (XTrapezoid)); |
||
1836 | if (unlikely (xtraps == NULL)) |
||
1837 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1838 | } |
||
1839 | |||
1840 | dx = -dst_x << 16; |
||
1841 | dy = -dst_y << 16; |
||
1842 | for (i = 0; i < traps->num_traps; i++) { |
||
1843 | cairo_trapezoid_t *t = &traps->traps[i]; |
||
1844 | |||
1845 | /* top/bottom will be clamped to surface bounds */ |
||
1846 | xtraps[i].top = _cairo_fixed_to_16_16(t->top) + dy; |
||
1847 | xtraps[i].bottom = _cairo_fixed_to_16_16(t->bottom) + dy; |
||
1848 | |||
1849 | /* However, all the other coordinates will have been left untouched so |
||
1850 | * as not to introduce numerical error. Recompute them if they |
||
1851 | * exceed the 16.16 limits. |
||
1852 | */ |
||
1853 | if (unlikely (line_exceeds_16_16 (&t->left))) { |
||
1854 | project_line_x_onto_16_16 (&t->left, t->top, t->bottom, |
||
1855 | &xtraps[i].left); |
||
1856 | xtraps[i].left.p1.x += dx; |
||
1857 | xtraps[i].left.p2.x += dx; |
||
1858 | xtraps[i].left.p1.y = xtraps[i].top; |
||
1859 | xtraps[i].left.p2.y = xtraps[i].bottom; |
||
1860 | } else { |
||
1861 | xtraps[i].left.p1.x = _cairo_fixed_to_16_16(t->left.p1.x) + dx; |
||
1862 | xtraps[i].left.p1.y = _cairo_fixed_to_16_16(t->left.p1.y) + dy; |
||
1863 | xtraps[i].left.p2.x = _cairo_fixed_to_16_16(t->left.p2.x) + dx; |
||
1864 | xtraps[i].left.p2.y = _cairo_fixed_to_16_16(t->left.p2.y) + dy; |
||
1865 | } |
||
1866 | |||
1867 | if (unlikely (line_exceeds_16_16 (&t->right))) { |
||
1868 | project_line_x_onto_16_16 (&t->right, t->top, t->bottom, |
||
1869 | &xtraps[i].right); |
||
1870 | xtraps[i].right.p1.x += dx; |
||
1871 | xtraps[i].right.p2.x += dx; |
||
1872 | xtraps[i].right.p1.y = xtraps[i].top; |
||
1873 | xtraps[i].right.p2.y = xtraps[i].bottom; |
||
1874 | } else { |
||
1875 | xtraps[i].right.p1.x = _cairo_fixed_to_16_16(t->right.p1.x) + dx; |
||
1876 | xtraps[i].right.p1.y = _cairo_fixed_to_16_16(t->right.p1.y) + dy; |
||
1877 | xtraps[i].right.p2.x = _cairo_fixed_to_16_16(t->right.p2.x) + dx; |
||
1878 | xtraps[i].right.p2.y = _cairo_fixed_to_16_16(t->right.p2.y) + dy; |
||
1879 | } |
||
1880 | } |
||
1881 | |||
1882 | if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { |
||
1883 | src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p1.x); |
||
1884 | src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p1.y); |
||
1885 | } else { |
||
1886 | src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p2.x); |
||
1887 | src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p2.y); |
||
1888 | } |
||
1889 | src_x += dst_x; |
||
1890 | src_y += dst_y; |
||
1891 | |||
1892 | _cairo_xlib_surface_ensure_picture (dst); |
||
1893 | _cairo_xlib_surface_set_precision (dst, antialias); |
||
1894 | XRenderCompositeTrapezoids (dst->dpy, |
||
1895 | _render_operator (op), |
||
1896 | src->picture, dst->picture, |
||
1897 | pict_format, |
||
1898 | src_x, src_y, |
||
1899 | xtraps, traps->num_traps); |
||
1900 | |||
1901 | if (xtraps != xtraps_stack) |
||
1902 | free (xtraps); |
||
1903 | |||
1904 | return CAIRO_STATUS_SUCCESS; |
||
1905 | } |
||
1906 | |||
1907 | static cairo_int_status_t |
||
1908 | composite_tristrip (void *abstract_dst, |
||
1909 | cairo_operator_t op, |
||
1910 | cairo_surface_t *abstract_src, |
||
1911 | int src_x, |
||
1912 | int src_y, |
||
1913 | int dst_x, |
||
1914 | int dst_y, |
||
1915 | const cairo_rectangle_int_t *extents, |
||
1916 | cairo_antialias_t antialias, |
||
1917 | cairo_tristrip_t *strip) |
||
1918 | { |
||
1919 | cairo_xlib_surface_t *dst = abstract_dst; |
||
1920 | cairo_xlib_display_t *display = dst->display; |
||
1921 | cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; |
||
1922 | XRenderPictFormat *pict_format; |
||
1923 | XPointFixed points_stack[CAIRO_STACK_ARRAY_LENGTH (XPointFixed)]; |
||
1924 | XPointFixed *points = points_stack; |
||
1925 | int dx, dy; |
||
1926 | int i; |
||
1927 | |||
1928 | //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); |
||
1929 | |||
1930 | pict_format = |
||
1931 | _cairo_xlib_display_get_xrender_format (display, |
||
1932 | antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8); |
||
1933 | |||
1934 | if (strip->num_points > ARRAY_LENGTH (points_stack)) { |
||
1935 | points = _cairo_malloc_ab (strip->num_points, sizeof (XPointFixed)); |
||
1936 | if (unlikely (points == NULL)) |
||
1937 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1938 | } |
||
1939 | |||
1940 | dx = -dst_x << 16; |
||
1941 | dy = -dst_y << 16; |
||
1942 | for (i = 0; i < strip->num_points; i++) { |
||
1943 | cairo_point_t *p = &strip->points[i]; |
||
1944 | |||
1945 | points[i].x = _cairo_fixed_to_16_16(p->x) + dx; |
||
1946 | points[i].y = _cairo_fixed_to_16_16(p->y) + dy; |
||
1947 | } |
||
1948 | |||
1949 | src_x += _cairo_fixed_16_16_floor (points[0].x) + dst_x; |
||
1950 | src_y += _cairo_fixed_16_16_floor (points[0].y) + dst_y; |
||
1951 | |||
1952 | _cairo_xlib_surface_ensure_picture (dst); |
||
1953 | _cairo_xlib_surface_set_precision (dst, antialias); |
||
1954 | XRenderCompositeTriStrip (dst->dpy, |
||
1955 | _render_operator (op), |
||
1956 | src->picture, dst->picture, |
||
1957 | pict_format, |
||
1958 | src_x, src_y, |
||
1959 | points, strip->num_points); |
||
1960 | |||
1961 | if (points != points_stack) |
||
1962 | free (points); |
||
1963 | |||
1964 | return CAIRO_STATUS_SUCCESS; |
||
1965 | } |
||
1966 | |||
1967 | const cairo_compositor_t * |
||
1968 | _cairo_xlib_traps_compositor_get (void) |
||
1969 | { |
||
1970 | static cairo_traps_compositor_t compositor; |
||
1971 | |||
1972 | if (compositor.base.delegate == NULL) { |
||
1973 | _cairo_traps_compositor_init (&compositor, |
||
1974 | _cairo_xlib_mask_compositor_get ()); |
||
1975 | |||
1976 | compositor.acquire = acquire; |
||
1977 | compositor.release = release; |
||
1978 | compositor.set_clip_region = set_clip_region; |
||
1979 | compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern; |
||
1980 | compositor.draw_image_boxes = draw_image_boxes; |
||
1981 | compositor.copy_boxes = copy_boxes; |
||
1982 | compositor.fill_boxes = fill_boxes; |
||
1983 | compositor.check_composite = check_composite; |
||
1984 | compositor.composite = composite; |
||
1985 | compositor.lerp = lerp; |
||
1986 | //compositor.check_composite_boxes = check_composite_boxes; |
||
1987 | compositor.composite_boxes = composite_boxes; |
||
1988 | //compositor.check_composite_traps = check_composite_traps; |
||
1989 | compositor.composite_traps = composite_traps; |
||
1990 | //compositor.check_composite_tristrip = check_composite_tristrip; |
||
1991 | compositor.composite_tristrip = composite_tristrip; |
||
1992 | compositor.check_composite_glyphs = check_composite_glyphs; |
||
1993 | compositor.composite_glyphs = composite_glyphs; |
||
1994 | } |
||
1995 | |||
1996 | return &compositor.base; |
||
1997 | } |
||
1998 | |||
1999 | #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */>><>><>>>><>><>>>>>>>>>>>>>=>><>><>><>>>>>>>>>>>>>>>>>> |