Coverage Report

Created: 2026-02-09 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libvips/libvips/draw/draw_circle.c
Line
Count
Source
1
/* draw a draw_circle on an image
2
 *
3
 * Author N. Dessipris
4
 * Written on 30/05/1990
5
 * Updated on:
6
 * 22/7/93 JC
7
 *  - im_incheck() call added
8
 * 16/8/94 JC
9
 *  - im_incheck() changed to im_makerw()
10
 * 5/12/06
11
 *  - im_invalidate() after paint
12
 * 6/3/10
13
 *  - don't im_invalidate() after paint, this now needs to be at a higher
14
 *    level
15
 * 18/8/10
16
 *  - gtkdoc
17
 *  - rewritten: clips, fills, any bands, any format
18
 * 27/9/10
19
 *  - break base out to Draw
20
 * 3/2/14
21
 *  - redo as a class
22
 */
23
24
/*
25
26
  This file is part of VIPS.
27
28
  VIPS is free software; you can redistribute it and/or modify
29
  it under the terms of the GNU Lesser General Public License as published by
30
  the Free Software Foundation; either version 2 of the License, or
31
  (at your option) any later version.
32
33
  This program is distributed in the hope that it will be useful,
34
  but WITHOUT ANY WARRANTY; without even the implied warranty of
35
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
  GNU Lesser General Public License for more details.
37
38
  You should have received a copy of the GNU Lesser General Public License
39
  along with this program; if not, write to the Free Software
40
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
41
  02110-1301  USA
42
43
 */
44
45
/*
46
47
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
48
49
 */
50
51
#ifdef HAVE_CONFIG_H
52
#include <config.h>
53
#endif /*HAVE_CONFIG_H*/
54
#include <glib/gi18n-lib.h>
55
56
#include <string.h>
57
58
#include <vips/vips.h>
59
#include <vips/internal.h>
60
61
#include "drawink.h"
62
63
typedef struct _VipsDrawCircle {
64
  VipsDrawink parent_object;
65
66
  int cx;
67
  int cy;
68
  int radius;
69
  gboolean fill;
70
71
} VipsDrawCircle;
72
73
typedef struct _VipsDrawCircleClass {
74
  VipsDrawinkClass parent_class;
75
76
} VipsDrawCircleClass;
77
78
36
G_DEFINE_TYPE(VipsDrawCircle, vips_draw_circle, VIPS_TYPE_DRAWINK);
79
36
80
36
void
81
36
vips__draw_circle_direct(VipsImage *image, int cx, int cy, int r,
82
36
  VipsDrawScanline draw_scanline, void *client)
83
36
{
84
0
  int x, y, d;
85
86
0
  y = r;
87
0
  d = 3 - 2 * r;
88
89
0
  for (x = 0; x < y; x++) {
90
0
    draw_scanline(image, cy + y, cx - x, cx + x, 0, client);
91
0
    draw_scanline(image, cy - y, cx - x, cx + x, 1, client);
92
0
    draw_scanline(image, cy + x, cx - y, cx + y, 2, client);
93
0
    draw_scanline(image, cy - x, cx - y, cx + y, 3, client);
94
95
0
    if (d < 0)
96
0
      d += 4 * x + 6;
97
0
    else {
98
0
      d += 4 * (x - y) + 10;
99
0
      y--;
100
0
    }
101
0
  }
102
103
0
  if (x == y) {
104
0
    draw_scanline(image, cy + y, cx - x, cx + x, 0, client);
105
0
    draw_scanline(image, cy - y, cx - x, cx + x, 1, client);
106
0
    draw_scanline(image, cy + x, cx - y, cx + y, 2, client);
107
0
    draw_scanline(image, cy - x, cx - y, cx + y, 3, client);
108
0
  }
109
0
}
110
111
static inline void
112
vips_draw_circle_draw_point(VipsImage *image, int x, int y, void *client)
113
0
{
114
0
  VipsPel *ink = (VipsPel *) client;
115
0
  VipsPel *q = VIPS_IMAGE_ADDR(image, x, y);
116
0
  int psize = VIPS_IMAGE_SIZEOF_PEL(image);
117
118
0
  int j;
119
120
  /* Faster than memcopy() for n < about 20.
121
   */
122
0
  for (j = 0; j < psize; j++)
123
0
    q[j] = ink[j];
124
0
}
125
126
/* Paint endpoints, with clip.
127
 */
