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