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