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