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