Coverage Report

Created: 2026-02-09 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}