]> Pileus Git - ~andy/gtk/blob - modules/engines/pixbuf/pixbuf-render.c
Output Makefile for the pixbuf engine directory.
[~andy/gtk] / modules / engines / pixbuf / pixbuf-render.c
1 /* GTK+ Pixbuf Engine
2  * Copyright (C) 1998-2000 Red Hat, Inc.
3  *
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.
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  * Library General Public License for more details.
13  *
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.
18  *
19  * Written by Owen Taylor <otaylor@redhat.com>, based on code by
20  * Carsten Haitzler <raster@rasterman.com>
21  */
22
23 #include <string.h>
24
25 #include "pixbuf.h"
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27
28 static GCache *pixbuf_cache = NULL;
29
30 static GdkPixbuf *
31 bilinear_gradient (GdkPixbuf    *src,
32                    gint          src_x,
33                    gint          src_y,
34                    gint          width,
35                    gint          height)
36 {
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;
41   guint dest_rowstride;
42   guchar *dest_pixels;
43   GdkPixbuf *result;
44   int i, j, k;
45
46   p1 = src_pixels + (src_y - 1) * src_rowstride + (src_x - 1) * n_channels;
47   p2 = p1 + n_channels;
48   p3 = src_pixels + src_y * src_rowstride + (src_x - 1) * n_channels;
49   p4 = p3 + n_channels;
50
51   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
52                            width, height);
53   dest_rowstride = gdk_pixbuf_get_rowstride (result);
54   dest_pixels = gdk_pixbuf_get_pixels (result);
55
56   for (i = 0; i < height; i++)
57     {
58       guchar *p = dest_pixels + dest_rowstride *i;
59       guint v[4];
60       gint dv[4];
61
62       for (k = 0; k < n_channels; k++)
63         {
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);
66
67           dv[k] = (((gint)end - (gint)start) << 16) / (width + 1);
68           v[k] = (start << 16) + dv[k] + 0x8000;
69         }
70
71       for (j = width; j; j--)
72         {
73           for (k = 0; k < n_channels; k++)
74             {
75               *(p++) = v[k] >> 16;
76               v[k] += dv[k];
77             }
78         }
79     }
80
81   return result;
82 }
83
84 static GdkPixbuf *
85 horizontal_gradient (GdkPixbuf    *src,
86                      gint          src_x,
87                      gint          src_y,
88                      gint          width,
89                      gint          height)
90 {
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);
94   guint dest_rowstride;
95   guchar *dest_pixels;
96   GdkPixbuf *result;
97   int i, j, k;
98
99   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
100                            width, height);
101   dest_rowstride = gdk_pixbuf_get_rowstride (result);
102   dest_pixels = gdk_pixbuf_get_pixels (result);
103
104   for (i = 0; i < height; i++)
105     {
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;
109
110       guint v[4];
111       gint dv[4];
112
113       for (k = 0; k < n_channels; k++)
114         {
115           dv[k] = (((gint)p2[k] - (gint)p1[k]) << 16) / (width + 1);
116           v[k] = (p1[k] << 16) + dv[k] + 0x8000;
117         }
118       
119       for (j = width; j; j--)
120         {
121           for (k = 0; k < n_channels; k++)
122             {
123               *(p++) = v[k] >> 16;
124               v[k] += dv[k];
125             }
126         }
127     }
128
129   return result;
130 }
131
132 static GdkPixbuf *
133 vertical_gradient (GdkPixbuf    *src,
134                    gint          src_x,
135                    gint          src_y,
136                    gint          width,
137                    gint          height)
138 {
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;
144   guchar *dest_pixels;
145   GdkPixbuf *result;
146   int i, j;
147
148   top_pixels = src_pixels + (src_y - 1) * src_rowstride + (src_x) * n_channels;
149   bottom_pixels = top_pixels + src_rowstride;
150
151   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
152                            width, height);
153   dest_rowstride = gdk_pixbuf_get_rowstride (result);
154   dest_pixels = gdk_pixbuf_get_pixels (result);
155
156   for (i = 0; i < height; i++)
157     {
158       guchar *p = dest_pixels + dest_rowstride *i;
159       guchar *p1 = top_pixels;
160       guchar *p2 = bottom_pixels;
161
162       for (j = width * n_channels; j; j--)
163         *(p++) = ((height - i) * *(p1++) + (1 + i) * *(p2++)) / (height + 1);
164     }
165
166   return result;
167 }
168
169 static GdkPixbuf *
170 replicate_single (GdkPixbuf    *src,
171                   gint          src_x,
172                   gint          src_y,
173                   gint          width,
174                   gint          height)
175 {
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) +
179                     src_x * n_channels);
180   guchar r = *(pixels++);
181   guchar g = *(pixels++);
182   guchar b = *(pixels++);
183   guint dest_rowstride;
184   guchar *dest_pixels;
185   guchar a = 0;
186   GdkPixbuf *result;
187   int i, j;
188
189   if (n_channels == 4)
190     a = *(pixels++);
191
192   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
193                            width, height);
194   dest_rowstride = gdk_pixbuf_get_rowstride (result);
195   dest_pixels = gdk_pixbuf_get_pixels (result);
196   
197   for (i = 0; i < height; i++)
198     {
199       guchar *p = dest_pixels + dest_rowstride *i;
200
201       for (j = 0; j < width; j++)
202         {
203           *(p++) = r;
204           *(p++) = g;
205           *(p++) = b;
206
207           if (n_channels == 4)
208             *(p++) = a;
209         }
210     }
211
212   return result;
213 }
214
215 static GdkPixbuf *
216 replicate_rows (GdkPixbuf    *src,
217                 gint          src_x,
218                 gint          src_y,
219                 gint          width,
220                 gint          height)
221 {
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);
225   guchar *dest_pixels;
226   GdkPixbuf *result;
227   guint dest_rowstride;
228   int i;
229
230   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
231                            width, height);
232   dest_rowstride = gdk_pixbuf_get_rowstride (result);
233   dest_pixels = gdk_pixbuf_get_pixels (result);
234
235   for (i = 0; i < height; i++)
236     memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
237
238   return result;
239 }
240
241 static GdkPixbuf *
242 replicate_cols (GdkPixbuf    *src,
243                 gint          src_x,
244                 gint          src_y,
245                 gint          width,
246                 gint          height)
247 {
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);
251   guchar *dest_pixels;
252   GdkPixbuf *result;
253   guint dest_rowstride;
254   int i, j;
255
256   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
257                            width, height);
258   dest_rowstride = gdk_pixbuf_get_rowstride (result);
259   dest_pixels = gdk_pixbuf_get_pixels (result);
260
261   for (i = 0; i < height; i++)
262     {
263       guchar *p = dest_pixels + dest_rowstride * i;
264       guchar *q = pixels + src_rowstride * i;
265
266       guchar r = *(q++);
267       guchar g = *(q++);
268       guchar b = *(q++);
269       guchar a = 0;
270       
271       if (n_channels == 4)
272         a = *(q++);
273
274       for (j = 0; j < width; j++)
275         {
276           *(p++) = r;
277           *(p++) = g;
278           *(p++) = b;
279
280           if (n_channels == 4)
281             *(p++) = a;
282         }
283     }
284
285   return result;
286 }
287
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
291  */
292 static void
293 pixbuf_render (GdkPixbuf    *src,
294                guint         hints,
295                GdkWindow    *window,
296                GdkBitmap    *mask,
297                GdkRectangle *clip_rect,
298                gint          src_x,
299                gint          src_y,
300                gint          src_width,
301                gint          src_height,
302                gint          dest_x,
303                gint          dest_y,
304                gint          dest_width,
305                gint          dest_height)
306 {
307   GdkPixbuf *tmp_pixbuf;
308   GdkRectangle rect;
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);
313
314   if (dest_width <= 0 || dest_height <= 0)
315     return;
316
317   rect.x = dest_x;
318   rect.y = dest_y;
319   rect.width = dest_width;
320   rect.height = dest_height;
321
322   if (hints & THEME_MISSING)
323     return;
324
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.
329    */
330   if (!mask && clip_rect)
331     {
332       if (!gdk_rectangle_intersect (clip_rect, &rect, &rect))
333         return;
334     }
335
336   if (dest_width == src_width && dest_height == src_height)
337     {
338       tmp_pixbuf = g_object_ref (src);
339
340       x_offset = src_x + rect.x - dest_x;
341       y_offset = src_y + rect.y - dest_y;
342     }
343   else if (src_width == 0 && src_height == 0)
344     {
345       tmp_pixbuf = bilinear_gradient (src, src_x, src_y, dest_width, dest_height);      
346       
347       x_offset = rect.x - dest_x;
348       y_offset = rect.y - dest_y;
349     }
350   else if (src_width == 0 && dest_height == src_height)
351     {
352       tmp_pixbuf = horizontal_gradient (src, src_x, src_y, dest_width, dest_height);      
353       
354       x_offset = rect.x - dest_x;
355       y_offset = rect.y - dest_y;
356     }
357   else if (src_height == 0 && dest_width == src_width)
358     {
359       tmp_pixbuf = vertical_gradient (src, src_x, src_y, dest_width, dest_height);
360       
361       x_offset = rect.x - dest_x;
362       y_offset = rect.y - dest_y;
363     }
364   else if ((hints & THEME_CONSTANT_COLS) && (hints & THEME_CONSTANT_ROWS))
365     {
366       tmp_pixbuf = replicate_single (src, src_x, src_y, dest_width, dest_height);
367
368       x_offset = rect.x - dest_x;
369       y_offset = rect.y - dest_y;
370     }
371   else if (dest_width == src_width && (hints & THEME_CONSTANT_COLS))
372     {
373       tmp_pixbuf = replicate_rows (src, src_x, src_y, dest_width, dest_height);
374
375       x_offset = rect.x - dest_x;
376       y_offset = rect.y - dest_y;
377     }
378   else if (dest_height == src_height && (hints & THEME_CONSTANT_ROWS))
379     {
380       tmp_pixbuf = replicate_cols (src, src_x, src_y, dest_width, dest_height);
381
382       x_offset = rect.x - dest_x;
383       y_offset = rect.y - dest_y;
384     }
385   else 
386     {
387       double x_scale = (double)dest_width / src_width;
388       double y_scale = (double)dest_height / src_height;
389       guchar *pixels;
390       GdkPixbuf *partial_src;
391       
392       pixels = (gdk_pixbuf_get_pixels (src)
393                 + src_y * src_rowstride
394                 + src_x * src_n_channels);
395
396       partial_src = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB,
397                                               has_alpha,
398                                               8, src_width, src_height,
399                                               src_rowstride,
400                                               NULL, NULL);
401                                                   
402       tmp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
403                                    has_alpha, 8,
404                                    rect.width, rect.height);
405
406       gdk_pixbuf_scale (partial_src, tmp_pixbuf,
407                         0, 0, rect.width, rect.height,
408                         dest_x - rect.x, dest_y - rect.y, 
409                         x_scale, y_scale,
410                         GDK_INTERP_BILINEAR);
411
412       g_object_unref (partial_src);
413
414       x_offset = 0;
415       y_offset = 0;
416     }
417
418   if (mask)
419     {
420       gdk_pixbuf_render_threshold_alpha (tmp_pixbuf, mask,
421                                          x_offset, y_offset,
422                                          rect.x, rect.y,
423                                          rect.width, rect.height,
424                                          128);
425     }
426
427   gdk_draw_pixbuf (window, NULL, tmp_pixbuf,
428                    x_offset, y_offset,
429                    rect.x, rect.y,
430                    rect.width, rect.height,
431                    GDK_RGB_DITHER_NORMAL,
432                    0, 0);
433   g_object_unref (tmp_pixbuf);
434 }
435
436 ThemePixbuf *
437 theme_pixbuf_new (void)
438 {
439   ThemePixbuf *result = g_new0 (ThemePixbuf, 1);
440   result->filename = NULL;
441   result->pixbuf = NULL;
442
443   result->stretch = TRUE;
444   result->border_left = 0;
445   result->border_right = 0;
446   result->border_bottom = 0;
447   result->border_top = 0;
448
449   return result;
450 }
451
452 void
453 theme_pixbuf_destroy (ThemePixbuf *theme_pb)
454 {
455   theme_pixbuf_set_filename (theme_pb, NULL);
456   g_free (theme_pb);
457 }
458
459 void         
460 theme_pixbuf_set_filename (ThemePixbuf *theme_pb,
461                            const char  *filename)
462 {
463   if (theme_pb->pixbuf)
464     {
465       g_cache_remove (pixbuf_cache, theme_pb->pixbuf);
466       theme_pb->pixbuf = NULL;
467     }
468
469   if (theme_pb->filename)
470     g_free (theme_pb->filename);
471
472   if (filename)
473     theme_pb->filename = g_strdup (filename);
474   else
475     theme_pb->filename = NULL;
476 }
477
478 static guint
479 compute_hint (GdkPixbuf *pixbuf,
480               gint       x0,
481               gint       x1,
482               gint       y0,
483               gint       y1)
484 {
485   int i, j;
486   int hints = THEME_CONSTANT_ROWS | THEME_CONSTANT_COLS | THEME_MISSING;
487   int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
488   
489   guchar *data = gdk_pixbuf_get_pixels (pixbuf);
490   int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
491
492   if (x0 == x1 || y0 == y1)
493     return 0;
494
495   for (i = y0; i < y1; i++)
496     {
497       guchar *p = data + i * rowstride + x0 * n_channels;
498       guchar r = p[0];
499       guchar g = p[1];
500       guchar b = p[2];
501       guchar a = 0;
502       
503       if (n_channels == 4)
504         a = p[3];
505
506       for (j = x0; j < x1 ; j++)
507         {
508           if (n_channels != 4 || p[3] != 0)
509             {
510               hints &= ~THEME_MISSING;
511               if (!(hints & THEME_CONSTANT_ROWS))
512                 goto cols;
513             }
514           
515           if (r != *(p++) ||
516               g != *(p++) ||
517               b != *(p++) ||
518               (n_channels != 4 && a != *(p++)))
519             {
520               hints &= ~THEME_CONSTANT_ROWS;
521               if (!(hints & THEME_MISSING))
522                 goto cols;
523             }
524         }
525     }
526
527  cols:
528   for (i = y0 + 1; i < y1; i++)
529     {
530       guchar *base = data + y0 * rowstride + x0 * n_channels;
531       guchar *p = data + i * rowstride + x0 * n_channels;
532
533       if (memcmp (p, base, n_channels * (x1 - x0)) != 0)
534         {
535           hints &= ~THEME_CONSTANT_COLS;
536           return hints;
537         }
538     }
539
540   return hints;
541 }
542
543 static void
544 theme_pixbuf_compute_hints (ThemePixbuf *theme_pb)
545 {
546   int i, j;
547   gint width = gdk_pixbuf_get_width (theme_pb->pixbuf);
548   gint height = gdk_pixbuf_get_height (theme_pb->pixbuf);
549
550   if (theme_pb->border_left + theme_pb->border_right > width ||
551       theme_pb->border_top + theme_pb->border_bottom > height)
552     {
553       g_warning ("Invalid borders specified for theme pixmap:\n"
554                  "        %s,\n"
555                  "borders don't fit within the image", theme_pb->filename);
556       if (theme_pb->border_left + theme_pb->border_right > width)
557         {
558           theme_pb->border_left = width / 2;
559           theme_pb->border_right = (width + 1) / 2;
560         }
561       if (theme_pb->border_bottom + theme_pb->border_top > height)
562         {
563           theme_pb->border_top = height / 2;
564           theme_pb->border_bottom = (height + 1) / 2;
565         }
566     }
567   
568   for (i = 0; i < 3; i++)
569     {
570       gint y0, y1;
571
572       switch (i)
573         {
574         case 0:
575           y0 = 0;
576           y1 = theme_pb->border_top;
577           break;
578         case 1:
579           y0 = theme_pb->border_top;
580           y1 = height - theme_pb->border_bottom;
581           break;
582         default:
583           y0 = height - theme_pb->border_bottom;
584           y1 = height;
585           break;
586         }
587       
588       for (j = 0; j < 3; j++)
589         {
590           gint x0, x1;
591           
592           switch (j)
593             {
594             case 0:
595               x0 = 0;
596               x1 = theme_pb->border_left;
597               break;
598             case 1:
599               x0 = theme_pb->border_left;
600               x1 = width - theme_pb->border_right;
601               break;
602             default:
603               x0 = width - theme_pb->border_right;
604               x1 = width;
605               break;
606             }
607
608           theme_pb->hints[i][j] = compute_hint (theme_pb->pixbuf, x0, x1, y0, y1);
609         }
610     }
611   
612 }
613
614 void
615 theme_pixbuf_set_border (ThemePixbuf *theme_pb,
616                          gint         left,
617                          gint         right,
618                          gint         top,
619                          gint         bottom)
620 {
621   theme_pb->border_left = left;
622   theme_pb->border_right = right;
623   theme_pb->border_top = top;
624   theme_pb->border_bottom = bottom;
625
626   if (theme_pb->pixbuf)
627     theme_pixbuf_compute_hints (theme_pb);
628 }
629
630 void
631 theme_pixbuf_set_stretch (ThemePixbuf *theme_pb,
632                           gboolean     stretch)
633 {
634   theme_pb->stretch = stretch;
635
636   if (theme_pb->pixbuf)
637     theme_pixbuf_compute_hints (theme_pb);
638 }
639
640 GdkPixbuf *
641 pixbuf_cache_value_new (gchar *filename)
642 {
643   GError *err = NULL;
644     
645   GdkPixbuf *result = gdk_pixbuf_new_from_file (filename, &err);
646   if (!result)
647     {
648       g_warning ("Pixbuf theme: Cannot load pixmap file %s: %s\n",
649                  filename, err->message);
650       g_error_free (err);
651     }
652
653   return result;
654 }
655
656 GdkPixbuf *
657 theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb)
658 {
659   if (!theme_pb->pixbuf)
660     {
661       if (!pixbuf_cache)
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);
667       
668       theme_pb->pixbuf = g_cache_insert (pixbuf_cache, theme_pb->filename);
669
670       if (theme_pb->stretch)
671         theme_pixbuf_compute_hints (theme_pb);
672     }
673   
674   return theme_pb->pixbuf;
675 }
676
677 void
678 theme_pixbuf_render (ThemePixbuf  *theme_pb,
679                      GdkWindow    *window,
680                      GdkBitmap    *mask,
681                      GdkRectangle *clip_rect,
682                      guint         component_mask,
683                      gboolean      center,
684                      gint          x,
685                      gint          y,
686                      gint          width,
687                      gint          height)
688 {
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);
693
694   if (!pixbuf)
695     return;
696
697   if (theme_pb->stretch)
698     {
699       src_x[0] = 0;
700       src_x[1] = theme_pb->border_left;
701       src_x[2] = pixbuf_width - theme_pb->border_right;
702       src_x[3] = pixbuf_width;
703       
704       src_y[0] = 0;
705       src_y[1] = theme_pb->border_top;
706       src_y[2] = pixbuf_height - theme_pb->border_bottom;
707       src_y[3] = pixbuf_height;
708       
709       dest_x[0] = x;
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;
713
714       dest_y[0] = y;
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;
718
719       if (component_mask & COMPONENT_ALL)
720         component_mask = (COMPONENT_ALL - 1) & ~component_mask;
721
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]);
728       
729       if (component_mask & COMPONENT_NORTH_WEST)
730         RENDER_COMPONENT (0, 1, 0, 1);
731
732       if (component_mask & COMPONENT_NORTH)
733         RENDER_COMPONENT (1, 2, 0, 1);
734
735       if (component_mask & COMPONENT_NORTH_EAST)
736         RENDER_COMPONENT (2, 3, 0, 1);
737
738       if (component_mask & COMPONENT_WEST)
739         RENDER_COMPONENT (0, 1, 1, 2);
740
741       if (component_mask & COMPONENT_CENTER)
742         RENDER_COMPONENT (1, 2, 1, 2);
743
744       if (component_mask & COMPONENT_EAST)
745         RENDER_COMPONENT (2, 3, 1, 2);
746
747       if (component_mask & COMPONENT_SOUTH_WEST)
748         RENDER_COMPONENT (0, 1, 2, 3);
749
750       if (component_mask & COMPONENT_SOUTH)
751         RENDER_COMPONENT (1, 2, 2, 3);
752
753       if (component_mask & COMPONENT_SOUTH_EAST)
754         RENDER_COMPONENT (2, 3, 2, 3);
755     }
756   else
757     {
758       if (center)
759         {
760           x += (width - pixbuf_width) / 2;
761           y += (height - pixbuf_height) / 2;
762           
763           pixbuf_render (pixbuf, 0, window, NULL, clip_rect,
764                          0, 0,
765                          pixbuf_width, pixbuf_height,
766                          x, y,
767                          pixbuf_width, pixbuf_height);
768         }
769       else
770         {
771           GdkPixmap *tmp_pixmap;
772           GdkGC *tmp_gc;
773           GdkGCValues gc_values;
774
775           tmp_pixmap = gdk_pixmap_new (window,
776                                        pixbuf_width,
777                                        pixbuf_height,
778                                        -1);
779           tmp_gc = gdk_gc_new (tmp_pixmap);
780           gdk_draw_pixbuf (tmp_pixmap, tmp_gc, pixbuf,
781                            0, 0, 
782                            0, 0,
783                            pixbuf_width, pixbuf_height,
784                            GDK_RGB_DITHER_NORMAL,
785                            0, 0);
786           g_object_unref (tmp_gc);
787
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);
792           if (clip_rect)
793             gdk_draw_rectangle (window, tmp_gc, TRUE,
794                                 clip_rect->x, clip_rect->y, clip_rect->width, clip_rect->height);
795           else
796             gdk_draw_rectangle (window, tmp_gc, TRUE, x, y, width, height);
797           
798           g_object_unref (tmp_gc);
799           g_object_unref (tmp_pixmap);
800         }
801     }
802 }