Coverage Report

Created: 2025-01-28 06:40

/src/libvips/libvips/conversion/extract.c
Line
Count
Source (jump to first uncovered line)
1
/* extract an area and/or a set of bands
2
 *
3
 * Copyright: 1990, J. Cupitt
4
 *
5
 * Author: J. Cupitt
6
 * Written on: 12/02/1990
7
 * Modified on: 4/6/92, J.Cupitt
8
 *  - speed up! why wasn't this done before? Why am I stupid?
9
 *  - layout, messages fixed
10
 * now extracts IM_CODING_LABQ to IM_CODING_LABQ file: K.Martinez 1/7/93
11
 * 2/7/93 JC
12
 *  - adapted for partial v2
13
 *  - ANSIfied
14
 * 7/7/93 JC
15
 *  - behaviour for IM_CODING_LABQ fixed
16
 *  - better messages
17
 * 7/10/94 JC
18
 *  - new IM_NEW()
19
 * 22/2/95 JC
20
 *  - new use of im_region_region()
21
 * 6/7/98 JC
22
 *  - im_extract_area() and im_extract_band() added
23
 * 11/7/01 JC
24
 *  - im_extract_band() now numbers from zero
25
 * 7/11/01 JC
26
 *  - oh what pain, im_extract now numbers bands from zero as well
27
 * 6/9/02 JC
28
 *  - zero xoff/yoff for extracted area
29
 * 14/4/04 JC
30
 *  - nope, -ve the origin
31
 * 17/7/04
32
 *  - added im_extract_bands(), remove many bands from image
33
 * 24/3/09
34
 *  - added IM_CODING_RAD support
35
 * 29/1/10
36
 *  - cleanups
37
 *  - gtkdoc
38
 * 26/10/11
39
 *  - redone as a class
40
 */
41
42
/*
43
44
  This file is part of VIPS.
45
46
  VIPS is free software; you can redistribute it and/or modify
47
  it under the terms of the GNU Lesser General Public License as published by
48
  the Free Software Foundation; either version 2 of the License, or
49
  (at your option) any later version.
50
51
  This program is distributed in the hope that it will be useful,
52
  but WITHOUT ANY WARRANTY; without even the implied warranty of
53
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
54
  GNU Lesser General Public License for more details.
55
56
  You should have received a copy of the GNU Lesser General Public License
57
  along with this program; if not, write to the Free Software
58
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
59
  02110-1301  USA
60
61
 */
62
63
/*
64
65
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
66
67
 */
68
69
/*
70
#define VIPS_DEBUG
71
 */
72
73
#ifdef HAVE_CONFIG_H
74
#include <config.h>
75
#endif /*HAVE_CONFIG_H*/
76
#include <glib/gi18n-lib.h>
77
78
#include <stdio.h>
79
#include <string.h>
80
#include <stdlib.h>
81
82
#include <vips/vips.h>
83
#include <vips/internal.h>
84
#include <vips/debug.h>
85
86
#include "pconversion.h"
87
88
#include "bandary.h"
89
90
typedef struct _VipsExtractArea {
91
  VipsConversion parent_instance;
92
93
  /* The input image.
94
   */
95
  VipsImage *in;
96
97
  int left;
98
  int top;
99
  int width;
100
  int height;
101
102
} VipsExtractArea;
103
104
typedef VipsConversionClass VipsExtractAreaClass;
105
106
G_DEFINE_TYPE(VipsExtractArea, vips_extract_area, VIPS_TYPE_CONVERSION);
107
108
/* Extract an area. Can just use pointers.
109
 */
110
static int
111
vips_extract_area_gen(VipsRegion *out_region,
112
  void *seq, void *a, void *b, gboolean *stop)
113
151k
{
114
151k
  VipsRegion *ir = (VipsRegion *) seq;
115
151k
  VipsExtractArea *extract = (VipsExtractArea *) b;
116
151k
  VipsRect iarea;
117
118
  /* Ask for input we need. Translate from demand in or's space to
119
   * demand in ir's space.
120
   */
121
151k
  iarea = out_region->valid;
122
151k
  iarea.left += extract->left;
123
151k
  iarea.top += extract->top;
124
151k
  if (vips_region_prepare(ir, &iarea))
125
14.1k
    return -1;
126
127
  /* Attach or to ir.
128
   */
129
137k
  if (vips_region_region(out_region, ir,
130
137k
      &out_region->valid, iarea.left, iarea.top))
131
0
    return -1;
132
133
137k
  return 0;
134
137k
}
135
136
static int
137
vips_extract_area_build(VipsObject *object)
138
51.3k
{
139
51.3k
  VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object);
