// Port of GD copyResampled
#define floor2(exp) ((int) exp)
void
image_downsize_gd(image *im)
{
int x, y;
float sy1, sy2, sx1, sx2;
int dstX = 0, dstY = 0, srcX = 0, srcY = 0;
float width_scale, height_scale;
int dstW = im->target_width;
int dstH = im->target_height;
int srcW = im->width;
int srcH = im->height;
if (im->height_padding) {
dstY = im->height_padding;
dstH = im->height_inner;
}
if (im->width_padding) {
dstX = im->width_padding;
dstW = im->width_inner;
}
width_scale = (float)srcW / dstW;
height_scale = (float)srcH / dstH;
for (y = dstY; (y < dstY + dstH); y++) {
sy1 = (float)(y - dstY) * height_scale;
sy2 = (float)((y + 1) - dstY) * height_scale;
for (x = dstX; (x < dstX + dstW); x++) {
float sx, sy;
float spixels = 0;
float red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
if (!im->has_alpha)
alpha = 255.0;
sx1 = (float)(x - dstX) * width_scale;
sx2 = (float)((x + 1) - dstX) * width_scale;
sy = sy1;
//DEBUG_TRACE("sx1 %.2f, sx2 %.2f, sy1 %.2f, sy2 %.2f\n", sx1, sx2, sy1, sy2);
do {
float yportion;
//DEBUG_TRACE(" yportion(sy %.2f, sy1 %.2f, sy2 %.2f) = ", sy, sy1, sy2);
if (floor2(sy) == floor2(sy1)) {
yportion = 1.0 - (sy - floor2(sy));
if (yportion > sy2 - sy1) {
yportion = sy2 - sy1;
}
sy = floor2(sy);
}
else if (sy == floor2(sy2)) {
yportion = sy2 - floor2(sy2);
}
else {
yportion = 1.0;
}
//DEBUG_TRACE("%.2f\n", yportion);
sx = sx1;
do {
float xportion = 1.0;
float pcontribution;
pix p;
//DEBUG_TRACE(" xportion(sx %.2f, sx1 %.2f, sx2 %.2f) = ", sx, sx1, sx2);
if (floor2(sx) == floor2(sx1)) {
xportion = 1.0 - (sx - floor2(sx));
if (xportion > sx2 - sx1) {
xportion = sx2 - sx1;
}
sx = floor2(sx);
}
else if (sx == floor2(sx2)) {
xportion = sx2 - floor2(sx2);
}
else {
xportion = 1.0;
}
//DEBUG_TRACE("%.2f\n", xportion);
pcontribution = xportion * yportion;
p = get_pix(im, (int32_t)sx + srcX, (int32_t)sy + srcY);
/*
DEBUG_TRACE(" merging with pix %d, %d: src %x (%d %d %d %d), pcontribution %.2f\n",
(int32_t)sx + srcX, (int32_t)sy + srcY,
p, COL_RED(p), COL_GREEN(p), COL_BLUE(p), COL_ALPHA(p), pcontribution);
*/
red += COL_RED(p) * pcontribution;
green += COL_GREEN(p) * pcontribution;
blue += COL_BLUE(p) * pcontribution;
if (im->has_alpha)
alpha += COL_ALPHA(p) * pcontribution;
spixels += pcontribution;
sx += 1.0;
} while (sx < sx2);
sy += 1.0;
} while (sy < sy2);
if (spixels != 0.0) {
//DEBUG_TRACE(" rgba (%.2f %.2f %.2f %.2f) spixels %.2f\n", red, green, blue, alpha, spixels);
spixels = 1 / spixels;
red *= spixels;
green *= spixels;
blue *= spixels;
if (im->has_alpha)
alpha *= spixels;
}
/* Clamping to allow for rounding errors above */
if (red > 255.0) red = 255.0;
if (green > 255.0) green = 255.0;
if (blue > 255.0) blue = 255.0;
if (im->has_alpha && alpha > 255.0) alpha = 255.0;
/*
DEBUG_TRACE(" -> %d, %d %x (%d %d %d %d)\n",
x, y, COL_FULL((int)red, (int)green, (int)blue, (int)alpha),
(int)red, (int)green, (int)blue, (int)alpha);
*/
if (im->orientation != ORIENTATION_NORMAL) {
int ox, oy; // new destination pixel coordinates after rotating
image_get_rotated_coords(im, x, y, &ox, &oy);
if (im->orientation >= 5) {
// 90 and 270 rotations, width/height are swapped so we have to use alternate put_pix method
put_pix_rotated(
im, ox, oy, im->target_height,
COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
);
}
else {
put_pix(
im, ox, oy,
COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
);
}
}
else {
put_pix(
im, x, y,
COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
);
}
}
}
}
void
image_downsize_gd_fixed_point(image *im)
{
int x, y;
fixed_t sy1, sy2, sx1, sx2;
int dstX = 0, dstY = 0, srcX = 0, srcY = 0;
fixed_t width_scale, height_scale;
int dstW = im->target_width;
int dstH = im->target_height;
int srcW = im->width;
int srcH = im->height;
if (im->height_padding) {
dstY = im->height_padding;
dstH = im->height_inner;
}
if (im->width_padding) {
dstX = im->width_padding;
dstW = im->width_inner;
}
width_scale = fixed_div(int_to_fixed(srcW), int_to_fixed(dstW));
height_scale = fixed_div(int_to_fixed(srcH), int_to_fixed(dstH));
for (y = dstY; (y < dstY + dstH); y++) {
sy1 = fixed_mul(int_to_fixed(y - dstY), height_scale);
sy2 = fixed_mul(int_to_fixed((y + 1) - dstY), height_scale);
for (x = dstX; (x < dstX + dstW); x++) {
fixed_t sx, sy;
fixed_t spixels = 0;
fixed_t red = 0, green = 0, blue = 0, alpha = 0;
if (!im->has_alpha)
alpha = FIXED_255;
sx1 = fixed_mul(int_to_fixed(x - dstX), width_scale);
sx2 = fixed_mul(int_to_fixed((x + 1) - dstX), width_scale);
sy = sy1;
/*
DEBUG_TRACE("sx1 %f, sx2 %f, sy1 %f, sy2 %f\n",
fixed_to_float(sx1), fixed_to_float(sx2), fixed_to_float(sy1), fixed_to_float(sy2));
*/
do {
fixed_t yportion;
//DEBUG_TRACE(" yportion(sy %f, sy1 %f, sy2 %f) = ", fixed_to_float(sy), fixed_to_float(sy1), fixed_to_float(sy2));
if (fixed_floor(sy) == fixed_floor(sy1)) {
yportion = FIXED_1 - (sy - fixed_floor(sy));
if (yportion > sy2 - sy1) {
yportion = sy2 - sy1;
}
sy = fixed_floor(sy);
}
else if (sy == fixed_floor(sy2)) {
yportion = sy2 - fixed_floor(sy2);
}
else {
yportion = FIXED_1;
}
//DEBUG_TRACE("%f\n", fixed_to_float(yportion));
sx = sx1;
do {
fixed_t xportion;
fixed_t pcontribution;
pix p;
//DEBUG_TRACE(" xportion(sx %f, sx1 %f, sx2 %f) = ", fixed_to_float(sx), fixed_to_float(sx1), fixed_to_float(sx2));
if (fixed_floor(sx) == fixed_floor(sx1)) {
xportion = FIXED_1 - (sx - fixed_floor(sx));
if (xportion > sx2 - sx1) {
xportion = sx2 - sx1;
}
sx = fixed_floor(sx);
}
else if (sx == fixed_floor(sx2)) {
xportion = sx2 - fixed_floor(sx2);
}
else {
xportion = FIXED_1;
}
//DEBUG_TRACE("%f\n", fixed_to_float(xportion));
pcontribution = fixed_mul(xportion, yportion);
p = get_pix(im, fixed_to_int(sx + srcX), fixed_to_int(sy + srcY));
/*
DEBUG_TRACE(" merging with pix %d, %d: src %x (%d %d %d %d), pcontribution %f\n",
fixed_to_int(sx + srcX), fixed_to_int(sy + srcY),
p, COL_RED(p), COL_GREEN(p), COL_BLUE(p), COL_ALPHA(p), fixed_to_float(pcontribution));
*/
red += fixed_mul(int_to_fixed(COL_RED(p)), pcontribution);
green += fixed_mul(int_to_fixed(COL_GREEN(p)), pcontribution);
blue += fixed_mul(int_to_fixed(COL_BLUE(p)), pcontribution);
if (im->has_alpha)
alpha += fixed_mul(int_to_fixed(COL_ALPHA(p)), pcontribution);
spixels += pcontribution;
sx += FIXED_1;
} while (sx < sx2);
sy += FIXED_1;
} while (sy < sy2);
// If rgba get too large for the fixed-point representation, fallback to the floating point routine
// This should only happen with very large images
if (red < 0 || green < 0 || blue < 0 || alpha < 0) {
warn("fixed-point overflow: %d %d %d %d\n", red, green, blue, alpha);
return image_downsize_gd(im);
}
if (spixels != 0) {
/*
DEBUG_TRACE(" rgba (%f %f %f %f) spixels %f\n",
fixed_to_float(red), fixed_to_float(green), fixed_to_float(blue), fixed_to_float(alpha), fixed_to_float(spixels));
*/
spixels = fixed_div(FIXED_1, spixels);
red = fixed_mul(red, spixels);
green = fixed_mul(green, spixels);
blue = fixed_mul(blue, spixels);
if (im->has_alpha)
alpha = fixed_mul(alpha, spixels);
}
/* Clamping to allow for rounding errors above */
if (red > FIXED_255) red = FIXED_255;
if (green > FIXED_255) green = FIXED_255;
if (blue > FIXED_255) blue = FIXED_255;
if (im->has_alpha && alpha > FIXED_255) alpha = FIXED_255;
/*
DEBUG_TRACE(" -> %d, %d %x (%d %d %d %d)\n",
x, y, COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha)),
fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha));
*/
if (im->orientation != ORIENTATION_NORMAL) {
int ox, oy; // new destination pixel coordinates after rotating
image_get_rotated_coords(im, x, y, &ox, &oy);
if (im->orientation >= 5) {
// 90 and 270 rotations, width/height are swapped so we have to use alternate put_pix method
put_pix_rotated(
im, ox, oy, im->target_height,
COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
);
}
else {
put_pix(
im, ox, oy,
COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
);
}
}
else {
put_pix(
im, x, y,
COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
);
}
}
}
}