1 /*
2  * Copyright © 2014 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #include "glamor_priv.h"
24 #include "glamor_transfer.h"
25 
26 /* XXX a kludge for now */
27 void
glamor_format_for_pixmap(PixmapPtr pixmap,GLenum * format,GLenum * type)28 glamor_format_for_pixmap(PixmapPtr pixmap, GLenum *format, GLenum *type)
29 {
30     switch (pixmap->drawable.depth) {
31     case 24:
32     case 32:
33         *format = GL_BGRA;
34         *type = GL_UNSIGNED_INT_8_8_8_8_REV;
35         break;
36     case 30:
37         *format = GL_BGRA;
38         *type = GL_UNSIGNED_INT_2_10_10_10_REV;
39         break;
40     case 16:
41         *format = GL_RGB;
42         *type = GL_UNSIGNED_SHORT_5_6_5;
43         break;
44     case 15:
45         *format = GL_BGRA;
46         *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
47         break;
48     case 8:
49         *format = glamor_get_screen_private(pixmap->drawable.pScreen)->one_channel_format;
50         *type = GL_UNSIGNED_BYTE;
51         break;
52     default:
53         FatalError("Invalid pixmap depth %d\n", pixmap->drawable.depth);
54         break;
55     }
56 }
57 
58 /*
59  * Write a region of bits into a pixmap
60  */
61 void
glamor_upload_boxes(PixmapPtr pixmap,BoxPtr in_boxes,int in_nbox,int dx_src,int dy_src,int dx_dst,int dy_dst,uint8_t * bits,uint32_t byte_stride)62 glamor_upload_boxes(PixmapPtr pixmap, BoxPtr in_boxes, int in_nbox,
63                     int dx_src, int dy_src,
64                     int dx_dst, int dy_dst,
65                     uint8_t *bits, uint32_t byte_stride)
66 {
67     ScreenPtr                   screen = pixmap->drawable.pScreen;
68     glamor_screen_private       *glamor_priv = glamor_get_screen_private(screen);
69     glamor_pixmap_private       *priv = glamor_get_pixmap_private(pixmap);
70     int                         box_index;
71     int                         bytes_per_pixel = pixmap->drawable.bitsPerPixel >> 3;
72     GLenum                      type;
73     GLenum                      format;
74 
75     glamor_format_for_pixmap(pixmap, &format, &type);
76 
77     glamor_make_current(glamor_priv);
78 
79     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
80 
81     if (glamor_priv->has_unpack_subimage)
82         glPixelStorei(GL_UNPACK_ROW_LENGTH, byte_stride / bytes_per_pixel);
83 
84     glamor_pixmap_loop(priv, box_index) {
85         BoxPtr                  box = glamor_pixmap_box_at(priv, box_index);
86         glamor_pixmap_fbo       *fbo = glamor_pixmap_fbo_at(priv, box_index);
87         BoxPtr                  boxes = in_boxes;
88         int                     nbox = in_nbox;
89 
90         glamor_bind_texture(glamor_priv, GL_TEXTURE0, fbo, TRUE);
91 
92         while (nbox--) {
93 
94             /* compute drawable coordinates */
95             int x1 = MAX(boxes->x1 + dx_dst, box->x1);
96             int x2 = MIN(boxes->x2 + dx_dst, box->x2);
97             int y1 = MAX(boxes->y1 + dy_dst, box->y1);
98             int y2 = MIN(boxes->y2 + dy_dst, box->y2);
99 
100             size_t ofs = (y1 - dy_dst + dy_src) * byte_stride;
101             ofs += (x1 - dx_dst + dx_src) * bytes_per_pixel;
102 
103             boxes++;
104 
105             if (x2 <= x1 || y2 <= y1)
106                 continue;
107 
108             if (glamor_priv->has_unpack_subimage ||
109                 x2 - x1 == byte_stride / bytes_per_pixel) {
110                 glTexSubImage2D(GL_TEXTURE_2D, 0,
111                                 x1 - box->x1, y1 - box->y1,
112                                 x2 - x1, y2 - y1,
113                                 format, type,
114                                 bits + ofs);
115             } else {
116                 for (; y1 < y2; y1++, ofs += byte_stride)
117                     glTexSubImage2D(GL_TEXTURE_2D, 0,
118                                     x1 - box->x1, y1 - box->y1,
119                                     x2 - x1, 1,
120                                     format, type,
121                                     bits + ofs);
122             }
123         }
124     }
125 
126     if (glamor_priv->has_unpack_subimage)
127         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
128 }
129 
130 /*
131  * Upload a region of data
132  */
133 
134 void
glamor_upload_region(PixmapPtr pixmap,RegionPtr region,int region_x,int region_y,uint8_t * bits,uint32_t byte_stride)135 glamor_upload_region(PixmapPtr pixmap, RegionPtr region,
136                      int region_x, int region_y,
137                      uint8_t *bits, uint32_t byte_stride)
138 {
139     glamor_upload_boxes(pixmap, RegionRects(region), RegionNumRects(region),
140                         -region_x, -region_y,
141                         0, 0,
142                         bits, byte_stride);
143 }
144 
145 /*
146  * Take the data in the pixmap and stuff it back into the FBO
147  */
148 void
glamor_upload_pixmap(PixmapPtr pixmap)149 glamor_upload_pixmap(PixmapPtr pixmap)
150 {
151     BoxRec box;
152 
153     box.x1 = 0;
154     box.x2 = pixmap->drawable.width;
155     box.y1 = 0;
156     box.y2 = pixmap->drawable.height;
157     glamor_upload_boxes(pixmap, &box, 1, 0, 0, 0, 0,
158                         pixmap->devPrivate.ptr, pixmap->devKind);
159 }
160 
161 /*
162  * Read stuff from the pixmap FBOs and write to memory
163  */
164 void
glamor_download_boxes(PixmapPtr pixmap,BoxPtr in_boxes,int in_nbox,int dx_src,int dy_src,int dx_dst,int dy_dst,uint8_t * bits,uint32_t byte_stride)165 glamor_download_boxes(PixmapPtr pixmap, BoxPtr in_boxes, int in_nbox,
166                       int dx_src, int dy_src,
167                       int dx_dst, int dy_dst,
168                       uint8_t *bits, uint32_t byte_stride)
169 {
170     ScreenPtr screen = pixmap->drawable.pScreen;
171     glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
172     glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap);
173     int box_index;
174     int bytes_per_pixel = pixmap->drawable.bitsPerPixel >> 3;
175     GLenum type;
176     GLenum format;
177 
178     glamor_format_for_pixmap(pixmap, &format, &type);
179 
180     glamor_make_current(glamor_priv);
181 
182     glPixelStorei(GL_PACK_ALIGNMENT, 4);
183     if (glamor_priv->has_pack_subimage)
184         glPixelStorei(GL_PACK_ROW_LENGTH, byte_stride / bytes_per_pixel);
185 
186     glamor_pixmap_loop(priv, box_index) {
187         BoxPtr                  box = glamor_pixmap_box_at(priv, box_index);
188         glamor_pixmap_fbo       *fbo = glamor_pixmap_fbo_at(priv, box_index);
189         BoxPtr                  boxes = in_boxes;
190         int                     nbox = in_nbox;
191 
192         /* This should not be called on GLAMOR_FBO_NO_FBO-allocated pixmaps. */
193         assert(fbo->fb);
194         glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb);
195 
196         while (nbox--) {
197 
198             /* compute drawable coordinates */
199             int                     x1 = MAX(boxes->x1 + dx_src, box->x1);
200             int                     x2 = MIN(boxes->x2 + dx_src, box->x2);
201             int                     y1 = MAX(boxes->y1 + dy_src, box->y1);
202             int                     y2 = MIN(boxes->y2 + dy_src, box->y2);
203             size_t ofs = (y1 - dy_src + dy_dst) * byte_stride;
204             ofs += (x1 - dx_src + dx_dst) * bytes_per_pixel;
205 
206             boxes++;
207 
208             if (x2 <= x1 || y2 <= y1)
209                 continue;
210 
211             if (glamor_priv->has_pack_subimage ||
212                 x2 - x1 == byte_stride / bytes_per_pixel) {
213                 glReadPixels(x1 - box->x1, y1 - box->y1, x2 - x1, y2 - y1, format, type, bits + ofs);
214             } else {
215                 for (; y1 < y2; y1++, ofs += byte_stride)
216                     glReadPixels(x1 - box->x1, y1 - box->y1, x2 - x1, 1, format, type, bits + ofs);
217             }
218         }
219     }
220     if (glamor_priv->has_pack_subimage)
221         glPixelStorei(GL_PACK_ROW_LENGTH, 0);
222 }
223 
224 /*
225  * Read data from the pixmap FBO
226  */
227 void
glamor_download_rect(PixmapPtr pixmap,int x,int y,int w,int h,uint8_t * bits)228 glamor_download_rect(PixmapPtr pixmap, int x, int y, int w, int h, uint8_t *bits)
229 {
230     BoxRec      box;
231 
232     box.x1 = x;
233     box.x2 = x + w;
234     box.y1 = y;
235     box.y2 = y + h;
236 
237     glamor_download_boxes(pixmap, &box, 1, 0, 0, -x, -y,
238                           bits, PixmapBytePad(w, pixmap->drawable.depth));
239 }
240 
241 /*
242  * Pull the data from the FBO down to the pixmap
243  */
244 void
glamor_download_pixmap(PixmapPtr pixmap)245 glamor_download_pixmap(PixmapPtr pixmap)
246 {
247     BoxRec      box;
248 
249     box.x1 = 0;
250     box.x2 = pixmap->drawable.width;
251     box.y1 = 0;
252     box.y2 = pixmap->drawable.height;
253 
254     glamor_download_boxes(pixmap, &box, 1, 0, 0, 0, 0,
255                           pixmap->devPrivate.ptr, pixmap->devKind);
256 }
257