128
static void
129
vips_draw_circle_draw_endpoints_clip(VipsImage *image,
130
  int y, int x1, int x2, int quadrant, void *client)
131
0
{
132
0
  if (y >= 0 &&
133
0
    y < image->Ysize) {
134
0
    if (x1 >= 0 &&
135
0
      x1 < image->Xsize)
136
0
      vips_draw_circle_draw_point(image, x1, y, client);
137
0
    if (x2 >= 0 &&
138
0
      x2 < image->Xsize)
139
0
      vips_draw_circle_draw_point(image, x2, y, client);
140
0
  }
141
0
}
142
143
/* Paint endpoints, no clip.
144
 */
145
static void
146
vips_draw_circle_draw_endpoints_noclip(VipsImage *image,
147
  int y, int x1, int x2, int quadrant, void *client)
148
0
{
149
0
  vips_draw_circle_draw_point(image, x1, y, client);
150
0
  vips_draw_circle_draw_point(image, x2, y, client);
151
0
}
152
153
/* Paint scanline.
154
 */
155
static void
156
vips_draw_circle_draw_scanline(VipsImage *image,
157
  int y, int x1, int x2, int quadrant, void *client)
158
0
{
159
0
  VipsPel *ink = (VipsPel *) client;
160
0
  int psize = VIPS_IMAGE_SIZEOF_PEL(image);
161
162
0
  VipsPel *q;
163
0
  int len;
164
0
  int i, j;
165
166
0
  g_assert(x1 <= x2);
167
168
0
  if (y < 0 ||
169
0
    y >= image->Ysize)
170
0
    return;
171
0
  if (x1 < 0 &&
172
0
    x2 < 0)
173
0
    return;
174
0
  if (x1 >= image->Xsize &&
175
0
    x2 >= image->Xsize)
176
0
    return;
177
0
  x1 = VIPS_CLIP(0, x1, image->Xsize - 1);
178
0
  x2 = VIPS_CLIP(0, x2, image->Xsize - 1);
179
180
0
  q = VIPS_IMAGE_ADDR(image, x1, y);
181
0
  len = x2 - x1 + 1;
182
183
0
  for (i = 0; i < len; i++) {
184
0
    for (j = 0; j < psize; j++)
185
0
      q[j] = ink[j];
186
187
0
    q += psize;
188
0
  }
189
0
}
190
191
static int
192
vips_draw_circle_build(VipsObject *object)
193
0
{
194
0
  VipsDraw *draw = VIPS_DRAW(object);
195
0
  VipsDrawink *drawink = VIPS_DRAWINK(object);
196
0
  VipsDrawCircle *circle = (VipsDrawCircle *) object;
197
198
0
  VipsDrawScanline draw_scanline;
199
200
0
  if (VIPS_OBJECT_CLASS(vips_draw_circle_parent_class)->build(object))
201
0
    return -1;
202
203
0
  if (circle->fill)
204
0
    draw_scanline = vips_draw_circle_draw_scanline;
205
0
  else if (circle->cx - circle->radius >= 0 &&
206
0
    circle->cx + circle->radius < draw->image->Xsize &&
207
0
    circle->cy - circle->radius >= 0 &&
208
0
    circle->cy + circle->radius < draw->image->Ysize)
209
0
    draw_scanline = vips_draw_circle_draw_endpoints_noclip;
210
0
  else
211
0
    draw_scanline = vips_draw_circle_draw_endpoints_clip;
212
213
0
  vips__draw_circle_direct(draw->image,
214
0
    circle->cx, circle->cy, circle->radius,
215
0
    draw_scanline, drawink->pixel_ink);
216
217
0
  return 0;
218
0
}
219
220
static void
221
vips_draw_circle_class_init(VipsDrawCircleClass *class)
222
18
{
223
18
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
224
18
  VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(class);
225
226
18
  gobject_class->set_property = vips_object_set_property;
227
18
  gobject_class->get_property = vips_object_get_property;
228
229
18
  vobject_class->nickname = "draw_circle";
230
18
  vobject_class->description = _("draw a circle on an image");
231
18
  vobject_class->build = vips_draw_circle_build;
232
233
18
  VIPS_ARG_INT(class, "cx", 3,
234
18
    _("cx"),
235
18
    _("Centre of draw_circle"),
236
18
    VIPS_ARGUMENT_REQUIRED_INPUT,
237
18
    G_STRUCT_OFFSET(VipsDrawCircle, cx),
238
18
    -1000000000, 1000000000, 0);
239
240
18
  VIPS_ARG_INT(class, "cy", 4,
241
18
    _("cy"),
242
18
    _("Centre of draw_circle"),
243
18
    VIPS_ARGUMENT_REQUIRED_INPUT,
244
18
    G_STRUCT_OFFSET(VipsDrawCircle, cy),
245
18
    -1000000000, 1000000000, 0);
246
247
18
  VIPS_ARG_INT(class, "radius", 5,
248
18
    _("Radius"),
249
18
    _("Radius in pixels"),
250
18
    VIPS_ARGUMENT_REQUIRED_INPUT,
251
18
    G_STRUCT_OFFSET(VipsDrawCircle, radius),
252
18
    0, 1000000000, 0);
253
254
18
  VIPS_ARG_BOOL(class, "fill", 6,
255
18
    _("Fill"),
256
18
    _("Draw a solid object"),
257
18
    VIPS_ARGUMENT_OPTIONAL_INPUT,
258
18
    G_STRUCT_OFFSET(VipsDrawCircle, fill),
259
18
    FALSE);
260
18
}
261
262
static void
263
vips_draw_circle_init(VipsDrawCircle *circle)
264
0
{
265
0
  circle->fill = FALSE;
266
0
}
267
268
static int
269
vips_draw_circlev(VipsImage *image,
270
  double *ink, int n, int cx, int cy, int radius, va_list ap)
