rsvg-image.c 6.76 KB
Newer Older
1 2
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 sts=4 expandtab: */
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
   rsvg-image.c: Image loading and displaying

   Copyright (C) 2000 Eazel, Inc.
   Copyright (C) 2002, 2003, 2004, 2005 Dom Lachowicz <cinamod@hotmail.com>
   Copyright (C) 2003, 2004, 2005 Caleb Moore <c.moore@student.unsw.edu.au>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this program; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Authors: Raph Levien <raph@artofcode.com>, 
            Dom Lachowicz <cinamod@hotmail.com>, 
            Caleb Moore <c.moore@student.unsw.edu.au>
*/

30 31
#include "config.h"

32 33 34 35 36
#include "rsvg-image.h"
#include <string.h>
#include <math.h>
#include <errno.h>
#include "rsvg-css.h"
37
#include "rsvg-io.h"
38
#include "rsvg-styles.h"
39

40
cairo_surface_t *
41 42
rsvg_cairo_surface_new_from_href (RsvgHandle *handle,
                                  const char *href,
43
                                  GError **error)
44
{
45
    char *data;
46
    gsize data_len;
47
    char *mime_type = NULL;
48
    GdkPixbufLoader *loader = NULL;
49
    GdkPixbuf *pixbuf = NULL;
50
    cairo_surface_t *surface = NULL;
51

52
    data = _rsvg_handle_acquire_data (handle, href, &mime_type, &data_len, error);
53
    if (data == NULL)
54
        return NULL;
55

56
    if (mime_type) {
57 58 59 60 61
        loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, error);
    } else {
        loader = gdk_pixbuf_loader_new ();
    }

62 63
    if (loader == NULL)
        goto out;
64

65
    if (!gdk_pixbuf_loader_write (loader, (guchar *) data, data_len, error)) {
66
        gdk_pixbuf_loader_close (loader, NULL);
67
        goto out;
68 69
    }

70 71
    if (!gdk_pixbuf_loader_close (loader, error))
        goto out;
72

73
    pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
74

75 76 77 78 79 80
    if (!pixbuf) {
        g_set_error (error,
                     GDK_PIXBUF_ERROR,
                     GDK_PIXBUF_ERROR_FAILED,
                      _("Failed to load image '%s': reason not known, probably a corrupt image file"),
                      href);
81
        goto out;
82
    }
83 84

    surface = rsvg_cairo_surface_from_pixbuf (pixbuf);
85

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
    if (mime_type == NULL) {
        /* Try to get the information from the loader */
        GdkPixbufFormat *format;
        char **mime_types;

        if ((format = gdk_pixbuf_loader_get_format (loader)) != NULL) {
            mime_types = gdk_pixbuf_format_get_mime_types (format);

            if (mime_types != NULL)
                mime_type = g_strdup (mime_types[0]);
            g_strfreev (mime_types);
        }
    }

    if ((handle->priv->flags & RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA) != 0 &&
        mime_type != NULL &&
102
        cairo_surface_set_mime_data (surface, mime_type, (guchar *) data,
103 104 105 106 107 108 109 110 111
                                     data_len, g_free, data) == CAIRO_STATUS_SUCCESS) {
        data = NULL; /* transferred to the surface */
    }

  out:
    if (loader)
        g_object_unref (loader);
    g_free (mime_type);
    g_free (data);
112 113 114 115

    return surface;
}

116
static void
117
rsvg_node_image_free (gpointer impl)
118
{
119
    RsvgNodeImage *image = impl;
120

121 122 123 124
    if (image->surface)
        cairo_surface_destroy (image->surface);

    g_free (image);
125 126
}

127
static void
128
rsvg_node_image_draw (RsvgNode *node, gpointer impl, RsvgDrawingCtx *ctx, int dominate)
129
{
130 131
    RsvgNodeImage *z = impl;
    RsvgState *state;
132 133
    unsigned int aspect_ratio = z->preserve_aspect_ratio;
    gdouble x, y, w, h;
134
    cairo_surface_t *surface = z->surface;
Dom Lachowicz's avatar
Dom Lachowicz committed
135

136
    if (surface == NULL)
137
        return;
Dom Lachowicz's avatar
Dom Lachowicz committed
138

139 140 141 142
    x = rsvg_length_normalize (&z->x, ctx);
    y = rsvg_length_normalize (&z->y, ctx);
    w = rsvg_length_normalize (&z->w, ctx);
    h = rsvg_length_normalize (&z->h, ctx);
143

144 145 146
    state = rsvg_node_get_state (node);

    rsvg_state_reinherit_top (ctx, state, dominate);
147

148
    rsvg_push_discrete_layer (ctx);
149

150
    if (!rsvg_current_state (ctx)->overflow && (aspect_ratio & RSVG_ASPECT_RATIO_SLICE)) {
151
        rsvg_drawing_ctx_add_clipping_rect (ctx, x, y, w, h);
152
    }
153

154 155 156 157
    rsvg_aspect_ratio_compute (aspect_ratio, 
                               (double) cairo_image_surface_get_width (surface),
                               (double) cairo_image_surface_get_height (surface), 
                               &x, &y, &w, &h);
158

159
    rsvg_render_surface (ctx, surface, x, y, w, h);
160

161
    rsvg_pop_discrete_layer (ctx);
162 163
}

164
static void
165
rsvg_node_image_set_atts (RsvgNode *node, gpointer impl, RsvgHandle *handle, RsvgPropertyBag *atts)
166
{
167
    RsvgNodeImage *image = impl;
168
    const char *value;
169

170 171 172 173 174 175 176 177 178 179 180
    if ((value = rsvg_property_bag_lookup (atts, "x")))
        image->x = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
    if ((value = rsvg_property_bag_lookup (atts, "y")))
        image->y = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
    if ((value = rsvg_property_bag_lookup (atts, "width")))
        image->w = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
    if ((value = rsvg_property_bag_lookup (atts, "height")))
        image->h = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
    /* path is used by some older adobe illustrator versions */
    if ((value = rsvg_property_bag_lookup (atts, "path"))
        || (value = rsvg_property_bag_lookup (atts, "xlink:href"))) {
181
        image->surface = rsvg_cairo_surface_new_from_href (handle,
182 183 184 185
                                                           value, 
                                                           NULL);

        if (!image->surface) {
186
#ifdef G_ENABLE_DEBUG
187
            g_warning ("Couldn't load image: %s\n", value);
188
#endif
189
        }
190
    }
191

192
    if ((value = rsvg_property_bag_lookup (atts, "preserveAspectRatio")))
193
        image->preserve_aspect_ratio = rsvg_aspect_ratio_parse (value);
194
}
195

196
RsvgNode *
197
rsvg_new_image (const char *element_name, RsvgNode *parent)
198
{
199
    RsvgNodeImage *image;
200

201
    image = g_new0 (RsvgNodeImage, 1);
202
    image->surface = NULL;
203
    image->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
204
    image->x = image->y = image->w = image->h = rsvg_length_parse ("0", LENGTH_DIR_BOTH);
205 206 207 208 209 210 211 212

    return rsvg_rust_cnode_new (RSVG_NODE_TYPE_IMAGE,
                                parent,
                                rsvg_state_new (),
                                image,
                                rsvg_node_image_set_atts,
                                rsvg_node_image_draw,
                                rsvg_node_image_free);
213
}