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