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