271
0
{
272
0
  VipsArea *area_ink;
273
0
  int result;
274
275
0
  area_ink = VIPS_AREA(vips_array_double_new(ink, n));
276
0
  result = vips_call_split("draw_circle", ap,
277
0
    image, area_ink, cx, cy, radius);
278
0
  vips_area_unref(area_ink);
279
280
0
  return result;
281
0
}
282
283
/**
284
 * vips_draw_circle: (method)
285
 * @image: image to draw on
286
 * @ink: (array length=n): value to draw
287
 * @n: length of ink array
288
 * @cx: centre of draw_circle
289
 * @cy: centre of draw_circle
290
 * @radius: draw_circle radius
291
 * @...: `NULL`-terminated list of optional named arguments
292
 *
293
 * Draws a circle on @image.
294
 *
295
 * If @fill is `TRUE` then the circle is filled,
296
 * otherwise a 1-pixel-wide perimeter is drawn.
297
 *
298
 * @ink is an array of double containing values to draw.
299
 *
300
 * ::: tip "Optional arguments"
301
 *     * @fill: `gboolean`, fill the draw_circle
302
 *
303
 * ::: seealso
304
 *     [method@Image.draw_circle1], [method@Image.draw_line].
305
 *
306
 * Returns: 0 on success, or -1 on error.
307
 */
308
int
309
vips_draw_circle(VipsImage *image,
310
  double *ink, int n, int cx, int cy, int radius, ...)
311
0
{
312
0
  va_list ap;
313
0
  int result;
314
315
0
  va_start(ap, radius);
316
0
  result = vips_draw_circlev(image, ink, n, cx, cy, radius, ap);
317
0
  va_end(ap);
318
319
0
  return result;
320
0
}
321
322
/**
323
 * vips_draw_circle1: (method)
324
 * @image: image to draw on
325
 * @ink: value to draw
326
 * @cx: centre of draw_circle
327
 * @cy: centre of draw_circle
328
 * @radius: draw_circle radius
329
 * @...: `NULL`-terminated list of optional named arguments
330
 *
331
 * As [method@Image.draw_circle], but just takes a single double for @ink.
332
 *
333
 * ::: tip "Optional arguments"
334
 *     * @fill: `gboolean`, fill the draw_circle
335
 *
336
 * ::: seealso
337
 *     [method@Image.draw_circle].
338
 *
339
 * Returns: 0 on success, or -1 on error.
340
 */
341
int
342
vips_draw_circle1(VipsImage *image,
343
  double ink, int cx, int cy, int radius, ...)
344
0
{
345
0
  double array_ink[1];
346
0
  va_list ap;
347
0
  int result;
348
349
0
  array_ink[0] = ink;
350
351
0
  va_start(ap, radius);
352
0
  result = vips_draw_circlev(image, array_ink, 1, cx, cy, radius, ap);
353
0
  va_end(ap);
354
355
0
  return result;
356
0
}