/src/libvips/libvips/foreign/csvsave.c
Line | Count | Source |
1 | | /* save to csv |
2 | | * |
3 | | * 2/12/11 |
4 | | * - wrap a class around the csv writer |
5 | | * 21/2/20 |
6 | | * - rewrite for the VipsTarget API |
7 | | */ |
8 | | |
9 | | /* |
10 | | |
11 | | This file is part of VIPS. |
12 | | |
13 | | VIPS is free software; you can redistribute it and/or modify |
14 | | it under the terms of the GNU Lesser General Public License as published by |
15 | | the Free Software Foundation; either version 2 of the License, or |
16 | | (at your option) any later version. |
17 | | |
18 | | This program is distributed in the hope that it will be useful, |
19 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 | | GNU Lesser General Public License for more details. |
22 | | |
23 | | You should have received a copy of the GNU Lesser General Public License |
24 | | along with this program; if not, write to the Free Software |
25 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
26 | | 02110-1301 USA |
27 | | |
28 | | */ |
29 | | |
30 | | /* |
31 | | |
32 | | These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
33 | | |
34 | | */ |
35 | | |
36 | | /* |
37 | | #define DEBUG_VERBOSE |
38 | | #define DEBUG |
39 | | */ |
40 | | |
41 | | #ifdef HAVE_CONFIG_H |
42 | | #include <config.h> |
43 | | #endif /*HAVE_CONFIG_H*/ |
44 | | #include <glib/gi18n-lib.h> |
45 | | |
46 | | #include <stdio.h> |
47 | | #include <stdlib.h> |
48 | | #include <string.h> |
49 | | |
50 | | #include <vips/vips.h> |
51 | | |
52 | | #include "pforeign.h" |
53 | | |
54 | | typedef struct _VipsForeignSaveCsv { |
55 | | VipsForeignSave parent_object; |
56 | | |
57 | | VipsTarget *target; |
58 | | |
59 | | const char *separator; |
60 | | } VipsForeignSaveCsv; |
61 | | |
62 | | typedef VipsForeignSaveClass VipsForeignSaveCsvClass; |
63 | | |
64 | 72 | G_DEFINE_ABSTRACT_TYPE(VipsForeignSaveCsv, vips_foreign_save_csv, |
65 | 72 | VIPS_TYPE_FOREIGN_SAVE); |
66 | 72 | |
67 | 72 | static void |
68 | 72 | vips_foreign_save_csv_dispose(GObject *gobject) |
69 | 18.8k | { |
70 | 18.8k | VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) gobject; |
71 | | |
72 | 18.8k | VIPS_UNREF(csv->target); |
73 | | |
74 | 18.8k | G_OBJECT_CLASS(vips_foreign_save_csv_parent_class)->dispose(gobject); |
75 | 18.8k | } |
76 | | |
77 | | #define PRINT_INT(TYPE) \ |
78 | 309k | { \ |
79 | 309k | TYPE *pt = (TYPE *) p; \ |
80 | 309k | \ |
81 | 19.1M | for (x = 0; x < image->Xsize; x++) { \ |
82 | 18.8M | if (x > 0) \ |
83 | 18.8M | vips_target_writes(csv->target, csv->separator); \ |
84 | 18.8M | vips_target_writef(csv->target, "%d", pt[x]); \ |
85 | 18.8M | } \ |
86 | 309k | } |
87 | | |
88 | | #define PRINT_FLOAT(TYPE) \ |
89 | 1.36k | { \ |
90 | 1.36k | TYPE *pt = (TYPE *) p; \ |
91 | 1.36k | char buf[G_ASCII_DTOSTR_BUF_SIZE]; \ |
92 | 1.36k | \ |
93 | 29.6k | for (x = 0; x < image->Xsize; x++) { \ |
94 | 28.2k | if (x > 0) \ |
95 | 28.2k | vips_target_writes(csv->target, csv->separator); \ |
96 | 28.2k | g_ascii_dtostr(buf, G_ASCII_DTOSTR_BUF_SIZE, pt[x]); \ |
97 | 28.2k | vips_target_writes(csv->target, buf); \ |
98 | 28.2k | } \ |
99 | 1.36k | } |
100 | | |
101 | | #define PRINT_COMPLEX(TYPE) \ |
102 | 0 | { \ |
103 | 0 | TYPE *pt = (TYPE *) p; \ |
104 | 0 | char buf[G_ASCII_DTOSTR_BUF_SIZE]; \ |
105 | 0 | \ |
106 | 0 | for (x = 0; x < image->Xsize; x++) { \ |
107 | 0 | if (x > 0) \ |
108 | 0 | vips_target_writes(csv->target, csv->separator); \ |
109 | 0 | VIPS_TARGET_PUTC(csv->target, '('); \ |
110 | 0 | g_ascii_dtostr(buf, G_ASCII_DTOSTR_BUF_SIZE, pt[0]); \ |
111 | 0 | vips_target_writes(csv->target, buf); \ |
112 | 0 | VIPS_TARGET_PUTC(csv->target, ','); \ |
113 | 0 | g_ascii_dtostr(buf, G_ASCII_DTOSTR_BUF_SIZE, pt[1]); \ |
114 | 0 | vips_target_writes(csv->target, buf); \ |
115 | 0 | VIPS_TARGET_PUTC(csv->target, ')'); \ |
116 | 0 | pt += 2; \ |
117 | 0 | } \ |
118 | 0 | } |
119 | | |
120 | | static int |
121 | | vips_foreign_save_csv_block(VipsRegion *region, VipsRect *area, void *a) |
122 | 6.42k | { |
123 | 6.42k | VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) a; |
124 | 6.42k | VipsImage *image = region->im; |
125 | | |
126 | 6.42k | int x, y; |
127 | | |
128 | 317k | for (y = 0; y < area->height; y++) { |
129 | 310k | VipsPel *p = VIPS_REGION_ADDR(region, 0, area->top + y); |
130 | | |
131 | 310k | switch (image->BandFmt) { |
132 | 284k | case VIPS_FORMAT_UCHAR: |
133 | 284k | PRINT_INT(unsigned char); |
134 | 284k | break; |
135 | 324 | case VIPS_FORMAT_CHAR: |
136 | 324 | PRINT_INT(char); |
137 | 324 | break; |
138 | 20.9k | case VIPS_FORMAT_USHORT: |
139 | 20.9k | PRINT_INT(unsigned short); |
140 | 20.9k | break; |
141 | 394 | case VIPS_FORMAT_SHORT: |
142 | 394 | PRINT_INT(short); |
143 | 394 | break; |
144 | 2.47k | case VIPS_FORMAT_UINT: |
145 | 2.47k | PRINT_INT(unsigned int); |
146 | 2.47k | break; |
147 | 247 | case VIPS_FORMAT_INT: |
148 | 247 | PRINT_INT(int); |
149 | 247 | break; |
150 | 1.36k | case VIPS_FORMAT_FLOAT: |
151 | 1.36k | PRINT_FLOAT(float); |
152 | 1.36k | break; |
153 | 0 | case VIPS_FORMAT_DOUBLE: |
154 | 0 | PRINT_FLOAT(double); |
155 | 0 | break; |
156 | 0 | case VIPS_FORMAT_COMPLEX: |
157 | 0 | PRINT_COMPLEX(float); |
158 | 0 | break; |
159 | 0 | case VIPS_FORMAT_DPCOMPLEX: |
160 | 0 | PRINT_COMPLEX(double); |
161 | 0 | break; |
162 | | |
163 | 0 | default: |
164 | 0 | g_assert_not_reached(); |
165 | 310k | } |
166 | | |
167 | 310k | if (vips_target_writes(csv->target, "\n")) |
168 | 0 | return -1; |
169 | 310k | } |
170 | | |
171 | 6.42k | return 0; |
172 | 6.42k | } |
173 | | |
174 | | static int |
175 | | vips_foreign_save_csv_build(VipsObject *object) |
176 | 18.8k | { |
177 | 18.8k | VipsForeignSave *save = (VipsForeignSave *) object; |
178 | 18.8k | VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) object; |
179 | 18.8k | VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object); |
180 | | |
181 | 18.8k | if (VIPS_OBJECT_CLASS(vips_foreign_save_csv_parent_class)->build(object)) |
182 | 2 | return -1; |
183 | | |
184 | 18.8k | if (vips_check_mono(class->nickname, save->ready) || |
185 | 18.8k | vips_check_uncoded(class->nickname, save->ready)) |
186 | 1 | return -1; |
187 | | |
188 | 18.8k | if (vips_sink_disc(save->ready, vips_foreign_save_csv_block, csv)) |
189 | 12.3k | return -1; |
190 | | |
191 | 6.42k | if (vips_target_end(csv->target)) |
192 | 0 | return -1; |
193 | | |
194 | 6.42k | return 0; |
195 | 6.42k | } |
196 | | |
197 | | static const char *vips_foreign_save_csv_suffs[] = { |
198 | | ".csv", |
199 | | NULL |
200 | | }; |
201 | | |
202 | | static void |
203 | | vips_foreign_save_csv_class_init(VipsForeignSaveCsvClass *class) |
204 | 18 | { |
205 | 18 | GObjectClass *gobject_class = G_OBJECT_CLASS(class); |
206 | 18 | VipsObjectClass *object_class = (VipsObjectClass *) class; |
207 | 18 | VipsForeignClass *foreign_class = (VipsForeignClass *) class; |
208 | 18 | VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; |
209 | | |
210 | 18 | gobject_class->dispose = vips_foreign_save_csv_dispose; |
211 | 18 | gobject_class->set_property = vips_object_set_property; |
212 | 18 | gobject_class->get_property = vips_object_get_property; |
213 | | |
214 | 18 | object_class->nickname = "csvsave_base"; |
215 | 18 | object_class->description = _("save image to csv"); |
216 | 18 | object_class->build = vips_foreign_save_csv_build; |
217 | | |
218 | 18 | foreign_class->suffs = vips_foreign_save_csv_suffs; |
219 | | |
220 | 18 | save_class->saveable = VIPS_FOREIGN_SAVEABLE_MONO; |
221 | | |
222 | 18 | VIPS_ARG_STRING(class, "separator", 13, |
223 | 18 | _("Separator"), |
224 | 18 | _("Separator characters"), |
225 | 18 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
226 | 18 | G_STRUCT_OFFSET(VipsForeignSaveCsv, separator), |
227 | 18 | "\t"); |
228 | 18 | } |
229 | | |
230 | | static void |
231 | | vips_foreign_save_csv_init(VipsForeignSaveCsv *csv) |
232 | 18.8k | { |
233 | 18.8k | csv->separator = g_strdup("\t"); |
234 | 18.8k | } |
235 | | |
236 | | typedef struct _VipsForeignSaveCsvFile { |
237 | | VipsForeignSaveCsv parent_object; |
238 | | |
239 | | char *filename; |
240 | | } VipsForeignSaveCsvFile; |
241 | | |
242 | | typedef VipsForeignSaveCsvClass VipsForeignSaveCsvFileClass; |
243 | | |
244 | 36 | G_DEFINE_TYPE(VipsForeignSaveCsvFile, vips_foreign_save_csv_file, |
245 | 36 | vips_foreign_save_csv_get_type()); |
246 | 36 | |
247 | 36 | static int |
248 | 36 | vips_foreign_save_csv_file_build(VipsObject *object) |
249 | 36 | { |
250 | 0 | VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) object; |
251 | 0 | VipsForeignSaveCsvFile *file = (VipsForeignSaveCsvFile *) object; |
252 | |
|
253 | 0 | if (file->filename && |
254 | 0 | !(csv->target = vips_target_new_to_file(file->filename))) |
255 | 0 | return -1; |
256 | | |
257 | 0 | return VIPS_OBJECT_CLASS(vips_foreign_save_csv_file_parent_class) |
258 | 0 | ->build(object); |
259 | 0 | } |
260 | | |
261 | | static void |
262 | | vips_foreign_save_csv_file_class_init(VipsForeignSaveCsvFileClass *class) |
263 | 18 | { |
264 | 18 | GObjectClass *gobject_class = G_OBJECT_CLASS(class); |
265 | 18 | VipsObjectClass *object_class = (VipsObjectClass *) class; |
266 | | |
267 | 18 | gobject_class->set_property = vips_object_set_property; |
268 | 18 | gobject_class->get_property = vips_object_get_property; |
269 | | |
270 | 18 | object_class->nickname = "csvsave"; |
271 | 18 | object_class->build = vips_foreign_save_csv_file_build; |
272 | | |
273 | 18 | VIPS_ARG_STRING(class, "filename", 1, |
274 | 18 | _("Filename"), |
275 | 18 | _("Filename to save to"), |
276 | 18 | VIPS_ARGUMENT_REQUIRED_INPUT, |
277 | 18 | G_STRUCT_OFFSET(VipsForeignSaveCsvFile, filename), |
278 | 18 | NULL); |
279 | 18 | } |
280 | | |
281 | | static void |
282 | | vips_foreign_save_csv_file_init(VipsForeignSaveCsvFile *file) |
283 | 0 | { |
284 | 0 | } |
285 | | |
286 | | typedef struct _VipsForeignSaveCsvTarget { |
287 | | VipsForeignSaveCsv parent_object; |
288 | | |
289 | | VipsTarget *target; |
290 | | } VipsForeignSaveCsvTarget; |
291 | | |
292 | | typedef VipsForeignSaveCsvClass VipsForeignSaveCsvTargetClass; |
293 | | |
294 | 36 | G_DEFINE_TYPE(VipsForeignSaveCsvTarget, vips_foreign_save_csv_target, |
295 | 36 | vips_foreign_save_csv_get_type()); |
296 | 36 | |
297 | 36 | static int |
298 | 36 | vips_foreign_save_csv_target_build(VipsObject *object) |
299 | 18.8k | { |
300 | 18.8k | VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) object; |
301 | 18.8k | VipsForeignSaveCsvTarget *target = (VipsForeignSaveCsvTarget *) object; |
302 | | |
303 | 18.8k | if (target->target) { |
304 | 18.8k | csv->target = target->target; |
305 | 18.8k | g_object_ref(csv->target); |
306 | 18.8k | } |
307 | | |
308 | 18.8k | return VIPS_OBJECT_CLASS(vips_foreign_save_csv_target_parent_class) |
309 | 18.8k | ->build(object); |
310 | 18.8k | } |
311 | | |
312 | | static void |
313 | | vips_foreign_save_csv_target_class_init(VipsForeignSaveCsvTargetClass *class) |
314 | 18 | { |
315 | 18 | GObjectClass *gobject_class = G_OBJECT_CLASS(class); |
316 | 18 | VipsObjectClass *object_class = (VipsObjectClass *) class; |
317 | | |
318 | 18 | gobject_class->set_property = vips_object_set_property; |
319 | 18 | gobject_class->get_property = vips_object_get_property; |
320 | | |
321 | 18 | object_class->nickname = "csvsave_target"; |
322 | 18 | object_class->build = vips_foreign_save_csv_target_build; |
323 | | |
324 | 18 | VIPS_ARG_OBJECT(class, "target", 1, |
325 | 18 | _("Target"), |
326 | 18 | _("Target to save to"), |
327 | 18 | VIPS_ARGUMENT_REQUIRED_INPUT, |
328 | 18 | G_STRUCT_OFFSET(VipsForeignSaveCsvTarget, target), |
329 | 18 | VIPS_TYPE_TARGET); |
330 | 18 | } |
331 | | |
332 | | static void |
333 | | vips_foreign_save_csv_target_init(VipsForeignSaveCsvTarget *target) |
334 | 18.8k | { |
335 | 18.8k | } |
336 | | |
337 | | /** |
338 | | * vips_csvsave: (method) |
339 | | * @in: image to save |
340 | | * @filename: file to write to |
341 | | * @...: `NULL`-terminated list of optional named arguments |
342 | | * |
343 | | * Writes the pixels in @in to the @filename as CSV (comma-separated values). |
344 | | * |
345 | | * The image is written |
346 | | * one line of text per scanline. Complex numbers are written as |
347 | | * "(real,imaginary)" and will need extra parsing I guess. Only the first band |
348 | | * is written. |
349 | | * |
350 | | * @separator gives the string to use to separate numbers in the output. |
351 | | * The default is "\\t" (tab). |
352 | | * |
353 | | * ::: tip "Optional arguments" |
354 | | * * @separator: `gchararray`, separator string |
355 | | * |
356 | | * ::: seealso |
357 | | * [method@Image.write_to_file]. |
358 | | * |
359 | | * Returns: 0 on success, -1 on error. |
360 | | */ |
361 | | int |
362 | | vips_csvsave(VipsImage *in, const char *filename, ...) |
363 | 0 | { |
364 | 0 | va_list ap; |
365 | 0 | int result; |
366 | |
|
367 | 0 | va_start(ap, filename); |
368 | 0 | result = vips_call_split("csvsave", ap, in, filename); |
369 | 0 | va_end(ap); |
370 | |
|
371 | 0 | return result; |
372 | 0 | } |
373 | | |
374 | | /** |
375 | | * vips_csvsave_target: (method) |
376 | | * @in: image to save |
377 | | * @target: save image to this target |
378 | | * @...: `NULL`-terminated list of optional named arguments |
379 | | * |
380 | | * As [method@Image.csvsave], but save to a target. |
381 | | * |
382 | | * ::: tip "Optional arguments" |
383 | | * * @separator: `gchararray`, separator string |
384 | | * |
385 | | * ::: seealso |
386 | | * [method@Image.csvsave]. |
387 | | * |
388 | | * Returns: 0 on success, -1 on error. |
389 | | */ |
390 | | int |
391 | | vips_csvsave_target(VipsImage *in, VipsTarget *target, ...) |
392 | 0 | { |
393 | 0 | va_list ap; |
394 | 0 | int result; |
395 | |
|
396 | 0 | va_start(ap, target); |
397 | 0 | result = vips_call_split("csvsave_target", ap, in, target); |
398 | 0 | va_end(ap); |
399 | |
|
400 | 0 | return result; |
401 | 0 | } |