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