1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <gegl.h>
24 #include <gtk/gtk.h>
25
26 #include "display-types.h"
27
28 #include "gimpdisplayxfer.h"
29
30
31 #define NUM_PAGES 2
32
33 typedef struct _RTree RTree;
34 typedef struct _RTreeNode RTreeNode;
35
36 struct _RTreeNode
37 {
38 RTreeNode *children[2];
39 RTreeNode *next;
40 gint x, y, w, h;
41 };
42
43 struct _RTree
44 {
45 RTreeNode root;
46 RTreeNode *available;
47 };
48
49 struct _GimpDisplayXfer
50 {
51 /* track subregions of render_surface for efficient uploads */
52 RTree rtree;
53 cairo_surface_t *render_surface[NUM_PAGES];
54 gint page;
55 };
56
57
58 gint GIMP_DISPLAY_RENDER_BUF_WIDTH = 256;
59 gint GIMP_DISPLAY_RENDER_BUF_HEIGHT = 256;
60
61
62 static RTreeNode *
rtree_node_create(RTree * rtree,RTreeNode ** prev,gint x,gint y,gint w,gint h)63 rtree_node_create (RTree *rtree,
64 RTreeNode **prev,
65 gint x,
66 gint y,
67 gint w,
68 gint h)
69 {
70 RTreeNode *node;
71
72 gimp_assert (x >= 0 && x+w <= rtree->root.w);
73 gimp_assert (y >= 0 && y+h <= rtree->root.h);
74
75 if (w <= 0 || h <= 0)
76 return NULL;
77
78 node = g_slice_alloc (sizeof (*node));
79
80 node->children[0] = NULL;
81 node->children[1] = NULL;
82 node->x = x;
83 node->y = y;
84 node->w = w;
85 node->h = h;
86
87 node->next = *prev;
88 *prev = node;
89
90 return node;
91 }
92
93 static void
rtree_node_destroy(RTree * rtree,RTreeNode * node)94 rtree_node_destroy (RTree *rtree,
95 RTreeNode *node)
96 {
97 gint i;
98
99 for (i = 0; i < 2; i++)
100 {
101 if (node->children[i])
102 rtree_node_destroy (rtree, node->children[i]);
103 }
104
105 g_slice_free (RTreeNode, node);
106 }
107
108 static RTreeNode *
rtree_node_insert(RTree * rtree,RTreeNode ** prev,RTreeNode * node,gint w,gint h)109 rtree_node_insert (RTree *rtree,
110 RTreeNode **prev,
111 RTreeNode *node,
112 gint w,
113 gint h)
114 {
115 *prev = node->next;
116
117 if (((node->w - w) | (node->h - h)) > 1)
118 {
119 gint ww = node->w - w;
120 gint hh = node->h - h;
121
122 if (ww >= hh)
123 {
124 node->children[0] = rtree_node_create (rtree, prev,
125 node->x + w, node->y,
126 ww, node->h);
127 node->children[1] = rtree_node_create (rtree, prev,
128 node->x, node->y + h,
129 w, hh);
130 }
131 else
132 {
133 node->children[0] = rtree_node_create (rtree, prev,
134 node->x, node->y + h,
135 node->w, hh);
136 node->children[1] = rtree_node_create (rtree, prev,
137 node->x + w, node->y,
138 ww, h);
139 }
140 }
141
142 return node;
143 }
144
145 static RTreeNode *
rtree_insert(RTree * rtree,gint w,gint h)146 rtree_insert (RTree *rtree,
147 gint w,
148 gint h)
149 {
150 RTreeNode *node, **prev;
151
152 for (prev = &rtree->available; (node = *prev); prev = &node->next)
153 if (node->w >= w && node->h >= h)
154 return rtree_node_insert (rtree, prev, node, w, h);
155
156 return NULL;
157 }
158
159 static void
rtree_init(RTree * rtree,gint w,gint h)160 rtree_init (RTree *rtree,
161 gint w,
162 gint h)
163 {
164 rtree->root.x = 0;
165 rtree->root.y = 0;
166 rtree->root.w = w;
167 rtree->root.h = h;
168 rtree->root.children[0] = NULL;
169 rtree->root.children[1] = NULL;
170 rtree->root.next = NULL;
171 rtree->available = &rtree->root;
172 }
173
174 static void
rtree_reset(RTree * rtree)175 rtree_reset (RTree *rtree)
176 {
177 gint i;
178
179 for (i = 0; i < 2; i++)
180 {
181 if (rtree->root.children[i] == NULL)
182 continue;
183
184 rtree_node_destroy (rtree, rtree->root.children[i]);
185 rtree->root.children[i] = NULL;
186 }
187
188 rtree->root.next = NULL;
189 rtree->available = &rtree->root;
190 }
191
192 static void
xfer_destroy(void * data)193 xfer_destroy (void *data)
194 {
195 GimpDisplayXfer *xfer = data;
196 gint i;
197
198 for (i = 0; i < NUM_PAGES; i++)
199 cairo_surface_destroy (xfer->render_surface[i]);
200
201 rtree_reset (&xfer->rtree);
202 g_free (xfer);
203 }
204
205 GimpDisplayXfer *
gimp_display_xfer_realize(GtkWidget * widget)206 gimp_display_xfer_realize (GtkWidget *widget)
207 {
208 GdkScreen *screen;
209 GimpDisplayXfer *xfer;
210 const gchar *env;
211
212 env = g_getenv ("GIMP_DISPLAY_RENDER_BUF_SIZE");
213 if (env)
214 {
215 gint width = atoi (env);
216 gint height = width;
217
218 env = strchr (env, 'x');
219 if (env)
220 height = atoi (env + 1);
221
222 if (width > 0 && width <= 8192 &&
223 height > 0 && height <= 8192)
224 {
225 GIMP_DISPLAY_RENDER_BUF_WIDTH = width;
226 GIMP_DISPLAY_RENDER_BUF_HEIGHT = height;
227 }
228 }
229
230 screen = gtk_widget_get_screen (widget);
231 xfer = g_object_get_data (G_OBJECT (screen), "gimp-display-xfer");
232
233 if (xfer == NULL)
234 {
235 cairo_t *cr;
236 gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH;
237 gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT;
238 int n;
239
240 xfer = g_new (GimpDisplayXfer, 1);
241 rtree_init (&xfer->rtree, w, h);
242
243 cr = gdk_cairo_create (gtk_widget_get_window (widget));
244 for (n = 0; n < NUM_PAGES; n++)
245 {
246 xfer->render_surface[n] =
247 cairo_surface_create_similar_image (cairo_get_target (cr),
248 CAIRO_FORMAT_ARGB32, w, h);
249 cairo_surface_mark_dirty (xfer->render_surface[n]);
250 }
251 cairo_destroy (cr);
252 xfer->page = 0;
253
254 g_object_set_data_full (G_OBJECT (screen),
255 "gimp-display-xfer",
256 xfer, xfer_destroy);
257 }
258
259 return xfer;
260 }
261
262 cairo_surface_t *
gimp_display_xfer_get_surface(GimpDisplayXfer * xfer,gint w,gint h,gint * src_x,gint * src_y)263 gimp_display_xfer_get_surface (GimpDisplayXfer *xfer,
264 gint w,
265 gint h,
266 gint *src_x,
267 gint *src_y)
268 {
269 RTreeNode *node;
270
271 gimp_assert (w <= GIMP_DISPLAY_RENDER_BUF_WIDTH &&
272 h <= GIMP_DISPLAY_RENDER_BUF_HEIGHT);
273
274 node = rtree_insert (&xfer->rtree, w, h);
275 if (node == NULL)
276 {
277 xfer->page = (xfer->page + 1) % NUM_PAGES;
278 cairo_surface_flush (xfer->render_surface[xfer->page]);
279 rtree_reset (&xfer->rtree);
280 cairo_surface_mark_dirty (xfer->render_surface[xfer->page]); /* XXX */
281 node = rtree_insert (&xfer->rtree, w, h);
282 gimp_assert (node != NULL);
283 }
284
285 *src_x = node->x;
286 *src_y = node->y;
287
288 return xfer->render_surface[xfer->page];
289 }
290