4 maskimg.c - implements masked images/image subsets
14 #define IMAGER_NO_CONTEXT
23 A pointer to this type of object is kept in the ext_data of a masked
32 i_img_dim xbase, ybase;
33 i_sample_t *samps; /* temp space */
36 #define MASKEXT(im) ((i_img_mask_ext *)((im)->ext_data))
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);
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);
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);
63 The basic data we copy into a masked image.
67 static i_img IIM_base_masked =
70 0, 0, 0, /* xsize, ysize, bytes */
73 i_palette_type, /* type */
76 { 0, 0, NULL }, /* tags */
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 */
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 */
99 i_destroy_masked, /* i_f_destroy */
101 NULL, /* i_f_gsamp_bits */
102 NULL, /* i_f_psamp_bits */
104 psamp_masked, /* i_f_psamp */
105 psampf_masked /* i_f_psampf */
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)
111 Create a new masked image.
113 The image mask is optional, in which case the image is just a view of
114 a rectangular portion of the image.
116 The mask only has an effect of writing to the image, the entire view
117 of the underlying image is readable.
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).
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.
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) {
135 im_clear_error(aIMCTX);
136 if (x >= targ->xsize || y >= targ->ysize) {
137 im_push_error(aIMCTX, 0, "subset outside of target image");
146 if (x+w > targ->xsize)
148 if (y+h > targ->ysize)
151 im = im_img_alloc(aIMCTX);
153 memcpy(im, &IIM_base_masked, sizeof(i_img));
154 i_tags_new(&im->tags);
157 im->channels = targ->channels;
158 im->bits = targ->bits;
159 im->type = targ->type;
160 ext = mymalloc(sizeof(*ext));
165 ext->samps = mymalloc(sizeof(i_sample_t) * im->xsize);
168 im_img_init(aIMCTX, im);
174 =item i_destroy_masked(i_img *im)
176 The destruction handler for masked images.
178 Releases the ext_data.
185 static void i_destroy_masked(i_img *im) {
186 myfree(MASKEXT(im)->samps);
187 myfree(im->ext_data);
191 =item i_ppix_masked(i_img *im, i_img_dim x, i_img_dim y, const i_color *pix)
193 Write a pixel to a masked image.
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);
203 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
208 if (i_gsamp(ext->mask, x, x+1, y, &samp, NULL, 1) && !samp)
209 return 0; /* pretend it was good */
211 result = i_ppix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
212 im->type = ext->targ->type;
217 =item i_ppixf_masked(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix)
219 Write a pixel to a masked image.
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);
229 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
234 if (i_gsamp(ext->mask, x, x+1, y, &samp, NULL, 1) && !samp)
235 return 0; /* pretend it was good */
237 result = i_ppixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
238 im->type = ext->targ->type;
243 =item i_plin_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals)
245 Write a row of data to a masked image.
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);
254 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
260 i_sample_t *samps = ext->samps;
263 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
267 /* the idea is to make a fast scan to see how often the state
269 i_img_dim changes = 0;
270 for (i = 0; i < w-1; ++i)
271 if (!samps[i] != !samps[i+1])
273 if (changes > w/3) /* just rough */
277 /* we'd be calling a usually more complicated i_plin function
278 almost as often as the usually simple i_ppix(), so just
281 for (i = 0; i < w; ++i) {
283 i_ppix(ext->targ, l + i + ext->xbase, y + ext->ybase, vals + i);
285 im->type = ext->targ->type;
289 /* the scan above indicates there should be some contiguous
290 regions, look for them and render
295 while (i < w && !samps[i])
298 while (i < w && samps[i])
301 i_plin(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
302 y + ext->ybase, vals + start);
304 im->type = ext->targ->type;
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;
321 =item i_plinf_masked(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals)
323 Write a row of data to a masked image.
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) {
337 i_sample_t *samps = ext->samps;
340 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
344 /* the idea is to make a fast scan to see how often the state
346 i_img_dim changes = 0;
347 for (i = 0; i < w-1; ++i)
348 if (!samps[i] != !samps[i+1])
350 if (changes > w/3) /* just rough */
354 /* we'd be calling a usually more complicated i_plin function
355 almost as often as the usually simple i_ppix(), so just
358 for (i = 0; i < w; ++i) {
360 i_ppixf(ext->targ, l + i + ext->xbase, y + ext->ybase, vals+i);
362 im->type = ext->targ->type;
366 /* the scan above indicates there should be some contiguous
367 regions, look for them and render
372 while (i < w && !samps[i])
375 while (i < w && samps[i])
378 i_plinf(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
379 y + ext->ybase, vals + start);
381 im->type = ext->targ->type;
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;
398 =item i_gpix_masked(i_img *im, i_img_dim x, i_img_dim y, i_color *pix)
400 Read a pixel from a masked image.
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);
409 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
412 return i_gpix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
416 =item i_gpixf_masked(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix)
418 Read a pixel from a masked image.
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);
427 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
430 return i_gpixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
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) {
438 return i_glin(ext->targ, l + ext->xbase, r + ext->xbase,
439 y + ext->ybase, 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) {
451 return i_glinf(ext->targ, l + ext->xbase, r + ext->xbase,
452 y + ext->ybase, vals);
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) {
465 return i_gsamp(ext->targ, l + ext->xbase, r + ext->xbase,
466 y + ext->ybase, samp, chans, 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) {
479 return i_gsampf(ext->targ, l + ext->xbase, r + ext->xbase,
480 y + ext->ybase, samp, chans, chan_count);
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) {
492 return i_gpal(ext->targ, l + ext->xbase, r + ext->xbase,
493 y + ext->ybase, 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) {
507 i_sample_t *samps = ext->samps;
511 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
514 while (i < w && !samps[i])
517 while (i < w && samps[i])
520 i_ppal(ext->targ, l+start+ext->xbase, l+i+ext->xbase,
521 y+ext->ybase, vals+start);
526 return i_ppal(ext->targ, l + ext->xbase, r + ext->xbase,
527 y + ext->ybase, vals);
538 i_psamp() implementation for masked images.
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);
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;
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;
561 i_gsamp(ext->mask, l, r, y, mask_samps, NULL, 1);
562 /* not optimizing this yet */
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;
570 samples += chan_count;
572 while (i < w && mask_samps[i]) {
575 samples += chan_count;
577 result += i_psamp(ext->targ, run_left, x, work_y, run_samps, chans, chan_count);
582 samples += chan_count;
583 result += chan_count; /* pretend we wrote masked off pixels */
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;
592 ext->targ->ch_mask = old_ch_mask;
597 i_push_error(0, "Image position outside of image");
603 =item psampf_masked()
605 i_psampf() implementation for masked images.
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);
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;
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;
628 i_gsamp(ext->mask, l, r, y, mask_samps, NULL, 1);
629 /* not optimizing this yet */
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;
637 samples += chan_count;
639 while (i < w && mask_samps[i]) {
642 samples += chan_count;
644 result += i_psampf(ext->targ, run_left, x, work_y, run_samps, chans, chan_count);
649 samples += chan_count;
650 result += chan_count; /* pretend we wrote masked off pixels */
655 result = i_psampf(ext->targ, l + ext->xbase, r + ext->xbase,
656 y + ext->ybase, samples,
658 im->type = ext->targ->type;
660 ext->targ->ch_mask = old_ch_mask;
665 i_push_error(0, "Image position outside of image");
676 Tony Cook <tony@develop-help.com>