2 * Copyright (C) 1998-2000 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library 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.
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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library 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.
19 * Written by Owen Taylor <otaylor@redhat.com>, based on code by
20 * Carsten Haitzler <raster@rasterman.com>
26 #include <gdk-pixbuf/gdk-pixbuf.h>
28 static GCache *pixbuf_cache = NULL;
31 bilinear_gradient (GdkPixbuf *src,
37 guint n_channels = gdk_pixbuf_get_n_channels (src);
38 guint src_rowstride = gdk_pixbuf_get_rowstride (src);
39 guchar *src_pixels = gdk_pixbuf_get_pixels (src);
40 guchar *p1, *p2, *p3, *p4;
46 p1 = src_pixels + (src_y - 1) * src_rowstride + (src_x - 1) * n_channels;
48 p3 = src_pixels + src_y * src_rowstride + (src_x - 1) * n_channels;
51 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
53 dest_rowstride = gdk_pixbuf_get_rowstride (result);
54 dest_pixels = gdk_pixbuf_get_pixels (result);
56 for (i = 0; i < height; i++)
58 guchar *p = dest_pixels + dest_rowstride *i;
62 for (k = 0; k < n_channels; k++)
64 guint start = ((height - i) * p1[k] + (1 + i) * p3[k]) / (height + 1);
65 guint end = ((height - i) * p2[k] + (1 + i) * p4[k]) / (height + 1);
67 dv[k] = (((gint)end - (gint)start) << 16) / (width + 1);
68 v[k] = (start << 16) + dv[k] + 0x8000;
71 for (j = width; j; j--)
73 for (k = 0; k < n_channels; k++)
85 horizontal_gradient (GdkPixbuf *src,
91 guint n_channels = gdk_pixbuf_get_n_channels (src);
92 guint src_rowstride = gdk_pixbuf_get_rowstride (src);
93 guchar *src_pixels = gdk_pixbuf_get_pixels (src);
99 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
101 dest_rowstride = gdk_pixbuf_get_rowstride (result);
102 dest_pixels = gdk_pixbuf_get_pixels (result);
104 for (i = 0; i < height; i++)
106 guchar *p = dest_pixels + dest_rowstride *i;
107 guchar *p1 = src_pixels + (src_y + i) * src_rowstride + (src_x - 1) * n_channels;
108 guchar *p2 = p1 + n_channels;
113 for (k = 0; k < n_channels; k++)
115 dv[k] = (((gint)p2[k] - (gint)p1[k]) << 16) / (width + 1);
116 v[k] = (p1[k] << 16) + dv[k] + 0x8000;
119 for (j = width; j; j--)
121 for (k = 0; k < n_channels; k++)
133 vertical_gradient (GdkPixbuf *src,
139 guint n_channels = gdk_pixbuf_get_n_channels (src);
140 guint src_rowstride = gdk_pixbuf_get_rowstride (src);
141 guchar *src_pixels = gdk_pixbuf_get_pixels (src);
142 guchar *top_pixels, *bottom_pixels;
143 guint dest_rowstride;
148 top_pixels = src_pixels + (src_y - 1) * src_rowstride + (src_x) * n_channels;
149 bottom_pixels = top_pixels + src_rowstride;
151 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
153 dest_rowstride = gdk_pixbuf_get_rowstride (result);
154 dest_pixels = gdk_pixbuf_get_pixels (result);
156 for (i = 0; i < height; i++)
158 guchar *p = dest_pixels + dest_rowstride *i;
159 guchar *p1 = top_pixels;
160 guchar *p2 = bottom_pixels;
162 for (j = width * n_channels; j; j--)
163 *(p++) = ((height - i) * *(p1++) + (1 + i) * *(p2++)) / (height + 1);
170 replicate_single (GdkPixbuf *src,
176 guint n_channels = gdk_pixbuf_get_n_channels (src);
177 guchar *pixels = (gdk_pixbuf_get_pixels (src) +
178 src_y * gdk_pixbuf_get_rowstride (src) +
180 guchar r = *(pixels++);
181 guchar g = *(pixels++);
182 guchar b = *(pixels++);
183 guint dest_rowstride;
192 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
194 dest_rowstride = gdk_pixbuf_get_rowstride (result);
195 dest_pixels = gdk_pixbuf_get_pixels (result);
197 for (i = 0; i < height; i++)
199 guchar *p = dest_pixels + dest_rowstride *i;
201 for (j = 0; j < width; j++)
216 replicate_rows (GdkPixbuf *src,
222 guint n_channels = gdk_pixbuf_get_n_channels (src);
223 guint src_rowstride = gdk_pixbuf_get_rowstride (src);
224 guchar *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x * n_channels);
227 guint dest_rowstride;
230 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
232 dest_rowstride = gdk_pixbuf_get_rowstride (result);
233 dest_pixels = gdk_pixbuf_get_pixels (result);
235 for (i = 0; i < height; i++)
236 memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
242 replicate_cols (GdkPixbuf *src,
248 guint n_channels = gdk_pixbuf_get_n_channels (src);
249 guint src_rowstride = gdk_pixbuf_get_rowstride (src);
250 guchar *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x * n_channels);
253 guint dest_rowstride;
256 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
258 dest_rowstride = gdk_pixbuf_get_rowstride (result);
259 dest_pixels = gdk_pixbuf_get_pixels (result);
261 for (i = 0; i < height; i++)
263 guchar *p = dest_pixels + dest_rowstride * i;
264 guchar *q = pixels + src_rowstride * i;
274 for (j = 0; j < width; j++)
288 /* Scale the rectangle (src_x, src_y, src_width, src_height)
289 * onto the rectangle (dest_x, dest_y, dest_width, dest_height)
290 * of the destination, clip by clip_rect and render
293 pixbuf_render (GdkPixbuf *src,
297 GdkRectangle *clip_rect,
307 GdkPixbuf *tmp_pixbuf;
309 int x_offset, y_offset;
310 gboolean has_alpha = gdk_pixbuf_get_has_alpha (src);
311 gint src_rowstride = gdk_pixbuf_get_rowstride (src);
312 gint src_n_channels = gdk_pixbuf_get_n_channels (src);
314 if (dest_width <= 0 || dest_height <= 0)
319 rect.width = dest_width;
320 rect.height = dest_height;
322 if (hints & THEME_MISSING)
325 /* FIXME: Because we use the mask to shape windows, we don't use
326 * clip_rect to clip what we draw to the mask, only to clip
327 * what we actually draw. But this leads to the horrible ineffiency
328 * of scale the whole image to get a little bit of it.
330 if (!mask && clip_rect)
332 if (!gdk_rectangle_intersect (clip_rect, &rect, &rect))
336 if (dest_width == src_width && dest_height == src_height)
338 tmp_pixbuf = g_object_ref (src);
340 x_offset = src_x + rect.x - dest_x;
341 y_offset = src_y + rect.y - dest_y;
343 else if (src_width == 0 && src_height == 0)
345 tmp_pixbuf = bilinear_gradient (src, src_x, src_y, dest_width, dest_height);
347 x_offset = rect.x - dest_x;
348 y_offset = rect.y - dest_y;
350 else if (src_width == 0 && dest_height == src_height)
352 tmp_pixbuf = horizontal_gradient (src, src_x, src_y, dest_width, dest_height);
354 x_offset = rect.x - dest_x;
355 y_offset = rect.y - dest_y;
357 else if (src_height == 0 && dest_width == src_width)
359 tmp_pixbuf = vertical_gradient (src, src_x, src_y, dest_width, dest_height);
361 x_offset = rect.x - dest_x;
362 y_offset = rect.y - dest_y;
364 else if ((hints & THEME_CONSTANT_COLS) && (hints & THEME_CONSTANT_ROWS))
366 tmp_pixbuf = replicate_single (src, src_x, src_y, dest_width, dest_height);
368 x_offset = rect.x - dest_x;
369 y_offset = rect.y - dest_y;
371 else if (dest_width == src_width && (hints & THEME_CONSTANT_COLS))
373 tmp_pixbuf = replicate_rows (src, src_x, src_y, dest_width, dest_height);
375 x_offset = rect.x - dest_x;
376 y_offset = rect.y - dest_y;
378 else if (dest_height == src_height && (hints & THEME_CONSTANT_ROWS))
380 tmp_pixbuf = replicate_cols (src, src_x, src_y, dest_width, dest_height);
382 x_offset = rect.x - dest_x;
383 y_offset = rect.y - dest_y;
387 double x_scale = (double)dest_width / src_width;
388 double y_scale = (double)dest_height / src_height;
390 GdkPixbuf *partial_src;
392 pixels = (gdk_pixbuf_get_pixels (src)
393 + src_y * src_rowstride
394 + src_x * src_n_channels);
396 partial_src = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB,
398 8, src_width, src_height,
402 tmp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
404 rect.width, rect.height);
406 gdk_pixbuf_scale (partial_src, tmp_pixbuf,
407 0, 0, rect.width, rect.height,
408 dest_x - rect.x, dest_y - rect.y,
410 GDK_INTERP_BILINEAR);
412 g_object_unref (partial_src);
420 gdk_pixbuf_render_threshold_alpha (tmp_pixbuf, mask,
423 rect.width, rect.height,
427 gdk_draw_pixbuf (window, NULL, tmp_pixbuf,
430 rect.width, rect.height,
431 GDK_RGB_DITHER_NORMAL,
433 g_object_unref (tmp_pixbuf);
437 theme_pixbuf_new (void)
439 ThemePixbuf *result = g_new0 (ThemePixbuf, 1);
440 result->filename = NULL;
441 result->pixbuf = NULL;
443 result->stretch = TRUE;
444 result->border_left = 0;
445 result->border_right = 0;
446 result->border_bottom = 0;
447 result->border_top = 0;
453 theme_pixbuf_destroy (ThemePixbuf *theme_pb)
455 theme_pixbuf_set_filename (theme_pb, NULL);
460 theme_pixbuf_set_filename (ThemePixbuf *theme_pb,
461 const char *filename)
463 if (theme_pb->pixbuf)
465 g_cache_remove (pixbuf_cache, theme_pb->pixbuf);
466 theme_pb->pixbuf = NULL;
469 if (theme_pb->filename)
470 g_free (theme_pb->filename);
473 theme_pb->filename = g_strdup (filename);
475 theme_pb->filename = NULL;
479 compute_hint (GdkPixbuf *pixbuf,
486 int hints = THEME_CONSTANT_ROWS | THEME_CONSTANT_COLS | THEME_MISSING;
487 int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
489 guchar *data = gdk_pixbuf_get_pixels (pixbuf);
490 int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
492 if (x0 == x1 || y0 == y1)
495 for (i = y0; i < y1; i++)
497 guchar *p = data + i * rowstride + x0 * n_channels;
506 for (j = x0; j < x1 ; j++)
508 if (n_channels != 4 || p[3] != 0)
510 hints &= ~THEME_MISSING;
511 if (!(hints & THEME_CONSTANT_ROWS))
518 (n_channels != 4 && a != *(p++)))
520 hints &= ~THEME_CONSTANT_ROWS;
521 if (!(hints & THEME_MISSING))
528 for (i = y0 + 1; i < y1; i++)
530 guchar *base = data + y0 * rowstride + x0 * n_channels;
531 guchar *p = data + i * rowstride + x0 * n_channels;
533 if (memcmp (p, base, n_channels * (x1 - x0)) != 0)
535 hints &= ~THEME_CONSTANT_COLS;
544 theme_pixbuf_compute_hints (ThemePixbuf *theme_pb)
547 gint width = gdk_pixbuf_get_width (theme_pb->pixbuf);
548 gint height = gdk_pixbuf_get_height (theme_pb->pixbuf);
550 if (theme_pb->border_left + theme_pb->border_right > width ||
551 theme_pb->border_top + theme_pb->border_bottom > height)
553 g_warning ("Invalid borders specified for theme pixmap:\n"
555 "borders don't fit within the image", theme_pb->filename);
556 if (theme_pb->border_left + theme_pb->border_right > width)
558 theme_pb->border_left = width / 2;
559 theme_pb->border_right = (width + 1) / 2;
561 if (theme_pb->border_bottom + theme_pb->border_top > height)
563 theme_pb->border_top = height / 2;
564 theme_pb->border_bottom = (height + 1) / 2;
568 for (i = 0; i < 3; i++)
576 y1 = theme_pb->border_top;
579 y0 = theme_pb->border_top;
580 y1 = height - theme_pb->border_bottom;
583 y0 = height - theme_pb->border_bottom;
588 for (j = 0; j < 3; j++)
596 x1 = theme_pb->border_left;
599 x0 = theme_pb->border_left;
600 x1 = width - theme_pb->border_right;
603 x0 = width - theme_pb->border_right;
608 theme_pb->hints[i][j] = compute_hint (theme_pb->pixbuf, x0, x1, y0, y1);
615 theme_pixbuf_set_border (ThemePixbuf *theme_pb,
621 theme_pb->border_left = left;
622 theme_pb->border_right = right;
623 theme_pb->border_top = top;
624 theme_pb->border_bottom = bottom;
626 if (theme_pb->pixbuf)
627 theme_pixbuf_compute_hints (theme_pb);
631 theme_pixbuf_set_stretch (ThemePixbuf *theme_pb,
634 theme_pb->stretch = stretch;
636 if (theme_pb->pixbuf)
637 theme_pixbuf_compute_hints (theme_pb);
641 pixbuf_cache_value_new (gchar *filename)
645 GdkPixbuf *result = gdk_pixbuf_new_from_file (filename, &err);
648 g_warning ("Pixbuf theme: Cannot load pixmap file %s: %s\n",
649 filename, err->message);
657 theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb)
659 if (!theme_pb->pixbuf)
662 pixbuf_cache = g_cache_new ((GCacheNewFunc)pixbuf_cache_value_new,
663 (GCacheDestroyFunc)g_object_unref,
664 (GCacheDupFunc)g_strdup,
665 (GCacheDestroyFunc)g_free,
666 g_str_hash, g_direct_hash, g_str_equal);
668 theme_pb->pixbuf = g_cache_insert (pixbuf_cache, theme_pb->filename);
670 if (theme_pb->stretch)
671 theme_pixbuf_compute_hints (theme_pb);
674 return theme_pb->pixbuf;
678 theme_pixbuf_render (ThemePixbuf *theme_pb,
681 GdkRectangle *clip_rect,
682 guint component_mask,
689 GdkPixbuf *pixbuf = theme_pixbuf_get_pixbuf (theme_pb);
690 gint src_x[4], src_y[4], dest_x[4], dest_y[4];
691 gint pixbuf_width = gdk_pixbuf_get_width (pixbuf);
692 gint pixbuf_height = gdk_pixbuf_get_height (pixbuf);
697 if (theme_pb->stretch)
700 src_x[1] = theme_pb->border_left;
701 src_x[2] = pixbuf_width - theme_pb->border_right;
702 src_x[3] = pixbuf_width;
705 src_y[1] = theme_pb->border_top;
706 src_y[2] = pixbuf_height - theme_pb->border_bottom;
707 src_y[3] = pixbuf_height;
710 dest_x[1] = x + theme_pb->border_left;
711 dest_x[2] = x + width - theme_pb->border_right;
712 dest_x[3] = x + width;
715 dest_y[1] = y + theme_pb->border_top;
716 dest_y[2] = y + height - theme_pb->border_bottom;
717 dest_y[3] = y + height;
719 if (component_mask & COMPONENT_ALL)
720 component_mask = (COMPONENT_ALL - 1) & ~component_mask;
722 #define RENDER_COMPONENT(X1,X2,Y1,Y2) \
723 pixbuf_render (pixbuf, theme_pb->hints[Y1][X1], window, mask, clip_rect, \
724 src_x[X1], src_y[Y1], \
725 src_x[X2] - src_x[X1], src_y[Y2] - src_y[Y1], \
726 dest_x[X1], dest_y[Y1], \
727 dest_x[X2] - dest_x[X1], dest_y[Y2] - dest_y[Y1]);
729 if (component_mask & COMPONENT_NORTH_WEST)
730 RENDER_COMPONENT (0, 1, 0, 1);
732 if (component_mask & COMPONENT_NORTH)
733 RENDER_COMPONENT (1, 2, 0, 1);
735 if (component_mask & COMPONENT_NORTH_EAST)
736 RENDER_COMPONENT (2, 3, 0, 1);
738 if (component_mask & COMPONENT_WEST)
739 RENDER_COMPONENT (0, 1, 1, 2);
741 if (component_mask & COMPONENT_CENTER)
742 RENDER_COMPONENT (1, 2, 1, 2);
744 if (component_mask & COMPONENT_EAST)
745 RENDER_COMPONENT (2, 3, 1, 2);
747 if (component_mask & COMPONENT_SOUTH_WEST)
748 RENDER_COMPONENT (0, 1, 2, 3);
750 if (component_mask & COMPONENT_SOUTH)
751 RENDER_COMPONENT (1, 2, 2, 3);
753 if (component_mask & COMPONENT_SOUTH_EAST)
754 RENDER_COMPONENT (2, 3, 2, 3);
760 x += (width - pixbuf_width) / 2;
761 y += (height - pixbuf_height) / 2;
763 pixbuf_render (pixbuf, 0, window, NULL, clip_rect,
765 pixbuf_width, pixbuf_height,
767 pixbuf_width, pixbuf_height);
771 GdkPixmap *tmp_pixmap;
773 GdkGCValues gc_values;
775 tmp_pixmap = gdk_pixmap_new (window,
779 tmp_gc = gdk_gc_new (tmp_pixmap);
780 gdk_draw_pixbuf (tmp_pixmap, tmp_gc, pixbuf,
783 pixbuf_width, pixbuf_height,
784 GDK_RGB_DITHER_NORMAL,
786 g_object_unref (tmp_gc);
788 gc_values.fill = GDK_TILED;
789 gc_values.tile = tmp_pixmap;
790 tmp_gc = gdk_gc_new_with_values (window,
791 &gc_values, GDK_GC_FILL | GDK_GC_TILE);
793 gdk_draw_rectangle (window, tmp_gc, TRUE,
794 clip_rect->x, clip_rect->y, clip_rect->width, clip_rect->height);
796 gdk_draw_rectangle (window, tmp_gc, TRUE, x, y, width, height);
798 g_object_unref (tmp_gc);
799 g_object_unref (tmp_pixmap);