#include "evas_common.h" #include "evas_private.h" #include #include 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 }; 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