re-work XS handling of channel lists
[imager.git] / maskimg.c
CommitLineData
faa9b3e7
TC
1/*
2=head1 NAME
3
4maskimg.c - implements masked images/image subsets
5
6=head1 SYNOPSIS
7
8=head1 DESCRIPTION
9
10=over
11=cut
12*/
13
92bda632
TC
14#include "imager.h"
15#include "imageri.h"
faa9b3e7
TC
16
17#include <stdio.h>
18/*
19=item i_img_mask_ext
20
21A pointer to this type of object is kept in the ext_data of a masked
22image.
23
24=cut
25*/
26
27typedef struct {
28 i_img *targ;
29 i_img *mask;
8d14daab 30 i_img_dim xbase, ybase;
faa9b3e7
TC
31 i_sample_t *samps; /* temp space */
32} i_img_mask_ext;
33
34#define MASKEXT(im) ((i_img_mask_ext *)((im)->ext_data))
35
36static void i_destroy_masked(i_img *im);
8d14daab
TC
37static int i_ppix_masked(i_img *im, i_img_dim x, i_img_dim y, const i_color *pix);
38static int i_ppixf_masked(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix);
39static 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);
40static 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);
41static int i_gpix_masked(i_img *im, i_img_dim x, i_img_dim y, i_color *pix);
42static int i_gpixf_masked(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix);
43static i_img_dim i_glin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals);
44static i_img_dim i_glinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals);
45static 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,
18accb2a 46 int const *chans, int chan_count);
8d14daab 47static 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,
18accb2a 48 int const *chans, int chan_count);
8d14daab
TC
49static i_img_dim i_gpal_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_palidx *vals);
50static 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);
faa9b3e7
TC
51
52/*
53=item IIM_base_masked
54
55The basic data we copy into a masked image.
56
57=cut
58*/
59static i_img IIM_base_masked =
60{
61 0, /* channels set */
62 0, 0, 0, /* xsize, ysize, bytes */
9a88a5e6 63 ~0U, /* ch_mask */
faa9b3e7
TC
64 i_8_bits, /* bits */
65 i_palette_type, /* type */
66 1, /* virtual */
67 NULL, /* idata */
68 { 0, 0, NULL }, /* tags */
69 NULL, /* ext_data */
70
71 i_ppix_masked, /* i_f_ppix */
72 i_ppixf_masked, /* i_f_ppixf */
73 i_plin_masked, /* i_f_plin */
74 i_plinf_masked, /* i_f_plinf */
75 i_gpix_masked, /* i_f_gpix */
76 i_gpixf_masked, /* i_f_gpixf */
77 i_glin_masked, /* i_f_glin */
78 i_glinf_masked, /* i_f_glinf */
79 i_gsamp_masked, /* i_f_gsamp */
80 i_gsampf_masked, /* i_f_gsampf */
81
82 i_gpal_masked, /* i_f_gpal */
83 i_ppal_masked, /* i_f_ppal */
84 i_addcolors_forward, /* i_f_addcolors */
85 i_getcolors_forward, /* i_f_getcolors */
86 i_colorcount_forward, /* i_f_colorcount */
87 i_maxcolors_forward, /* i_f_maxcolors */
88 i_findcolor_forward, /* i_f_findcolor */
89 i_setcolors_forward, /* i_f_setcolors */
90
91 i_destroy_masked, /* i_f_destroy */
92};
93
94/*
8d14daab 95=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)
faa9b3e7
TC
96
97Create a new masked image.
98
99The image mask is optional, in which case the image is just a view of
100a rectangular portion of the image.
101
102The mask only has an effect of writing to the image, the entire view
103of the underlying image is readable.
104
105pixel access to mimg(x,y) is translated to targ(x+xbase, y+ybase), as long
106as (0 <= x < w) and (0 <= y < h).
107
108For a pixel to be writable, the pixel mask(x,y) must have non-zero in
109it's first channel. No scaling of the pixel is done, the channel
110sample is treated as boolean.
111
112=cut
113*/
114
8d14daab 115i_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) {
faa9b3e7
TC
116 i_img *im;
117 i_img_mask_ext *ext;
118
119 i_clear_error();
120 if (x >= targ->xsize || y >= targ->ysize) {
121 i_push_error(0, "subset outside of target image");
122 return NULL;
123 }
124 if (mask) {
125 if (w > mask->xsize)
126 w = mask->xsize;
127 if (h > mask->ysize)
128 h = mask->ysize;
129 }
130 if (x+w > targ->xsize)
131 w = targ->xsize - x;
132 if (y+h > targ->ysize)
133 h = targ->ysize - y;
134
135 im = mymalloc(sizeof(i_img));
136 memcpy(im, &IIM_base_masked, sizeof(i_img));
137 im->xsize = w;
138 im->ysize = h;
139 im->channels = targ->channels;
140 im->bits = targ->bits;
141 im->type = targ->type;
142 ext = mymalloc(sizeof(*ext));
143 ext->targ = targ;
144 ext->mask = mask;
145 ext->xbase = x;
146 ext->ybase = y;
147 ext->samps = mymalloc(sizeof(i_sample_t) * im->xsize);
148 im->ext_data = ext;
149
150 return im;
151}
152
153/*
154=item i_destroy_masked(i_img *im)
155
156The destruction handler for masked images.
157
158Releases the ext_data.
159
160Internal function.
161
162=cut
163*/
164
165static void i_destroy_masked(i_img *im) {
166 myfree(MASKEXT(im)->samps);
167 myfree(im->ext_data);
168}
169
170/*
8d14daab 171=item i_ppix_masked(i_img *im, i_img_dim x, i_img_dim y, const i_color *pix)
faa9b3e7
TC
172
173Write a pixel to a masked image.
174
175Internal function.
176
177=cut
178*/
8d14daab 179static int i_ppix_masked(i_img *im, i_img_dim x, i_img_dim y, const i_color *pix) {
faa9b3e7
TC
180 i_img_mask_ext *ext = MASKEXT(im);
181 int result;
182
183 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
184 return -1;
185 if (ext->mask) {
186 i_sample_t samp;
187
188 if (i_gsamp(ext->mask, x, x+1, y, &samp, NULL, 1) && !samp)
189 return 0; /* pretend it was good */
190 }
191 result = i_ppix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
192 im->type = ext->targ->type;
193 return result;
194}
195
196/*
8d14daab 197=item i_ppixf_masked(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix)
faa9b3e7
TC
198
199Write a pixel to a masked image.
200
201Internal function.
202
203=cut
204*/
8d14daab 205static int i_ppixf_masked(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix) {
faa9b3e7
TC
206 i_img_mask_ext *ext = MASKEXT(im);
207 int result;
208
209 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
210 return -1;
211 if (ext->mask) {
212 i_sample_t samp;
213
214 if (i_gsamp(ext->mask, x, x+1, y, &samp, NULL, 1) && !samp)
215 return 0; /* pretend it was good */
216 }
217 result = i_ppixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
218 im->type = ext->targ->type;
219 return result;
220}
221
222/*
8d14daab 223=item i_plin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals)
faa9b3e7
TC
224
225Write a row of data to a masked image.
226
227Internal function.
228
229=cut
230*/
8d14daab 231static 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) {
faa9b3e7 232 i_img_mask_ext *ext = MASKEXT(im);
faa9b3e7
TC
233
234 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
235 if (r > im->xsize)
236 r = im->xsize;
237 if (ext->mask) {
8d14daab 238 i_img_dim i;
faa9b3e7
TC
239 int simple = 0;
240 i_sample_t *samps = ext->samps;
8d14daab 241 i_img_dim w = r - l;
faa9b3e7
TC
242
243 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
244 if (w < 10)
416e9814 245 simple = 1;
faa9b3e7
TC
246 else {
247 /* the idea is to make a fast scan to see how often the state
248 changes */
8d14daab 249 i_img_dim changes = 0;
faa9b3e7
TC
250 for (i = 0; i < w-1; ++i)
251 if (!samps[i] != !samps[i+1])
252 ++changes;
253 if (changes > w/3) /* just rough */
254 simple = 1;
255 }
256 if (simple) {
257 /* we'd be calling a usually more complicated i_plin function
258 almost as often as the usually simple i_ppix(), so just
259 do a simple scan
260 */
261 for (i = 0; i < w; ++i) {
262 if (samps[i])
263 i_ppix(ext->targ, l + i + ext->xbase, y + ext->ybase, vals + i);
264 }
265 im->type = ext->targ->type;
266 return r-l;
267 }
268 else {
269 /* the scan above indicates there should be some contiguous
270 regions, look for them and render
271 */
8d14daab 272 i_img_dim start;
faa9b3e7
TC
273 i = 0;
274 while (i < w) {
275 while (i < w && !samps[i])
276 ++i;
277 start = i;
278 while (i < w && samps[i])
279 ++i;
280 if (i != start)
281 i_plin(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
282 y + ext->ybase, vals + start);
283 }
284 im->type = ext->targ->type;
285 return w;
286 }
287 }
288 else {
8d14daab 289 i_img_dim result = i_plin(ext->targ, l + ext->xbase, r + ext->xbase,
faa9b3e7
TC
290 y + ext->ybase, vals);
291 im->type = ext->targ->type;
292 return result;
293 }
294 }
295 else {
296 return 0;
297 }
298}
299
300/*
8d14daab 301=item i_plinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals)
faa9b3e7
TC
302
303Write a row of data to a masked image.
304
305Internal function.
306
307=cut
308*/
8d14daab 309static 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) {
faa9b3e7
TC
310 i_img_mask_ext *ext = MASKEXT(im);
311 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
312 if (r > im->xsize)
313 r = im->xsize;
314 if (ext->mask) {
8d14daab 315 i_img_dim i;
faa9b3e7
TC
316 int simple = 0;
317 i_sample_t *samps = ext->samps;
8d14daab 318 i_img_dim w = r - l;
faa9b3e7
TC
319
320 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
321 if (w < 10)
416e9814 322 simple = 1;
faa9b3e7
TC
323 else {
324 /* the idea is to make a fast scan to see how often the state
325 changes */
8d14daab 326 i_img_dim changes = 0;
faa9b3e7
TC
327 for (i = 0; i < w-1; ++i)
328 if (!samps[i] != !samps[i+1])
329 ++changes;
330 if (changes > w/3) /* just rough */
331 simple = 1;
332 }
333 if (simple) {
334 /* we'd be calling a usually more complicated i_plin function
335 almost as often as the usually simple i_ppix(), so just
336 do a simple scan
337 */
338 for (i = 0; i < w; ++i) {
339 if (samps[i])
340 i_ppixf(ext->targ, l + i + ext->xbase, y + ext->ybase, vals+i);
341 }
342 im->type = ext->targ->type;
343 return r-l;
344 }
345 else {
346 /* the scan above indicates there should be some contiguous
347 regions, look for them and render
348 */
8d14daab 349 i_img_dim start;
faa9b3e7
TC
350 i = 0;
351 while (i < w) {
352 while (i < w && !samps[i])
353 ++i;
354 start = i;
355 while (i < w && samps[i])
356 ++i;
357 if (i != start)
358 i_plinf(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
359 y + ext->ybase, vals + start);
360 }
361 im->type = ext->targ->type;
362 return w;
363 }
364 }
365 else {
8d14daab 366 i_img_dim result = i_plinf(ext->targ, l + ext->xbase, r + ext->xbase,
faa9b3e7
TC
367 y + ext->ybase, vals);
368 im->type = ext->targ->type;
369 return result;
370 }
371 }
372 else {
373 return 0;
374 }
375}
376
377/*
8d14daab 378=item i_gpix_masked(i_img *im, i_img_dim x, i_img_dim y, i_color *pix)
faa9b3e7
TC
379
380Read a pixel from a masked image.
381
382Internal.
383
384=cut
385*/
8d14daab 386static int i_gpix_masked(i_img *im, i_img_dim x, i_img_dim y, i_color *pix) {
faa9b3e7
TC
387 i_img_mask_ext *ext = MASKEXT(im);
388
389 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
390 return -1;
391
392 return i_gpix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
393}
394
395/*
8d14daab 396=item i_gpixf_masked(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix)
faa9b3e7
TC
397
398Read a pixel from a masked image.
399
400Internal.
401
402=cut
403*/
8d14daab 404static int i_gpixf_masked(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix) {
faa9b3e7
TC
405 i_img_mask_ext *ext = MASKEXT(im);
406
407 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
408 return -1;
409
410 return i_gpixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
411}
412
8d14daab 413static i_img_dim i_glin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals) {
faa9b3e7
TC
414 i_img_mask_ext *ext = MASKEXT(im);
415 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
416 if (r > im->xsize)
417 r = im->xsize;
418 return i_glin(ext->targ, l + ext->xbase, r + ext->xbase,
419 y + ext->ybase, vals);
420 }
421 else {
422 return 0;
423 }
424}
425
8d14daab 426static i_img_dim i_glinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals) {
faa9b3e7
TC
427 i_img_mask_ext *ext = MASKEXT(im);
428 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
429 if (r > im->xsize)
430 r = im->xsize;
431 return i_glinf(ext->targ, l + ext->xbase, r + ext->xbase,
432 y + ext->ybase, vals);
433 }
434 else {
435 return 0;
436 }
437}
438
8d14daab 439static 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,
18accb2a 440 int const *chans, int chan_count) {
faa9b3e7
TC
441 i_img_mask_ext *ext = MASKEXT(im);
442 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
443 if (r > im->xsize)
444 r = im->xsize;
445 return i_gsamp(ext->targ, l + ext->xbase, r + ext->xbase,
446 y + ext->ybase, samp, chans, chan_count);
447 }
448 else {
449 return 0;
450 }
451}
452
8d14daab 453static 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,
18accb2a 454 int const *chans, int chan_count) {
faa9b3e7
TC
455 i_img_mask_ext *ext = MASKEXT(im);
456 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
457 if (r > im->xsize)
458 r = im->xsize;
459 return i_gsampf(ext->targ, l + ext->xbase, r + ext->xbase,
460 y + ext->ybase, samp, chans, chan_count);
461 }
462 else {
463 return 0;
464 }
465}
466
8d14daab 467static i_img_dim i_gpal_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_palidx *vals) {
faa9b3e7
TC
468 i_img_mask_ext *ext = MASKEXT(im);
469 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
470 if (r > im->xsize)
471 r = im->xsize;
472 return i_gpal(ext->targ, l + ext->xbase, r + ext->xbase,
473 y + ext->ybase, vals);
474 }
475 else {
476 return 0;
477 }
478}
479
8d14daab 480static 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) {
faa9b3e7
TC
481 i_img_mask_ext *ext = MASKEXT(im);
482 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
483 if (r > im->xsize)
484 r = im->xsize;
485 if (ext->mask) {
8d14daab 486 i_img_dim i;
faa9b3e7 487 i_sample_t *samps = ext->samps;
8d14daab
TC
488 i_img_dim w = r - l;
489 i_img_dim start;
faa9b3e7 490
416e9814 491 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
faa9b3e7
TC
492 i = 0;
493 while (i < w) {
494 while (i < w && !samps[i])
495 ++i;
496 start = i;
497 while (i < w && samps[i])
498 ++i;
499 if (i != start)
500 i_ppal(ext->targ, l+start+ext->xbase, l+i+ext->xbase,
501 y+ext->ybase, vals+start);
502 }
503 return w;
504 }
505 else {
506 return i_ppal(ext->targ, l + ext->xbase, r + ext->xbase,
507 y + ext->ybase, vals);
508 }
509 }
510 else {
511 return 0;
512 }
513}
514
b8c2033e
AMH
515
516/*
517=back
518
519=head1 AUTHOR
520
521Tony Cook <tony@develop-help.com>
522
523=head1 SEE ALSO
524
525Imager(3)
526
527=cut
528*/