Want more features on Pastebin? Sign Up, it's FREE!
Guest

hippo-canvas-theme-image.c

By: a guest on Aug 25th, 2011  |  syntax: C  |  size: 7.71 KB  |  views: 30  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
  2.  
  3. #include <config.h>
  4.  
  5. #include "hippo-canvas-theme-image.h"
  6.  
  7. #ifdef HAVE_LIBRSVG
  8. #include <librsvg/rsvg.h>
  9. #include <librsvg/rsvg-cairo.h>
  10. #endif
  11.  
  12. typedef enum {
  13.   THEME_IMAGE_SURFACE,
  14.   THEME_IMAGE_SVG
  15. } ThemeImageType;
  16.  
  17. struct _HippoCanvasThemeImage {
  18.     GObject parent;
  19.  
  20.     ThemeImageType type;
  21.    
  22.     union {
  23. #ifdef HAVE_LIBRSVG
  24.         RsvgHandle      *svg;
  25. #endif 
  26.         cairo_surface_t *surface;
  27.     } u;
  28.  
  29.     int border_top;
  30.     int border_right;
  31.     int border_bottom;
  32.     int border_left;
  33. };
  34.  
  35. struct _HippoCanvasThemeImageClass {
  36.     GObjectClass parent_class;
  37.  
  38. };
  39.  
  40. G_DEFINE_TYPE(HippoCanvasThemeImage, hippo_canvas_theme_image, G_TYPE_OBJECT)
  41.  
  42. GQuark
  43. hippo_canvas_theme_image_error_quark (void)
  44. {
  45.     return g_quark_from_static_string ("hippo-canvas-theme-image-error-quark");
  46. }
  47.  
  48. static void
  49. hippo_canvas_theme_image_finalize(GObject *object)
  50. {
  51.     HippoCanvasThemeImage *image = HIPPO_CANVAS_THEME_IMAGE(object);
  52.  
  53.     switch (image->type) {
  54.     case THEME_IMAGE_SURFACE:
  55.         if (image->u.surface) {
  56.             cairo_surface_destroy(image->u.surface);
  57.             image->u.surface = NULL;
  58.         }
  59.         break;
  60.     case THEME_IMAGE_SVG:
  61. #ifdef HAVE_LIBRSVG
  62.         if (image->u.svg) {
  63.             g_object_unref(image->u.svg);
  64.             image->u.svg = NULL;
  65.         }
  66. #endif 
  67.         break;
  68.     }
  69.  
  70.     G_OBJECT_CLASS(hippo_canvas_theme_image_parent_class)->finalize(object);
  71. }
  72.  
  73. static void
  74. hippo_canvas_theme_image_class_init(HippoCanvasThemeImageClass *klass)
  75. {
  76.     GObjectClass *object_class = G_OBJECT_CLASS (klass);
  77.  
  78.     object_class->finalize = hippo_canvas_theme_image_finalize;
  79. }
  80.  
  81. static void
  82. hippo_canvas_theme_image_init(HippoCanvasThemeImage *image)
  83. {
  84. }
  85.  
  86. HippoCanvasThemeImage *
  87. hippo_canvas_theme_image_new (const char *filename,
  88.                               int         border_top,
  89.                               int         border_right,
  90.                               int         border_bottom,
  91.                               int         border_left,
  92.                               GError    **error)
  93. {
  94.     HippoCanvasThemeImage *image;
  95.  
  96.     g_return_val_if_fail(error == NULL || *error == NULL, NULL);
  97.  
  98.     image = g_object_new(HIPPO_TYPE_CANVAS_THEME_IMAGE, NULL);
  99.  
  100. #ifdef HAVE_LIBRSVG
  101.     if (g_str_has_suffix(filename, ".svg") || g_str_has_suffix(filename, ".SVG")) {
  102.         image->type = THEME_IMAGE_SVG;
  103.  
  104.         image->u.svg = rsvg_handle_new_from_file(filename, error);
  105.         if (image->u.svg == NULL) {
  106.             /* librsvg does a horrible job setting error, it's probably NULL */
  107.             if (*error == NULL)
  108.                 g_set_error(error,
  109.                             HIPPO_CANVAS_THEME_IMAGE_ERROR,
  110.                             HIPPO_CANVAS_THEME_IMAGE_ERROR_FAILED,               
  111.                             "Failed to read SVG image '%s'", filename);
  112.             goto fail;
  113.         }
  114.     } else
  115. #endif 
  116.     if (g_str_has_suffix(filename, ".png") || g_str_has_suffix(filename, ".PNG")) {
  117.         image->type = THEME_IMAGE_SURFACE;
  118.  
  119.         image->u.surface = cairo_image_surface_create_from_png(filename);
  120.         if (image->u.surface == NULL) {
  121.             g_set_error(error,
  122.                         HIPPO_CANVAS_THEME_IMAGE_ERROR,
  123.                         HIPPO_CANVAS_THEME_IMAGE_ERROR_FAILED,           
  124.                         "Failed to read PNG image '%s'", filename);
  125.             goto fail;
  126.         }
  127.     } else {
  128.         g_set_error(error,
  129.                     HIPPO_CANVAS_THEME_IMAGE_ERROR,
  130.                     HIPPO_CANVAS_THEME_IMAGE_ERROR_FAILED,               
  131.                     "Unknown filetype for image '%s'", filename);
  132.         goto fail;
  133.     }
  134.  
  135.     image->border_top = border_top;
  136.     image->border_right = border_right;
  137.     image->border_bottom = border_bottom;
  138.     image->border_left = border_left;
  139.  
  140.     return image;
  141.  
  142. fail:
  143.     g_object_unref(image);
  144.     return NULL;
  145. }
  146.  
  147. void
  148. hippo_canvas_theme_image_render (HippoCanvasThemeImage *image,
  149.                                  cairo_t               *cr,
  150.                                  int                    x,
  151.                                  int                    y,
  152.                                  int                    width,
  153.                                  int                    height)
  154. {
  155.     int source_width = 0;
  156.     int source_height = 0;
  157.  
  158.     int source_x1 = 0, source_x2 = 0, source_y1 = 0, source_y2 = 0;
  159.     int dest_x1 = 0, dest_x2 = 0, dest_y1 = 0, dest_y2 = 0;
  160.  
  161.     int i, j;
  162.  
  163.     /* To draw a theme image, we divide the source and destination into 9
  164.      * pieces and draw each piece separately. (Some pieces may not exist
  165.      * if we have 0-width borders, in which case they'll be skipped)
  166.      *
  167.      *                         i=0               i=1              i=2
  168.      *                     border_left                        border_right
  169.      *                    +------------+--------------------+--------------+
  170.      * j=0: border_top    |            |                    |              |
  171.      *                    +------------+--------------------+--------------+
  172.      * j=1                |            |                    |              |
  173.      *                    +------------+--------------------+--------------+
  174.      * j=2: border_bottom |            |                    |              |
  175.      *                    +------------+--------------------+--------------+
  176.      *
  177.      */
  178.        
  179.     switch (image->type) {
  180.     case THEME_IMAGE_SURFACE:
  181.         source_width = cairo_image_surface_get_width(image->u.surface);
  182.         source_height = cairo_image_surface_get_height(image->u.surface);
  183.         break;
  184.     case THEME_IMAGE_SVG:
  185.     {
  186. #ifdef HAVE_LIBRSVG
  187.         RsvgDimensionData dimensions;
  188.         rsvg_handle_get_dimensions(image->u.svg, &dimensions);
  189.         source_width = dimensions.width;
  190.         source_height = dimensions.height;
  191. #endif 
  192.         break;
  193.     }
  194.     }
  195.  
  196.     for (j = 0; j < 3; j++) {
  197.         switch (j) {
  198.         case 0:
  199.             source_y1 = 0;
  200.             source_y2 = image->border_top;
  201.             dest_y1 = y;
  202.             dest_y2 = y + image->border_top;
  203.             break;
  204.         case 1:
  205.             source_y1 = image->border_top;
  206.             source_y2 = source_height - image->border_bottom;
  207.             dest_y1 = y + image->border_top;
  208.             dest_y2 = y + height - image->border_bottom;
  209.             break;
  210.         case 2:
  211.             source_y1 = source_height - image->border_bottom;
  212.             source_y2 = source_height;
  213.             dest_y1 = y + height - image->border_bottom;
  214.             dest_y2 = y + height;
  215.             break;
  216.         }
  217.        
  218.         if (dest_y2 <= dest_y1)
  219.             continue;
  220.  
  221.         /* pixbuf-theme-engine has a nice interpretation of source_y2 == source_y1,
  222.          * dest_y2 != dest_y1, which is to linearly interpolate between the surrounding
  223.          * areas. We could do that for the surface case by setting
  224.          *
  225.          *   source_y1 == y - 0.5
  226.          *   source_y2 == y + 0.5
  227.          *
  228.          * but it's hard for the SVG case. source_y2 < source_y1 is pathological ... someone
  229.          * specified borders that sum up larger than the image.
  230.          */
  231.          if (source_y2 <= source_y1)
  232.             continue;
  233.            
  234.         for (i = 0; i < 3; i++) {
  235.             switch (i) {
  236.             case 0:
  237.                 source_x1 = 0;
  238.                 source_x2 = image->border_left;
  239.                 dest_x1 = x;
  240.                 dest_x2 = x + image->border_left;
  241.                 break;
  242.             case 1:
  243.                 source_x1 = image->border_left;
  244.                 source_x2 = source_width - image->border_right;
  245.                 dest_x1 = x + image->border_left;
  246.                 dest_x2 = x + width - image->border_right;
  247.                 break;
  248.             case 2:
  249.                 source_x1 = source_width - image->border_right;
  250.                 source_x2 = source_width;
  251.                 dest_x1 = x + width - image->border_right;
  252.                 dest_x2 = x + width;
  253.                 break;
  254.             }
  255.  
  256.             if (dest_x2 <= dest_x1)
  257.                 continue;
  258.            
  259.             if (source_x2 <= source_x1)
  260.                 continue;
  261.  
  262.             cairo_save(cr);
  263.            
  264.             cairo_rectangle(cr, dest_x1, dest_y1, dest_x2 - dest_x1, dest_y2 - dest_y1);
  265.             cairo_clip(cr);
  266.            
  267.             cairo_translate(cr, dest_x1, dest_y1);
  268.             cairo_scale(cr,
  269.                         (double)(dest_x2 - dest_x1) / (source_x2 - source_x1),
  270.                         (double)(dest_y2 - dest_y1) / (source_y2 - source_y1));
  271.  
  272.             switch (image->type) {
  273.             case THEME_IMAGE_SURFACE:
  274.                 cairo_set_source_surface(cr, image->u.surface, - source_x1, - source_y1);
  275.                 cairo_paint(cr);
  276.                 break;
  277.             case THEME_IMAGE_SVG:
  278. #ifdef HAVE_LIBRSVG
  279.                 cairo_translate(cr, - source_x1, - source_y1);
  280.                 rsvg_handle_render_cairo(image->u.svg, cr);
  281. #endif         
  282.                 break;
  283.             }
  284.            
  285.             cairo_restore(cr);
  286.         }
  287.     }
  288. }
clone this paste RAW Paste Data