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 *
gdk_image_ref(GdkImage * image)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
gdk_image_unref(GdkImage * image)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*
gdk_image_get(GdkWindow * drawable,gint x,gint y,gint width,gint height)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  * Deprecated: 2.22: #GdkImage should not be used anymore.
114  **/
115 void
gdk_image_set_colormap(GdkImage * image,GdkColormap * colormap)116 gdk_image_set_colormap (GdkImage       *image,
117                         GdkColormap    *colormap)
118 {
119   g_return_if_fail (GDK_IS_IMAGE (image));
120   g_return_if_fail (GDK_IS_COLORMAP (colormap));
121 
122   if (image->colormap != colormap)
123     {
124       if (image->colormap)
125 	g_object_unref (image->colormap);
126 
127       image->colormap = colormap;
128       g_object_ref (image->colormap);
129     }
130 }
131 
132 /**
133  * gdk_image_get_colormap:
134  * @image: a #GdkImage
135  *
136  * Retrieves the colormap for a given image, if it exists.  An image
137  * will have a colormap if the drawable from which it was created has
138  * a colormap, or if a colormap was set explicitely with
139  * gdk_image_set_colormap().
140  *
141  * Return value: colormap for the image
142  *
143  * Deprecated: 2.22: #GdkImage should not be used anymore.
144  **/
145 GdkColormap *
gdk_image_get_colormap(GdkImage * image)146 gdk_image_get_colormap (GdkImage *image)
147 {
148   g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);
149 
150   return image->colormap;
151 }
152 
153 /**
154  * gdk_image_get_image_type:
155  * @image: a #GdkImage
156  *
157  * Determines the type of a given image.
158  *
159  * Return value: the #GdkImageType of the image
160  *
161  * Since: 2.22
162  *
163  * Deprecated: 2.22: #GdkImage should not be used anymore.
164  **/
165 GdkImageType
gdk_image_get_image_type(GdkImage * image)166 gdk_image_get_image_type (GdkImage *image)
167 {
168   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
169 
170   return image->type;
171 }
172 
173 /**
174  * gdk_image_get_visual:
175  * @image: a #GdkImage
176  *
177  * Determines the visual that was used to create the image.
178  *
179  * Return value: a #GdkVisual
180  *
181  * Since: 2.22
182  *
183  * Deprecated: 2.22: #GdkImage should not be used anymore.
184  **/
185 GdkVisual *
gdk_image_get_visual(GdkImage * image)186 gdk_image_get_visual (GdkImage *image)
187 {
188   g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);
189 
190   return image->visual;
191 }
192 
193 /**
194  * gdk_image_get_byte_order:
195  * @image: a #GdkImage
196  *
197  * Determines the byte order of the image.
198  *
199  * Return value: a #GdkVisual
200  *
201  * Since: 2.22
202  *
203  * Deprecated: 2.22: #GdkImage should not be used anymore.
204  **/
205 GdkByteOrder
gdk_image_get_byte_order(GdkImage * image)206 gdk_image_get_byte_order (GdkImage *image)
207 {
208   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
209 
210   return image->byte_order;
211 }
212 
213 /**
214  * gdk_image_get_width:
215  * @image: a #GdkImage
216  *
217  * Determines the width of the image.
218  *
219  * Return value: the width
220  *
221  * Since: 2.22
222  *
223  * Deprecated: 2.22: #GdkImage should not be used anymore.
224  **/
225 gint
gdk_image_get_width(GdkImage * image)226 gdk_image_get_width (GdkImage *image)
227 {
228   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
229 
230   return image->width;
231 }
232 
233 /**
234  * gdk_image_get_height:
235  * @image: a #GdkImage
236  *
237  * Determines the height of the image.
238  *
239  * Return value: the height
240  *
241  * Since: 2.22
242  *
243  * Deprecated: 2.22: #GdkImage should not be used anymore.
244  **/
245 gint
gdk_image_get_height(GdkImage * image)246 gdk_image_get_height (GdkImage *image)
247 {
248   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
249 
250   return image->height;
251 }
252 
253 /**
254  * gdk_image_get_depth:
255  * @image: a #GdkImage
256  *
257  * Determines the depth of the image.
258  *
259  * Return value: the depth
260  *
261  * Since: 2.22
262  *
263  * Deprecated: 2.22: #GdkImage should not be used anymore.
264  **/
265 guint16
gdk_image_get_depth(GdkImage * image)266 gdk_image_get_depth (GdkImage *image)
267 {
268   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
269 
270   return image->depth;
271 }
272 
273 /**
274  * gdk_image_get_bytes_per_pixel:
275  * @image: a #GdkImage
276  *
277  * Determines the number of bytes per pixel of the image.
278  *
279  * Return value: the bytes per pixel
280  *
281  * Since: 2.22
282  *
283  * Deprecated: 2.22: #GdkImage should not be used anymore.
284  **/
285 guint16
gdk_image_get_bytes_per_pixel(GdkImage * image)286 gdk_image_get_bytes_per_pixel (GdkImage *image)
287 {
288   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
289 
290   return image->bpp;
291 }
292 
293 /**
294  * gdk_image_get_bytes_per_line:
295  * @image: a #GdkImage
296  *
297  * Determines the number of bytes per line of the image.
298  *
299  * Return value: the bytes per line
300  *
301  * Since: 2.22
302  *
303  * Deprecated: 2.22: #GdkImage should not be used anymore.
304  **/
305 guint16
gdk_image_get_bytes_per_line(GdkImage * image)306 gdk_image_get_bytes_per_line (GdkImage *image)
307 {
308   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
309 
310   return image->bpl;
311 }
312 
313 /**
314  * gdk_image_get_bits_per_pixel:
315  * @image: a #GdkImage
316  *
317  * Determines the number of bits per pixel of the image.
318  *
319  * Return value: the bits per pixel
320  *
321  * Since: 2.22
322  *
323  * Deprecated: 2.22: #GdkImage should not be used anymore.
324  **/
325 guint16
gdk_image_get_bits_per_pixel(GdkImage * image)326 gdk_image_get_bits_per_pixel (GdkImage *image)
327 {
328   g_return_val_if_fail (GDK_IS_IMAGE (image), 0);
329 
330   return image->bits_per_pixel;
331 }
332 
333 /**
334  * gdk_image_get_pixels:
335  * @image: a #GdkImage
336  *
337  * Returns a pointer to the pixel data of the image.
338  *
339  * Returns: the pixel data of the image
340  *
341  * Since: 2.22
342  *
343  * Deprecated: 2.22: #GdkImage should not be used anymore.
344  */
345 gpointer
gdk_image_get_pixels(GdkImage * image)346 gdk_image_get_pixels (GdkImage *image)
347 {
348   g_return_val_if_fail (GDK_IS_IMAGE (image), NULL);
349 
350   return image->mem;
351 }
352 
353 /* We have N_REGION GDK_SCRATCH_IMAGE_WIDTH x GDK_SCRATCH_IMAGE_HEIGHT regions divided
354  * up between n_images different images. possible_n_images gives
355  * various divisors of N_REGIONS. The reason for allowing this
356  * flexibility is that we want to create as few images as possible,
357  * but we want to deal with the abberant systems that have a SHMMAX
358  * limit less than
359  *
360  * GDK_SCRATCH_IMAGE_WIDTH * GDK_SCRATCH_IMAGE_HEIGHT * N_REGIONS * 4 (384k)
361  *
362  * (Are there any such?)
363  */
364 #define N_REGIONS 6
365 static const int possible_n_images[] = { 1, 2, 3, 6 };
366 
367 /* We allocate one GdkScratchImageInfo structure for each
368  * depth where we are allocating scratch images. (Future: one
369  * per depth, per display)
370  */
371 typedef struct _GdkScratchImageInfo GdkScratchImageInfo;
372 
373 struct _GdkScratchImageInfo {
374   gint depth;
375 
376   gint n_images;
377   GdkImage *static_image[N_REGIONS];
378   gint static_image_idx;
379 
380   /* In order to optimize filling fractions, we simultaneously fill in up
381    * to three regions of size GDK_SCRATCH_IMAGE_WIDTH * GDK_SCRATCH_IMAGE_HEIGHT: one
382    * for images that are taller than GDK_SCRATCH_IMAGE_HEIGHT / 2, and must
383    * be tiled horizontally. One for images that are wider than
384    * GDK_SCRATCH_IMAGE_WIDTH / 2 and must be tiled vertically, and a third
385    * for images smaller than GDK_SCRATCH_IMAGE_HEIGHT / 2 x GDK_SCRATCH_IMAGE_WIDTH x 2
386    * that we tile in horizontal rows.
387    */
388   gint horiz_idx;
389   gint horiz_y;
390   gint vert_idx;
391   gint vert_x;
392 
393   /* tile_y1 and tile_y2 define the horizontal band into
394    * which we are tiling images. tile_x is the x extent to
395    * which that is filled
396    */
397   gint tile_idx;
398   gint tile_x;
399   gint tile_y1;
400   gint tile_y2;
401 
402   GdkScreen *screen;
403 };
404 
405 static GSList *scratch_image_infos = NULL;
406 
407 static gboolean
allocate_scratch_images(GdkScratchImageInfo * info,gint n_images,gboolean shared)408 allocate_scratch_images (GdkScratchImageInfo *info,
409 			 gint                 n_images,
410 			 gboolean             shared)
411 {
412   gint i;
413 
414   for (i = 0; i < n_images; i++)
415     {
416       info->static_image[i] = _gdk_image_new_for_depth (info->screen,
417 							shared ? GDK_IMAGE_SHARED : GDK_IMAGE_NORMAL,
418 							NULL,
419 							GDK_SCRATCH_IMAGE_WIDTH * (N_REGIONS / n_images),
420 							GDK_SCRATCH_IMAGE_HEIGHT,
421 							info->depth);
422 
423       if (!info->static_image[i])
424 	{
425 	  gint j;
426 
427 	  for (j = 0; j < i; j++)
428 	    g_object_unref (info->static_image[j]);
429 
430 	  return FALSE;
431 	}
432     }
433 
434   return TRUE;
435 }
436 
437 static void
scratch_image_info_display_closed(GdkDisplay * display,gboolean is_error,GdkScratchImageInfo * image_info)438 scratch_image_info_display_closed (GdkDisplay          *display,
439                                    gboolean             is_error,
440                                    GdkScratchImageInfo *image_info)
441 {
442   gint i;
443 
444   g_signal_handlers_disconnect_by_func (display,
445                                         scratch_image_info_display_closed,
446                                         image_info);
447 
448   scratch_image_infos = g_slist_remove (scratch_image_infos, image_info);
449 
450   for (i = 0; i < image_info->n_images; i++)
451     g_object_unref (image_info->static_image[i]);
452 
453   g_free (image_info);
454 }
455 
456 static GdkScratchImageInfo *
scratch_image_info_for_depth(GdkScreen * screen,gint depth)457 scratch_image_info_for_depth (GdkScreen *screen,
458 			      gint       depth)
459 {
460   GSList *tmp_list;
461   GdkScratchImageInfo *image_info;
462   gint i;
463 
464   tmp_list = scratch_image_infos;
465   while (tmp_list)
466     {
467       image_info = tmp_list->data;
468       if (image_info->depth == depth && image_info->screen == screen)
469 	return image_info;
470 
471       tmp_list = tmp_list->next;
472     }
473 
474   image_info = g_new (GdkScratchImageInfo, 1);
475 
476   image_info->depth = depth;
477   image_info->screen = screen;
478 
479   g_signal_connect (gdk_screen_get_display (screen), "closed",
480                     G_CALLBACK (scratch_image_info_display_closed),
481                     image_info);
482 
483   /* Try to allocate as few possible shared images */
484   for (i=0; i < G_N_ELEMENTS (possible_n_images); i++)
485     {
486       if (allocate_scratch_images (image_info, possible_n_images[i], TRUE))
487 	{
488 	  image_info->n_images = possible_n_images[i];
489 	  break;
490 	}
491     }
492 
493   /* If that fails, just allocate N_REGIONS normal images */
494   if (i == G_N_ELEMENTS (possible_n_images))
495     {
496       allocate_scratch_images (image_info, N_REGIONS, FALSE);
497       image_info->n_images = N_REGIONS;
498     }
499 
500   image_info->static_image_idx = 0;
501 
502   image_info->horiz_y = GDK_SCRATCH_IMAGE_HEIGHT;
503   image_info->vert_x = GDK_SCRATCH_IMAGE_WIDTH;
504   image_info->tile_x = GDK_SCRATCH_IMAGE_WIDTH;
505   image_info->tile_y1 = image_info->tile_y2 = GDK_SCRATCH_IMAGE_HEIGHT;
506 
507   scratch_image_infos = g_slist_prepend (scratch_image_infos, image_info);
508 
509   return image_info;
510 }
511 
512 /* Defining NO_FLUSH can cause inconsistent screen updates, but is useful
513    for performance evaluation. */
514 
515 #undef NO_FLUSH
516 
517 #ifdef VERBOSE
518 static gint sincelast;
519 #endif
520 
521 static gint
alloc_scratch_image(GdkScratchImageInfo * image_info)522 alloc_scratch_image (GdkScratchImageInfo *image_info)
523 {
524   if (image_info->static_image_idx == N_REGIONS)
525     {
526 #ifndef NO_FLUSH
527       gdk_flush ();
528 #endif
529 #ifdef VERBOSE
530       g_print ("flush, %d puts since last flush\n", sincelast);
531       sincelast = 0;
532 #endif
533       image_info->static_image_idx = 0;
534 
535       /* Mark all regions that we might be filling in as completely
536        * full, to force new tiles to be allocated for subsequent
537        * images
538        */
539       image_info->horiz_y = GDK_SCRATCH_IMAGE_HEIGHT;
540       image_info->vert_x = GDK_SCRATCH_IMAGE_WIDTH;
541       image_info->tile_x = GDK_SCRATCH_IMAGE_WIDTH;
542       image_info->tile_y1 = image_info->tile_y2 = GDK_SCRATCH_IMAGE_HEIGHT;
543     }
544   return image_info->static_image_idx++;
545 }
546 
547 /**
548  * _gdk_image_get_scratch:
549  * @screen: a #GdkScreen
550  * @width: desired width
551  * @height: desired height
552  * @depth: depth of image
553  * @x: X location within returned image of scratch image
554  * @y: Y location within returned image of scratch image
555  *
556  * Allocates an image of size width/height, up to a maximum
557  * of GDK_SCRATCH_IMAGE_WIDTHxGDK_SCRATCH_IMAGE_HEIGHT that is
558  * suitable to use on @screen.
559  *
560  * Return value: a scratch image. This must be used by a
561  *  call to gdk_image_put() before any other calls to
562  *  _gdk_image_get_scratch()
563  **/
564 GdkImage *
_gdk_image_get_scratch(GdkScreen * screen,gint width,gint height,gint depth,gint * x,gint * y)565 _gdk_image_get_scratch (GdkScreen   *screen,
566 			gint	     width,
567 			gint	     height,
568 			gint	     depth,
569 			gint	    *x,
570 			gint	    *y)
571 {
572   GdkScratchImageInfo *image_info;
573   GdkImage *image;
574   gint idx;
575 
576   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
577 
578   image_info = scratch_image_info_for_depth (screen, depth);
579 
580   if (width >= (GDK_SCRATCH_IMAGE_WIDTH >> 1))
581     {
582       if (height >= (GDK_SCRATCH_IMAGE_HEIGHT >> 1))
583 	{
584 	  idx = alloc_scratch_image (image_info);
585 	  *x = 0;
586 	  *y = 0;
587 	}
588       else
589 	{
590 	  if (height + image_info->horiz_y > GDK_SCRATCH_IMAGE_HEIGHT)
591 	    {
592 	      image_info->horiz_idx = alloc_scratch_image (image_info);
593 	      image_info->horiz_y = 0;
594 	    }
595 	  idx = image_info->horiz_idx;
596 	  *x = 0;
597 	  *y = image_info->horiz_y;
598 	  image_info->horiz_y += height;
599 	}
600     }
601   else
602     {
603       if (height >= (GDK_SCRATCH_IMAGE_HEIGHT >> 1))
604 	{
605 	  if (width + image_info->vert_x > GDK_SCRATCH_IMAGE_WIDTH)
606 	    {
607 	      image_info->vert_idx = alloc_scratch_image (image_info);
608 	      image_info->vert_x = 0;
609 	    }
610 	  idx = image_info->vert_idx;
611 	  *x = image_info->vert_x;
612 	  *y = 0;
613 	  /* using 3 and -4 would be slightly more efficient on 32-bit machines
614 	     with > 1bpp displays */
615 	  image_info->vert_x += (width + 7) & -8;
616 	}
617       else
618 	{
619 	  if (width + image_info->tile_x > GDK_SCRATCH_IMAGE_WIDTH)
620 	    {
621 	      image_info->tile_y1 = image_info->tile_y2;
622 	      image_info->tile_x = 0;
623 	    }
624 	  if (height + image_info->tile_y1 > GDK_SCRATCH_IMAGE_HEIGHT)
625 	    {
626 	      image_info->tile_idx = alloc_scratch_image (image_info);
627 	      image_info->tile_x = 0;
628 	      image_info->tile_y1 = 0;
629 	      image_info->tile_y2 = 0;
630 	    }
631 	  if (height + image_info->tile_y1 > image_info->tile_y2)
632 	    image_info->tile_y2 = height + image_info->tile_y1;
633 	  idx = image_info->tile_idx;
634 	  *x = image_info->tile_x;
635 	  *y = image_info->tile_y1;
636 	  image_info->tile_x += (width + 7) & -8;
637 	}
638     }
639   image = image_info->static_image[idx * image_info->n_images / N_REGIONS];
640   *x += GDK_SCRATCH_IMAGE_WIDTH * (idx % (N_REGIONS / image_info->n_images));
641 #ifdef VERBOSE
642   g_print ("index %d, x %d, y %d (%d x %d)\n", idx, *x, *y, width, height);
643   sincelast++;
644 #endif
645   return image;
646 }
647 
648 GdkImage*
gdk_image_new(GdkImageType type,GdkVisual * visual,gint width,gint height)649 gdk_image_new (GdkImageType  type,
650 	       GdkVisual    *visual,
651 	       gint          width,
652 	       gint          height)
653 {
654   return _gdk_image_new_for_depth (gdk_visual_get_screen (visual), type,
655 				   visual, width, height, -1);
656 }
657 
658 #define __GDK_IMAGE_C__
659 #include "gdkaliasdef.c"
660