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 #include "intl.h"
25 
26 #include "private.h"
27 
28 #include "autotrace.h"
29 #include "exception.h"
30 
31 #include "fit.h"
32 #include "bitmap.h"
33 #include "spline.h"
34 
35 #include "input.h"
36 
37 #include "xstd.h"
38 #include "image-header.h"
39 #include "image-proc.h"
40 #include "quantize.h"
41 #include "thin-image.h"
42 #include "despeckle.h"
43 
44 #include <locale.h>
45 #include <time.h>
46 #include <stdlib.h>
47 #include <string.h>
48 
49 #define AT_DEFAULT_DPI 72
50 
at_fitting_opts_new(void)51 at_fitting_opts_type *at_fitting_opts_new(void)
52 {
53   at_fitting_opts_type *opts;
54   XMALLOC(opts, sizeof(at_fitting_opts_type));
55   *opts = new_fitting_opts();
56   return opts;
57 }
58 
at_fitting_opts_copy(at_fitting_opts_type * original)59 at_fitting_opts_type *at_fitting_opts_copy(at_fitting_opts_type * original)
60 {
61   at_fitting_opts_type *new_opts;
62   if (original == NULL)
63     return NULL;
64 
65   new_opts = at_fitting_opts_new();
66   *new_opts = *original;
67   if (original->background_color)
68     new_opts->background_color = at_color_copy(original->background_color);
69   return new_opts;
70 }
71 
at_fitting_opts_free(at_fitting_opts_type * opts)72 void at_fitting_opts_free(at_fitting_opts_type * opts)
73 {
74   free(opts->background_color);
75   free(opts);
76 }
77 
at_input_opts_new(void)78 at_input_opts_type *at_input_opts_new(void)
79 {
80   at_input_opts_type *opts;
81   XMALLOC(opts, sizeof(at_input_opts_type));
82   opts->background_color = NULL;
83   opts->charcode = 0;
84   return opts;
85 }
86 
at_input_opts_copy(at_input_opts_type * original)87 at_input_opts_type *at_input_opts_copy(at_input_opts_type * original)
88 {
89   at_input_opts_type *opts;
90   opts = at_input_opts_new();
91   *opts = *original;
92   if (original->background_color)
93     opts->background_color = at_color_copy(original->background_color);
94   return opts;
95 }
96 
at_input_opts_free(at_input_opts_type * opts)97 void at_input_opts_free(at_input_opts_type * opts)
98 {
99   free(opts->background_color);
100   free(opts);
101 }
102 
at_output_opts_new(void)103 at_output_opts_type *at_output_opts_new(void)
104 {
105   at_output_opts_type *opts;
106   XMALLOC(opts, sizeof(at_output_opts_type));
107   opts->dpi = AT_DEFAULT_DPI;
108   return opts;
109 }
110 
at_output_opts_copy(at_output_opts_type * original)111 at_output_opts_type *at_output_opts_copy(at_output_opts_type * original)
112 {
113   at_output_opts_type *opts = at_output_opts_new();
114   *opts = *original;
115   return opts;
116 }
117 
at_output_opts_free(at_output_opts_type * opts)118 void at_output_opts_free(at_output_opts_type * opts)
119 {
120   free(opts);
121 }
122 
at_bitmap_read(at_bitmap_reader * reader,gchar * filename,at_input_opts_type * opts,at_msg_func msg_func,gpointer msg_data)123 at_bitmap *at_bitmap_read(at_bitmap_reader * reader, gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data)
124 {
125   gboolean new_opts = FALSE;
126   at_bitmap *bitmap;
127   XMALLOC(bitmap, sizeof(at_bitmap));
128   if (opts == NULL) {
129     opts = at_input_opts_new();
130     new_opts = TRUE;
131   }
132   *bitmap = (*reader->func) (filename, opts, msg_func, msg_data, reader->data);
133   if (new_opts)
134     at_input_opts_free(opts);
135   return bitmap;
136 }
137 
at_bitmap_new(unsigned short width,unsigned short height,unsigned int planes)138 at_bitmap *at_bitmap_new(unsigned short width, unsigned short height, unsigned int planes)
139 {
140   at_bitmap *bitmap;
141   XMALLOC(bitmap, sizeof(at_bitmap));
142   *bitmap = at_bitmap_init(NULL, width, height, planes);
143   return bitmap;
144 }
145 
at_bitmap_copy(const at_bitmap * src)146 at_bitmap *at_bitmap_copy(const at_bitmap * src)
147 {
148   at_bitmap *dist;
149   unsigned short width, height, planes;
150 
151   width = at_bitmap_get_width(src);
152   height = at_bitmap_get_height(src);
153   planes = at_bitmap_get_planes(src);
154 
155   dist = at_bitmap_new(width, height, planes);
156   memcpy(dist->bitmap, src->bitmap, width * height * planes * sizeof(unsigned char));
157   return dist;
158 }
159 
at_bitmap_init(unsigned char * area,unsigned short width,unsigned short height,unsigned int planes)160 at_bitmap at_bitmap_init(unsigned char *area, unsigned short width, unsigned short height, unsigned int planes)
161 {
162   at_bitmap bitmap;
163 
164   if (area)
165     bitmap.bitmap = area;
166   else {
167     if (0 == (width * height))
168       bitmap.bitmap = NULL;
169     else
170       XCALLOC(bitmap.bitmap, width * height * planes * sizeof(unsigned char));
171   }
172 
173   bitmap.width = width;
174   bitmap.height = height;
175   bitmap.np = planes;
176   return bitmap;
177 }
178 
at_bitmap_free(at_bitmap * bitmap)179 void at_bitmap_free(at_bitmap * bitmap)
180 {
181   free(AT_BITMAP_BITS(bitmap));
182   free(bitmap);
183 }
184 
at_bitmap_get_width(const at_bitmap * bitmap)185 unsigned short at_bitmap_get_width(const at_bitmap * bitmap)
186 {
187   return bitmap->width;
188 }
189 
at_bitmap_get_height(const at_bitmap * bitmap)190 unsigned short at_bitmap_get_height(const at_bitmap * bitmap)
191 {
192   return bitmap->height;
193 }
194 
at_bitmap_get_planes(const at_bitmap * bitmap)195 unsigned short at_bitmap_get_planes(const at_bitmap * bitmap)
196 {
197   /* Here we use cast rather changing the type definition of
198      at_bitmap::np to keep binary compatibility. */
199   return (unsigned short)bitmap->np;
200 }
201 
at_bitmap_get_color(const at_bitmap * bitmap,unsigned int row,unsigned int col,at_color * color)202 void at_bitmap_get_color(const at_bitmap * bitmap, unsigned int row, unsigned int col, at_color * color)
203 {
204   unsigned char *p;
205   g_return_if_fail(color);
206   g_return_if_fail(bitmap);
207 
208   p = AT_BITMAP_PIXEL(bitmap, row, col);
209   if (at_bitmap_get_planes(bitmap) >= 3)
210     at_color_set(color, p[0], p[1], p[2]);
211   else
212     at_color_set(color, p[0], p[0], p[0]);
213 }
214 
at_bitmap_equal_color(const at_bitmap * bitmap,unsigned int row,unsigned int col,at_color * color)215 gboolean at_bitmap_equal_color(const at_bitmap * bitmap, unsigned int row, unsigned int col, at_color * color)
216 {
217   at_color c;
218 
219   g_return_val_if_fail(bitmap, FALSE);
220   g_return_val_if_fail(color, FALSE);
221 
222   at_bitmap_get_color(bitmap, row, col, &c);
223   return at_color_equal(&c, color);
224 }
225 
at_splines_new(at_bitmap * bitmap,at_fitting_opts_type * opts,at_msg_func msg_func,gpointer msg_data)226 at_splines_type *at_splines_new(at_bitmap * bitmap, at_fitting_opts_type * opts, at_msg_func msg_func, gpointer msg_data)
227 {
228   return at_splines_new_full(bitmap, opts, msg_func, msg_data, NULL, NULL, NULL, NULL);
229 }
230 
231 /* at_splines_new_full modify its argument: BITMAP
232    when despeckle, quantize and/or thin_image are invoked. */
at_splines_new_full(at_bitmap * bitmap,at_fitting_opts_type * opts,at_msg_func msg_func,gpointer msg_data,at_progress_func notify_progress,gpointer progress_data,at_testcancel_func test_cancel,gpointer testcancel_data)233 at_splines_type *at_splines_new_full(at_bitmap * bitmap, at_fitting_opts_type * opts, at_msg_func msg_func, gpointer msg_data, at_progress_func notify_progress, gpointer progress_data, at_testcancel_func test_cancel, gpointer testcancel_data)
234 {
235   image_header_type image_header;
236   at_splines_type *splines = NULL;
237   pixel_outline_list_type pixels;
238   QuantizeObj *myQuant = NULL;  /* curently not used */
239   at_exception_type exp = at_exception_new(msg_func, msg_data);
240   at_distance_map dist_map, *dist = NULL;
241 
242 #define CANCELP (test_cancel && test_cancel(testcancel_data))
243 #define FATALP  (at_exception_got_fatal(&exp))
244 #define FREE_SPLINE() do {if (splines) {at_splines_free(splines); splines = NULL;}} while(0)
245 
246 #define CANCEL_THEN_CLEANUP_DIST() if (CANCELP) goto cleanup_dist;
247 #define CANCEL_THEN_CLEANUP_PIXELS() if (CANCELP) {FREE_SPLINE(); goto cleanup_pixels;}
248 
249 #define FATAL_THEN_RETURN() if (FATALP) return splines;
250 #define FATAL_THEN_CLEANUP_DIST() if (FATALP) goto cleanup_dist;
251 #define FATAL_THEN_CLEANUP_PIXELS() if (FATALP) {FREE_SPLINE(); goto cleanup_pixels;}
252 
253   if (opts->despeckle_level > 0) {
254     despeckle(bitmap, opts->despeckle_level, opts->despeckle_tightness, opts->noise_removal, &exp);
255     FATAL_THEN_RETURN();
256   }
257 
258   image_header.width = at_bitmap_get_width(bitmap);
259   image_header.height = at_bitmap_get_height(bitmap);
260 
261   if (opts->color_count > 0) {
262     quantize(bitmap, opts->color_count, opts->background_color, &myQuant, &exp);
263     if (myQuant)
264       quantize_object_free(myQuant);  /* curently not used */
265     FATAL_THEN_RETURN();
266   }
267 
268   if (opts->centerline) {
269     if (opts->preserve_width) {
270       /* Preserve line width prior to thinning. */
271       dist_map = new_distance_map(bitmap, 255, /*padded= */ TRUE, &exp);
272       dist = &dist_map;
273       FATAL_THEN_RETURN();
274     }
275     /* Hereafter, dist is allocated. dist must be freed if
276        the execution is canceled or exception is raised;
277        use FATAL_THEN_CLEANUP_DIST. */
278     thin_image(bitmap, opts->background_color, &exp);
279     FATAL_THEN_CLEANUP_DIST()
280   }
281 
282   /* Hereafter, pixels is allocated. pixels must be freed if
283      the execution is canceled; use CANCEL_THEN_CLEANUP_PIXELS. */
284   if (opts->centerline) {
285     at_color background_color = { 0xff, 0xff, 0xff };
286     if (opts->background_color)
287       background_color = *opts->background_color;
288 
289     pixels = find_centerline_pixels(bitmap, background_color, notify_progress, progress_data, test_cancel, testcancel_data, &exp);
290   } else
291     pixels = find_outline_pixels(bitmap, opts->background_color, notify_progress, progress_data, test_cancel, testcancel_data, &exp);
292   FATAL_THEN_CLEANUP_DIST();
293   CANCEL_THEN_CLEANUP_DIST();
294 
295   XMALLOC(splines, sizeof(at_splines_type));
296   *splines = fitted_splines(pixels, opts, dist, image_header.width, image_header.height, &exp, notify_progress, progress_data, test_cancel, testcancel_data);
297   FATAL_THEN_CLEANUP_PIXELS();
298   CANCEL_THEN_CLEANUP_PIXELS();
299 
300   if (notify_progress)
301     notify_progress(1.0, progress_data);
302 
303 cleanup_pixels:
304   free_pixel_outline_list(&pixels);
305 cleanup_dist:
306   if (dist)
307     free_distance_map(dist);
308   return splines;
309 #undef CANCELP
310 #undef FATALP
311 #undef FREE_SPLINE
312 #undef CANCEL_THEN_CLEANUP_DIST
313 #undef CANCEL_THEN_CLEANUP_PIXELS
314 
315 #undef FATAL_THEN_RETURN
316 #undef FATAL_THEN_CLEANUP_DIST
317 #undef FATAL_THEN_CLEANUP_PIXELS
318 
319 }
320 
at_splines_write(at_spline_writer * writer,FILE * writeto,gchar * file_name,at_output_opts_type * opts,at_splines_type * splines,at_msg_func msg_func,gpointer msg_data)321 void at_splines_write(at_spline_writer * writer, FILE * writeto, gchar * file_name, at_output_opts_type * opts, at_splines_type * splines, at_msg_func msg_func, gpointer msg_data)
322 {
323   gboolean new_opts = FALSE;
324   int llx, lly, urx, ury;
325   llx = 0;
326   lly = 0;
327   urx = splines->width;
328   ury = splines->height;
329 
330   if (!file_name)
331     file_name = "";
332 
333   if (opts == NULL) {
334     new_opts = TRUE;
335     opts = at_output_opts_new();
336   }
337 
338   setlocale(LC_NUMERIC, "C");
339   (*writer->func) (writeto, file_name, llx, lly, urx, ury, opts, *splines, msg_func, msg_data, writer->data);
340   if (new_opts)
341     at_output_opts_free(opts);
342 }
343 
at_splines_free(at_splines_type * splines)344 void at_splines_free(at_splines_type * splines)
345 {
346   free_spline_list_array(splines);
347   if (splines->background_color)
348     at_color_free(splines->background_color);
349   free(splines);
350 }
351 
at_version(gboolean long_format)352 const char *at_version(gboolean long_format)
353 {
354   if (long_format)
355     return "AutoTrace version " AUTOTRACE_VERSION;
356 
357   return AUTOTRACE_VERSION;
358 }
359 
at_home_site(void)360 const char *at_home_site(void)
361 {
362   return AUTOTRACE_WEB;
363 }
364 
autotrace_init(void)365 void autotrace_init(void)
366 {
367   static int initialized = 0;
368   if (!initialized) {
369 #ifdef ENABLE_NLS
370     setlocale(LC_ALL, "");
371     //bindtextdomain(PACKAGE, LOCALEDIR);
372 #endif /* Def: ENABLE_NLS */
373 
374     /* Initialize subsystems */
375     at_input_init();
376     at_output_init();
377     at_module_init();
378 
379     initialized = 1;
380   }
381 }
382 
at_fitting_opts_doc_func(char * string)383 const char *at_fitting_opts_doc_func(char *string)
384 {
385   return _(string);
386 }
387