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