diff options
Diffstat (limited to 'libraries/evas/src/modules/loaders/svg/evas_image_load_svg.c')
-rw-r--r-- | libraries/evas/src/modules/loaders/svg/evas_image_load_svg.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/libraries/evas/src/modules/loaders/svg/evas_image_load_svg.c b/libraries/evas/src/modules/loaders/svg/evas_image_load_svg.c new file mode 100644 index 0000000..f1c8452 --- /dev/null +++ b/libraries/evas/src/modules/loaders/svg/evas_image_load_svg.c | |||
@@ -0,0 +1,280 @@ | |||
1 | #include "evas_common.h" | ||
2 | #include "evas_private.h" | ||
3 | |||
4 | #include <librsvg/rsvg.h> | ||
5 | #include <librsvg/rsvg-cairo.h> | ||
6 | |||
7 | static inline Eina_Bool evas_image_load_file_is_svg(const char *file) EINA_ARG_NONNULL(1) EINA_PURE; | ||
8 | 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); | ||
9 | 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); | ||
10 | |||
11 | Evas_Image_Load_Func evas_image_load_svg_func = | ||
12 | { | ||
13 | EINA_FALSE, | ||
14 | evas_image_load_file_head_svg, | ||
15 | evas_image_load_file_data_svg, | ||
16 | NULL | ||
17 | }; | ||
18 | |||
19 | static int rsvg_initialized = 0; | ||
20 | |||
21 | |||
22 | static inline Eina_Bool evas_image_load_file_is_svg(const char *file) | ||
23 | { | ||
24 | int i, len = strlen(file); | ||
25 | Eina_Bool is_gz = EINA_FALSE; | ||
26 | |||
27 | for (i = len - 1; i > 0; i--) | ||
28 | { | ||
29 | if (file[i] == '.') | ||
30 | { | ||
31 | if (is_gz) | ||
32 | break; | ||
33 | else if (strcasecmp(file + i + 1, "gz") == 0) | ||
34 | is_gz = EINA_TRUE; | ||
35 | else | ||
36 | break; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | if (i < 1) return EINA_FALSE; | ||
41 | i++; | ||
42 | if (i >= len) return EINA_FALSE; | ||
43 | if (strncasecmp(file + i, "svg", 3) != 0) return EINA_FALSE; | ||
44 | i += 3; | ||
45 | if (is_gz) | ||
46 | { | ||
47 | if (file[i] == '.') return EINA_TRUE; | ||
48 | else return EINA_FALSE; | ||
49 | } | ||
50 | else | ||
51 | { | ||
52 | if (file[i] == '\0') return EINA_TRUE; | ||
53 | else if (((file[i] == 'z') || (file[i] == 'Z')) && (!file[i + 1])) return EINA_TRUE; | ||
54 | else return EINA_FALSE; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | static Eina_Bool | ||
59 | evas_image_load_file_head_svg(Image_Entry *ie, const char *file, const char *key __UNUSED__, int *error) | ||
60 | { | ||
61 | RsvgHandle *rsvg; | ||
62 | RsvgDimensionData dim; | ||
63 | int w, h; | ||
64 | |||
65 | /* ignore all files not called .svg or .svg.gz - because rsvg has a leak | ||
66 | * where closing the handle doesn't free mem */ | ||
67 | if (!evas_image_load_file_is_svg(file)) | ||
68 | { | ||
69 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
70 | return EINA_FALSE; | ||
71 | } | ||
72 | |||
73 | rsvg = rsvg_handle_new_from_file(file, NULL); | ||
74 | if (!rsvg) | ||
75 | { | ||
76 | *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; | ||
77 | return EINA_FALSE; | ||
78 | } | ||
79 | |||
80 | rsvg_handle_set_dpi(rsvg, 75.0); | ||
81 | rsvg_handle_get_dimensions(rsvg, &dim); | ||
82 | w = dim.width; | ||
83 | h = dim.height; | ||
84 | if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || | ||
85 | IMG_TOO_BIG(w, h)) | ||
86 | { | ||
87 | rsvg_handle_close(rsvg, NULL); | ||
88 | g_object_unref(rsvg); | ||
89 | if (IMG_TOO_BIG(w, h)) | ||
90 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
91 | else | ||
92 | *error = EVAS_LOAD_ERROR_GENERIC; | ||
93 | return EINA_FALSE; | ||
94 | } | ||
95 | if (ie->load_opts.scale_down_by > 1) | ||
96 | { | ||
97 | w /= ie->load_opts.scale_down_by; | ||
98 | h /= ie->load_opts.scale_down_by; | ||
99 | } | ||
100 | else if (ie->load_opts.dpi > 0.0) | ||
101 | { | ||
102 | w = (w * ie->load_opts.dpi) / 75.0; | ||
103 | h = (h * ie->load_opts.dpi) / 75.0; | ||
104 | } | ||
105 | else if ((ie->load_opts.w > 0) && | ||
106 | (ie->load_opts.h > 0)) | ||
107 | { | ||
108 | unsigned int w2, h2; | ||
109 | |||
110 | w2 = ie->load_opts.w; | ||
111 | h2 = (ie->load_opts.w * h) / w; | ||
112 | if (h2 > ie->load_opts.h) | ||
113 | { | ||
114 | h2 = ie->load_opts.h; | ||
115 | w2 = (ie->load_opts.h * w) / h; | ||
116 | } | ||
117 | w = w2; | ||
118 | h = h2; | ||
119 | } | ||
120 | if (w < 1) w = 1; | ||
121 | if (h < 1) h = 1; | ||
122 | ie->w = w; | ||
123 | ie->h = h; | ||
124 | ie->flags.alpha = 1; | ||
125 | rsvg_handle_close(rsvg, NULL); | ||
126 | g_object_unref(rsvg); | ||
127 | |||
128 | *error = EVAS_LOAD_ERROR_NONE; | ||
129 | return EINA_TRUE; | ||
130 | } | ||
131 | |||
132 | /** FIXME: All evas loaders need to be tightened up **/ | ||
133 | static Eina_Bool | ||
134 | evas_image_load_file_data_svg(Image_Entry *ie, const char *file, const char *key __UNUSED__, int *error) | ||
135 | { | ||
136 | DATA32 *pixels; | ||
137 | RsvgHandle *rsvg; | ||
138 | RsvgDimensionData dim; | ||
139 | int w, h; | ||
140 | cairo_surface_t *surface; | ||
141 | cairo_t *cr; | ||
142 | |||
143 | /* ignore all files not called .svg or .svg.gz - because rsvg has a leak | ||
144 | * where closing the handle doesn't free mem */ | ||
145 | if (!evas_image_load_file_is_svg(file)) | ||
146 | { | ||
147 | *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; | ||
148 | return EINA_FALSE; | ||
149 | } | ||
150 | |||
151 | rsvg = rsvg_handle_new_from_file(file, NULL); | ||
152 | if (!rsvg) | ||
153 | { | ||
154 | *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; | ||
155 | return EINA_FALSE; | ||
156 | } | ||
157 | |||
158 | rsvg_handle_set_dpi(rsvg, 75.0); | ||
159 | rsvg_handle_get_dimensions(rsvg, &dim); | ||
160 | w = dim.width; | ||
161 | h = dim.height; | ||
162 | if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE)) | ||
163 | { | ||
164 | rsvg_handle_close(rsvg, NULL); | ||
165 | g_object_unref(rsvg); | ||
166 | if (IMG_TOO_BIG(w, h)) | ||
167 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
168 | else | ||
169 | *error = EVAS_LOAD_ERROR_GENERIC; | ||
170 | return EINA_FALSE; | ||
171 | } | ||
172 | if (ie->load_opts.scale_down_by > 1) | ||
173 | { | ||
174 | w /= ie->load_opts.scale_down_by; | ||
175 | h /= ie->load_opts.scale_down_by; | ||
176 | } | ||
177 | else if (ie->load_opts.dpi > 0.0) | ||
178 | { | ||
179 | w = (w * ie->load_opts.dpi) / 75.0; | ||
180 | h = (h * ie->load_opts.dpi) / 75.0; | ||
181 | } | ||
182 | else if ((ie->load_opts.w > 0) && | ||
183 | (ie->load_opts.h > 0)) | ||
184 | { | ||
185 | unsigned int w2, h2; | ||
186 | |||
187 | w2 = ie->load_opts.w; | ||
188 | h2 = (ie->load_opts.w * h) / w; | ||
189 | if (h2 > ie->load_opts.h) | ||
190 | { | ||
191 | h2 = ie->load_opts.h; | ||
192 | w2 = (ie->load_opts.h * w) / h; | ||
193 | } | ||
194 | w = w2; | ||
195 | h = h2; | ||
196 | } | ||
197 | if (w < 1) w = 1; | ||
198 | if (h < 1) h = 1; | ||
199 | if ((w != (int)ie->w) || (h != (int)ie->h)) | ||
200 | { | ||
201 | *error = EVAS_LOAD_ERROR_GENERIC; | ||
202 | goto error; | ||
203 | } | ||
204 | ie->flags.alpha = 1; | ||
205 | evas_cache_image_surface_alloc(ie, w, h); | ||
206 | pixels = evas_cache_image_pixels(ie); | ||
207 | if (!pixels) | ||
208 | { | ||
209 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
210 | goto error; | ||
211 | } | ||
212 | |||
213 | memset(pixels, 0, w * h * sizeof(DATA32)); | ||
214 | surface = cairo_image_surface_create_for_data((unsigned char *)pixels, CAIRO_FORMAT_ARGB32, | ||
215 | w, h, w * sizeof(DATA32)); | ||
216 | if (!surface) | ||
217 | { | ||
218 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
219 | goto error; | ||
220 | } | ||
221 | cr = cairo_create(surface); | ||
222 | if (!cr) | ||
223 | { | ||
224 | cairo_surface_destroy(surface); | ||
225 | *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; | ||
226 | goto error; | ||
227 | } | ||
228 | |||
229 | cairo_scale(cr, | ||
230 | (double)ie->w / dim.em, | ||
231 | (double)ie->h / dim.ex); | ||
232 | rsvg_handle_render_cairo(rsvg, cr); | ||
233 | cairo_surface_destroy(surface); | ||
234 | /* need to check if this is required... */ | ||
235 | cairo_destroy(cr); | ||
236 | rsvg_handle_close(rsvg, NULL); | ||
237 | g_object_unref(rsvg); | ||
238 | evas_common_image_set_alpha_sparse(ie); | ||
239 | return EINA_TRUE; | ||
240 | |||
241 | error: | ||
242 | rsvg_handle_close(rsvg, NULL); | ||
243 | g_object_unref(rsvg); | ||
244 | return EINA_FALSE; | ||
245 | } | ||
246 | |||
247 | static int | ||
248 | module_open(Evas_Module *em) | ||
249 | { | ||
250 | if (!em) return 0; | ||
251 | em->functions = (void *)(&evas_image_load_svg_func); | ||
252 | if (!rsvg_initialized) rsvg_init(); | ||
253 | rsvg_initialized = 1; | ||
254 | return 1; | ||
255 | } | ||
256 | |||
257 | static void | ||
258 | module_close(Evas_Module *em __UNUSED__) | ||
259 | { | ||
260 | if (!rsvg_initialized) return; | ||
261 | //rsvg_term(); | ||
262 | //rsvg_initialized = 0; | ||
263 | } | ||
264 | |||
265 | static Evas_Module_Api evas_modapi = | ||
266 | { | ||
267 | EVAS_MODULE_API_VERSION, | ||
268 | "svg", | ||
269 | "none", | ||
270 | { | ||
271 | module_open, | ||
272 | module_close | ||
273 | } | ||
274 | }; | ||
275 | |||
276 | EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, svg); | ||
277 | |||
278 | #ifndef EVAS_STATIC_BUILD_SVG | ||
279 | EVAS_EINA_MODULE_DEFINE(image_loader, svg); | ||
280 | #endif | ||