]> Pileus Git - ~andy/gtk/blob - gdk/gdkimage.c
Add multi-monitor offset. (#141842, John Ehresman)
[~andy/gtk] / gdk / gdkimage.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <config.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30
31 #include "gdk.h"                /* For gdk_flush() */
32 #include "gdkimage.h"
33 #include "gdkprivate.h"
34 #include "gdkinternals.h"       /* For scratch_image code */
35
36 /**
37  * gdk_image_ref:
38  * @image: a #GdkImage
39  * 
40  * Deprecated function; use g_object_ref() instead.
41  * 
42  * Return value: the image
43  **/
44 GdkImage *
45 gdk_image_ref (GdkImage *image)
46 {
47   return (GdkImage *) g_object_ref (image);
48 }
49
50 /**
51  * gdk_image_unref:
52  * @image: a #GdkImage
53  * 
54  * Deprecated function; use g_object_unref() instead.
55  * 
56  **/
57 void
58 gdk_image_unref (GdkImage *image)
59 {
60   g_return_if_fail (GDK_IS_IMAGE (image));
61
62   g_object_unref (image);
63 }
64
65 /**
66  * gdk_image_get:
67  * @drawable: a #GdkDrawable
68  * @x: x coordinate in @window
69  * @y: y coordinate in @window
70  * @width: width of area in @window
71  * @height: height of area in @window
72  * 
73  * This is a deprecated wrapper for gdk_drawable_get_image();
74  * gdk_drawable_get_image() should be used instead. Or even better: in
75  * most cases gdk_pixbuf_get_from_drawable() is the most convenient
76  * choice.
77  * 
78  * Return value: a new #GdkImage or %NULL
79  **/
80 GdkImage*
81 gdk_image_get (GdkWindow *drawable,
82                gint       x,
83                gint       y,
84                gint       width,
85                gint       height)
86 {
87   g_return_val_if_fail (GDK_IS_DRAWABLE (drawable), NULL);
88   g_return_val_if_fail (x >= 0, NULL);
89   g_return_val_if_fail (y >= 0, NULL);
90   g_return_val_if_fail (width >= 0, NULL);
91   g_return_val_if_fail (height >= 0, NULL);
92   
93   return gdk_drawable_get_image (drawable, x, y, width, height);
94 }
95
96 /**
97  * gdk_image_set_colormap:
98  * @image: a #GdkImage
99  * @colormap: a #GdkColormap
100  * 
101  * Sets the colormap for the image to the given colormap.  Normally
102  * there's no need to use this function, images are created with the
103  * correct colormap if you get the image from a drawable. If you
104  * create the image from scratch, use the colormap of the drawable you
105  * intend to render the image to.
106  **/
107 void
108 gdk_image_set_colormap (GdkImage       *image,
109                         GdkColormap    *colormap)
110 {
111   g_return_if_fail (GDK_IS_IMAGE (image));
112   g_return_if_fail (GDK_IS_COLORMAP (colormap));
113
114   if (image->colormap != colormap)
115     {
116       if (image->colormap)
117         g_object_unref (image->colormap);
118
119       image->colormap = colormap;
120       g_object_ref (image->colormap);
121     }
122     
123 }
124
125 /**
126  * gdk_image_get_colormap:
127  * @image: a #GdkImage
128  * 
129  * Retrieves the colormap for a given image, if it exists.  An image
130  * will have a colormap if the drawable from which it was created has
131  * a colormap, or if a colormap was set explicitely with
132  * gdk_image_set_colormap().
133  * 
134  * Return value: colormap for the image
135  **/
136 GdkColormap *
137 gdk_image_get_colormap (GdkImage *image)
138 {
139   g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);
140
141   return image->colormap;
142 }
143
144 /* We have N_REGION GDK_SCRATCH_IMAGE_WIDTH x GDK_SCRATCH_IMAGE_HEIGHT regions divided
145  * up between n_images different images. possible_n_images gives
146  * various divisors of N_REGIONS. The reason for allowing this
147  * flexibility is that we want to create as few images as possible,
148  * but we want to deal with the abberant systems that have a SHMMAX
149  * limit less than
150  *
151  * GDK_SCRATCH_IMAGE_WIDTH * GDK_SCRATCH_IMAGE_HEIGHT * N_REGIONS * 4 (384k)
152  *
153  * (Are there any such?)
154  */
155 #define N_REGIONS 6
156 static const int possible_n_images[] = { 1, 2, 3, 6 };
157
158 /* We allocate one GdkScratchImageInfo structure for each
159  * depth where we are allocating scratch images. (Future: one
160  * per depth, per display)
161  */
162 typedef struct _GdkScratchImageInfo GdkScratchImageInfo;
163
164 struct _GdkScratchImageInfo {
165   gint depth;
166   
167   gint n_images;
168   GdkImage *static_image[N_REGIONS];
169   gint static_image_idx;
170
171   /* In order to optimize filling fractions, we simultaneously fill in up
172    * to three regions of size GDK_SCRATCH_IMAGE_WIDTH * GDK_SCRATCH_IMAGE_HEIGHT: one
173    * for images that are taller than GDK_SCRATCH_IMAGE_HEIGHT / 2, and must
174    * be tiled horizontally. One for images that are wider than
175    * GDK_SCRATCH_IMAGE_WIDTH / 2 and must be tiled vertically, and a third
176    * for images smaller than GDK_SCRATCH_IMAGE_HEIGHT / 2 x GDK_SCRATCH_IMAGE_WIDTH x 2
177    * that we tile in horizontal rows.
178    */
179   gint horiz_idx;
180   gint horiz_y;
181   gint vert_idx;
182   gint vert_x;
183   
184   /* tile_y1 and tile_y2 define the horizontal band into
185    * which we are tiling images. tile_x is the x extent to
186    * which that is filled
187    */
188   gint tile_idx;
189   gint tile_x;
190   gint tile_y1;
191   gint tile_y2;
192
193   GdkScreen *screen;
194 };
195
196 static GSList *scratch_image_infos = NULL;
197
198 static gboolean
199 allocate_scratch_images (GdkScratchImageInfo *info,
200                          gint                 n_images,
201                          gboolean             shared)
202 {
203   gint i;
204   
205   for (i = 0; i < n_images; i++)
206     {
207       info->static_image[i] = _gdk_image_new_for_depth (info->screen,
208                                                         shared ? GDK_IMAGE_SHARED : GDK_IMAGE_NORMAL,
209                                                         NULL,
210                                                         GDK_SCRATCH_IMAGE_WIDTH * (N_REGIONS / n_images), 
211                                                         GDK_SCRATCH_IMAGE_HEIGHT,
212                                                         info->depth);
213       
214       if (!info->static_image[i])
215         {
216           gint j;
217           
218           for (j = 0; j < i; j++)
219             g_object_unref (info->static_image[i]);
220           
221           return FALSE;
222         }
223     }
224   
225   return TRUE;
226 }
227
228 static GdkScratchImageInfo *
229 scratch_image_info_for_depth (GdkScreen *screen,
230                               gint       depth)
231 {
232   GSList *tmp_list;
233   GdkScratchImageInfo *image_info;
234   gint i;
235
236   tmp_list = scratch_image_infos;
237   while (tmp_list)
238     {
239       image_info = tmp_list->data;
240       if (image_info->depth == depth && image_info->screen == screen)
241         return image_info;
242       
243       tmp_list = tmp_list->next;
244     }
245
246   image_info = g_new (GdkScratchImageInfo, 1);
247
248   image_info->depth = depth;
249   image_info->screen = screen;
250
251   /* Try to allocate as few possible shared images */
252   for (i=0; i < G_N_ELEMENTS (possible_n_images); i++)
253     {
254       if (allocate_scratch_images (image_info, possible_n_images[i], TRUE))
255         {
256           image_info->n_images = possible_n_images[i];
257           break;
258         }
259     }
260
261   /* If that fails, just allocate N_REGIONS normal images */
262   if (i == G_N_ELEMENTS (possible_n_images))
263     {
264       allocate_scratch_images (image_info, N_REGIONS, FALSE);
265       image_info->n_images = N_REGIONS;
266     }
267
268   image_info->static_image_idx = 0;
269
270   image_info->horiz_y = GDK_SCRATCH_IMAGE_HEIGHT;
271   image_info->vert_x = GDK_SCRATCH_IMAGE_WIDTH;
272   image_info->tile_x = GDK_SCRATCH_IMAGE_WIDTH;
273   image_info->tile_y1 = image_info->tile_y2 = GDK_SCRATCH_IMAGE_HEIGHT;
274
275   scratch_image_infos = g_slist_prepend (scratch_image_infos, image_info);
276
277   return image_info;
278 }
279
280 /* Defining NO_FLUSH can cause inconsistent screen updates, but is useful
281    for performance evaluation. */
282
283 #undef NO_FLUSH
284
285 #ifdef VERBOSE
286 static gint sincelast;
287 #endif
288
289 static gint
290 alloc_scratch_image (GdkScratchImageInfo *image_info)
291 {
292   if (image_info->static_image_idx == N_REGIONS)
293     {
294 #ifndef NO_FLUSH
295       gdk_flush ();
296 #endif
297 #ifdef VERBOSE
298       g_print ("flush, %d puts since last flush\n", sincelast);
299       sincelast = 0;
300 #endif
301       image_info->static_image_idx = 0;
302
303       /* Mark all regions that we might be filling in as completely
304        * full, to force new tiles to be allocated for subsequent
305        * images
306        */
307       image_info->horiz_y = GDK_SCRATCH_IMAGE_HEIGHT;
308       image_info->vert_x = GDK_SCRATCH_IMAGE_WIDTH;
309       image_info->tile_x = GDK_SCRATCH_IMAGE_WIDTH;
310       image_info->tile_y1 = image_info->tile_y2 = GDK_SCRATCH_IMAGE_HEIGHT;
311     }
312   return image_info->static_image_idx++;
313 }
314
315 /**
316  * _gdk_image_get_scratch:
317  * @screen: a #GdkScreen
318  * @width: desired width
319  * @height: desired height
320  * @depth: depth of image 
321  * @x: X location within returned image of scratch image
322  * @y: Y location within returned image of scratch image
323  * 
324  * Allocates an image of size width/height, up to a maximum
325  * of GDK_SCRATCH_IMAGE_WIDTHxGDK_SCRATCH_IMAGE_HEIGHT that is
326  * suitable to use on @screen.
327  * 
328  * Return value: a scratch image. This must be used by a
329  *  call to gdk_image_put() before any other calls to
330  *  _gdk_image_get_scratch()
331  **/
332 GdkImage *
333 _gdk_image_get_scratch (GdkScreen   *screen,
334                         gint         width,                     
335                         gint         height,
336                         gint         depth,
337                         gint        *x,
338                         gint        *y)
339 {
340   GdkScratchImageInfo *image_info;
341   GdkImage *image;
342   gint idx;
343   
344   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
345
346   image_info = scratch_image_info_for_depth (screen, depth);
347
348   if (width >= (GDK_SCRATCH_IMAGE_WIDTH >> 1))
349     {
350       if (height >= (GDK_SCRATCH_IMAGE_HEIGHT >> 1))
351         {
352           idx = alloc_scratch_image (image_info);
353           *x = 0;
354           *y = 0;
355         }
356       else
357         {
358           if (height + image_info->horiz_y > GDK_SCRATCH_IMAGE_HEIGHT)
359             {
360               image_info->horiz_idx = alloc_scratch_image (image_info);
361               image_info->horiz_y = 0;
362             }
363           idx = image_info->horiz_idx;
364           *x = 0;
365           *y = image_info->horiz_y;
366           image_info->horiz_y += height;
367         }
368     }
369   else
370     {
371       if (height >= (GDK_SCRATCH_IMAGE_HEIGHT >> 1))
372         {
373           if (width + image_info->vert_x > GDK_SCRATCH_IMAGE_WIDTH)
374             {
375               image_info->vert_idx = alloc_scratch_image (image_info);
376               image_info->vert_x = 0;
377             }
378           idx = image_info->vert_idx;
379           *x = image_info->vert_x;
380           *y = 0;
381           /* using 3 and -4 would be slightly more efficient on 32-bit machines
382              with > 1bpp displays */
383           image_info->vert_x += (width + 7) & -8;
384         }
385       else
386         {
387           if (width + image_info->tile_x > GDK_SCRATCH_IMAGE_WIDTH)
388             {
389               image_info->tile_y1 = image_info->tile_y2;
390               image_info->tile_x = 0;
391             }
392           if (height + image_info->tile_y1 > GDK_SCRATCH_IMAGE_HEIGHT)
393             {
394               image_info->tile_idx = alloc_scratch_image (image_info);
395               image_info->tile_x = 0;
396               image_info->tile_y1 = 0;
397               image_info->tile_y2 = 0;
398             }
399           if (height + image_info->tile_y1 > image_info->tile_y2)
400             image_info->tile_y2 = height + image_info->tile_y1;
401           idx = image_info->tile_idx;
402           *x = image_info->tile_x;
403           *y = image_info->tile_y1;
404           image_info->tile_x += (width + 7) & -8;
405         }
406     }
407   image = image_info->static_image[idx * image_info->n_images / N_REGIONS];
408   *x += GDK_SCRATCH_IMAGE_WIDTH * (idx % (N_REGIONS / image_info->n_images));
409 #ifdef VERBOSE
410   g_print ("index %d, x %d, y %d (%d x %d)\n", idx, *x, *y, width, height);
411   sincelast++;
412 #endif
413   return image;
414 }
415
416 GdkImage*
417 gdk_image_new (GdkImageType  type,
418                GdkVisual    *visual,
419                gint          width,
420                gint          height)
421 {
422   return _gdk_image_new_for_depth (gdk_visual_get_screen (visual), type,
423                                    visual, width, height, -1);
424 }