]> Pileus Git - ~andy/gtk/blob - modules/engines/pixbuf/pixbuf-render.c
pixbuf-engine: replace call to 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       cairo_t *cr;
481       
482       cr = gdk_cairo_create (window);
483       if (mask)
484         {
485           gdk_pixbuf_render_threshold_alpha (tmp_pixbuf, mask,
486                                              x_offset, y_offset,
487                                              rect.x, rect.y,
488                                              rect.width, rect.height,
489                                              128);
490         }
491
492       gdk_cairo_set_source_pixbuf (cr, 
493                                    tmp_pixbuf,
494                                    -x_offset + rect.x, 
495                                    -y_offset + rect.y);
496       gdk_cairo_rectangle (cr, &rect);
497       cairo_fill (cr);
498
499       cairo_destroy (cr);
500       g_object_unref (tmp_pixbuf);
501     }
502 }
503
504 ThemePixbuf *
505 theme_pixbuf_new (void)
506 {
507   ThemePixbuf *result = g_new0 (ThemePixbuf, 1);
508   result->filename = NULL;
509   result->pixbuf = NULL;
510
511   result->stretch = TRUE;
512   result->border_left = 0;
513   result->border_right = 0;
514   result->border_bottom = 0;
515   result->border_top = 0;
516
517   return result;
518 }
519
520 void
521 theme_pixbuf_destroy (ThemePixbuf *theme_pb)
522 {
523   theme_pixbuf_set_filename (theme_pb, NULL);
524   g_free (theme_pb);
525 }
526
527 void         
528 theme_pixbuf_set_filename (ThemePixbuf *theme_pb,
529                            const char  *filename)
530 {
531   if (theme_pb->pixbuf)
532     {
533       g_cache_remove (pixbuf_cache, theme_pb->pixbuf);
534       theme_pb->pixbuf = NULL;
535     }
536
537   g_free (theme_pb->filename);
538
539   if (filename)
540     theme_pb->filename = g_strdup (filename);
541   else
542     theme_pb->filename = NULL;
543 }
544
545 static guint
546 compute_hint (GdkPixbuf *pixbuf,
547               gint       x0,
548               gint       x1,
549               gint       y0,
550               gint       y1)
551 {
552   int i, j;
553   int hints = THEME_CONSTANT_ROWS | THEME_CONSTANT_COLS | THEME_MISSING;
554   int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
555   
556   guchar *data = gdk_pixbuf_get_pixels (pixbuf);
557   int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
558
559   if (x0 == x1 || y0 == y1)
560     return 0;
561
562   for (i = y0; i < y1; i++)
563     {
564       guchar *p = data + i * rowstride + x0 * n_channels;
565       guchar r = p[0];
566       guchar g = p[1];
567       guchar b = p[2];
568       guchar a = 0;
569       
570       if (n_channels == 4)
571         a = p[3];
572
573       for (j = x0; j < x1 ; j++)
574         {
575           if (n_channels != 4 || p[3] != 0)
576             {
577               hints &= ~THEME_MISSING;
578               if (!(hints & THEME_CONSTANT_ROWS))
579                 goto cols;
580             }
581           
582           if (r != *(p++) ||
583               g != *(p++) ||
584               b != *(p++) ||
585               (n_channels != 4 && a != *(p++)))
586             {
587               hints &= ~THEME_CONSTANT_ROWS;
588               if (!(hints & THEME_MISSING))
589                 goto cols;
590             }
591         }
592     }
593
594  cols:
595   for (i = y0 + 1; i < y1; i++)
596     {
597       guchar *base = data + y0 * rowstride + x0 * n_channels;
598       guchar *p = data + i * rowstride + x0 * n_channels;
599
600       if (memcmp (p, base, n_channels * (x1 - x0)) != 0)
601         {
602           hints &= ~THEME_CONSTANT_COLS;
603           return hints;
604         }
605     }
606
607   return hints;
608 }
609
610 static void
611 theme_pixbuf_compute_hints (ThemePixbuf *theme_pb)
612 {
613   int i, j;
614   gint width = gdk_pixbuf_get_width (theme_pb->pixbuf);
615   gint height = gdk_pixbuf_get_height (theme_pb->pixbuf);
616
617   if (theme_pb->border_left + theme_pb->border_right > width ||
618       theme_pb->border_top + theme_pb->border_bottom > height)
619     {
620       g_warning ("Invalid borders specified for theme pixmap:\n"
621                  "        %s,\n"
622                  "borders don't fit within the image", theme_pb->filename);
623       if (theme_pb->border_left + theme_pb->border_right > width)
624         {
625           theme_pb->border_left = width / 2;
626           theme_pb->border_right = (width + 1) / 2;
627         }
628       if (theme_pb->border_bottom + theme_pb->border_top > height)
629         {
630           theme_pb->border_top = height / 2;
631           theme_pb->border_bottom = (height + 1) / 2;
632         }
633     }
634   
635   for (i = 0; i < 3; i++)
636     {
637       gint y0, y1;
638
639       switch (i)
640         {
641         case 0:
642           y0 = 0;
643           y1 = theme_pb->border_top;
644           break;
645         case 1:
646           y0 = theme_pb->border_top;
647           y1 = height - theme_pb->border_bottom;
648           break;
649         default:
650           y0 = height - theme_pb->border_bottom;
651           y1 = height;
652           break;
653         }
654       
655       for (j = 0; j < 3; j++)
656         {
657           gint x0, x1;
658           
659           switch (j)
660             {
661             case 0:
662               x0 = 0;
663               x1 = theme_pb->border_left;
664               break;
665             case 1:
666               x0 = theme_pb->border_left;
667               x1 = width - theme_pb->border_right;
668               break;
669             default:
670               x0 = width - theme_pb->border_right;
671               x1 = width;
672               break;
673             }
674
675           theme_pb->hints[i][j] = compute_hint (theme_pb->pixbuf, x0, x1, y0, y1);
676         }
677     }
678   
679 }
680
681 void
682 theme_pixbuf_set_border (ThemePixbuf *theme_pb,
683                          gint         left,
684                          gint         right,
685                          gint         top,
686                          gint         bottom)
687 {
688   theme_pb->border_left = left;
689   theme_pb->border_right = right;
690   theme_pb->border_top = top;
691   theme_pb->border_bottom = bottom;
692
693   if (theme_pb->pixbuf)
694     theme_pixbuf_compute_hints (theme_pb);
695 }
696
697 void
698 theme_pixbuf_set_stretch (ThemePixbuf *theme_pb,
699                           gboolean     stretch)
700 {
701   theme_pb->stretch = stretch;
702
703   if (theme_pb->pixbuf)
704     theme_pixbuf_compute_hints (theme_pb);
705 }
706
707 static GdkPixbuf *
708 pixbuf_cache_value_new (gchar *filename)
709 {
710   GError *err = NULL;
711     
712   GdkPixbuf *result = gdk_pixbuf_new_from_file (filename, &err);
713   if (!result)
714     {
715       g_warning ("Pixbuf theme: Cannot load pixmap file %s: %s\n",
716                  filename, err->message);
717       g_error_free (err);
718     }
719
720   return result;
721 }
722
723 GdkPixbuf *
724 theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb)
725 {
726   if (!theme_pb->pixbuf)
727     {
728       if (!pixbuf_cache)
729         pixbuf_cache = g_cache_new ((GCacheNewFunc)pixbuf_cache_value_new,
730                                     (GCacheDestroyFunc)g_object_unref,
731                                     (GCacheDupFunc)g_strdup,
732                                     (GCacheDestroyFunc)g_free,
733                                     g_str_hash, g_direct_hash, g_str_equal);
734       
735       theme_pb->pixbuf = g_cache_insert (pixbuf_cache, theme_pb->filename);
736
737       if (theme_pb->stretch)
738         theme_pixbuf_compute_hints (theme_pb);
739     }
740   
741   return theme_pb->pixbuf;
742 }
743
744 void
745 theme_pixbuf_render (ThemePixbuf  *theme_pb,
746                      GdkWindow    *window,
747                      GdkBitmap    *mask,
748                      GdkRectangle *clip_rect,
749                      guint         component_mask,
750                      gboolean      center,
751                      gint          x,
752                      gint          y,
753                      gint          width,
754                      gint          height)
755 {
756   GdkPixbuf *pixbuf = theme_pixbuf_get_pixbuf (theme_pb);
757   gint src_x[4], src_y[4], dest_x[4], dest_y[4];
758   gint pixbuf_width = gdk_pixbuf_get_width (pixbuf);
759   gint pixbuf_height = gdk_pixbuf_get_height (pixbuf);
760
761   if (!pixbuf)
762     return;
763
764   if (theme_pb->stretch)
765     {
766       if (component_mask & COMPONENT_ALL)
767         component_mask = (COMPONENT_ALL - 1) & ~component_mask;
768
769       src_x[0] = 0;
770       src_x[1] = theme_pb->border_left;
771       src_x[2] = pixbuf_width - theme_pb->border_right;
772       src_x[3] = pixbuf_width;
773       
774       src_y[0] = 0;
775       src_y[1] = theme_pb->border_top;
776       src_y[2] = pixbuf_height - theme_pb->border_bottom;
777       src_y[3] = pixbuf_height;
778       
779       dest_x[0] = x;
780       dest_x[1] = x + theme_pb->border_left;
781       dest_x[2] = x + width - theme_pb->border_right;
782       dest_x[3] = x + width;
783
784       if (dest_x[1] > dest_x[2])
785         {
786           component_mask &= ~(COMPONENT_NORTH | COMPONENT_SOUTH | COMPONENT_CENTER);
787           dest_x[1] = dest_x[2] = (dest_x[1] + dest_x[2]) / 2;
788         }
789
790       dest_y[0] = y;
791       dest_y[1] = y + theme_pb->border_top;
792       dest_y[2] = y + height - theme_pb->border_bottom;
793       dest_y[3] = y + height;
794
795       if (dest_y[1] > dest_y[2])
796         {
797           component_mask &= ~(COMPONENT_EAST | COMPONENT_WEST | COMPONENT_CENTER);
798           dest_y[1] = dest_y[2] = (dest_y[1] + dest_y[2]) / 2;
799         }
800
801
802
803 #define RENDER_COMPONENT(X1,X2,Y1,Y2)                                            \
804         pixbuf_render (pixbuf, theme_pb->hints[Y1][X1], window, mask, clip_rect, \
805                        src_x[X1], src_y[Y1],                                     \
806                        src_x[X2] - src_x[X1], src_y[Y2] - src_y[Y1],             \
807                        dest_x[X1], dest_y[Y1],                                   \
808                        dest_x[X2] - dest_x[X1], dest_y[Y2] - dest_y[Y1]);
809       
810       if (component_mask & COMPONENT_NORTH_WEST)
811         RENDER_COMPONENT (0, 1, 0, 1);
812
813       if (component_mask & COMPONENT_NORTH)
814         RENDER_COMPONENT (1, 2, 0, 1);
815
816       if (component_mask & COMPONENT_NORTH_EAST)
817         RENDER_COMPONENT (2, 3, 0, 1);
818
819       if (component_mask & COMPONENT_WEST)
820         RENDER_COMPONENT (0, 1, 1, 2);
821
822       if (component_mask & COMPONENT_CENTER)
823         RENDER_COMPONENT (1, 2, 1, 2);
824
825       if (component_mask & COMPONENT_EAST)
826         RENDER_COMPONENT (2, 3, 1, 2);
827
828       if (component_mask & COMPONENT_SOUTH_WEST)
829         RENDER_COMPONENT (0, 1, 2, 3);
830
831       if (component_mask & COMPONENT_SOUTH)
832         RENDER_COMPONENT (1, 2, 2, 3);
833
834       if (component_mask & COMPONENT_SOUTH_EAST)
835         RENDER_COMPONENT (2, 3, 2, 3);
836     }
837   else
838     {
839       if (center)
840         {
841           x += (width - pixbuf_width) / 2;
842           y += (height - pixbuf_height) / 2;
843           
844           pixbuf_render (pixbuf, 0, window, NULL, clip_rect,
845                          0, 0,
846                          pixbuf_width, pixbuf_height,
847                          x, y,
848                          pixbuf_width, pixbuf_height);
849         }
850       else
851         {
852           cairo_t *cr = gdk_cairo_create (window);
853
854           gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
855           cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
856
857           if (clip_rect)
858             gdk_cairo_rectangle (cr, clip_rect);
859           else
860             cairo_rectangle (cr, x, y, width, height);
861           
862           cairo_fill (cr);
863
864           cairo_destroy (cr);
865         }
866     }
867 }