efl/legacy/evas/src/modules/loaders/svg/evas_image_load_svg.c

282 lines
6.6 KiB
C

#include "evas_common.h"
#include "evas_private.h"
#include <librsvg/rsvg.h>
#include <librsvg/rsvg-cairo.h>
static inline Eina_Bool evas_image_load_file_is_svg(const char *file) EINA_ARG_NONNULL(1) EINA_PURE;
static Eina_Bool evas_image_load_file_head_svg(Image_Entry *ie, const char *file, const char *key, int *error) EINA_ARG_NONNULL(1, 2, 4);
static Eina_Bool evas_image_load_file_data_svg(Image_Entry *ie, const char *file, const char *key, int *error) EINA_ARG_NONNULL(1, 2, 4);
Evas_Image_Load_Func evas_image_load_svg_func =
{
EINA_FALSE,
evas_image_load_file_head_svg,
evas_image_load_file_data_svg,
NULL,
EINA_FALSE
};
static int rsvg_initialized = 0;
static inline Eina_Bool evas_image_load_file_is_svg(const char *file)
{
int i, len = strlen(file);
Eina_Bool is_gz = EINA_FALSE;
for (i = len - 1; i > 0; i--)
{
if (file[i] == '.')
{
if (is_gz)
break;
else if (strcasecmp(file + i + 1, "gz") == 0)
is_gz = EINA_TRUE;
else
break;
}
}
if (i < 1) return EINA_FALSE;
i++;
if (i >= len) return EINA_FALSE;
if (strncasecmp(file + i, "svg", 3) != 0) return EINA_FALSE;
i += 3;
if (is_gz)
{
if (file[i] == '.') return EINA_TRUE;
else return EINA_FALSE;
}
else
{
if (file[i] == '\0') return EINA_TRUE;
else if (((file[i] == 'z') || (file[i] == 'Z')) && (!file[i + 1])) return EINA_TRUE;
else return EINA_FALSE;
}
}
static Eina_Bool
evas_image_load_file_head_svg(Image_Entry *ie, const char *file, const char *key __UNUSED__, int *error)
{
RsvgHandle *rsvg;
RsvgDimensionData dim;
int w, h;
/* ignore all files not called .svg or .svg.gz - because rsvg has a leak
* where closing the handle doesn't free mem */
if (!evas_image_load_file_is_svg(file))
{
*error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
return EINA_FALSE;
}
rsvg = rsvg_handle_new_from_file(file, NULL);
if (!rsvg)
{
*error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
return EINA_FALSE;
}
rsvg_handle_set_dpi(rsvg, 75.0);
rsvg_handle_get_dimensions(rsvg, &dim);
w = dim.width;
h = dim.height;
if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) ||
IMG_TOO_BIG(w, h))
{
rsvg_handle_close(rsvg, NULL);
g_object_unref(rsvg);
if (IMG_TOO_BIG(w, h))
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
else
*error = EVAS_LOAD_ERROR_GENERIC;
return EINA_FALSE;
}
if (ie->load_opts.scale_down_by > 1)
{
w /= ie->load_opts.scale_down_by;
h /= ie->load_opts.scale_down_by;
}
else if (ie->load_opts.dpi > 0.0)
{
w = (w * ie->load_opts.dpi) / 75.0;
h = (h * ie->load_opts.dpi) / 75.0;
}
else if ((ie->load_opts.w > 0) &&
(ie->load_opts.h > 0))
{
unsigned int w2, h2;
w2 = ie->load_opts.w;
h2 = (ie->load_opts.w * h) / w;
if (h2 > ie->load_opts.h)
{
h2 = ie->load_opts.h;
w2 = (ie->load_opts.h * w) / h;
}
w = w2;
h = h2;
}
if (w < 1) w = 1;
if (h < 1) h = 1;
ie->w = w;
ie->h = h;
ie->flags.alpha = 1;
rsvg_handle_close(rsvg, NULL);
g_object_unref(rsvg);
*error = EVAS_LOAD_ERROR_NONE;
return EINA_TRUE;
}
/** FIXME: All evas loaders need to be tightened up **/
static Eina_Bool
evas_image_load_file_data_svg(Image_Entry *ie, const char *file, const char *key __UNUSED__, int *error)
{
DATA32 *pixels;
RsvgHandle *rsvg;
RsvgDimensionData dim;
int w, h;
cairo_surface_t *surface;
cairo_t *cr;
/* ignore all files not called .svg or .svg.gz - because rsvg has a leak
* where closing the handle doesn't free mem */
if (!evas_image_load_file_is_svg(file))
{
*error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
return EINA_FALSE;
}
rsvg = rsvg_handle_new_from_file(file, NULL);
if (!rsvg)
{
*error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
return EINA_FALSE;
}
rsvg_handle_set_dpi(rsvg, 75.0);
rsvg_handle_get_dimensions(rsvg, &dim);
w = dim.width;
h = dim.height;
if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE))
{
rsvg_handle_close(rsvg, NULL);
g_object_unref(rsvg);
if (IMG_TOO_BIG(w, h))
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
else
*error = EVAS_LOAD_ERROR_GENERIC;
return EINA_FALSE;
}
if (ie->load_opts.scale_down_by > 1)
{
w /= ie->load_opts.scale_down_by;
h /= ie->load_opts.scale_down_by;
}
else if (ie->load_opts.dpi > 0.0)
{
w = (w * ie->load_opts.dpi) / 75.0;
h = (h * ie->load_opts.dpi) / 75.0;
}
else if ((ie->load_opts.w > 0) &&
(ie->load_opts.h > 0))
{
unsigned int w2, h2;
w2 = ie->load_opts.w;
h2 = (ie->load_opts.w * h) / w;
if (h2 > ie->load_opts.h)
{
h2 = ie->load_opts.h;
w2 = (ie->load_opts.h * w) / h;
}
w = w2;
h = h2;
}
if (w < 1) w = 1;
if (h < 1) h = 1;
if ((w != (int)ie->w) || (h != (int)ie->h))
{
*error = EVAS_LOAD_ERROR_GENERIC;
goto error;
}
ie->flags.alpha = 1;
evas_cache_image_surface_alloc(ie, w, h);
pixels = evas_cache_image_pixels(ie);
if (!pixels)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto error;
}
memset(pixels, 0, w * h * sizeof(DATA32));
surface = cairo_image_surface_create_for_data((unsigned char *)pixels, CAIRO_FORMAT_ARGB32,
w, h, w * sizeof(DATA32));
if (!surface)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto error;
}
cr = cairo_create(surface);
if (!cr)
{
cairo_surface_destroy(surface);
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto error;
}
cairo_scale(cr,
(double)ie->w / dim.em,
(double)ie->h / dim.ex);
rsvg_handle_render_cairo(rsvg, cr);
cairo_surface_destroy(surface);
/* need to check if this is required... */
cairo_destroy(cr);
rsvg_handle_close(rsvg, NULL);
g_object_unref(rsvg);
evas_common_image_set_alpha_sparse(ie);
return EINA_TRUE;
error:
rsvg_handle_close(rsvg, NULL);
g_object_unref(rsvg);
return EINA_FALSE;
}
static int
module_open(Evas_Module *em)
{
if (!em) return 0;
em->functions = (void *)(&evas_image_load_svg_func);
if (!rsvg_initialized) rsvg_init();
rsvg_initialized = 1;
return 1;
}
static void
module_close(Evas_Module *em __UNUSED__)
{
if (!rsvg_initialized) return;
//rsvg_term();
//rsvg_initialized = 0;
}
static Evas_Module_Api evas_modapi =
{
EVAS_MODULE_API_VERSION,
"svg",
"none",
{
module_open,
module_close
}
};
EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, svg);
#ifndef EVAS_STATIC_BUILD_SVG
EVAS_EINA_MODULE_DEFINE(image_loader, svg);
#endif