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