140
51.3k
  VipsConversion *conversion = VIPS_CONVERSION(object);
141
51.3k
  VipsExtractArea *extract = (VipsExtractArea *) object;
142
143
51.3k
  if (VIPS_OBJECT_CLASS(vips_extract_area_parent_class)->build(object))
144
0
    return -1;
145
146
51.3k
  if (extract->left + extract->width > extract->in->Xsize ||
147
51.3k
    extract->top + extract->height > extract->in->Ysize ||
148
51.3k
    extract->left < 0 || extract->top < 0 ||
149
51.3k
    extract->width <= 0 || extract->height <= 0) {
150
0
    vips_error(class->nickname, "%s", _("bad extract area"));
151
0
    return -1;
152
0
  }
153
154
51.3k
  if (vips_image_pio_input(extract->in) ||
155
51.3k
    vips_check_coding_known(class->nickname, extract->in))
156
0
    return -1;
157
158
51.3k
  if (vips_image_pipelinev(conversion->out,
159
51.3k
      VIPS_DEMAND_STYLE_THINSTRIP, extract->in, NULL))
160
0
    return -1;
161
162
51.3k
  conversion->out->Xsize = extract->width;
163
51.3k
  conversion->out->Ysize = extract->height;
164
51.3k
  conversion->out->Xoffset = -extract->left;
165
51.3k
  conversion->out->Yoffset = -extract->top;
166
167
51.3k
  if (vips_image_generate(conversion->out,
168
51.3k
      vips_start_one, vips_extract_area_gen, vips_stop_one,
169
51.3k
      extract->in, extract))
170
0
    return -1;
171
172
51.3k
  return 0;
173
51.3k
}
174
175
static void
176
vips_extract_area_class_init(VipsExtractAreaClass *class)
177
32
{
178
32
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
179
32
  VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(class);
180
32
  VipsOperationClass *operation_class = VIPS_OPERATION_CLASS(class);
181
182
32
  VIPS_DEBUG_MSG("vips_extract_area_class_init\n");
183
184
32
  gobject_class->set_property = vips_object_set_property;
185
32
  gobject_class->get_property = vips_object_get_property;
186
187
32
  vobject_class->nickname = "extract_area";
188
32
  vobject_class->description = _("extract an area from an image");
189
32
  vobject_class->build = vips_extract_area_build;
190
191
32
  operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
192
193
32
  VIPS_ARG_IMAGE(class, "input", 1,
194
32
    _("Input"),
195
32
    _("Input image"),
196
32
    VIPS_ARGUMENT_REQUIRED_INPUT,
197
32
    G_STRUCT_OFFSET(VipsExtractArea, in));
198
199
32
  VIPS_ARG_INT(class, "left", 3,
200
32
    _("Left"),
201
32
    _("Left edge of extract area"),
202
32
    VIPS_ARGUMENT_REQUIRED_INPUT,
203
32
    G_STRUCT_OFFSET(VipsExtractArea, left),
204
32
    -VIPS_MAX_COORD, VIPS_MAX_COORD, 0);
205
206
32
  VIPS_ARG_INT(class, "top", 4,
207
32
    _("Top"),
208
32
    _("Top edge of extract area"),
209
32
    VIPS_ARGUMENT_REQUIRED_INPUT,
210
32
    G_STRUCT_OFFSET(VipsExtractArea, top),
211
32
    -VIPS_MAX_COORD, VIPS_MAX_COORD, 0);
212
213
32
  VIPS_ARG_INT(class, "width", 5,
214
32
    _("Width"),
215
32
    _("Width of extract area"),
216
32
    VIPS_ARGUMENT_REQUIRED_INPUT,
217
32
    G_STRUCT_OFFSET(VipsExtractArea, width),
218
32
    1, VIPS_MAX_COORD, 1);
219
220
32
  VIPS_ARG_INT(class, "height", 6,
221
32
    _("Height"),
222
32
    _("Height of extract area"),
223
32
    VIPS_ARGUMENT_REQUIRED_INPUT,
224
32
    G_STRUCT_OFFSET(VipsExtractArea, height),
225
32
    1, VIPS_MAX_COORD, 1);
226
32
}
227
228
#ifdef __EMSCRIPTEN__
229
static void
230
vips_crop_init_adapter(VipsExtractArea *extract, void *dummy)
231
{
232
  vips_extract_area_init(extract);
233
}
234
#endif
235
236
static void
237
vips_extract_area_init(VipsExtractArea *extract)
238
51.3k
{
239
51.3k
}
240
241
/**
242
 * vips_extract_area: (method)
243
 * @in: input image
244
 * @out: (out): output image
245
 * @left: left edge of area to extract
246
 * @top: top edge of area to extract
247
 * @width: width of area to extract
248
 * @height: height of area to extract
249
 * @...: %NULL-terminated list of optional named arguments
250
 *
251
 * Extract an area from an image. The area must fit within @in.
252
 *
253
 * See also: vips_extract_bands(), vips_smartcrop().
254
 *
255
 * Returns: 0 on success, -1 on error.
256
 */
