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