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