1 /* cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Intel Corporation
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it either under the terms of the GNU Lesser General Public
7  * License version 2.1 as published by the Free Software Foundation
8  * (the "LGPL") or, at your option, under the terms of the Mozilla
9  * Public License Version 1.1 (the "MPL"). If you do not alter this
10  * notice, a recipient may use your version of this file under either
11  * the MPL or the LGPL.
12  *
13  * You should have received a copy of the LGPL along with this library
14  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16  * You should have received a copy of the MPL along with this library
17  * in the file COPYING-MPL-1.1
18  *
19  * The contents of this file are subject to the Mozilla Public License
20  * Version 1.1 (the "License"); you may not use this file except in
21  * compliance with the License. You may obtain a copy of the License at
22  * http://www.mozilla.org/MPL/
23  *
24  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26  * the specific language governing rights and limitations.
27  *
28  * Contributor(s):
29  *	Chris Wilson <chris@chris-wilson.co.uk>
30  */
31 
32 #include "cairoint.h"
33 
34 #include "cairo-xcb-private.h"
35 
36 #include <xcb/xcbext.h>
37 
38 xcb_pixmap_t
_cairo_xcb_connection_create_pixmap(cairo_xcb_connection_t * connection,uint8_t depth,xcb_drawable_t drawable,uint16_t width,uint16_t height)39 _cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection,
40 				     uint8_t depth,
41 				     xcb_drawable_t drawable,
42 				     uint16_t width,
43 				     uint16_t height)
44 {
45     xcb_pixmap_t pixmap = _cairo_xcb_connection_get_xid (connection);
46 
47     assert (width > 0);
48     assert (height > 0);
49     xcb_create_pixmap (connection->xcb_connection,
50 		       depth, pixmap, drawable,
51 		       width, height);
52     return pixmap;
53 }
54 
55 void
_cairo_xcb_connection_free_pixmap(cairo_xcb_connection_t * connection,xcb_pixmap_t pixmap)56 _cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection,
57 				   xcb_pixmap_t pixmap)
58 {
59     xcb_free_pixmap (connection->xcb_connection, pixmap);
60     _cairo_xcb_connection_put_xid (connection, pixmap);
61 }
62 
63 xcb_gcontext_t
_cairo_xcb_connection_create_gc(cairo_xcb_connection_t * connection,xcb_drawable_t drawable,uint32_t value_mask,uint32_t * values)64 _cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection,
65 				 xcb_drawable_t drawable,
66 				 uint32_t value_mask,
67 				 uint32_t *values)
68 {
69     xcb_gcontext_t gc = _cairo_xcb_connection_get_xid (connection);
70     xcb_create_gc (connection->xcb_connection, gc, drawable,
71 		   value_mask, values);
72     return gc;
73 }
74 
75 void
_cairo_xcb_connection_free_gc(cairo_xcb_connection_t * connection,xcb_gcontext_t gc)76 _cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection,
77 			       xcb_gcontext_t gc)
78 {
79     xcb_free_gc (connection->xcb_connection, gc);
80     _cairo_xcb_connection_put_xid (connection, gc);
81 }
82 
83 void
_cairo_xcb_connection_change_gc(cairo_xcb_connection_t * connection,xcb_gcontext_t gc,uint32_t value_mask,uint32_t * values)84 _cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection,
85 				 xcb_gcontext_t gc,
86 				 uint32_t value_mask,
87 				 uint32_t *values)
88 {
89     xcb_change_gc (connection->xcb_connection, gc,
90 		   value_mask, values);
91 }
92 
93 void
_cairo_xcb_connection_copy_area(cairo_xcb_connection_t * connection,xcb_drawable_t src,xcb_drawable_t dst,xcb_gcontext_t gc,int16_t src_x,int16_t src_y,int16_t dst_x,int16_t dst_y,uint16_t width,uint16_t height)94 _cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection,
95 				 xcb_drawable_t src,
96 				 xcb_drawable_t dst,
97 				 xcb_gcontext_t gc,
98 				 int16_t src_x,
99 				 int16_t src_y,
100 				 int16_t dst_x,
101 				 int16_t dst_y,
102 				 uint16_t width,
103 				 uint16_t height)
104 {
105     xcb_copy_area (connection->xcb_connection, src, dst, gc,
106 		   src_x, src_y, dst_x, dst_y, width, height);
107 }
108 
109 void
_cairo_xcb_connection_poly_fill_rectangle(cairo_xcb_connection_t * connection,xcb_drawable_t dst,xcb_gcontext_t gc,uint32_t num_rectangles,xcb_rectangle_t * rectangles)110 _cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection,
111 					   xcb_drawable_t dst,
112 					   xcb_gcontext_t gc,
113 					   uint32_t num_rectangles,
114 					   xcb_rectangle_t *rectangles)
115 {
116     xcb_poly_fill_rectangle (connection->xcb_connection, dst, gc,
117 			     num_rectangles, rectangles);
118 }
119 
120 void
_cairo_xcb_connection_put_image(cairo_xcb_connection_t * connection,xcb_drawable_t dst,xcb_gcontext_t gc,uint16_t width,uint16_t height,int16_t dst_x,int16_t dst_y,uint8_t depth,uint32_t stride,void * data)121 _cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection,
122 				 xcb_drawable_t dst,
123 				 xcb_gcontext_t gc,
124 				 uint16_t width,
125 				 uint16_t height,
126 				 int16_t dst_x,
127 				 int16_t dst_y,
128 				 uint8_t depth,
129 				 uint32_t stride,
130 				 void *data)
131 {
132     const uint32_t req_size = 18;
133     uint32_t length = height * stride;
134     uint32_t len = (req_size + length) >> 2;
135 
136     if (len < connection->maximum_request_length) {
137 	xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP,
138 		       dst, gc, width, height, dst_x, dst_y, 0, depth,
139 		       length, data);
140     } else {
141 	int rows = (connection->maximum_request_length - req_size - 4) / stride;
142 	if (rows > 0) {
143 	    do {
144 		if (rows > height)
145 		    rows = height;
146 
147 		length = rows * stride;
148 
149 		xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP,
150 			       dst, gc, width, rows, dst_x, dst_y, 0, depth, length, data);
151 
152 		height -= rows;
153 		dst_y += rows;
154 		data = (char *) data + length;
155 	    } while (height);
156 	} else {
157 	    ASSERT_NOT_REACHED;
158 	}
159     }
160 }
161 
162 static void
_cairo_xcb_connection_do_put_subimage(cairo_xcb_connection_t * connection,xcb_drawable_t dst,xcb_gcontext_t gc,int16_t src_x,int16_t src_y,uint16_t width,uint16_t height,uint16_t cpp,int stride,int16_t dst_x,int16_t dst_y,uint8_t depth,void * _data)163 _cairo_xcb_connection_do_put_subimage (cairo_xcb_connection_t *connection,
164 				       xcb_drawable_t dst,
165 				       xcb_gcontext_t gc,
166 				       int16_t src_x,
167 				       int16_t src_y,
168 				       uint16_t width,
169 				       uint16_t height,
170 				       uint16_t cpp,
171 				       int stride,
172 				       int16_t dst_x,
173 				       int16_t dst_y,
174 				       uint8_t depth,
175 				       void *_data)
176 {
177     xcb_protocol_request_t xcb_req = {
178 	0 /* count */,
179 	0 /* ext */,
180 	XCB_PUT_IMAGE /* opcode */,
181 	1 /* isvoid (doesn't cause a reply) */
182     };
183     xcb_put_image_request_t req;
184     struct iovec vec_stack[CAIRO_STACK_ARRAY_LENGTH (struct iovec)];
185     struct iovec *vec = vec_stack;
186     uint32_t len = 0;
187     uint8_t *data = _data;
188     int n = 3;
189     /* Two extra entries are needed for xcb, two for us */
190     int entries_needed = height + 2 + 2;
191 
192     req.format = XCB_IMAGE_FORMAT_Z_PIXMAP;
193     req.drawable = dst;
194     req.gc = gc;
195     req.width = width;
196     req.height = height;
197     req.dst_x = dst_x;
198     req.dst_y = dst_y;
199     req.left_pad = 0;
200     req.depth = depth;
201     req.pad0[0] = 0;
202     req.pad0[1] = 0;
203 
204     if (entries_needed > ARRAY_LENGTH (vec_stack)) {
205 	vec = _cairo_malloc_ab (entries_needed, sizeof (struct iovec));
206 	if (unlikely (vec == NULL)) {
207 	    /* XXX loop over ARRAY_LENGTH (vec_stack) */
208 	    return;
209 	}
210     }
211 
212     data += src_y * stride + src_x * cpp;
213     /* vec[1] will be used in XCB if it has to use BigRequests or insert a sync,
214      * vec[0] is used if the internal queue needs to be flushed. */
215     vec[2].iov_base = (char *) &req;
216     vec[2].iov_len = sizeof (req);
217 
218     /* Now comes the actual data */
219     while (height--) {
220 	vec[n].iov_base = data;
221 	vec[n].iov_len = cpp * width;
222 	len += cpp * width;
223 	data += stride;
224 	n++;
225     }
226 
227     /* And again some padding */
228     vec[n].iov_base = 0;
229     vec[n].iov_len = -len & 3;
230     n++;
231 
232     /* For efficiency reasons, this functions writes the request "directly" to
233      * the xcb connection to avoid having to copy the data around. */
234     assert (n == entries_needed);
235     xcb_req.count = n - 2;
236     xcb_send_request (connection->xcb_connection, 0, &vec[2], &xcb_req);
237 
238     if (vec != vec_stack)
239 	free (vec);
240 }
241 
242 void
_cairo_xcb_connection_put_subimage(cairo_xcb_connection_t * connection,xcb_drawable_t dst,xcb_gcontext_t gc,int16_t src_x,int16_t src_y,uint16_t width,uint16_t height,uint16_t cpp,int stride,int16_t dst_x,int16_t dst_y,uint8_t depth,void * _data)243 _cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection,
244 				    xcb_drawable_t dst,
245 				    xcb_gcontext_t gc,
246 				    int16_t src_x,
247 				    int16_t src_y,
248 				    uint16_t width,
249 				    uint16_t height,
250 				    uint16_t cpp,
251 				    int stride,
252 				    int16_t dst_x,
253 				    int16_t dst_y,
254 				    uint8_t depth,
255 				    void *_data)
256 {
257     const uint32_t req_size = sizeof(xcb_put_image_request_t);
258     uint32_t length = height * cpp * width;
259     uint32_t len = (req_size + length) >> 2;
260 
261     if (len < connection->maximum_request_length) {
262 	_cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y,
263 			width, height, cpp, stride, dst_x, dst_y, depth, _data);
264     } else {
265 	int rows = (connection->maximum_request_length - req_size - 4) / (cpp * width);
266 	if (rows > 0) {
267 	    do {
268 		if (rows > height)
269 		    rows = height;
270 
271 		_cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y,
272 			width, rows, cpp, stride, dst_x, dst_y, depth, _data);
273 
274 		height -= rows;
275 		dst_y += rows;
276 		_data = (char *) _data + stride * rows;
277 	    } while (height);
278 	} else {
279 	    ASSERT_NOT_REACHED;
280 	}
281     }
282 }
283 
284 xcb_get_image_reply_t *
_cairo_xcb_connection_get_image(cairo_xcb_connection_t * connection,xcb_drawable_t src,int16_t src_x,int16_t src_y,uint16_t width,uint16_t height)285 _cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection,
286 				 xcb_drawable_t src,
287 				 int16_t src_x,
288 				 int16_t src_y,
289 				 uint16_t width,
290 				 uint16_t height)
291 {
292     return xcb_get_image_reply (connection->xcb_connection,
293 				xcb_get_image (connection->xcb_connection,
294 					       XCB_IMAGE_FORMAT_Z_PIXMAP,
295 					       src,
296 					       src_x, src_y,
297 					       width, height,
298 					       (uint32_t) -1),
299 				NULL);
300 }
301