67868500972b0e9e389974deb9b5213569e5ce94
[imager.git] / maskimg.c
1 /*
2 =head1 NAME
3
4 maskimg.c - implements masked images/image subsets
5
6 =head1 SYNOPSIS
7
8 =head1 DESCRIPTION
9
10 =over
11 =cut
12 */
13
14 #include "imager.h"
15 #include "imageri.h"
16
17 #include <stdio.h>
18 /*
19 =item i_img_mask_ext
20
21 A pointer to this type of object is kept in the ext_data of a masked 
22 image.
23
24 =cut
25 */
26
27 typedef 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
36 static void i_destroy_masked(i_img *im);
37 static int i_ppix_masked(i_img *im, int x, int y, i_color *pix);
38 static int i_ppixf_masked(i_img *im, int x, int y, i_fcolor *pix);
39 static int i_plin_masked(i_img *im, int l, int r, int y, i_color *vals);
40 static int i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals);
41 static int i_gpix_masked(i_img *im, int x, int y, i_color *pix);
42 static int i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix);
43 static int i_glin_masked(i_img *im, int l, int r, int y, i_color *vals);
44 static int i_glinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals);
45 static int i_gsamp_masked(i_img *im, int l, int r, int y, i_sample_t *samp, 
46                           int const *chans, int chan_count);
47 static int i_gsampf_masked(i_img *im, int l, int r, int y, i_fsample_t *samp, 
48                            int const *chans, int chan_count);
49 static int i_gpal_masked(i_img *im, int l, int r, int y, i_palidx *vals);
50 static int i_ppal_masked(i_img *im, int l, int r, int y, i_palidx *vals);
51
52 /*
53 =item IIM_base_masked
54
55 The basic data we copy into a masked image.
56
57 =cut
58 */
59 static i_img IIM_base_masked =
60 {
61   0, /* channels set */
62   0, 0, 0, /* xsize, ysize, bytes */
63   ~0U, /* ch_mask */
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
97 Create a new masked image.
98
99 The image mask is optional, in which case the image is just a view of
100 a rectangular portion of the image.
101
102 The mask only has an effect of writing to the image, the entire view
103 of the underlying image is readable.
104
105 pixel access to mimg(x,y) is translated to targ(x+xbase, y+ybase), as long 
106 as (0 <= x < w) and (0 <= y < h).
107
108 For a pixel to be writable, the pixel mask(x,y) must have non-zero in
109 it's first channel.  No scaling of the pixel is done, the channel 
110 sample is treated as boolean.
111
112 =cut
113 */
114
115 i_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
156 The destruction handler for masked images.
157
158 Releases the ext_data.
159
160 Internal function.
161
162 =cut
163 */
164
165 static 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
173 Write a pixel to a masked image.
174
175 Internal function.
176
177 =cut
178 */
179 static 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
199 Write a pixel to a masked image.
200
201 Internal function.
202
203 =cut
204 */
205 static 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
225 Write a row of data to a masked image.
226
227 Internal function.
228
229 =cut
230 */
231 static 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
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)
245         simple = 0;
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 /*
301 =item i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals)
302
303 Write a row of data to a masked image.
304
305 Internal function.
306
307 =cut
308 */
309 static int i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals) {
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)
322         simple = 0;
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
380 Read a pixel from a masked image.
381
382 Internal.
383
384 =cut
385 */
386 static 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
398 Read a pixel from a masked image.
399
400 Internal.
401
402 =cut
403 */
404 static 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
413 static 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
426 static 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
439 static int i_gsamp_masked(i_img *im, int l, int r, int y, i_sample_t *samp, 
440                           int const *chans, int chan_count) {
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
453 static int i_gsampf_masked(i_img *im, int l, int r, int y, i_fsample_t *samp, 
454                           int const *chans, int chan_count) {
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
467 static 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
480 static int i_ppal_masked(i_img *im, int l, int r, int y, i_palidx *vals) {
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       
491       i = 0;
492       while (i < w) {
493         while (i < w && !samps[i])
494           ++i;
495         start = i;
496         while (i < w && samps[i])
497           ++i;
498         if (i != start)
499           i_ppal(ext->targ, l+start+ext->xbase, l+i+ext->xbase, 
500                  y+ext->ybase, vals+start);
501       }
502       return w;
503     }
504     else {
505       return i_ppal(ext->targ, l + ext->xbase, r + ext->xbase, 
506                     y + ext->ybase, vals);
507     }
508   }
509   else {
510     return 0;
511   }
512 }
513
514
515 /*
516 =back
517
518 =head1 AUTHOR
519
520 Tony Cook <tony@develop-help.com>
521
522 =head1 SEE ALSO
523
524 Imager(3)
525
526 =cut
527 */