Fixed i_transform2() so malloc(0) doesn't happen. Also corrected pod errors and
[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
14#include "image.h"
15#include "imagei.h"
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);
37static int i_ppix_masked(i_img *im, int x, int y, i_color *pix);
38static int i_ppixf_masked(i_img *im, int x, int y, i_fcolor *pix);
39static int i_plin_masked(i_img *im, int l, int r, int y, i_color *vals);
40static int i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals);
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,
46 int *chans, int chan_count);
47static int i_gsampf_masked(i_img *im, int l, int r, int y, i_fsample_t *samp,
48 int *chans, int chan_count);
49static int i_gpal_masked(i_img *im, int l, int r, int y, i_palidx *vals);
50static int i_ppal_masked(i_img *im, int l, int r, int y, i_palidx *vals);
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/*
171=item i_ppix_masked(i_img *im, int x, int y, i_color *pix)
172
173Write a pixel to a masked image.
174
175Internal function.
176
177=cut
178*/
179static int i_ppix_masked(i_img *im, int x, int y, i_color *pix) {
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/*
197=item i_ppixf_masked(i_img *im, int x, int y, i_fcolor *pix)
198
199Write a pixel to a masked image.
200
201Internal function.
202
203=cut
204*/
205static int i_ppixf_masked(i_img *im, int x, int y, i_fcolor *pix) {
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/*
223=item i_plin_masked(i_img *im, int l, int r, int y, i_color *vals)
224
225Write a row of data to a masked image.
226
227Internal function.
228
229=cut
230*/
231static int i_plin_masked(i_img *im, int l, int r, int y, i_color *vals) {
232 i_img_mask_ext *ext = MASKEXT(im);
233 int result;
234
235 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
236 if (r > im->xsize)
237 r = im->xsize;
238 if (ext->mask) {
239 int i;
240 int simple = 0;
241 i_sample_t *samps = ext->samps;
242 int w = r - l;
243
244 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
245 if (w < 10)
246 simple = 0;
247 else {
248 /* the idea is to make a fast scan to see how often the state
249 changes */
250 int changes = 0;
251 for (i = 0; i < w-1; ++i)
252 if (!samps[i] != !samps[i+1])
253 ++changes;
254 if (changes > w/3) /* just rough */
255 simple = 1;
256 }
257 if (simple) {
258 /* we'd be calling a usually more complicated i_plin function
259 almost as often as the usually simple i_ppix(), so just
260 do a simple scan
261 */
262 for (i = 0; i < w; ++i) {
263 if (samps[i])
264 i_ppix(ext->targ, l + i + ext->xbase, y + ext->ybase, vals + i);
265 }
266 im->type = ext->targ->type;
267 return r-l;
268 }
269 else {
270 /* the scan above indicates there should be some contiguous
271 regions, look for them and render
272 */
273 int start;
274 i = 0;
275 while (i < w) {
276 while (i < w && !samps[i])
277 ++i;
278 start = i;
279 while (i < w && samps[i])
280 ++i;
281 if (i != start)
282 i_plin(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
283 y + ext->ybase, vals + start);
284 }
285 im->type = ext->targ->type;
286 return w;
287 }
288 }
289 else {
290 int result = i_plin(ext->targ, l + ext->xbase, r + ext->xbase,
291 y + ext->ybase, vals);
292 im->type = ext->targ->type;
293 return result;
294 }
295 }
296 else {
297 return 0;
298 }
299}
300
301/*
302=item i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals)
303
304Write a row of data to a masked image.
305
306Internal function.
307
308=cut
309*/
310static int i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals) {
311 i_img_mask_ext *ext = MASKEXT(im);
312 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
313 if (r > im->xsize)
314 r = im->xsize;
315 if (ext->mask) {
316 int i;
317 int simple = 0;
318 i_sample_t *samps = ext->samps;
319 int w = r - l;
320
321 i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
322 if (w < 10)
323 simple = 0;
324 else {
325 /* the idea is to make a fast scan to see how often the state
326 changes */
327 int changes = 0;
328 for (i = 0; i < w-1; ++i)
329 if (!samps[i] != !samps[i+1])
330 ++changes;
331 if (changes > w/3) /* just rough */
332 simple = 1;
333 }
334 if (simple) {
335 /* we'd be calling a usually more complicated i_plin function
336 almost as often as the usually simple i_ppix(), so just
337 do a simple scan
338 */
339 for (i = 0; i < w; ++i) {
340 if (samps[i])
341 i_ppixf(ext->targ, l + i + ext->xbase, y + ext->ybase, vals+i);
342 }
343 im->type = ext->targ->type;
344 return r-l;
345 }
346 else {
347 /* the scan above indicates there should be some contiguous
348 regions, look for them and render
349 */
350 int start;
351 i = 0;
352 while (i < w) {
353 while (i < w && !samps[i])
354 ++i;
355 start = i;
356 while (i < w && samps[i])
357 ++i;
358 if (i != start)
359 i_plinf(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
360 y + ext->ybase, vals + start);
361 }
362 im->type = ext->targ->type;
363 return w;
364 }
365 }
366 else {
367 int result = i_plinf(ext->targ, l + ext->xbase, r + ext->xbase,
368 y + ext->ybase, vals);
369 im->type = ext->targ->type;
370 return result;
371 }
372 }
373 else {
374 return 0;
375 }
376}
377
378/*
379=item i_gpix_masked(i_img *im, int x, int y, i_color *pix)
380
381Read a pixel from a masked image.
382
383Internal.
384
385=cut
386*/
387static int i_gpix_masked(i_img *im, int x, int y, i_color *pix) {
388 i_img_mask_ext *ext = MASKEXT(im);
389
390 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
391 return -1;
392
393 return i_gpix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
394}
395
396/*
397=item i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix)
398
399Read a pixel from a masked image.
400
401Internal.
402
403=cut
404*/
405static int i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix) {
406 i_img_mask_ext *ext = MASKEXT(im);
407
408 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
409 return -1;
410
411 return i_gpixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
412}
413
414static int i_glin_masked(i_img *im, int l, int r, int y, i_color *vals) {
415 i_img_mask_ext *ext = MASKEXT(im);
416 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
417 if (r > im->xsize)
418 r = im->xsize;
419 return i_glin(ext->targ, l + ext->xbase, r + ext->xbase,
420 y + ext->ybase, vals);
421 }
422 else {
423 return 0;
424 }
425}
426
427static int i_glinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals) {
428 i_img_mask_ext *ext = MASKEXT(im);
429 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
430 if (r > im->xsize)
431 r = im->xsize;
432 return i_glinf(ext->targ, l + ext->xbase, r + ext->xbase,
433 y + ext->ybase, vals);
434 }
435 else {
436 return 0;
437 }
438}
439
440static int i_gsamp_masked(i_img *im, int l, int r, int y, i_sample_t *samp,
441 int *chans, int chan_count) {
442 i_img_mask_ext *ext = MASKEXT(im);
443 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
444 if (r > im->xsize)
445 r = im->xsize;
446 return i_gsamp(ext->targ, l + ext->xbase, r + ext->xbase,
447 y + ext->ybase, samp, chans, chan_count);
448 }
449 else {
450 return 0;
451 }
452}
453
454static int i_gsampf_masked(i_img *im, int l, int r, int y, i_fsample_t *samp,
455 int *chans, int chan_count) {
456 i_img_mask_ext *ext = MASKEXT(im);
457 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
458 if (r > im->xsize)
459 r = im->xsize;
460 return i_gsampf(ext->targ, l + ext->xbase, r + ext->xbase,
461 y + ext->ybase, samp, chans, chan_count);
462 }
463 else {
464 return 0;
465 }
466}
467
468static int i_gpal_masked(i_img *im, int l, int r, int y, i_palidx *vals) {
469 i_img_mask_ext *ext = MASKEXT(im);
470 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
471 if (r > im->xsize)
472 r = im->xsize;
473 return i_gpal(ext->targ, l + ext->xbase, r + ext->xbase,
474 y + ext->ybase, vals);
475 }
476 else {
477 return 0;
478 }
479}
480
481static int i_ppal_masked(i_img *im, int l, int r, int y, i_palidx *vals) {
482 i_img_mask_ext *ext = MASKEXT(im);
483 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
484 if (r > im->xsize)
485 r = im->xsize;
486 if (ext->mask) {
487 int i;
488 i_sample_t *samps = ext->samps;
489 int w = r - l;
490 int start;
491
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*/