257
int
258
vips_extract_area(VipsImage *in, VipsImage **out,
259
  int left, int top, int width, int height, ...)
260
51.3k
{
261
51.3k
  va_list ap;
262
51.3k
  int result;
263
264
51.3k
  va_start(ap, height);
265
51.3k
  result = vips_call_split("extract_area", ap, in, out,
266
51.3k
    left, top, width, height);
267
51.3k
  va_end(ap);
268
269
51.3k
  return result;
270
51.3k
}
271
272
/* A synonym for extract_area.
273
 */
274
275
GType
276
vips_crop_get_type(void)
277
33
{
278
33
  static gsize gtype_id = 0;
279
280
33
  if (g_once_init_enter(&gtype_id)) {
281
33
    GType new_type = g_type_register_static_simple(VIPS_TYPE_CONVERSION,
282
33
      g_intern_static_string("crop"),
283
33
      sizeof(VipsExtractAreaClass),
284
33
      (GClassInitFunc) (void (*)(void)) vips_extract_area_class_intern_init,
285
33
      sizeof(VipsExtractArea),
286
#ifdef __EMSCRIPTEN__
287
      (GInstanceInitFunc) vips_crop_init_adapter,
288
#else
289
33
      (GInstanceInitFunc) (void (*)(void)) vips_extract_area_init,
290
33
#endif
291
33
      (GTypeFlags) 0);
292
293
33
    g_once_init_leave(&gtype_id, new_type);
294
33
  }
295
296
33
  return (GType) gtype_id;
297
33
}
298
299
/**
300
 * vips_crop: (method)
301
 * @in: input image
302
 * @out: (out): output image
303
 * @left: left edge of area to extract
304
 * @top: top edge of area to extract
305
 * @width: width of area to extract
306
 * @height: height of area to extract
307
 * @...: %NULL-terminated list of optional named arguments
308
 *
309
 * A synonym for vips_extract_area().
310
 *
311
 * See also: vips_extract_bands(), vips_smartcrop().
312
 *
313
 * Returns: 0 on success, -1 on error.
314
 */
315
int
316
vips_crop(VipsImage *in, VipsImage **out,
317
  int left, int top, int width, int height, ...)
318
0
{
319
0
  va_list ap;
320
0
  int result;
321
322
0
  va_start(ap, height);
323
0
  result = vips_call_split("crop", ap, in, out,
324
0
    left, top, width, height);
325
0
  va_end(ap);
326
327
0
  return result;
328
0
}
329
330
typedef struct _VipsExtractBand {
331
  VipsBandary parent_instance;
332
333
  /* The input image.
334
   */
335
  VipsImage *in;
336
337
  int band;
338
  int n;
339
} VipsExtractBand;
340
341
typedef VipsBandaryClass VipsExtractBandClass;
342
343
G_DEFINE_TYPE(VipsExtractBand, vips_extract_band, VIPS_TYPE_BANDARY);
344
345
static void
346
vips_extract_band_buffer(VipsBandarySequence *seq,
347
  VipsPel *out, VipsPel **in, int width)
348
4.62M
{
349
4.62M
  VipsBandary *bandary = seq->bandary;
350
4.62M
  VipsConversion *conversion = (VipsConversion *) bandary;
351
4.62M
  VipsExtractBand *extract = (VipsExtractBand *) bandary;
352
4.62M
  VipsImage *im = bandary->ready[0];
353
4.62M
  int es = VIPS_IMAGE_SIZEOF_ELEMENT(im);
354
4.62M
  int ips = VIPS_IMAGE_SIZEOF_PEL(im);
355
4.62M
  const int ops = VIPS_IMAGE_SIZEOF_PEL(conversion->out);
356
357
4.62M
  VipsPel *restrict p;
358
4.62M
  VipsPel *restrict q;
359
4.62M
  int x, z;
360
361
4.62M
  p = in[0] + extract->band * es;
362
4.62M
  q = out;
363
4.62M
  if (ops == 1) {
364
39.4M
    for (x = 0; x < width; x++) {
365
38.9M
      q[x] = p[0];
366
38.9M
      p += ips;
367
38.9M
    }
368
562k
  }
369
4.05M
  else {
370
186M
    for (x = 0; x < width; x++) {
371
1.06G
      for (z = 0; z < ops; z++)
372
880M
        q[z] = p[z];
373
374
181M
      p += ips;
375
181M
      q += ops;
376
181M
    }
377
4.05M
  }
378
4.62M
}
379
380
static int
381
vips_extract_band_build(VipsObject *object)
382
270k
{
383
270k
  VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object);
