/src/libvips/libvips/draw/draw_rect.c
Line | Count | Source |
1 | | /* Fill Rect r of image im with pels of colour ink. |
2 | | * |
3 | | * Copyright: J. Cupitt |
4 | | * Written: 15/06/1992 |
5 | | * 22/7/93 JC |
6 | | * - im_incheck() added |
7 | | * 16/8/94 JC |
8 | | * - im_incheck() changed to im_makerw() |
9 | | * 5/12/06 |
10 | | * - im_invalidate() after paint |
11 | | * 6/3/10 |
12 | | * - don't im_invalidate() after paint, this now needs to be at a higher |
13 | | * level |
14 | | * 22/9/10 |
15 | | * - gtk-doc |
16 | | * - added 'fill' |
17 | | * - renamed as im_draw_rect() for consistency |
18 | | * 27/9/10 |
19 | | * - memcpy() subsequent lines of the rect |
20 | | * 10/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 <stdio.h> |
57 | | #include <stdlib.h> |
58 | | #include <string.h> |
59 | | |
60 | | #include <vips/vips.h> |
61 | | #include <vips/internal.h> |
62 | | |
63 | | #include "drawink.h" |
64 | | |
65 | | typedef struct _VipsDrawRect { |
66 | | VipsDrawink parent_object; |
67 | | |
68 | | /* Parameters. |
69 | | */ |
70 | | int left; |
71 | | int top; |
72 | | int width; |
73 | | int height; |
74 | | gboolean fill; |
75 | | |
76 | | } VipsDrawRect; |
77 | | |
78 | | typedef struct _VipsDrawRectClass { |
79 | | VipsDrawinkClass parent_class; |
80 | | |
81 | | } VipsDrawRectClass; |
82 | | |
83 | 38 | G_DEFINE_TYPE(VipsDrawRect, vips_draw_rect, VIPS_TYPE_DRAWINK); |
84 | 38 | |
85 | 38 | static int |
86 | 38 | vips_draw_rect_build(VipsObject *object) |
87 | 38 | { |
88 | 11 | VipsDraw *draw = VIPS_DRAW(object); |
89 | 11 | VipsDrawink *drawink = VIPS_DRAWINK(object); |
90 | 11 | VipsArea *ink = VIPS_AREA(drawink->ink); |
91 | 11 | VipsDrawRect *draw_rect = (VipsDrawRect *) object; |
92 | 11 | int left = draw_rect->left; |
93 | 11 | int top = draw_rect->top; |
94 | 11 | int width = draw_rect->width; |
95 | 11 | int height = draw_rect->height; |
96 | | |
97 | 11 | VipsRect image; |
98 | 11 | VipsRect rect; |
99 | 11 | VipsRect clip; |
100 | | |
101 | 11 | if (VIPS_OBJECT_CLASS(vips_draw_rect_parent_class)->build(object)) |
102 | 1 | return -1; |
103 | | |
104 | | /* Also use a solid fill for very narrow unfilled rects. |
105 | | */ |
106 | 10 | if (!draw_rect->fill && |
107 | 10 | width > 2 && |
108 | 5 | height > 2) |
109 | 1 | return vips_draw_rect(draw->image, |
110 | 1 | ink->data, ink->n, |
111 | 1 | left, top, width, 1, NULL) || |
112 | 1 | vips_draw_rect(draw->image, |
113 | 1 | ink->data, ink->n, |
114 | 1 | left + width - 1, top, 1, height, NULL) || |
115 | 1 | vips_draw_rect(draw->image, |
116 | 1 | ink->data, ink->n, |
117 | 1 | left, top + height - 1, width, 1, NULL) || |
118 | 1 | vips_draw_rect(draw->image, |
119 | 1 | ink->data, ink->n, |
120 | 1 | left, top, 1, height, NULL); |
121 | | |
122 | 9 | image.left = 0; |
123 | 9 | image.top = 0; |
124 | 9 | image.width = draw->image->Xsize; |
125 | 9 | image.height = draw->image->Ysize; |
126 | 9 | rect.left = left; |
127 | 9 | rect.top = top; |
128 | 9 | rect.width = width; |
129 | 9 | rect.height = height; |
130 | 9 | vips_rect_intersectrect(&rect, &image, &clip); |
131 | | |
132 | 9 | if (!vips_rect_isempty(&clip)) { |
133 | 6 | VipsPel *to = |
134 | 6 | VIPS_IMAGE_ADDR(draw->image, clip.left, clip.top); |
135 | | |
136 | 6 | VipsPel *q; |
137 | 6 | int x, y; |
138 | | |
139 | | /* We plot the first line pointwise, then memcpy() it for the |
140 | | * subsequent lines. |
141 | | */ |
142 | | |
143 | 6 | q = to; |
144 | 35 | for (x = 0; x < clip.width; x++) { |
145 | 29 | vips__drawink_pel(drawink, q); |
146 | 29 | q += draw->psize; |
147 | 29 | } |
148 | | |
149 | 6 | q = to + draw->lsize; |
150 | 8 | for (y = 1; y < clip.height; y++) { |
151 | 2 | memcpy(q, to, clip.width * draw->psize); |
152 | 2 | q += draw->lsize; |
153 | 2 | } |
154 | 6 | } |
155 | | |
156 | 9 | return 0; |
157 | 10 | } |
158 | | |
159 | | static void |
160 | | vips_draw_rect_class_init(VipsDrawRectClass *class) |
161 | 19 | { |
162 | 19 | GObjectClass *gobject_class = G_OBJECT_CLASS(class); |
163 | 19 | VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(class); |
164 | | |
165 | 19 | gobject_class->set_property = vips_object_set_property; |
166 | 19 | gobject_class->get_property = vips_object_get_property; |
167 | | |
168 | 19 | vobject_class->nickname = "draw_rect"; |
169 | 19 | vobject_class->description = _("paint a rectangle on an image"); |
170 | 19 | vobject_class->build = vips_draw_rect_build; |
171 | | |
172 | 19 | VIPS_ARG_INT(class, "left", 6, |
173 | 19 | _("Left"), |
174 | 19 | _("Rect to fill"), |
175 | 19 | VIPS_ARGUMENT_REQUIRED_INPUT, |
176 | 19 | G_STRUCT_OFFSET(VipsDrawRect, left), |
177 | 19 | -1000000000, 1000000000, 0); |
178 | | |
179 | 19 | VIPS_ARG_INT(class, "top", 7, |
180 | 19 | _("Top"), |
181 | 19 | _("Rect to fill"), |
182 | 19 | VIPS_ARGUMENT_REQUIRED_INPUT, |
183 | 19 | G_STRUCT_OFFSET(VipsDrawRect, top), |
184 | 19 | -1000000000, 1000000000, 0); |
185 | | |
186 | 19 | VIPS_ARG_INT(class, "width", 8, |
187 | 19 | _("Width"), |
188 | 19 | _("Rect to fill"), |
189 | 19 | VIPS_ARGUMENT_REQUIRED_INPUT, |
190 | 19 | G_STRUCT_OFFSET(VipsDrawRect, width), |
191 | 19 | -1000000000, 1000000000, 0); |
192 | | |
193 | 19 | VIPS_ARG_INT(class, "height", 9, |
194 | 19 | _("Height"), |
195 | 19 | _("Rect to fill"), |
196 | 19 | VIPS_ARGUMENT_REQUIRED_INPUT, |
197 | 19 | G_STRUCT_OFFSET(VipsDrawRect, height), |
198 | 19 | -1000000000, 1000000000, 0); |
199 | | |
200 | 19 | VIPS_ARG_BOOL(class, "fill", 10, |
201 | 19 | _("Fill"), |
202 | 19 | _("Draw a solid object"), |
203 | 19 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
204 | 19 | G_STRUCT_OFFSET(VipsDrawRect, fill), |
205 | 19 | FALSE); |
206 | 19 | } |
207 | | |
208 | | static void |
209 | | vips_draw_rect_init(VipsDrawRect *draw_rect) |
210 | 12 | { |
211 | 12 | } |
212 | | |
213 | | static int |
214 | | vips_draw_rectv(VipsImage *image, |
215 | | double *ink, int n, int left, int top, int width, int height, |
216 | | va_list ap) |
217 | 4 | { |
218 | 4 | VipsArea *area_ink; |
219 | 4 | int result; |
220 | | |
221 | 4 | area_ink = VIPS_AREA(vips_array_double_new(ink, n)); |
222 | 4 | result = vips_call_split("draw_rect", ap, |
223 | 4 | image, area_ink, left, top, width, height); |
224 | 4 | vips_area_unref(area_ink); |
225 | | |
226 | 4 | return result; |
227 | 4 | } |
228 | | |
229 | | /** |
230 | | * vips_draw_rect: (method) |
231 | | * @image: image to draw on |
232 | | * @ink: (array length=n): value to draw |
233 | | * @n: length of ink array |
234 | | * @left: area to paint |
235 | | * @top: area to paint |
236 | | * @width: area to paint |
237 | | * @height: area to paint |
238 | | * @...: `NULL`-terminated list of optional named arguments |
239 | | * |
240 | | * Paint pixels within @left, @top, @width, @height in @image with @ink. |
241 | | * |
242 | | * If @fill is zero, just paint a 1-pixel-wide outline. |
243 | | * |
244 | | * ::: tip "Optional arguments" |
245 | | * * @fill: `gboolean`, fill the rect |
246 | | * |
247 | | * ::: seealso |
248 | | * [method@Image.draw_circle]. |
249 | | * |
250 | | * Returns: 0 on success, or -1 on error. |
251 | | */ |
252 | | int |
253 | | vips_draw_rect(VipsImage *image, |
254 | | double *ink, int n, int left, int top, int width, int height, ...) |
255 | 4 | { |
256 | 4 | va_list ap; |
257 | 4 | int result; |
258 | | |
259 | 4 | va_start(ap, height); |
260 | 4 | result = vips_draw_rectv(image, |
261 | 4 | ink, n, left, top, width, height, ap); |
262 | 4 | va_end(ap); |
263 | | |
264 | 4 | return result; |
265 | 4 | } |
266 | | |
267 | | /** |
268 | | * vips_draw_rect1: (method) |
269 | | * @image: image to draw on |
270 | | * @ink: value to draw |
271 | | * @left: area to paint |
272 | | * @top: area to paint |
273 | | * @width: area to paint |
274 | | * @height: area to paint |
275 | | * @...: `NULL`-terminated list of optional named arguments |
276 | | * |
277 | | * As [method@Image.draw_rect], but just take a single double for @ink. |
278 | | * |
279 | | * ::: tip "Optional arguments" |
280 | | * * @fill: `gboolean`, fill the rect |
281 | | * |
282 | | * ::: seealso |
283 | | * [method@Image.draw_rect]. |
284 | | * |
285 | | * Returns: 0 on success, or -1 on error. |
286 | | */ |
287 | | int |
288 | | vips_draw_rect1(VipsImage *image, |
289 | | double ink, int left, int top, int width, int height, ...) |
290 | 0 | { |
291 | 0 | double array_ink[1]; |
292 | 0 | va_list ap; |
293 | 0 | int result; |
294 | |
|
295 | 0 | array_ink[0] = ink; |
296 | |
|
297 | 0 | va_start(ap, height); |
298 | 0 | result = vips_draw_rectv(image, |
299 | 0 | array_ink, 1, left, top, width, height, ap); |
300 | 0 | va_end(ap); |
301 | |
|
302 | 0 | return result; |
303 | 0 | } |
304 | | |
305 | | /** |
306 | | * vips_draw_point: (method) |
307 | | * @image: image to draw on |
308 | | * @ink: (array length=n): value to draw |
309 | | * @n: length of ink array |
310 | | * @x: point to paint |
311 | | * @y: point to paint |
312 | | * @...: `NULL`-terminated list of optional named arguments |
313 | | * |
314 | | * As [method@Image.draw_rect], but draw a single pixel at @x, @y. |
315 | | * |
316 | | * ::: seealso |
317 | | * [method@Image.draw_rect]. |
318 | | * |
319 | | * Returns: 0 on success, or -1 on error. |
320 | | */ |
321 | | int |
322 | | vips_draw_point(VipsImage *image, double *ink, int n, int x, int y, ...) |
323 | 0 | { |
324 | 0 | va_list ap; |
325 | 0 | int result; |
326 | |
|
327 | 0 | va_start(ap, y); |
328 | 0 | result = vips_draw_rectv(image, ink, n, x, y, 1, 1, ap); |
329 | 0 | va_end(ap); |
330 | |
|
331 | 0 | return result; |
332 | 0 | } |
333 | | |
334 | | /** |
335 | | * vips_draw_point1: (method) |
336 | | * @image: image to draw on |
337 | | * @ink: value to draw |
338 | | * @x: point to draw |
339 | | * @y: point to draw |
340 | | * @...: `NULL`-terminated list of optional named arguments |
341 | | * |
342 | | * As [method@Image.draw_point], but just take a single double for @ink. |
343 | | * |
344 | | * ::: seealso |
345 | | * [method@Image.draw_point]. |
346 | | * |
347 | | * Returns: 0 on success, or -1 on error. |
348 | | */ |
349 | | int |
350 | | vips_draw_point1(VipsImage *image, double ink, int x, int y, ...) |
351 | 0 | { |
352 | 0 | double array_ink[1]; |
353 | 0 | va_list ap; |
354 | 0 | int result; |
355 | |
|
356 | 0 | array_ink[0] = ink; |
357 | |
|
358 | 0 | va_start(ap, y); |
359 | 0 | result = vips_draw_rectv(image, array_ink, 1, x, y, 1, 1, ap); |
360 | 0 | va_end(ap); |
361 | |
|
362 | 0 | return result; |
363 | 0 | } |