1 /*
2 =head1 NAME
3
4 maskimg.c - implements masked images/image subsets
5
6 =head1 SYNOPSIS
7
8 =head1 DESCRIPTION
9
10 =over
11 =cut
12 */
13
14 #define IMAGER_NO_CONTEXT
15
16 #include "imager.h"
17 #include "imageri.h"
18
19 #include <stdio.h>
20 /*
21 =item i_img_mask_ext
22
23 A pointer to this type of object is kept in the ext_data of a masked
24 image.
25
26 =cut
27 */
28
29 typedef struct {
30 i_img *targ;
31 i_img *mask;
32 i_img_dim xbase, ybase;
33 i_sample_t *samps; /* temp space */
34 } i_img_mask_ext;
35
36 #define MASKEXT(im) ((i_img_mask_ext *)((im)->ext_data))
37
38 static void i_destroy_masked(i_img *im);
39 static int i_ppix_masked(i_img *im, i_img_dim x, i_img_dim y, const i_color *pix);
40 static int i_ppixf_masked(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix);
41 static i_img_dim i_plin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals);
42 static i_img_dim i_plinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals);
43 static int i_gpix_masked(i_img *im, i_img_dim x, i_img_dim y, i_color *pix);
44 static int i_gpixf_masked(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix);
45 static i_img_dim i_glin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals);
46 static i_img_dim i_glinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals);
47 static i_img_dim i_gsamp_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samp,
48 int const *chans, int chan_count);
49 static i_img_dim i_gsampf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp,
50 int const *chans, int chan_count);
51 static i_img_dim i_gpal_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_palidx *vals);
52 static i_img_dim i_ppal_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_palidx *vals);
53 static i_img_dim
54 psamp_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
55 const i_sample_t *samples, const int *chans, int chan_count);
56 static i_img_dim
57 psampf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
58 const i_fsample_t *samples, const int *chans, int chan_count);
59
60 /*
61 =item IIM_base_masked
62
63 The basic data we copy into a masked image.
64
65 =cut
66 */
67 static i_img IIM_base_masked =
68 {
69 0, /* channels set */
70 0, 0, 0, /* xsize, ysize, bytes */
71 ~0U, /* ch_mask */
72 i_8_bits, /* bits */
73 i_palette_type, /* type */
74 1, /* virtual */
75 NULL, /* idata */
76 { 0, 0, NULL }, /* tags */
77 NULL, /* ext_data */
78
79 i_ppix_masked, /* i_f_ppix */
80 i_ppixf_masked, /* i_f_ppixf */
81 i_plin_masked, /* i_f_plin */
82 i_plinf_masked, /* i_f_plinf */
83 i_gpix_masked, /* i_f_gpix */
84 i_gpixf_masked, /* i_f_gpixf */
85 i_glin_masked, /* i_f_glin */
86 i_glinf_masked, /* i_f_glinf */
87 i_gsamp_masked, /* i_f_gsamp */
88 i_gsampf_masked, /* i_f_gsampf */
89
90 i_gpal_masked, /* i_f_gpal */
91 i_ppal_masked, /* i_f_ppal */
92 i_addcolors_forward, /* i_f_addcolors */
93 i_getcolors_forward, /* i_f_getcolors */
94 i_colorcount_forward, /* i_f_colorcount */
95 i_maxcolors_forward, /* i_f_maxcolors */
96 i_findcolor_forward, /* i_f_findcolor */
97 i_setcolors_forward, /* i_f_setcolors */
98
99 i_destroy_masked, /* i_f_destroy */
100
101 NULL, /* i_f_gsamp_bits */
102 NULL, /* i_f_psamp_bits */
103
104 psamp_masked, /* i_f_psamp */
105 psampf_masked /* i_f_psampf */
106 };
107
108 /*
109 =item i_img_masked_new(i_img *targ, i_img *mask, i_img_dim xbase, i_img_dim ybase, i_img_dim w, i_img_dim h)
110
111 Create a new masked image.
112
113 The image mask is optional, in which case the image is just a view of
114 a rectangular portion of the image.
115
116 The mask only has an effect of writing to the image, the entire view
117 of the underlying image is readable.
118
119 pixel access to mimg(x,y) is translated to targ(x+xbase, y+ybase), as long
120 as (0 <= x < w) and (0 <= y < h).
121
122 For a pixel to be writable, the pixel mask(x,y) must have non-zero in
123 it's first channel. No scaling of the pixel is done, the channel
124 sample is treated as boolean.
125
126 =cut
127 */
128
129 i_img *
i_img_masked_new(i_img * targ,i_img * mask,i_img_dim x,i_img_dim y,i_img_dim w,i_img_dim h)130 i_img_masked_new(i_img *targ, i_img *mask, i_img_dim x, i_img_dim y, i_img_dim w, i_img_dim h) {
131 i_img *im;
132 i_img_mask_ext *ext;
133 dIMCTXim(targ);
134
135 im_clear_error(aIMCTX);
136 if (x >= targ->xsize || y >= targ->ysize) {
137 im_push_error(aIMCTX, 0, "subset outside of target image");
138 return NULL;
139 }
140 if (mask) {
141 if (w > mask->xsize)
142 w = mask->xsize;
143 if (h > mask->ysize)
144 h = mask->ysize;
145 }
146 if (x+w > targ->xsize)
147 w = targ->xsize - x;
148 if (y+h > targ->ysize)
149 h = targ->ysize - y;
150
151 im = im_img_alloc(aIMCTX);
152
153 memcpy(im, &IIM_base_masked, sizeof(i_img));
154 i_tags_new(&im->tags);
155 im->xsize = w;
156 im->ysize = h;
157 im->channels = targ->channels;
158 im->bits = targ->bits;
159 im->type = targ->type;
160 ext = mymalloc(sizeof(*ext));
161 ext->targ = targ;
162 ext->mask = mask;
163 ext->xbase = x;
164 ext->ybase = y;
165 ext->samps = mymalloc(sizeof(i_sample_t) * im->xsize);
166 im->ext_data = ext;
167
168 im_img_init(aIMCTX, im);
169
170 return im;
171 }
172
173 /*
174 =item i_destroy_masked(i_img *im)
175
176 The destruction handler for masked images.
177
178 Releases the ext_data.
179
180 Internal function.
181
182 =cut
183 */
184
i_destroy_masked(i_img * im)185 static void i_destroy_masked(i_img *im) {
186 myfree(MASKEXT(im)->samps);
187 myfree(im->ext_data);
188 }
189
190 /*
191 =item i_ppix_masked(i_img *im, i_img_dim x, i_img_dim y, const i_color *pix)
192
193 Write a pixel to a masked image.
194
195 Internal function.
196
197 =cut
198 */
i_ppix_masked(i_img * im,i_img_dim x,i_img_dim y,const i_color * pix)199 static int i_ppix_masked(i_img *im, i_img_dim x, i_img_dim y, const i_color *pix) {
200 i_img_mask_ext *ext = MASKEXT(im);
201 int result;
202
203 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
204 return -1;
205 if (ext->mask) {
206 i_sample_t samp;
207
208 if (i_gsamp(ext->mask, x, x+1, y, &samp, NULL, 1) && !samp)
209 return 0; /* pretend it was good */
210 }
211 result = i_ppix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
212 im->type = ext->targ->type;
213 return result;
214 }
215
216 /*
217 =item i_ppixf_masked(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix)
218
219 Write a pixel to a masked image.
220
221 Internal function.
222
223 =cut
224 */
i_ppixf_masked(i_img * im,i_img_dim x,i_img_dim y,const i_fcolor * pix)225 static int i_ppixf_masked(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix) {
226 i_img_mask_ext *ext = MASKEXT(im);
227 int result;
228
229 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
230 return -1;
231 if (ext->mask) {
232 i_sample_t samp;
233
234 if (i_gsamp(ext->mask, x, x+1, y, &samp, NULL, 1) && !samp)
235 return 0; /* pretend it was good */
236 }
237 result = i_ppixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
238 im->type = ext->targ->type;
239 return result;
240 }
241
242 /*
243 =item i_plin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals)
244
245 Write a row of data to a masked image.
246
247 Internal function.
248
249 =cut
250 */
i_plin_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,const i_color * vals)251 static i_img_dim i_plin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals) {
252 i_img_mask_ext *ext = MASKEXT(im);
253
254 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
255 if (r > im->xsize)
256 r = im->xsize;
257 if (ext->mask) {
258 i_img_dim i;
259 int simple = 0;
260 i_sample_t *samps = ext->samps;
261 i_img_dim w = r - l;
262
263 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
264 if (w < 10)
265 simple = 1;
266 else {
267 /* the idea is to make a fast scan to see how often the state
268 changes */
269 i_img_dim changes = 0;
270 for (i = 0; i < w-1; ++i)
271 if (!samps[i] != !samps[i+1])
272 ++changes;
273 if (changes > w/3) /* just rough */
274 simple = 1;
275 }
276 if (simple) {
277 /* we'd be calling a usually more complicated i_plin function
278 almost as often as the usually simple i_ppix(), so just
279 do a simple scan
280 */
281 for (i = 0; i < w; ++i) {
282 if (samps[i])
283 i_ppix(ext->targ, l + i + ext->xbase, y + ext->ybase, vals + i);
284 }
285 im->type = ext->targ->type;
286 return r-l;
287 }
288 else {
289 /* the scan above indicates there should be some contiguous
290 regions, look for them and render
291 */
292 i_img_dim start;
293 i = 0;
294 while (i < w) {
295 while (i < w && !samps[i])
296 ++i;
297 start = i;
298 while (i < w && samps[i])
299 ++i;
300 if (i != start)
301 i_plin(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
302 y + ext->ybase, vals + start);
303 }
304 im->type = ext->targ->type;
305 return w;
306 }
307 }
308 else {
309 i_img_dim result = i_plin(ext->targ, l + ext->xbase, r + ext->xbase,
310 y + ext->ybase, vals);
311 im->type = ext->targ->type;
312 return result;
313 }
314 }
315 else {
316 return 0;
317 }
318 }
319
320 /*
321 =item i_plinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals)
322
323 Write a row of data to a masked image.
324
325 Internal function.
326
327 =cut
328 */
i_plinf_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,const i_fcolor * vals)329 static i_img_dim i_plinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals) {
330 i_img_mask_ext *ext = MASKEXT(im);
331 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
332 if (r > im->xsize)
333 r = im->xsize;
334 if (ext->mask) {
335 i_img_dim i;
336 int simple = 0;
337 i_sample_t *samps = ext->samps;
338 i_img_dim w = r - l;
339
340 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
341 if (w < 10)
342 simple = 1;
343 else {
344 /* the idea is to make a fast scan to see how often the state
345 changes */
346 i_img_dim changes = 0;
347 for (i = 0; i < w-1; ++i)
348 if (!samps[i] != !samps[i+1])
349 ++changes;
350 if (changes > w/3) /* just rough */
351 simple = 1;
352 }
353 if (simple) {
354 /* we'd be calling a usually more complicated i_plin function
355 almost as often as the usually simple i_ppix(), so just
356 do a simple scan
357 */
358 for (i = 0; i < w; ++i) {
359 if (samps[i])
360 i_ppixf(ext->targ, l + i + ext->xbase, y + ext->ybase, vals+i);
361 }
362 im->type = ext->targ->type;
363 return r-l;
364 }
365 else {
366 /* the scan above indicates there should be some contiguous
367 regions, look for them and render
368 */
369 i_img_dim start;
370 i = 0;
371 while (i < w) {
372 while (i < w && !samps[i])
373 ++i;
374 start = i;
375 while (i < w && samps[i])
376 ++i;
377 if (i != start)
378 i_plinf(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
379 y + ext->ybase, vals + start);
380 }
381 im->type = ext->targ->type;
382 return w;
383 }
384 }
385 else {
386 i_img_dim result = i_plinf(ext->targ, l + ext->xbase, r + ext->xbase,
387 y + ext->ybase, vals);
388 im->type = ext->targ->type;
389 return result;
390 }
391 }
392 else {
393 return 0;
394 }
395 }
396
397 /*
398 =item i_gpix_masked(i_img *im, i_img_dim x, i_img_dim y, i_color *pix)
399
400 Read a pixel from a masked image.
401
402 Internal.
403
404 =cut
405 */
i_gpix_masked(i_img * im,i_img_dim x,i_img_dim y,i_color * pix)406 static int i_gpix_masked(i_img *im, i_img_dim x, i_img_dim y, i_color *pix) {
407 i_img_mask_ext *ext = MASKEXT(im);
408
409 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
410 return -1;
411
412 return i_gpix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
413 }
414
415 /*
416 =item i_gpixf_masked(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix)
417
418 Read a pixel from a masked image.
419
420 Internal.
421
422 =cut
423 */
i_gpixf_masked(i_img * im,i_img_dim x,i_img_dim y,i_fcolor * pix)424 static int i_gpixf_masked(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix) {
425 i_img_mask_ext *ext = MASKEXT(im);
426
427 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
428 return -1;
429
430 return i_gpixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
431 }
432
i_glin_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,i_color * vals)433 static i_img_dim i_glin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals) {
434 i_img_mask_ext *ext = MASKEXT(im);
435 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
436 if (r > im->xsize)
437 r = im->xsize;
438 return i_glin(ext->targ, l + ext->xbase, r + ext->xbase,
439 y + ext->ybase, vals);
440 }
441 else {
442 return 0;
443 }
444 }
445
i_glinf_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,i_fcolor * vals)446 static i_img_dim i_glinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals) {
447 i_img_mask_ext *ext = MASKEXT(im);
448 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
449 if (r > im->xsize)
450 r = im->xsize;
451 return i_glinf(ext->targ, l + ext->xbase, r + ext->xbase,
452 y + ext->ybase, vals);
453 }
454 else {
455 return 0;
456 }
457 }
458
i_gsamp_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,i_sample_t * samp,int const * chans,int chan_count)459 static i_img_dim i_gsamp_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samp,
460 int const *chans, int chan_count) {
461 i_img_mask_ext *ext = MASKEXT(im);
462 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
463 if (r > im->xsize)
464 r = im->xsize;
465 return i_gsamp(ext->targ, l + ext->xbase, r + ext->xbase,
466 y + ext->ybase, samp, chans, chan_count);
467 }
468 else {
469 return 0;
470 }
471 }
472
i_gsampf_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,i_fsample_t * samp,int const * chans,int chan_count)473 static i_img_dim i_gsampf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp,
474 int const *chans, int chan_count) {
475 i_img_mask_ext *ext = MASKEXT(im);
476 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
477 if (r > im->xsize)
478 r = im->xsize;
479 return i_gsampf(ext->targ, l + ext->xbase, r + ext->xbase,
480 y + ext->ybase, samp, chans, chan_count);
481 }
482 else {
483 return 0;
484 }
485 }
486
i_gpal_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,i_palidx * vals)487 static i_img_dim i_gpal_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_palidx *vals) {
488 i_img_mask_ext *ext = MASKEXT(im);
489 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
490 if (r > im->xsize)
491 r = im->xsize;
492 return i_gpal(ext->targ, l + ext->xbase, r + ext->xbase,
493 y + ext->ybase, vals);
494 }
495 else {
496 return 0;
497 }
498 }
499
i_ppal_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,const i_palidx * vals)500 static i_img_dim i_ppal_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_palidx *vals) {
501 i_img_mask_ext *ext = MASKEXT(im);
502 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
503 if (r > im->xsize)
504 r = im->xsize;
505 if (ext->mask) {
506 i_img_dim i;
507 i_sample_t *samps = ext->samps;
508 i_img_dim w = r - l;
509 i_img_dim start;
510
511 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
512 i = 0;
513 while (i < w) {
514 while (i < w && !samps[i])
515 ++i;
516 start = i;
517 while (i < w && samps[i])
518 ++i;
519 if (i != start)
520 i_ppal(ext->targ, l+start+ext->xbase, l+i+ext->xbase,
521 y+ext->ybase, vals+start);
522 }
523 return w;
524 }
525 else {
526 return i_ppal(ext->targ, l + ext->xbase, r + ext->xbase,
527 y + ext->ybase, vals);
528 }
529 }
530 else {
531 return 0;
532 }
533 }
534
535 /*
536 =item psamp_masked()
537
538 i_psamp() implementation for masked images.
539
540 =cut
541 */
542
543 static i_img_dim
psamp_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,const i_sample_t * samples,const int * chans,int chan_count)544 psamp_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
545 const i_sample_t *samples, const int *chans, int chan_count) {
546 i_img_mask_ext *ext = MASKEXT(im);
547
548 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
549 unsigned old_ch_mask = ext->targ->ch_mask;
550 i_img_dim result = 0;
551 ext->targ->ch_mask = im->ch_mask;
552 if (r > im->xsize)
553 r = im->xsize;
554 if (ext->mask) {
555 i_img_dim w = r - l;
556 i_img_dim i = 0;
557 i_img_dim x = ext->xbase + l;
558 i_img_dim work_y = y + ext->ybase;
559 i_sample_t *mask_samps = ext->samps;
560
561 i_gsamp(ext->mask, l, r, y, mask_samps, NULL, 1);
562 /* not optimizing this yet */
563 while (i < w) {
564 if (mask_samps[i]) {
565 /* found a set mask value, try to do a run */
566 i_img_dim run_left = x;
567 const i_sample_t *run_samps = samples;
568 ++i;
569 ++x;
570 samples += chan_count;
571
572 while (i < w && mask_samps[i]) {
573 ++i;
574 ++x;
575 samples += chan_count;
576 }
577 result += i_psamp(ext->targ, run_left, x, work_y, run_samps, chans, chan_count);
578 }
579 else {
580 ++i;
581 ++x;
582 samples += chan_count;
583 result += chan_count; /* pretend we wrote masked off pixels */
584 }
585 }
586 }
587 else {
588 result = i_psamp(ext->targ, l + ext->xbase, r + ext->xbase,
589 y + ext->ybase, samples, chans, chan_count);
590 im->type = ext->targ->type;
591 }
592 ext->targ->ch_mask = old_ch_mask;
593 return result;
594 }
595 else {
596 dIMCTXim(im);
597 i_push_error(0, "Image position outside of image");
598 return -1;
599 }
600 }
601
602 /*
603 =item psampf_masked()
604
605 i_psampf() implementation for masked images.
606
607 =cut
608 */
609
610 static i_img_dim
psampf_masked(i_img * im,i_img_dim l,i_img_dim r,i_img_dim y,const i_fsample_t * samples,const int * chans,int chan_count)611 psampf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
612 const i_fsample_t *samples, const int *chans, int chan_count) {
613 i_img_mask_ext *ext = MASKEXT(im);
614
615 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
616 i_img_dim result = 0;
617 unsigned old_ch_mask = ext->targ->ch_mask;
618 ext->targ->ch_mask = im->ch_mask;
619 if (r > im->xsize)
620 r = im->xsize;
621 if (ext->mask) {
622 i_img_dim w = r - l;
623 i_img_dim i = 0;
624 i_img_dim x = ext->xbase + l;
625 i_img_dim work_y = y + ext->ybase;
626 i_sample_t *mask_samps = ext->samps;
627
628 i_gsamp(ext->mask, l, r, y, mask_samps, NULL, 1);
629 /* not optimizing this yet */
630 while (i < w) {
631 if (mask_samps[i]) {
632 /* found a set mask value, try to do a run */
633 i_img_dim run_left = x;
634 const i_fsample_t *run_samps = samples;
635 ++i;
636 ++x;
637 samples += chan_count;
638
639 while (i < w && mask_samps[i]) {
640 ++i;
641 ++x;
642 samples += chan_count;
643 }
644 result += i_psampf(ext->targ, run_left, x, work_y, run_samps, chans, chan_count);
645 }
646 else {
647 ++i;
648 ++x;
649 samples += chan_count;
650 result += chan_count; /* pretend we wrote masked off pixels */
651 }
652 }
653 }
654 else {
655 result = i_psampf(ext->targ, l + ext->xbase, r + ext->xbase,
656 y + ext->ybase, samples,
657 chans, chan_count);
658 im->type = ext->targ->type;
659 }
660 ext->targ->ch_mask = old_ch_mask;
661 return result;
662 }
663 else {
664 dIMCTXim(im);
665 i_push_error(0, "Image position outside of image");
666 return -1;
667 }
668 }
669
670
671 /*
672 =back
673
674 =head1 AUTHOR
675
676 Tony Cook <tony@develop-help.com>
677
678 =head1 SEE ALSO
679
680 Imager(3)
681
682 =cut
683 */
684