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