80397110b2cb916f0c56b92c072c33e6c5a39894
[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 "image.h"
15 #include "imagei.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 *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 *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   ~0, /* 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   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
304 Write a row of data to a masked image.
305
306 Internal function.
307
308 =cut
309 */
310 static 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
381 Read a pixel from a masked image.
382
383 Internal.
384
385 =cut
386 */
387 static 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
399 Read a pixel from a masked image.
400
401 Internal.
402
403 =cut
404 */
405 static 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
414 static 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
427 static 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
440 static 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
454 static 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
468 static 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
481 static 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