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/*
27 * Write a region of bits into a pixmap
28 */
29void
30glamor_upload_boxes(PixmapPtr pixmap, BoxPtr in_boxes, int in_nbox,
31                    int dx_src, int dy_src,
32                    int dx_dst, int dy_dst,
33                    uint8_t *bits, uint32_t byte_stride)
34{
35    ScreenPtr                   screen = pixmap->drawable.pScreen;
36    glamor_screen_private       *glamor_priv = glamor_get_screen_private(screen);
37    glamor_pixmap_private       *priv = glamor_get_pixmap_private(pixmap);
38    int                         box_index;
39    int                         bytes_per_pixel = pixmap->drawable.bitsPerPixel >> 3;
40    const struct glamor_format *f = glamor_format_for_pixmap(pixmap);
41
42    glamor_make_current(glamor_priv);
43
44    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
45
46    if (glamor_priv->has_unpack_subimage)
47        glPixelStorei(GL_UNPACK_ROW_LENGTH, byte_stride / bytes_per_pixel);
48
49    glamor_pixmap_loop(priv, box_index) {
50        BoxPtr                  box = glamor_pixmap_box_at(priv, box_index);
51        glamor_pixmap_fbo       *fbo = glamor_pixmap_fbo_at(priv, box_index);
52        BoxPtr                  boxes = in_boxes;
53        int                     nbox = in_nbox;
54
55        glamor_bind_texture(glamor_priv, GL_TEXTURE0, fbo, TRUE);
56
57        while (nbox--) {
58
59            /* compute drawable coordinates */
60            int x1 = MAX(boxes->x1 + dx_dst, box->x1);
61            int x2 = MIN(boxes->x2 + dx_dst, box->x2);
62            int y1 = MAX(boxes->y1 + dy_dst, box->y1);
63            int y2 = MIN(boxes->y2 + dy_dst, box->y2);
64
65            size_t ofs = (y1 - dy_dst + dy_src) * byte_stride;
66            ofs += (x1 - dx_dst + dx_src) * bytes_per_pixel;
67
68            boxes++;
69
70            if (x2 <= x1 || y2 <= y1)
71                continue;
72
73            if (glamor_priv->has_unpack_subimage ||
74                x2 - x1 == byte_stride / bytes_per_pixel) {
75                glTexSubImage2D(GL_TEXTURE_2D, 0,
76                                x1 - box->x1, y1 - box->y1,
77                                x2 - x1, y2 - y1,
78                                f->format, f->type,
79                                bits + ofs);
80            } else {
81                for (; y1 < y2; y1++, ofs += byte_stride)
82                    glTexSubImage2D(GL_TEXTURE_2D, 0,
83                                    x1 - box->x1, y1 - box->y1,
84                                    x2 - x1, 1,
85                                    f->format, f->type,
86                                    bits + ofs);
87            }
88        }
89    }
90
91    if (glamor_priv->has_unpack_subimage)
92        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
93}
94
95/*
96 * Upload a region of data
97 */
98
99void
100glamor_upload_region(PixmapPtr pixmap, RegionPtr region,
101                     int region_x, int region_y,
102                     uint8_t *bits, uint32_t byte_stride)
103{
104    glamor_upload_boxes(pixmap, RegionRects(region), RegionNumRects(region),
105                        -region_x, -region_y,
106                        0, 0,
107                        bits, byte_stride);
108}
109
110/*
111 * Take the data in the pixmap and stuff it back into the FBO
112 */
113void
114glamor_upload_pixmap(PixmapPtr pixmap)
115{
116    BoxRec box;
117
118    box.x1 = 0;
119    box.x2 = pixmap->drawable.width;
120    box.y1 = 0;
121    box.y2 = pixmap->drawable.height;
122    glamor_upload_boxes(pixmap, &box, 1, 0, 0, 0, 0,
123                        pixmap->devPrivate.ptr, pixmap->devKind);
124}
125
126/*
127 * Read stuff from the pixmap FBOs and write to memory
128 */
129void
130glamor_download_boxes(PixmapPtr pixmap, BoxPtr in_boxes, int in_nbox,
131                      int dx_src, int dy_src,
132                      int dx_dst, int dy_dst,
133                      uint8_t *bits, uint32_t byte_stride)
134{
135    ScreenPtr screen = pixmap->drawable.pScreen;
136    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
137    glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap);
138    int box_index;
139    int bytes_per_pixel = pixmap->drawable.bitsPerPixel >> 3;
140    const struct glamor_format *f = glamor_format_for_pixmap(pixmap);
141
142    glamor_make_current(glamor_priv);
143
144    glPixelStorei(GL_PACK_ALIGNMENT, 4);
145    if (glamor_priv->has_pack_subimage)
146        glPixelStorei(GL_PACK_ROW_LENGTH, byte_stride / bytes_per_pixel);
147
148    glamor_pixmap_loop(priv, box_index) {
149        BoxPtr                  box = glamor_pixmap_box_at(priv, box_index);
150        glamor_pixmap_fbo       *fbo = glamor_pixmap_fbo_at(priv, box_index);
151        BoxPtr                  boxes = in_boxes;
152        int                     nbox = in_nbox;
153
154        /* This should not be called on GLAMOR_FBO_NO_FBO-allocated pixmaps. */
155        assert(fbo->fb);
156        glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb);
157
158        while (nbox--) {
159
160            /* compute drawable coordinates */
161            int                     x1 = MAX(boxes->x1 + dx_src, box->x1);
162            int                     x2 = MIN(boxes->x2 + dx_src, box->x2);
163            int                     y1 = MAX(boxes->y1 + dy_src, box->y1);
164            int                     y2 = MIN(boxes->y2 + dy_src, box->y2);
165            size_t ofs = (y1 - dy_src + dy_dst) * byte_stride;
166            ofs += (x1 - dx_src + dx_dst) * bytes_per_pixel;
167
168            boxes++;
169
170            if (x2 <= x1 || y2 <= y1)
171                continue;
172
173            if (glamor_priv->has_pack_subimage ||
174                x2 - x1 == byte_stride / bytes_per_pixel) {
175                glReadPixels(x1 - box->x1, y1 - box->y1, x2 - x1, y2 - y1, f->format, f->type, bits + ofs);
176            } else {
177                for (; y1 < y2; y1++, ofs += byte_stride)
178                    glReadPixels(x1 - box->x1, y1 - box->y1, x2 - x1, 1, f->format, f->type, bits + ofs);
179            }
180        }
181    }
182    if (glamor_priv->has_pack_subimage)
183        glPixelStorei(GL_PACK_ROW_LENGTH, 0);
184}
185
186/*
187 * Read data from the pixmap FBO
188 */
189void
190glamor_download_rect(PixmapPtr pixmap, int x, int y, int w, int h, uint8_t *bits)
191{
192    BoxRec      box;
193
194    box.x1 = x;
195    box.x2 = x + w;
196    box.y1 = y;
197    box.y2 = y + h;
198
199    glamor_download_boxes(pixmap, &box, 1, 0, 0, -x, -y,
200                          bits, PixmapBytePad(w, pixmap->drawable.depth));
201}
202
203/*
204 * Pull the data from the FBO down to the pixmap
205 */
206void
207glamor_download_pixmap(PixmapPtr pixmap)
208{
209    BoxRec      box;
210
211    box.x1 = 0;
212    box.x2 = pixmap->drawable.width;
213    box.y1 = 0;
214    box.y2 = pixmap->drawable.height;
215
216    glamor_download_boxes(pixmap, &box, 1, 0, 0, 0, 0,
217                          pixmap->devPrivate.ptr, pixmap->devKind);
218}
219