switch to using size_t and i_img_dim strictly
[imager.git] / rubthru.im
1 #include "imager.h"
2
3 static int
4 rubthru_targ_noalpha(i_img *im, i_img *src,
5                      i_img_dim tx, i_img_dim ty, 
6                      i_img_dim src_minx, i_img_dim src_miny,
7                      i_img_dim src_maxx, i_img_dim src_maxy) {
8   i_img_dim x, y, ttx, tty;
9   int alphachan;
10   int ch;
11   i_img_dim width = src_maxx - src_minx;
12   int want_channels;
13
14   i_clear_error();
15
16   if (im->channels == 3 && (src->channels == 4 || src->channels == 2)) {
17     want_channels = 4;
18     alphachan = 3;
19   }
20   else if (im->channels == 1 && (src->channels == 4 || src->channels == 2)) {
21     want_channels = 2;
22     alphachan = 1;
23   }
24   else {
25     i_copyto(im, src, src_minx, src_miny, src_maxx, src_maxy, tx, ty);
26     return 1;
27   }
28
29 #code im->bits <= 8 && src->bits <= 8
30   IM_WORK_T alpha;
31   IM_COLOR *src_line, *dest_line;
32   
33   src_line = mymalloc(sizeof(IM_COLOR) * width);
34   dest_line = mymalloc(sizeof(IM_COLOR) * width);
35
36   tty = ty;
37   for(y = src_miny; y < src_maxy; y++) {
38     IM_COLOR *srcp = src_line;
39     IM_COLOR *destp = dest_line;
40     ttx = tx;
41     IM_GLIN(src, src_minx, src_maxx, y, src_line);
42     IM_GLIN(im, tx, tx + width, tty, dest_line);
43     if (src->channels != want_channels)
44       IM_ADAPT_COLORS(want_channels, src->channels, src_line, width);
45
46     for(x = src_minx; x < src_maxx; x++) {
47       alpha = srcp->channel[alphachan];
48       for (ch = 0; ch < im->channels; ++ch) {
49         IM_WORK_T samp = (alpha * srcp->channel[ch]
50                             + (IM_SAMPLE_MAX - alpha) * destp->channel[ch])/IM_SAMPLE_MAX;
51         destp->channel[ch] = IM_LIMIT(samp);
52       }
53       ++srcp;
54       ++destp;
55     }
56     IM_PLIN(im, tx, tx + width, tty, dest_line);
57     tty++;
58   }
59   myfree(src_line);
60   myfree(dest_line);
61 #/code
62
63   return 1;
64 }
65
66 static int
67 rubthru_targ_alpha(i_img *im, i_img *src, i_img_dim tx, i_img_dim ty, 
68                    i_img_dim src_minx, i_img_dim src_miny,
69                    i_img_dim src_maxx, i_img_dim src_maxy) {
70   i_img_dim x, y, ttx, tty;
71   int want_channels;
72   int alphachan;
73   int ch;
74   int targ_alpha_chan;
75   i_img_dim width = src_maxx - src_minx;
76   
77   if (im->channels == 4 && (src->channels == 4 || src->channels == 2)) {
78     alphachan = 3;
79     want_channels = 4;
80   }
81   else if (im->channels == 2 && (src->channels == 4 || src->channels == 2)) {
82     alphachan = 1;
83     want_channels = 2;
84   }
85   else {
86     i_copyto(im, src, src_minx, src_miny, src_maxx, src_maxy, tx, ty);
87     return 1;
88   }
89
90   targ_alpha_chan = im->channels - 1;
91
92 #code im->bits <= 8 && src->bits <= 8
93   IM_WORK_T src_alpha, orig_alpha, dest_alpha, remains;
94   IM_COLOR *src_line, *dest_line;
95
96   src_line = mymalloc(sizeof(IM_COLOR) * width);
97   dest_line = mymalloc(sizeof(IM_COLOR) * width);
98
99   tty = ty;
100   for(y = src_miny; y < src_maxy; y++) {
101     i_img_dim min_x, max_x;
102     IM_COLOR *srcp = src_line;
103     IM_COLOR *destp = dest_line;
104     IM_GLIN(src, src_minx, src_maxx, y, src_line);
105     if (src->channels != want_channels)
106       IM_ADAPT_COLORS(want_channels, src->channels, src_line, width);
107     min_x = src_minx;
108     max_x = src_maxx;
109
110     while (min_x < max_x && srcp->channel[alphachan] == 0) {
111       ++min_x;
112       ++srcp;
113     }
114     while (max_x > min_x && src_line[max_x-1].channel[alphachan] == 0) {
115       --max_x;
116     }
117
118     if (max_x > min_x) {
119       i_img_dim work_left = tx + min_x - src_minx;
120       i_img_dim work_width = max_x - min_x;
121       ttx = work_left;
122       IM_GLIN(im, work_left, work_left + work_width, tty, dest_line);
123       
124       for(x = min_x; x < max_x; x++) {
125         src_alpha = srcp->channel[alphachan];
126         if (src_alpha) {
127           remains = IM_SAMPLE_MAX - src_alpha;
128           orig_alpha = destp->channel[targ_alpha_chan];
129           dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
130           
131           for (ch = 0; ch < im->channels-1; ++ch) {
132             IM_WORK_T samp = 
133               ( src_alpha * srcp->channel[ch]
134                 + remains * destp->channel[ch] * orig_alpha / IM_SAMPLE_MAX 
135                 ) / dest_alpha;
136             destp->channel[ch] = IM_LIMIT(samp);
137           }
138           /* dest's alpha */
139           destp->channel[targ_alpha_chan] = dest_alpha;
140         }
141         ++srcp;
142         ++destp;
143         ttx++;
144       }
145       IM_PLIN(im, work_left, work_left + work_width, tty, dest_line);
146     }
147     tty++;
148   }
149   myfree(dest_line);
150   myfree(src_line);
151 #/code
152   return 1;
153 }
154
155 /*
156 =item i_rubthru(C<im>, C<src>, C<tx>, C<ty>, C<src_minx>, C<src_miny>, C<src_maxx>, C<src_maxy>)
157
158 =category Image
159
160 Takes the sub image C<src>[C<src_minx>, C<src_maxx>)[C<src_miny>, C<src_maxy>)> and
161 overlays it at (C<tx>,C<ty>) on the image object.
162
163 The alpha channel of each pixel in C<src> is used to control how much
164 the existing color in C<im> is replaced, if it is 255 then the color
165 is completely replaced, if it is 0 then the original color is left
166 unmodified.
167
168 =cut
169 */
170
171 int
172 i_rubthru(i_img *im, i_img *src, i_img_dim tx, i_img_dim ty, i_img_dim src_minx, i_img_dim src_miny,
173           i_img_dim src_maxx, i_img_dim src_maxy) {
174   if (src_minx < 0) {
175     tx -= src_minx;
176     src_minx = 0;
177   }
178   if (src_miny < 0) {
179     ty -= src_miny;
180     src_miny = 0;
181   }
182   if (tx < 0) {
183     src_minx -= tx;
184     tx = 0;
185   }
186   if (ty < 0) {
187     src_miny -= ty;
188     ty = 0;
189   }
190   if (src_maxx > src->xsize) {
191     src_maxx = src->xsize;
192   }
193   if (src_maxy > src->ysize) {
194     src_maxy = src->ysize;
195   }
196   if (tx >= im->xsize || ty >= im->ysize
197       || src_minx >= src_maxx || src_miny >= src_maxy) {
198     i_clear_error();
199     /* just do nothing, attempting to rubthrough outside the target isn't
200        worth being an error */
201     return 1;
202   }
203
204   if (im->channels == 1 || im->channels == 3)
205     return rubthru_targ_noalpha(im, src, tx, ty, src_minx, src_miny, 
206                                 src_maxx, src_maxy);
207   else
208     return rubthru_targ_alpha(im, src, tx, ty, src_minx, src_miny,
209                               src_maxx, src_maxy);
210 }