1 /* autotrace.c --- Autotrace API
2
3 Copyright (C) 2000, 2001, 2002 Martin Weber
4
5 The author can be contacted at <martweb@gmx.net>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif /* Def: HAVE_CONFIG_H */
24
25 #include "autotrace.h"
26 #include "exception.h"
27
28 #include "fit.h"
29 #include "bitmap.h"
30 #include "spline.h"
31
32 #include "input.h"
33
34 #include "xstd.h"
35 #include "image-header.h"
36 #include "image-proc.h"
37 #include "quantize.h"
38 #include "thin-image.h"
39 #include "despeckle.h"
40
41 #if HAVE_LIBPSTOEDIT
42 #include "output-pstoedit.h"
43 #endif /* HAVE_LIBPSTOEDIT */
44
45 #define AT_DEFAULT_DPI 72
46
47 at_fitting_opts_type *
at_fitting_opts_new(void)48 at_fitting_opts_new(void)
49 {
50 at_fitting_opts_type * opts;
51 XMALLOC(opts, sizeof(at_fitting_opts_type));
52 *opts = new_fitting_opts();
53 return opts;
54 }
55
56 at_fitting_opts_type *
at_fitting_opts_copy(at_fitting_opts_type * original)57 at_fitting_opts_copy (at_fitting_opts_type * original)
58 {
59 at_fitting_opts_type * new_opts;
60 if (original == NULL)
61 return NULL;
62
63 new_opts = at_fitting_opts_new ();
64 *new_opts = *original;
65 if (original->background_color)
66 new_opts->background_color = at_color_copy(original->background_color);
67 return new_opts;
68 }
69
70 void
at_fitting_opts_free(at_fitting_opts_type * opts)71 at_fitting_opts_free(at_fitting_opts_type * opts)
72 {
73 if (opts->background_color != NULL)
74 free (opts->background_color);
75 free(opts);
76 }
77
78 at_input_opts_type *
at_input_opts_new(void)79 at_input_opts_new(void)
80 {
81 at_input_opts_type * opts;
82 XMALLOC(opts, sizeof(at_input_opts_type));
83 opts->background_color = NULL;
84 return opts;
85 }
86
87 at_input_opts_type *
at_input_opts_copy(at_input_opts_type * original)88 at_input_opts_copy(at_input_opts_type * original)
89 {
90 at_input_opts_type * opts;
91 opts = at_input_opts_new();
92 if (original->background_color)
93 opts->background_color = at_color_copy(original->background_color);
94 return opts;
95 }
96
97 void
at_input_opts_free(at_input_opts_type * opts)98 at_input_opts_free(at_input_opts_type * opts)
99 {
100 if (opts->background_color != NULL)
101 free (opts->background_color);
102 free(opts);
103 }
104
105 at_output_opts_type *
at_output_opts_new(void)106 at_output_opts_new(void)
107 {
108 at_output_opts_type * opts;
109 XMALLOC(opts, sizeof(at_output_opts_type));
110 opts->dpi = AT_DEFAULT_DPI;
111 return opts;
112 }
113
114 at_output_opts_type *
at_output_opts_copy(at_output_opts_type * original)115 at_output_opts_copy(at_output_opts_type * original)
116 {
117 at_output_opts_type * opts = at_output_opts_new();
118 *opts = *original;
119 return opts;
120 }
121
122 void
at_output_opts_free(at_output_opts_type * opts)123 at_output_opts_free(at_output_opts_type * opts)
124 {
125 free(opts);
126 }
127
128 at_bitmap_type *
at_bitmap_read(at_input_read_func input_reader,at_string filename,at_input_opts_type * opts,at_msg_func msg_func,at_address msg_data)129 at_bitmap_read (at_input_read_func input_reader,
130 at_string filename,
131 at_input_opts_type * opts,
132 at_msg_func msg_func, at_address msg_data)
133 {
134 at_bool new_opts = false;
135 at_bitmap_type * bitmap;
136 XMALLOC(bitmap, sizeof(at_bitmap_type));
137 if (opts == NULL)
138 {
139 opts = at_input_opts_new();
140 new_opts = true;
141 }
142 *bitmap = (*input_reader) (filename, opts, msg_func, msg_data);
143 if (new_opts)
144 at_input_opts_free(opts);
145 return bitmap;
146 }
147
148 at_bitmap_type *
at_bitmap_new(unsigned short width,unsigned short height,unsigned int planes)149 at_bitmap_new(unsigned short width,
150 unsigned short height,
151 unsigned int planes)
152 {
153 at_bitmap_type * bitmap;
154 XMALLOC(bitmap, sizeof(at_bitmap_type));
155 *bitmap = at_bitmap_init(NULL, width, height, planes);
156 return bitmap;
157 }
158
159 at_bitmap_type *
at_bitmap_copy(at_bitmap_type * src)160 at_bitmap_copy(at_bitmap_type * src)
161 {
162 at_bitmap_type * dist;
163 unsigned short width, height, planes;
164
165 width = at_bitmap_get_width(src);
166 height = at_bitmap_get_height(src);
167 planes = at_bitmap_get_planes(src);
168
169 dist = at_bitmap_new(width, height, planes);
170 memcpy(dist->bitmap,
171 src->bitmap,
172 width * height * planes * sizeof(unsigned char));
173 return dist;
174 }
175
176 at_bitmap_type
at_bitmap_init(unsigned char * area,unsigned short width,unsigned short height,unsigned int planes)177 at_bitmap_init(unsigned char * area,
178 unsigned short width,
179 unsigned short height,
180 unsigned int planes)
181 {
182 at_bitmap_type bitmap;
183
184 if (area)
185 bitmap.bitmap = area;
186 else
187 {
188 if (0 == (width * height))
189 bitmap.bitmap = NULL;
190 else
191 XCALLOC(bitmap.bitmap, width * height * planes * sizeof(unsigned char));
192 }
193
194 bitmap.width = width;
195 bitmap.height = height;
196 bitmap.np = planes;
197 return bitmap;
198 }
199
200 void
at_bitmap_free(at_bitmap_type * bitmap)201 at_bitmap_free (at_bitmap_type * bitmap)
202 {
203 free_bitmap (bitmap);
204 free(bitmap);
205 }
206
207 unsigned short
at_bitmap_get_width(at_bitmap_type * bitmap)208 at_bitmap_get_width (at_bitmap_type * bitmap)
209 {
210 return bitmap->width;
211 }
212
213 unsigned short
at_bitmap_get_height(at_bitmap_type * bitmap)214 at_bitmap_get_height (at_bitmap_type * bitmap)
215 {
216 return bitmap->height;
217 }
218
219 unsigned short
at_bitmap_get_planes(at_bitmap_type * bitmap)220 at_bitmap_get_planes (at_bitmap_type * bitmap)
221 {
222 return bitmap->np;
223 }
224
225 at_splines_type *
at_splines_new(at_bitmap_type * bitmap,at_fitting_opts_type * opts,at_msg_func msg_func,at_address msg_data)226 at_splines_new (at_bitmap_type * bitmap,
227 at_fitting_opts_type * opts,
228 at_msg_func msg_func, at_address msg_data)
229 {
230 return at_splines_new_full(bitmap, opts,
231 msg_func, msg_data,
232 NULL, NULL, NULL, NULL);
233 }
234
235 /* at_splines_new_full modify its argument: BITMAP
236 when despeckle, quantize and/or thin_image are invoked. */
237 at_splines_type *
at_splines_new_full(at_bitmap_type * bitmap,at_fitting_opts_type * opts,at_msg_func msg_func,at_address msg_data,at_progress_func notify_progress,at_address progress_data,at_testcancel_func test_cancel,at_address testcancel_data)238 at_splines_new_full (at_bitmap_type * bitmap,
239 at_fitting_opts_type * opts,
240 at_msg_func msg_func,
241 at_address msg_data,
242 at_progress_func notify_progress,
243 at_address progress_data,
244 at_testcancel_func test_cancel,
245 at_address testcancel_data)
246 {
247 image_header_type image_header;
248 at_splines_type * splines = NULL;
249 pixel_outline_list_type pixels;
250 QuantizeObj *myQuant = NULL; /* curently not used */
251 at_exception_type exp = at_exception_new(msg_func, msg_data);
252 distance_map_type dist_map, *dist = NULL;
253
254 #define CANCELP (test_cancel && test_cancel(testcancel_data))
255 #define FATALP (at_exception_got_fatal(&exp))
256 #define FREE_SPLINE() do {if (splines) {at_splines_free(splines); splines = NULL;}} while(0)
257
258 #define CANCEL_THEN_CLEANUP_DIST() if (CANCELP) goto cleanup_dist;
259 #define CANCEL_THEN_CLEANUP_PIXELS() if (CANCELP) {FREE_SPLINE(); goto cleanup_pixels;}
260
261 #define FATAL_THEN_RETURN() if (FATALP) return splines;
262 #define FATAL_THEN_CLEANUP_DIST() if (FATALP) goto cleanup_dist;
263 #define FATAL_THEN_CLEANUP_PIXELS() if (FATALP) {FREE_SPLINE(); goto cleanup_pixels;}
264
265 if (opts->despeckle_level > 0)
266 {
267 despeckle (bitmap,
268 opts->despeckle_level,
269 opts->despeckle_tightness,
270 &exp);
271 FATAL_THEN_RETURN();
272 }
273
274 image_header.width = at_bitmap_get_width(bitmap);
275 image_header.height = at_bitmap_get_height(bitmap);
276
277 if (opts->color_count > 0)
278 {
279 quantize (bitmap, opts->color_count, opts->background_color, &myQuant, &exp);
280 if (myQuant)
281 quantize_object_free(myQuant); /* curently not used */
282 FATAL_THEN_RETURN();
283 }
284
285 if (opts->centerline)
286 {
287 if (opts->preserve_width)
288 {
289 /* Preserve line width prior to thinning. */
290 dist_map = new_distance_map(*bitmap, 255, /*padded=*/true, &exp);
291 dist = &dist_map;
292 FATAL_THEN_RETURN();
293 }
294 /* Hereafter, dist is allocated. dist must be freed if
295 the execution is canceled or exception is raised;
296 use FATAL_THEN_CLEANUP_DIST. */
297 thin_image (bitmap, opts->background_color, &exp);
298 FATAL_THEN_CLEANUP_DIST()
299 }
300
301 /* Hereafter, pixels is allocated. pixels must be freed if
302 the execution is canceled; use CANCEL_THEN_CLEANUP_PIXELS. */
303 if (opts->centerline)
304 {
305 color_type background_color = { 0xff, 0xff, 0xff };
306 if (opts->background_color)
307 background_color = *opts->background_color;
308
309 pixels = find_centerline_pixels(*bitmap, background_color,
310 notify_progress, progress_data,
311 test_cancel, testcancel_data, &exp);
312 }
313 else
314 pixels = find_outline_pixels(*bitmap, opts->background_color,
315 notify_progress, progress_data,
316 test_cancel, testcancel_data, &exp);
317 FATAL_THEN_CLEANUP_DIST();
318 CANCEL_THEN_CLEANUP_DIST();
319
320 XMALLOC(splines, sizeof(at_splines_type));
321 *splines = fitted_splines (pixels, opts, dist,
322 image_header.width,
323 image_header.height,
324 &exp,
325 notify_progress, progress_data,
326 test_cancel, testcancel_data);
327 FATAL_THEN_CLEANUP_PIXELS();
328 CANCEL_THEN_CLEANUP_PIXELS();
329
330 if (notify_progress)
331 notify_progress(1.0, progress_data);
332
333 cleanup_pixels:
334 free_pixel_outline_list (&pixels);
335 cleanup_dist:
336 if (dist)
337 free_distance_map (dist);
338 return splines;
339 #undef CANCELP
340 #undef FATALP
341 #undef FREE_SPLINE
342 #undef CANCEL_THEN_CLEANUP_DIST
343 #undef CANCEL_THEN_CLEANUP_PIXELS
344
345 #undef FATAL_THEN_RETURN
346 #undef FATAL_THEN_CLEANUP_DIST
347 #undef FATAL_THEN_CLEANUP_PIXELS
348
349 }
350
351 void
at_splines_write(at_output_write_func output_writer,FILE * writeto,at_string file_name,at_output_opts_type * opts,at_splines_type * splines,at_msg_func msg_func,at_address msg_data)352 at_splines_write (at_output_write_func output_writer,
353 FILE * writeto,
354 at_string file_name,
355 at_output_opts_type * opts,
356 at_splines_type * splines,
357 at_msg_func msg_func, at_address msg_data)
358 {
359 at_bool new_opts = false;
360 int llx, lly, urx, ury;
361 llx = 0;
362 lly = 0;
363 urx = splines->width;
364 ury = splines->height;
365
366 if (!file_name)
367 file_name = "";
368
369 if (opts == NULL)
370 {
371 new_opts = true;
372 opts = at_output_opts_new();
373 }
374 #if HAVE_LIBPSTOEDIT
375 if (output_pstoedit_is_writer(output_writer))
376 output_pstoedit_invoke_writer (output_writer,
377 writeto, file_name,
378 llx, lly, urx, ury,
379 opts,
380 *splines,
381 msg_func, msg_data);
382 else
383 (*output_writer) (writeto, file_name, llx, lly, urx, ury, opts, *splines,
384 msg_func, msg_data);
385 #else
386 (*output_writer) (writeto, file_name, llx, lly, urx, ury, opts, *splines,
387 msg_func, msg_data);
388 #endif /* HAVE_LIBPSTOEDIT */
389 if (new_opts)
390 at_output_opts_free(opts);
391 }
392
393 void
at_splines_free(at_splines_type * splines)394 at_splines_free (at_splines_type * splines)
395 {
396 free_spline_list_array (splines);
397 if (splines->background_color)
398 at_color_free(splines->background_color);
399 free(splines);
400 }
401
402 at_color_type *
at_color_new(unsigned char r,unsigned char g,unsigned char b)403 at_color_new (unsigned char r,
404 unsigned char g,
405 unsigned char b)
406 {
407 at_color_type * color;
408 XMALLOC (color, sizeof (at_color_type));
409 color->r = r;
410 color->g = g;
411 color->b = b;
412 return color;
413 }
414
415 at_color_type *
at_color_copy(at_color_type * original)416 at_color_copy (at_color_type * original)
417 {
418 if (original == NULL)
419 return NULL;
420 return at_color_new(original->r,
421 original->g,
422 original->b);
423 }
424
425 at_bool
at_color_equal(at_color_type * c1,at_color_type * c2)426 at_color_equal (at_color_type * c1, at_color_type * c2)
427 {
428 if (c1 == c2)
429 return true;
430 else
431 return (COLOR_EQUAL(*c1, *c2));
432 }
433
434 void
at_color_free(at_color_type * color)435 at_color_free(at_color_type * color)
436 {
437 free(color);
438 }
439
440 const char *
at_version(at_bool long_format)441 at_version (at_bool long_format)
442 {
443 if (long_format)
444 return "AutoTrace version " AUTOTRACE_VERSION;
445 else
446 return AUTOTRACE_VERSION;
447 }
448
449 const char *
at_home_site(void)450 at_home_site (void)
451 {
452 return AUTOTRACE_WEB;
453 }
454