0.84 release
[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;
30 int xbase, ybase;
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);
97ac0a96
TC
37static int i_ppix_masked(i_img *im, int x, int y, const i_color *pix);
38static int i_ppixf_masked(i_img *im, int x, int y, const i_fcolor *pix);
39static int i_plin_masked(i_img *im, int l, int r, int y, const i_color *vals);
40static int i_plinf_masked(i_img *im, int l, int r, int y, const i_fcolor *vals);
faa9b3e7
TC
41static int i_gpix_masked(i_img *im, int x, int y, i_color *pix);
42static int i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix);
43static int i_glin_masked(i_img *im, int l, int r, int y, i_color *vals);
44static int i_glinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals);
45static int i_gsamp_masked(i_img *im, int l, int r, int y, i_sample_t *samp,
18accb2a 46 int const *chans, int chan_count);
faa9b3e7 47static int i_gsampf_masked(i_img *im, int l, int r, int y, i_fsample_t *samp,
18accb2a 48 int const *chans, int chan_count);
faa9b3e7 49static int i_gpal_masked(i_img *im, int l, int r, int y, i_palidx *vals);
97ac0a96 50static int i_ppal_masked(i_img *im, int l, int r, int 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/*
95=item i_img_masked_new(i_img *targ, i_img *mask, int xbase, int ybase, int w, int h)
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
115i_img *i_img_masked_new(i_img *targ, i_img *mask, int x, int y, int w, int h) {
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/*
97ac0a96 171=item i_ppix_masked(i_img *im, int x, int y, const i_color *pix)
faa9b3e7
TC
172
173Write a pixel to a masked image.
174
175Internal function.
176
177=cut
178*/
97ac0a96 179static int i_ppix_masked(i_img *im, int x, int 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/*
97ac0a96 197=item i_ppixf_masked(i_img *im, int x, int y, const i_fcolor *pix)
faa9b3e7
TC
198
199Write a pixel to a masked image.
200
201Internal function.
202
203=cut
204*/
97ac0a96 205static int i_ppixf_masked(i_img *im, int x, int 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/*
97ac0a96 223=item i_plin_masked(i_img *im, int l, int r, int y, const i_color *vals)
faa9b3e7
TC
224
225Write a row of data to a masked image.
226
227Internal function.
228
229=cut
230*/
97ac0a96 231static int i_plin_masked(i_img *im, int l, int r, int 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) {
238 int i;
239 int simple = 0;
240 i_sample_t *samps = ext->samps;
241 int w = r - l;
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 */
249 int changes = 0;
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 */
272 int start;
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 {
289 int result = i_plin(ext->targ, l + ext->xbase, r + ext->xbase,
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/*
97ac0a96 301=item i_plinf_masked(i_img *im, int l, int r, int y, const i_fcolor *vals)
faa9b3e7
TC
302
303Write a row of data to a masked image.
304
305Internal function.
306
307=cut
308*/
97ac0a96 309static int i_plinf_masked(i_img *im, int l, int r, int 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) {
315 int i;
316 int simple = 0;
317 i_sample_t *samps = ext->samps;
318 int w = r - l;
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 */
326 int changes = 0;
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 */
349 int start;
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 {
366 int result = i_plinf(ext->targ, l + ext->xbase, r + ext->xbase,
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/*
378=item i_gpix_masked(i_img *im, int x, int y, i_color *pix)
379
380Read a pixel from a masked image.
381
382Internal.
383
384=cut
385*/
386static int i_gpix_masked(i_img *im, int x, int y, i_color *pix) {
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/*
396=item i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix)
397
398Read a pixel from a masked image.
399
400Internal.
401
402=cut
403*/
404static int i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix) {
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
413static int i_glin_masked(i_img *im, int l, int r, int y, i_color *vals) {
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
426static int i_glinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals) {
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
439static int i_gsamp_masked(i_img *im, int l, int r, int 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
453static int i_gsampf_masked(i_img *im, int l, int r, int 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
467static int i_gpal_masked(i_img *im, int l, int r, int y, i_palidx *vals) {
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
97ac0a96 480static int i_ppal_masked(i_img *im, int l, int r, int 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) {
486 int i;
487 i_sample_t *samps = ext->samps;
488 int w = r - l;
489 int start;
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*/