1 // Port of GD copyResampled
2 #define floor2(exp) ((int) exp)
3 
4 void
5 image_downsize_gd(image *im)
6 {
7   int x, y;
8   float sy1, sy2, sx1, sx2;
9   int dstX = 0, dstY = 0, srcX = 0, srcY = 0;
10   float width_scale, height_scale;
11 
12   int dstW = im->target_width;
13   int dstH = im->target_height;
14   int srcW = im->width;
15   int srcH = im->height;
16 
17   if (im->height_padding) {
18     dstY = im->height_padding;
19     dstH = im->height_inner;
20   }
21 
22   if (im->width_padding) {
23     dstX = im->width_padding;
24     dstW = im->width_inner;
25   }
26 
27   width_scale = (float)srcW / dstW;
28   height_scale = (float)srcH / dstH;
29 
30   for (y = dstY; (y < dstY + dstH); y++) {
31     sy1 = (float)(y - dstY) * height_scale;
32     sy2 = (float)((y + 1) - dstY) * height_scale;
33 
34     for (x = dstX; (x < dstX + dstW); x++) {
35       float sx, sy;
36   	  float spixels = 0;
37   	  float red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
38 
39   	  if (!im->has_alpha)
40         alpha = 255.0;
41 
42   	  sx1 = (float)(x - dstX) * width_scale;
43   	  sx2 = (float)((x + 1) - dstX) * width_scale;
44   	  sy = sy1;
45 
46       //DEBUG_TRACE("sx1 %.2f, sx2 %.2f, sy1 %.2f, sy2 %.2f\n", sx1, sx2, sy1, sy2);
47 
48   	  do {
49         float yportion;
50 
51         //DEBUG_TRACE("  yportion(sy %.2f, sy1 %.2f, sy2 %.2f) = ", sy, sy1, sy2);
52         if (floor2(sy) == floor2(sy1)) {
53           yportion = 1.0 - (sy - floor2(sy));
54     		  if (yportion > sy2 - sy1) {
55             yportion = sy2 - sy1;
56     		  }
57     		  sy = floor2(sy);
58     		}
59     		else if (sy == floor2(sy2)) {
60           yportion = sy2 - floor2(sy2);
61         }
62         else {
63           yportion = 1.0;
64         }
65         //DEBUG_TRACE("%.2f\n", yportion);
66 
67         sx = sx1;
68 
69         do {
70           float xportion = 1.0;
71     		  float pcontribution;
72     		  pix p;
73 
74     		  //DEBUG_TRACE("  xportion(sx %.2f, sx1 %.2f, sx2 %.2f) = ", sx, sx1, sx2);
75     		  if (floor2(sx) == floor2(sx1)) {
76     	      xportion = 1.0 - (sx - floor2(sx));
77     	      if (xportion > sx2 - sx1)	{
78               xportion = sx2 - sx1;
79     			  }
80     		    sx = floor2(sx);
81     		  }
82     		  else if (sx == floor2(sx2)) {
83             xportion = sx2 - floor2(sx2);
84           }
85     		  else {
86     		    xportion = 1.0;
87     		  }
88     		  //DEBUG_TRACE("%.2f\n", xportion);
89 
90     		  pcontribution = xportion * yportion;
91 
92     		  p = get_pix(im, (int32_t)sx + srcX, (int32_t)sy + srcY);
93 
94     		  /*
95     		  DEBUG_TRACE("  merging with pix %d, %d: src %x (%d %d %d %d), pcontribution %.2f\n",
96             (int32_t)sx + srcX, (int32_t)sy + srcY,
97             p, COL_RED(p), COL_GREEN(p), COL_BLUE(p), COL_ALPHA(p), pcontribution);
98           */
99 
100     		  red   += COL_RED(p)   * pcontribution;
101     		  green += COL_GREEN(p) * pcontribution;
102     		  blue  += COL_BLUE(p)  * pcontribution;
103 
104     		  if (im->has_alpha)
105     		    alpha += COL_ALPHA(p) * pcontribution;
106 
107     		  spixels += pcontribution;
108     		  sx += 1.0;
109     		} while (sx < sx2);
110 
111     		sy += 1.0;
112       } while (sy < sy2);
113 
114       if (spixels != 0.0) {
115         //DEBUG_TRACE("  rgba (%.2f %.2f %.2f %.2f) spixels %.2f\n", red, green, blue, alpha, spixels);
116         spixels = 1 / spixels;
117 	      red   *= spixels;
118 	      green *= spixels;
119 	      blue  *= spixels;
120 
121 	      if (im->has_alpha)
122 	        alpha *= spixels;
123 	    }
124 
125 	    /* Clamping to allow for rounding errors above */
126       if (red > 255.0)   red = 255.0;
127       if (green > 255.0) green = 255.0;
128       if (blue > 255.0)  blue = 255.0;
129       if (im->has_alpha && alpha > 255.0) alpha = 255.0;
130 
131       /*
132       DEBUG_TRACE("  -> %d, %d %x (%d %d %d %d)\n",
133         x, y, COL_FULL((int)red, (int)green, (int)blue, (int)alpha),
134         (int)red, (int)green, (int)blue, (int)alpha);
135       */
136 
137       if (im->orientation != ORIENTATION_NORMAL) {
138         int ox, oy; // new destination pixel coordinates after rotating
139 
140         image_get_rotated_coords(im, x, y, &ox, &oy);
141 
142         if (im->orientation >= 5) {
143           // 90 and 270 rotations, width/height are swapped so we have to use alternate put_pix method
144           put_pix_rotated(
145             im, ox, oy, im->target_height,
146             COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
147           );
148         }
149         else {
150           put_pix(
151             im, ox, oy,
152             COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
153           );
154         }
155       }
156       else {
157         put_pix(
158           im, x, y,
159           COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
160         );
161       }
162     }
163 	}
164 }
165 
166 void
167 image_downsize_gd_fixed_point(image *im)
168 {
169   int x, y;
170   fixed_t sy1, sy2, sx1, sx2;
171   int dstX = 0, dstY = 0, srcX = 0, srcY = 0;
172   fixed_t width_scale, height_scale;
173 
174   int dstW = im->target_width;
175   int dstH = im->target_height;
176   int srcW = im->width;
177   int srcH = im->height;
178 
179   if (im->height_padding) {
180     dstY = im->height_padding;
181     dstH = im->height_inner;
182   }
183 
184   if (im->width_padding) {
185     dstX = im->width_padding;
186     dstW = im->width_inner;
187   }
188 
189   width_scale = fixed_div(int_to_fixed(srcW), int_to_fixed(dstW));
190   height_scale = fixed_div(int_to_fixed(srcH), int_to_fixed(dstH));
191 
192   for (y = dstY; (y < dstY + dstH); y++) {
193     sy1 = fixed_mul(int_to_fixed(y - dstY), height_scale);
194     sy2 = fixed_mul(int_to_fixed((y + 1) - dstY), height_scale);
195 
196     for (x = dstX; (x < dstX + dstW); x++) {
197       fixed_t sx, sy;
198   	  fixed_t spixels = 0;
199   	  fixed_t red = 0, green = 0, blue = 0, alpha = 0;
200 
201   	  if (!im->has_alpha)
202         alpha = FIXED_255;
203 
204       sx1 = fixed_mul(int_to_fixed(x - dstX), width_scale);
205       sx2 = fixed_mul(int_to_fixed((x + 1) - dstX), width_scale);
206   	  sy = sy1;
207 
208   	  /*
209       DEBUG_TRACE("sx1 %f, sx2 %f, sy1 %f, sy2 %f\n",
210         fixed_to_float(sx1), fixed_to_float(sx2), fixed_to_float(sy1), fixed_to_float(sy2));
211       */
212 
213   	  do {
214         fixed_t yportion;
215 
216         //DEBUG_TRACE("  yportion(sy %f, sy1 %f, sy2 %f) = ", fixed_to_float(sy), fixed_to_float(sy1), fixed_to_float(sy2));
217 
218         if (fixed_floor(sy) == fixed_floor(sy1)) {
219           yportion = FIXED_1 - (sy - fixed_floor(sy));
220     		  if (yportion > sy2 - sy1) {
221             yportion = sy2 - sy1;
222     		  }
223     		  sy = fixed_floor(sy);
224     		}
225     		else if (sy == fixed_floor(sy2)) {
226           yportion = sy2 - fixed_floor(sy2);
227         }
228         else {
229           yportion = FIXED_1;
230         }
231 
232         //DEBUG_TRACE("%f\n", fixed_to_float(yportion));
233 
234         sx = sx1;
235 
236         do {
237           fixed_t xportion;
238     		  fixed_t pcontribution;
239     		  pix p;
240 
241     		  //DEBUG_TRACE("  xportion(sx %f, sx1 %f, sx2 %f) = ", fixed_to_float(sx), fixed_to_float(sx1), fixed_to_float(sx2));
242 
243     		  if (fixed_floor(sx) == fixed_floor(sx1)) {
244     	      xportion = FIXED_1 - (sx - fixed_floor(sx));
245     	      if (xportion > sx2 - sx1)	{
246               xportion = sx2 - sx1;
247     			  }
248     		    sx = fixed_floor(sx);
249     		  }
250     		  else if (sx == fixed_floor(sx2)) {
251             xportion = sx2 - fixed_floor(sx2);
252           }
253     		  else {
254     		    xportion = FIXED_1;
255     		  }
256 
257     		  //DEBUG_TRACE("%f\n", fixed_to_float(xportion));
258 
259     		  pcontribution = fixed_mul(xportion, yportion);
260 
261     		  p = get_pix(im, fixed_to_int(sx + srcX), fixed_to_int(sy + srcY));
262 
263     		  /*
264     		  DEBUG_TRACE("  merging with pix %d, %d: src %x (%d %d %d %d), pcontribution %f\n",
265             fixed_to_int(sx + srcX), fixed_to_int(sy + srcY),
266             p, COL_RED(p), COL_GREEN(p), COL_BLUE(p), COL_ALPHA(p), fixed_to_float(pcontribution));
267           */
268 
269           red   += fixed_mul(int_to_fixed(COL_RED(p)), pcontribution);
270           green += fixed_mul(int_to_fixed(COL_GREEN(p)), pcontribution);
271     		  blue  += fixed_mul(int_to_fixed(COL_BLUE(p)), pcontribution);
272 
273     		  if (im->has_alpha)
274     		    alpha += fixed_mul(int_to_fixed(COL_ALPHA(p)), pcontribution);
275 
276     		  spixels += pcontribution;
277     		  sx += FIXED_1;
278     		} while (sx < sx2);
279 
280         sy += FIXED_1;
281       } while (sy < sy2);
282 
283   	  // If rgba get too large for the fixed-point representation, fallback to the floating point routine
284 		  // This should only happen with very large images
285 		  if (red < 0 || green < 0 || blue < 0 || alpha < 0) {
286         warn("fixed-point overflow: %d %d %d %d\n", red, green, blue, alpha);
287         return image_downsize_gd(im);
288       }
289 
290       if (spixels != 0) {
291         /*
292         DEBUG_TRACE("  rgba (%f %f %f %f) spixels %f\n",
293           fixed_to_float(red), fixed_to_float(green), fixed_to_float(blue), fixed_to_float(alpha), fixed_to_float(spixels));
294         */
295 
296         spixels = fixed_div(FIXED_1, spixels);
297 
298         red   = fixed_mul(red, spixels);
299         green = fixed_mul(green, spixels);
300         blue  = fixed_mul(blue, spixels);
301 
302         if (im->has_alpha)
303           alpha = fixed_mul(alpha, spixels);
304 	    }
305 
306 	    /* Clamping to allow for rounding errors above */
307       if (red > FIXED_255)   red = FIXED_255;
308       if (green > FIXED_255) green = FIXED_255;
309       if (blue > FIXED_255)  blue = FIXED_255;
310       if (im->has_alpha && alpha > FIXED_255) alpha = FIXED_255;
311 
312       /*
313       DEBUG_TRACE("  -> %d, %d %x (%d %d %d %d)\n",
314         x, y, COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha)),
315         fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha));
316       */
317 
318       if (im->orientation != ORIENTATION_NORMAL) {
319         int ox, oy; // new destination pixel coordinates after rotating
320 
321         image_get_rotated_coords(im, x, y, &ox, &oy);
322 
323         if (im->orientation >= 5) {
324           // 90 and 270 rotations, width/height are swapped so we have to use alternate put_pix method
325           put_pix_rotated(
326             im, ox, oy, im->target_height,
327             COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
328           );
329         }
330         else {
331           put_pix(
332             im, ox, oy,
333             COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
334           );
335         }
336       }
337       else {
338         put_pix(
339           im, x, y,
340           COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
341         );
342       }
343 	  }
344 	}
345 }
346