]> Pileus Git - ~andy/gtk/blob - modules/engines/pixbuf/pixbuf-render.c
pixbuf-engine: Replace gdk_draw_pixbuf() with Cairo equivalent
[~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   if (src_x == 0 || src_y == 0)
47     {
48       g_warning ("invalid source position for bilinear gradient");
49       return NULL;
50     }
51
52   p1 = src_pixels + (src_y - 1) * src_rowstride + (src_x - 1) * n_channels;
53   p2 = p1 + n_channels;
54   p3 = src_pixels + src_y * src_rowstride + (src_x - 1) * n_channels;
55   p4 = p3 + n_channels;
56
57   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
58                            width, height);
59
60   if (result == NULL)
61     {
62       g_warning ("failed to create a %dx%d pixbuf", width, height);
63       return NULL;
64     }
65
66   dest_rowstride = gdk_pixbuf_get_rowstride (result);
67   dest_pixels = gdk_pixbuf_get_pixels (result);
68
69   for (i = 0; i < height; i++)
70     {
71       guchar *p = dest_pixels + dest_rowstride *i;
72       guint v[4];
73       gint dv[4];
74
75       for (k = 0; k < n_channels; k++)
76         {
77           guint start = ((height - i) * p1[k] + (1 + i) * p3[k]) / (height + 1);
78           guint end = ((height -  i) * p2[k] + (1 + i) * p4[k]) / (height + 1);
79
80           dv[k] = (((gint)end - (gint)start) << 16) / (width + 1);
81           v[k] = (start << 16) + dv[k] + 0x8000;
82         }
83
84       for (j = width; j; j--)
85         {
86           for (k = 0; k < n_channels; k++)
87             {
88               *(p++) = v[k] >> 16;
89               v[k] += dv[k];
90             }
91         }
92     }
93
94   return result;
95 }
96
97 static GdkPixbuf *
98 horizontal_gradient (GdkPixbuf    *src,
99                      gint          src_x,
100                      gint          src_y,
101                      gint          width,
102                      gint          height)
103 {
104   guint n_channels = gdk_pixbuf_get_n_channels (src);
105   guint src_rowstride = gdk_pixbuf_get_rowstride (src);
106   guchar *src_pixels = gdk_pixbuf_get_pixels (src);
107   guint dest_rowstride;
108   guchar *dest_pixels;
109   GdkPixbuf *result;
110   int i, j, k;
111
112   if (src_x == 0)
113     {
114       g_warning ("invalid source position for horizontal gradient");
115       return NULL;
116     }
117
118   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
119                            width, height);
120
121   if (result == NULL)
122     {
123       g_warning ("failed to create a %dx%d pixbuf", width, height);
124       return NULL;
125     }
126
127   dest_rowstride = gdk_pixbuf_get_rowstride (result);
128   dest_pixels = gdk_pixbuf_get_pixels (result);
129
130   for (i = 0; i < height; i++)
131     {
132       guchar *p = dest_pixels + dest_rowstride *i;
133       guchar *p1 = src_pixels + (src_y + i) * src_rowstride + (src_x - 1) * n_channels;
134       guchar *p2 = p1 + n_channels;
135
136       guint v[4];
137       gint dv[4];
138
139       for (k = 0; k < n_channels; k++)
140         {
141           dv[k] = (((gint)p2[k] - (gint)p1[k]) << 16) / (width + 1);
142           v[k] = (p1[k] << 16) + dv[k] + 0x8000;
143         }
144       
145       for (j = width; j; j--)
146         {
147           for (k = 0; k < n_channels; k++)
148             {
149               *(p++) = v[k] >> 16;
150               v[k] += dv[k];
151             }
152         }
153     }
154
155   return result;
156 }
157
158 static GdkPixbuf *
159 vertical_gradient (GdkPixbuf    *src,
160                    gint          src_x,
161                    gint          src_y,
162                    gint          width,
163                    gint          height)
164 {
165   guint n_channels = gdk_pixbuf_get_n_channels (src);
166   guint src_rowstride = gdk_pixbuf_get_rowstride (src);
167   guchar *src_pixels = gdk_pixbuf_get_pixels (src);
168   guchar *top_pixels, *bottom_pixels;
169   guint dest_rowstride;
170   guchar *dest_pixels;
171   GdkPixbuf *result;
172   int i, j;
173
174   if (src_y == 0)
175     {
176       g_warning ("invalid source position for vertical gradient");
177       return NULL;
178     }
179
180   top_pixels = src_pixels + (src_y - 1) * src_rowstride + (src_x) * n_channels;
181   bottom_pixels = top_pixels + src_rowstride;
182
183   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
184                            width, height);
185
186   if (result == NULL)
187     {
188       g_warning ("failed to create a %dx%d pixbuf", width, height);
189       return NULL;
190     }
191
192   dest_rowstride = gdk_pixbuf_get_rowstride (result);
193   dest_pixels = gdk_pixbuf_get_pixels (result);
194
195   for (i = 0; i < height; i++)
196     {
197       guchar *p = dest_pixels + dest_rowstride *i;
198       guchar *p1 = top_pixels;
199       guchar *p2 = bottom_pixels;
200
201       for (j = width * n_channels; j; j--)
202         *(p++) = ((height - i) * *(p1++) + (1 + i) * *(p2++)) / (height + 1);
203     }
204
205   return result;
206 }
207
208 static GdkPixbuf *
209 replicate_single (GdkPixbuf    *src,
210                   gint          src_x,
211                   gint          src_y,
212                   gint          width,
213                   gint          height)
214 {
215   guint n_channels = gdk_pixbuf_get_n_channels (src);
216   guchar *pixels = (gdk_pixbuf_get_pixels (src) +
217                     src_y * gdk_pixbuf_get_rowstride (src) +
218                     src_x * n_channels);
219   guchar r = *(pixels++);
220   guchar g = *(pixels++);
221   guchar b = *(pixels++);
222   guint dest_rowstride;
223   guchar *dest_pixels;
224   guchar a = 0;
225   GdkPixbuf *result;
226   int i, j;
227
228   if (n_channels == 4)
229     a = *(pixels++);
230
231   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
232                            width, height);
233
234   if (result == NULL)
235     {
236       g_warning ("failed to create a %dx%d pixbuf", width, height);
237       return NULL;
238     }
239
240   dest_rowstride = gdk_pixbuf_get_rowstride (result);
241   dest_pixels = gdk_pixbuf_get_pixels (result);
242   
243   for (i = 0; i < height; i++)
244     {
245       guchar *p = dest_pixels + dest_rowstride *i;
246
247       for (j = 0; j < width; j++)
248         {
249           *(p++) = r;
250           *(p++) = g;
251           *(p++) = b;
252
253           if (n_channels == 4)
254             *(p++) = a;
255         }
256     }
257
258   return result;
259 }
260
261 static GdkPixbuf *
262 replicate_rows (GdkPixbuf    *src,
263                 gint          src_x,
264                 gint          src_y,
265                 gint          width,
266                 gint          height)
267 {
268   guint n_channels = gdk_pixbuf_get_n_channels (src);
269   guint src_rowstride = gdk_pixbuf_get_rowstride (src);
270   guchar *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x * n_channels);
271   guchar *dest_pixels;
272   GdkPixbuf *result;
273   guint dest_rowstride;
274   int i;
275
276   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
277                            width, height);
278
279   if (result == NULL)
280     {
281       g_warning ("failed to create a %dx%d pixbuf", width, height);
282       return NULL;
283     }
284
285   dest_rowstride = gdk_pixbuf_get_rowstride (result);
286   dest_pixels = gdk_pixbuf_get_pixels (result);
287
288   for (i = 0; i < height; i++)
289     memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
290
291   return result;
292 }
293
294 static GdkPixbuf *
295 replicate_cols (GdkPixbuf    *src,
296                 gint          src_x,
297                 gint          src_y,
298                 gint          width,
299                 gint          height)
300 {
301   guint n_channels = gdk_pixbuf_get_n_channels (src);
302   guint src_rowstride = gdk_pixbuf_get_rowstride (src);
303   guchar *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x * n_channels);
304   guchar *dest_pixels;
305   GdkPixbuf *result;
306   guint dest_rowstride;
307   int i, j;
308
309   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
310                            width, height);
311
312   if (result == NULL)
313     {
314       g_warning ("failed to create a %dx%d pixbuf", width, height);
315       return NULL;
316     }
317
318   dest_rowstride = gdk_pixbuf_get_rowstride (result);
319   dest_pixels = gdk_pixbuf_get_pixels (result);
320
321   for (i = 0; i < height; i++)
322     {
323       guchar *p = dest_pixels + dest_rowstride * i;
324       guchar *q = pixels + src_rowstride * i;
325
326       guchar r = *(q++);
327       guchar g = *(q++);
328       guchar b = *(q++);
329       guchar a = 0;
330       
331       if (n_channels == 4)
332         a = *(q++);
333
334       for (j = 0; j < width; j++)
335         {
336           *(p++) = r;
337           *(p++) = g;
338           *(p++) = b;
339
340           if (n_channels == 4)
341             *(p++) = a;
342         }
343     }
344
345   return result;
346 }
347
348 /* Scale the rectangle (src_x, src_y, src_width, src_height)
349  * onto the rectangle (dest_x, dest_y, dest_width, dest_height)
350  * of the destination, clip by clip_rect and render
351  */
352 static void
353 pixbuf_render (GdkPixbuf    *src,
354                guint         hints,
355                GdkWindow    *window,
356                GdkBitmap    *mask,
357                GdkRectangle *clip_rect,
358                gint          src_x,
359                gint          src_y,
360                gint          src_width,
361                gint          src_height,
362                gint          dest_x,
363                gint          dest_y,
364                gint          dest_width,
365                gint          dest_height)
366 {
367   GdkPixbuf *tmp_pixbuf = NULL;
368   GdkRectangle rect;
369   int x_offset, y_offset;
370   gboolean has_alpha = gdk_pixbuf_get_has_alpha (src);
371   gint src_rowstride = gdk_pixbuf_get_rowstride (src);
372   gint src_n_channels = gdk_pixbuf_get_n_channels (src);
373
374   if (dest_width <= 0 || dest_height <= 0)
375     return;
376
377   rect.x = dest_x;
378   rect.y = dest_y;
379   rect.width = dest_width;
380   rect.height = dest_height;
381
382   if (hints & THEME_MISSING)
383     return;
384
385   /* FIXME: Because we use the mask to shape windows, we don't use
386    * clip_rect to clip what we draw to the mask, only to clip
387    * what we actually draw. But this leads to the horrible ineffiency
388    * of scale the whole image to get a little bit of it.
389    */
390   if (!mask && clip_rect)
391     {
392       if (!gdk_rectangle_intersect (clip_rect, &rect, &rect))
393         return;
394     }
395
396   if (dest_width == src_width && dest_height == src_height)
397     {
398       tmp_pixbuf = g_object_ref (src);
399
400       x_offset = src_x + rect.x - dest_x;
401       y_offset = src_y + rect.y - dest_y;
402     }
403   else if (src_width == 0 && src_height == 0)
404     {
405       tmp_pixbuf = bilinear_gradient (src, src_x, src_y, dest_width, dest_height);      
406       
407       x_offset = rect.x - dest_x;
408       y_offset = rect.y - dest_y;
409     }
410   else if (src_width == 0 && dest_height == src_height)
411     {
412       tmp_pixbuf = horizontal_gradient (src, src_x, src_y, dest_width, dest_height);      
413       
414       x_offset = rect.x - dest_x;
415       y_offset = rect.y - dest_y;
416     }
417   else if (src_height == 0 && dest_width == src_width)
418     {
419       tmp_pixbuf = vertical_gradient (src, src_x, src_y, dest_width, dest_height);
420       
421       x_offset = rect.x - dest_x;
422       y_offset = rect.y - dest_y;
423     }
424   else if ((hints & THEME_CONSTANT_COLS) && (hints & THEME_CONSTANT_ROWS))
425     {
426       tmp_pixbuf = replicate_single (src, src_x, src_y, dest_width, dest_height);
427
428       x_offset = rect.x - dest_x;
429       y_offset = rect.y - dest_y;
430     }
431   else if (dest_width == src_width && (hints & THEME_CONSTANT_COLS))
432     {
433       tmp_pixbuf = replicate_rows (src, src_x, src_y, dest_width, dest_height);
434
435       x_offset = rect.x - dest_x;
436       y_offset = rect.y - dest_y;
437     }
438   else if (dest_height == src_height && (hints & THEME_CONSTANT_ROWS))
439     {
440       tmp_pixbuf = replicate_cols (src, src_x, src_y, dest_width, dest_height);
441
442       x_offset = rect.x - dest_x;
443       y_offset = rect.y - dest_y;
444     }
445   else if (src_width > 0 && src_height > 0)
446     {
447       double x_scale = (double)dest_width / src_width;
448       double y_scale = (double)dest_height / src_height;
449       guchar *pixels;
450       GdkPixbuf *partial_src;
451       
452       pixels = (gdk_pixbuf_get_pixels (src)
453                 + src_y * src_rowstride
454                 + src_x * src_n_channels);
455
456       partial_src = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB,
457                                               has_alpha,
458                                               8, src_width, src_height,
459                                               src_rowstride,
460                                               NULL, NULL);
461                                                   
462       tmp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
463                                    has_alpha, 8,
464                                    rect.width, rect.height);
465
466       gdk_pixbuf_scale (partial_src, tmp_pixbuf,
467                         0, 0, rect.width, rect.height,
468                         dest_x - rect.x, dest_y - rect.y, 
469                         x_scale, y_scale,
470                         GDK_INTERP_BILINEAR);
471
472       g_object_unref (partial_src);
473
474       x_offset = 0;
475       y_offset = 0;
476     }
477
478   if (tmp_pixbuf)
479     {
480       if (mask)
481         {
482           gdk_pixbuf_render_threshold_alpha (tmp_pixbuf, mask,
483                                              x_offset, y_offset,
484                                              rect.x, rect.y,
485                                              rect.width, rect.height,
486                                              128);
487         }
488       
489       gdk_draw_pixbuf (window, NULL, tmp_pixbuf,
490                        x_offset, y_offset,
491                        rect.x, rect.y,
492                        rect.width, rect.height,
493                        GDK_RGB_DITHER_NORMAL,
494                        0, 0);
495       g_object_unref (tmp_pixbuf);
496     }
497 }
498
499 ThemePixbuf *
500 theme_pixbuf_new (void)
501 {
502   ThemePixbuf *result = g_new0 (ThemePixbuf, 1);
503   result->filename = NULL;
504   result->pixbuf = NULL;
505
506   result->stretch = TRUE;
507   result->border_left = 0;
508   result->border_right = 0;
509   result->border_bottom = 0;
510   result->border_top = 0;
511
512   return result;
513 }
514
515 void
516 theme_pixbuf_destroy (ThemePixbuf *theme_pb)
517 {
518   theme_pixbuf_set_filename (theme_pb, NULL);
519   g_free (theme_pb);
520 }
521
522 void         
523 theme_pixbuf_set_filename (ThemePixbuf *theme_pb,
524                            const char  *filename)
525 {
526   if (theme_pb->pixbuf)
527     {
528       g_cache_remove (pixbuf_cache, theme_pb->pixbuf);
529       theme_pb->pixbuf = NULL;
530     }
531
532   g_free (theme_pb->filename);
533
534   if (filename)
535     theme_pb->filename = g_strdup (filename);
536   else
537     theme_pb->filename = NULL;
538 }
539
540 static guint
541 compute_hint (GdkPixbuf *pixbuf,
542               gint       x0,
543               gint       x1,
544               gint       y0,
545               gint       y1)
546 {
547   int i, j;
548   int hints = THEME_CONSTANT_ROWS | THEME_CONSTANT_COLS | THEME_MISSING;
549   int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
550   
551   guchar *data = gdk_pixbuf_get_pixels (pixbuf);
552   int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
553
554   if (x0 == x1 || y0 == y1)
555     return 0;
556
557   for (i = y0; i < y1; i++)
558     {
559       guchar *p = data + i * rowstride + x0 * n_channels;
560       guchar r = p[0];
561       guchar g = p[1];
562       guchar b = p[2];
563       guchar a = 0;
564       
565       if (n_channels == 4)
566         a = p[3];
567
568       for (j = x0; j < x1 ; j++)
569         {
570           if (n_channels != 4 || p[3] != 0)
571             {
572               hints &= ~THEME_MISSING;
573               if (!(hints & THEME_CONSTANT_ROWS))
574                 goto cols;
575             }
576           
577           if (r != *(p++) ||
578               g != *(p++) ||
579               b != *(p++) ||
580               (n_channels != 4 && a != *(p++)))
581             {
582               hints &= ~THEME_CONSTANT_ROWS;
583               if (!(hints & THEME_MISSING))
584                 goto cols;
585             }
586         }
587     }
588
589  cols:
590   for (i = y0 + 1; i < y1; i++)
591     {
592       guchar *base = data + y0 * rowstride + x0 * n_channels;
593       guchar *p = data + i * rowstride + x0 * n_channels;
594
595       if (memcmp (p, base, n_channels * (x1 - x0)) != 0)
596         {
597           hints &= ~THEME_CONSTANT_COLS;
598           return hints;
599         }
600     }
601
602   return hints;
603 }
604
605 static void
606 theme_pixbuf_compute_hints (ThemePixbuf *theme_pb)
607 {
608   int i, j;
609   gint width = gdk_pixbuf_get_width (theme_pb->pixbuf);
610   gint height = gdk_pixbuf_get_height (theme_pb->pixbuf);
611
612   if (theme_pb->border_left + theme_pb->border_right > width ||
613       theme_pb->border_top + theme_pb->border_bottom > height)
614     {
615       g_warning ("Invalid borders specified for theme pixmap:\n"
616                  "        %s,\n"
617                  "borders don't fit within the image", theme_pb->filename);
618       if (theme_pb->border_left + theme_pb->border_right > width)
619         {
620           theme_pb->border_left = width / 2;
621           theme_pb->border_right = (width + 1) / 2;
622         }
623       if (theme_pb->border_bottom + theme_pb->border_top > height)
624         {
625           theme_pb->border_top = height / 2;
626           theme_pb->border_bottom = (height + 1) / 2;
627         }
628     }
629   
630   for (i = 0; i < 3; i++)
631     {
632       gint y0, y1;
633
634       switch (i)
635         {
636         case 0:
637           y0 = 0;
638           y1 = theme_pb->border_top;
639           break;
640         case 1:
641           y0 = theme_pb->border_top;
642           y1 = height - theme_pb->border_bottom;
643           break;
644         default:
645           y0 = height - theme_pb->border_bottom;
646           y1 = height;
647           break;
648         }
649       
650       for (j = 0; j < 3; j++)
651         {
652           gint x0, x1;
653           
654           switch (j)
655             {
656             case 0:
657               x0 = 0;
658               x1 = theme_pb->border_left;
659               break;
660             case 1:
661               x0 = theme_pb->border_left;
662               x1 = width - theme_pb->border_right;
663               break;
664             default:
665               x0 = width - theme_pb->border_right;
666               x1 = width;
667               break;
668             }
669
670           theme_pb->hints[i][j] = compute_hint (theme_pb->pixbuf, x0, x1, y0, y1);
671         }
672     }
673   
674 }
675
676 void
677 theme_pixbuf_set_border (ThemePixbuf *theme_pb,
678                          gint         left,
679                          gint         right,
680                          gint         top,
681                          gint         bottom)
682 {
683   theme_pb->border_left = left;
684   theme_pb->border_right = right;
685   theme_pb->border_top = top;
686   theme_pb->border_bottom = bottom;
687
688   if (theme_pb->pixbuf)
689     theme_pixbuf_compute_hints (theme_pb);
690 }
691
692 void
693 theme_pixbuf_set_stretch (ThemePixbuf *theme_pb,
694                           gboolean     stretch)
695 {
696   theme_pb->stretch = stretch;
697
698   if (theme_pb->pixbuf)
699     theme_pixbuf_compute_hints (theme_pb);
700 }
701
702 static GdkPixbuf *
703 pixbuf_cache_value_new (gchar *filename)
704 {
705   GError *err = NULL;
706     
707   GdkPixbuf *result = gdk_pixbuf_new_from_file (filename, &err);
708   if (!result)
709     {
710       g_warning ("Pixbuf theme: Cannot load pixmap file %s: %s\n",
711                  filename, err->message);
712       g_error_free (err);
713     }
714
715   return result;
716 }
717
718 GdkPixbuf *
719 theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb)
720 {
721   if (!theme_pb->pixbuf)
722     {
723       if (!pixbuf_cache)
724         pixbuf_cache = g_cache_new ((GCacheNewFunc)pixbuf_cache_value_new,
725                                     (GCacheDestroyFunc)g_object_unref,
726                                     (GCacheDupFunc)g_strdup,
727                                     (GCacheDestroyFunc)g_free,
728                                     g_str_hash, g_direct_hash, g_str_equal);
729       
730       theme_pb->pixbuf = g_cache_insert (pixbuf_cache, theme_pb->filename);
731
732       if (theme_pb->stretch)
733         theme_pixbuf_compute_hints (theme_pb);
734     }
735   
736   return theme_pb->pixbuf;
737 }
738
739 void
740 theme_pixbuf_render (ThemePixbuf  *theme_pb,
741                      GdkWindow    *window,
742                      GdkBitmap    *mask,
743                      GdkRectangle *clip_rect,
744                      guint         component_mask,
745                      gboolean      center,
746                      gint          x,
747                      gint          y,
748                      gint          width,
749                      gint          height)
750 {
751   GdkPixbuf *pixbuf = theme_pixbuf_get_pixbuf (theme_pb);
752   gint src_x[4], src_y[4], dest_x[4], dest_y[4];
753   gint pixbuf_width = gdk_pixbuf_get_width (pixbuf);
754   gint pixbuf_height = gdk_pixbuf_get_height (pixbuf);
755
756   if (!pixbuf)
757     return;
758
759   if (theme_pb->stretch)
760     {
761       if (component_mask & COMPONENT_ALL)
762         component_mask = (COMPONENT_ALL - 1) & ~component_mask;
763
764       src_x[0] = 0;
765       src_x[1] = theme_pb->border_left;
766       src_x[2] = pixbuf_width - theme_pb->border_right;
767       src_x[3] = pixbuf_width;
768       
769       src_y[0] = 0;
770       src_y[1] = theme_pb->border_top;
771       src_y[2] = pixbuf_height - theme_pb->border_bottom;
772       src_y[3] = pixbuf_height;
773       
774       dest_x[0] = x;
775       dest_x[1] = x + theme_pb->border_left;
776       dest_x[2] = x + width - theme_pb->border_right;
777       dest_x[3] = x + width;
778
779       if (dest_x[1] > dest_x[2])
780         {
781           component_mask &= ~(COMPONENT_NORTH | COMPONENT_SOUTH | COMPONENT_CENTER);
782           dest_x[1] = dest_x[2] = (dest_x[1] + dest_x[2]) / 2;
783         }
784
785       dest_y[0] = y;
786       dest_y[1] = y + theme_pb->border_top;
787       dest_y[2] = y + height - theme_pb->border_bottom;
788       dest_y[3] = y + height;
789
790       if (dest_y[1] > dest_y[2])
791         {
792           component_mask &= ~(COMPONENT_EAST | COMPONENT_WEST | COMPONENT_CENTER);
793           dest_y[1] = dest_y[2] = (dest_y[1] + dest_y[2]) / 2;
794         }
795
796
797
798 #define RENDER_COMPONENT(X1,X2,Y1,Y2)                                            \
799         pixbuf_render (pixbuf, theme_pb->hints[Y1][X1], window, mask, clip_rect, \
800                        src_x[X1], src_y[Y1],                                     \
801                        src_x[X2] - src_x[X1], src_y[Y2] - src_y[Y1],             \
802                        dest_x[X1], dest_y[Y1],                                   \
803                        dest_x[X2] - dest_x[X1], dest_y[Y2] - dest_y[Y1]);
804       
805       if (component_mask & COMPONENT_NORTH_WEST)
806         RENDER_COMPONENT (0, 1, 0, 1);
807
808       if (component_mask & COMPONENT_NORTH)
809         RENDER_COMPONENT (1, 2, 0, 1);
810
811       if (component_mask & COMPONENT_NORTH_EAST)
812         RENDER_COMPONENT (2, 3, 0, 1);
813
814       if (component_mask & COMPONENT_WEST)
815         RENDER_COMPONENT (0, 1, 1, 2);
816
817       if (component_mask & COMPONENT_CENTER)
818         RENDER_COMPONENT (1, 2, 1, 2);
819
820       if (component_mask & COMPONENT_EAST)
821         RENDER_COMPONENT (2, 3, 1, 2);
822
823       if (component_mask & COMPONENT_SOUTH_WEST)
824         RENDER_COMPONENT (0, 1, 2, 3);
825
826       if (component_mask & COMPONENT_SOUTH)
827         RENDER_COMPONENT (1, 2, 2, 3);
828
829       if (component_mask & COMPONENT_SOUTH_EAST)
830         RENDER_COMPONENT (2, 3, 2, 3);
831     }
832   else
833     {
834       if (center)
835         {
836           x += (width - pixbuf_width) / 2;
837           y += (height - pixbuf_height) / 2;
838           
839           pixbuf_render (pixbuf, 0, window, NULL, clip_rect,
840                          0, 0,
841                          pixbuf_width, pixbuf_height,
842                          x, y,
843                          pixbuf_width, pixbuf_height);
844         }
845       else
846         {
847           cairo_t *cr = gdk_cairo_create (window);
848
849           gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
850           cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
851
852           if (clip_rect)
853             gdk_cairo_rectangle (cr, clip_rect);
854           else
855             cairo_rectangle (cr, x, y, width, height);
856           
857           cairo_fill (cr);
858
859           cairo_destroy (cr);
860         }
861     }
862 }