384
270k
  VipsBandary *bandary = (VipsBandary *) object;
385
270k
  VipsExtractBand *extract = (VipsExtractBand *) object;
386
387
270k
  if (extract->in) {
388
270k
    int bands;
389
390
270k
    vips_image_decode_predict(extract->in, &bands, NULL);
391
392
270k
    bandary->n = 1;
393
270k
    bandary->in = &extract->in;
394
270k
    bandary->out_bands = extract->n;
395
396
270k
    if (extract->band + extract->n > bands) {
397
0
      vips_error(class->nickname,
398
0
        "%s", _("bad extract band"));
399
0
      return -1;
400
0
    }
401
402
270k
    if (extract->band == 0 &&
403
270k
      extract->n == bands)
404
15.7k
      return vips_bandary_copy(bandary);
405
270k
  }
406
407
254k
  if (VIPS_OBJECT_CLASS(vips_extract_band_parent_class)->build(object))
408
0
    return -1;
409
410
254k
  return 0;
411
254k
}
412
413
static void
414
vips_extract_band_class_init(VipsExtractBandClass *class)
415
16
{
416
16
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
417
16
  VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(class);
418
16
  VipsBandaryClass *bandary_class = VIPS_BANDARY_CLASS(class);
419
420
16
  VIPS_DEBUG_MSG("vips_extract_band_class_init\n");
421
422
16
  gobject_class->set_property = vips_object_set_property;
423
16
  gobject_class->get_property = vips_object_get_property;
424
425
16
  vobject_class->nickname = "extract_band";
426
16
  vobject_class->description = _("extract band from an image");
427
16
  vobject_class->build = vips_extract_band_build;
428
429
16
  bandary_class->process_line = vips_extract_band_buffer;
430
431
16
  VIPS_ARG_IMAGE(class, "in", 1,
432
16
    _("Input"),
433
16
    _("Input image"),
434
16
    VIPS_ARGUMENT_REQUIRED_INPUT,
435
16
    G_STRUCT_OFFSET(VipsExtractBand, in));
436
437
16
  VIPS_ARG_INT(class, "band", 3,
438
16
    _("Band"),
439
16
    _("Band to extract"),
440
16
    VIPS_ARGUMENT_REQUIRED_INPUT,
441
16
    G_STRUCT_OFFSET(VipsExtractBand, band),
442
16
    0, VIPS_MAX_COORD, 0);
443
444
16
  VIPS_ARG_INT(class, "n", 4,
445
16
    _("n"),
446
16
    _("Number of bands to extract"),
447
16
    VIPS_ARGUMENT_OPTIONAL_INPUT,
448
16
    G_STRUCT_OFFSET(VipsExtractBand, n),
449
16
    1, VIPS_MAX_COORD, 1);
450
16
}
451
452
static void
453
vips_extract_band_init(VipsExtractBand *extract)
454
289k
{
455
289k
  extract->n = 1;
456
289k
}
457
458
/**
459
 * vips_extract_band: (method)
460
 * @in: input image
461
 * @out: (out): output image
462
 * @band: index of first band to extract
463
 * @...: %NULL-terminated list of optional named arguments
464
 *
465
 * Optional arguments:
466
 *
467
 * * @n: number of bands to extract
468
 *
469
 * Extract a band or bands from an image. Extracting out of range is an error.
470
 *
471
 * @n defaults to 1.
472
 *
473
 * See also: vips_extract_area().
474
 *
475
 * Returns: 0 on success, -1 on error.
476
 */
477
int
478
vips_extract_band(VipsImage *in, VipsImage **out, int band, ...)
479
289k
{
480
289k
  va_list ap;
481
289k
  int result;
482
483
289k
  va_start(ap, band);
484
289k
  result = vips_call_split("extract_band", ap, in, out, band);
485
289k
  va_end(ap);
486
487
289k
  return result;
488
289k
}