From: Tony Cook Date: Thu, 5 Jan 2012 23:39:39 +0000 (+1100) Subject: rotate.c -> rotate.im transition X-Git-Tag: v0.88~13 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/999f4294d1b765b04c08c37630eccb4d3239ab14 rotate.c -> rotate.im transition convert to use the preprocessor to reduce code duplication --- diff --git a/.gitignore b/.gitignore index 27cc7a84..ba3c3655 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ imconfig.h paste.c render.c paste.c +rotate.c rubthru.c scale.c diff --git a/MANIFEST b/MANIFEST index 74e493b7..b3d91dab 100644 --- a/MANIFEST +++ b/MANIFEST @@ -238,7 +238,7 @@ regmach.h regops.perl render.im rendert.h Buffer rendering engine types -rotate.c +rotate.im rubthru.im samples/align-string.pl Demonstrate align_string method. samples/anaglyph.pl diff --git a/rotate.c b/rotate.c deleted file mode 100644 index 90cf5912..00000000 --- a/rotate.c +++ /dev/null @@ -1,552 +0,0 @@ -/* -=head1 NAME - - rotate.c - implements image rotations - -=head1 SYNOPSIS - - i_img *i_rotate90(i_img *src, int degrees) - -=head1 DESCRIPTION - -Implements basic 90 degree rotations of an image. - -Other rotations will be added as tuits become available. - -=cut -*/ - -#include "imager.h" -#include "imageri.h" -#include /* for floor() */ - -i_img *i_rotate90(i_img *src, int degrees) { - i_img *targ; - i_img_dim x, y; - - i_clear_error(); - - if (degrees == 180) { - /* essentially the same as flipxy(..., 2) except that it's not - done in place */ - targ = i_sametype(src, src->xsize, src->ysize); - if (src->type == i_direct_type) { - if (src->bits == i_8_bits) { - i_color *vals = mymalloc(src->xsize * sizeof(i_color)); - for (y = 0; y < src->ysize; ++y) { - i_color tmp; - i_glin(src, 0, src->xsize, y, vals); - for (x = 0; x < src->xsize/2; ++x) { - tmp = vals[x]; - vals[x] = vals[src->xsize - x - 1]; - vals[src->xsize - x - 1] = tmp; - } - i_plin(targ, 0, src->xsize, src->ysize - y - 1, vals); - } - myfree(vals); - } - else { - i_fcolor *vals = mymalloc(src->xsize * sizeof(i_fcolor)); - for (y = 0; y < src->ysize; ++y) { - i_fcolor tmp; - i_glinf(src, 0, src->xsize, y, vals); - for (x = 0; x < src->xsize/2; ++x) { - tmp = vals[x]; - vals[x] = vals[src->xsize - x - 1]; - vals[src->xsize - x - 1] = tmp; - } - i_plinf(targ, 0, src->xsize, src->ysize - y - 1, vals); - } - myfree(vals); - } - } - else { - i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx)); - - for (y = 0; y < src->ysize; ++y) { - i_palidx tmp; - i_gpal(src, 0, src->xsize, y, vals); - for (x = 0; x < src->xsize/2; ++x) { - tmp = vals[x]; - vals[x] = vals[src->xsize - x - 1]; - vals[src->xsize - x - 1] = tmp; - } - i_ppal(targ, 0, src->xsize, src->ysize - y - 1, vals); - } - - myfree(vals); - } - - return targ; - } - else if (degrees == 270 || degrees == 90) { - i_img_dim tx, txstart, txinc; - i_img_dim ty, tystart, tyinc; - - if (degrees == 270) { - txstart = 0; - txinc = 1; - tystart = src->xsize-1; - tyinc = -1; - } - else { - txstart = src->ysize-1; - txinc = -1; - tystart = 0; - tyinc = 1; - } - targ = i_sametype(src, src->ysize, src->xsize); - if (src->type == i_direct_type) { - if (src->bits == i_8_bits) { - i_color *vals = mymalloc(src->xsize * sizeof(i_color)); - - tx = txstart; - for (y = 0; y < src->ysize; ++y) { - i_glin(src, 0, src->xsize, y, vals); - ty = tystart; - for (x = 0; x < src->xsize; ++x) { - i_ppix(targ, tx, ty, vals+x); - ty += tyinc; - } - tx += txinc; - } - myfree(vals); - } - else { - i_fcolor *vals = mymalloc(src->xsize * sizeof(i_fcolor)); - - tx = txstart; - for (y = 0; y < src->ysize; ++y) { - i_glinf(src, 0, src->xsize, y, vals); - ty = tystart; - for (x = 0; x < src->xsize; ++x) { - i_ppixf(targ, tx, ty, vals+x); - ty += tyinc; - } - tx += txinc; - } - myfree(vals); - } - } - else { - i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx)); - - tx = txstart; - for (y = 0; y < src->ysize; ++y) { - i_gpal(src, 0, src->xsize, y, vals); - ty = tystart; - for (x = 0; x < src->xsize; ++x) { - i_ppal(targ, tx, tx+1, ty, vals+x); - ty += tyinc; - } - tx += txinc; - } - myfree(vals); - } - return targ; - } - else { - i_push_error(0, "i_rotate90() only rotates at 90, 180, or 270 degrees"); - return NULL; - } -} - -/* linear interpolation */ -static i_color interp_i_color(i_color before, i_color after, double pos, - int channels) { - i_color out; - int ch; - - pos -= floor(pos); - if (channels == 1 || channels == 3) { - for (ch = 0; ch < channels; ++ch) - out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; - } - else { - int total_cover = (1-pos) * before.channel[channels-1] - + pos * after.channel[channels-1]; - - total_cover = I_LIMIT_8(total_cover); - if (total_cover) { - double before_alpha = before.channel[channels-1] / 255.0; - double after_alpha = after.channel[channels-1] / 255.0; - double total_alpha = before_alpha * (1-pos) + after_alpha * pos; - - for (ch = 0; ch < channels-1; ++ch) { - int out_level = ((1-pos) * before.channel[ch] * before_alpha + - pos * after.channel[ch] * after_alpha + 0.5) / total_alpha; - - out.channel[ch] = I_LIMIT_8(out_level); - } - } - - out.channel[channels-1] = total_cover; - } - - return out; -} - -/* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */ -/* linear interpolation */ -static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos, - int channels) { - i_fcolor out; - int ch; - - pos -= floor(pos); - if (channels == 1 || channels == 3) { - for (ch = 0; ch < channels; ++ch) - out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; - } - else { - double total_cover = (1-pos) * before.channel[channels-1] - + pos * after.channel[channels-1]; - - total_cover = I_LIMIT_DOUBLE(total_cover); - if (total_cover) { - double before_alpha = before.channel[channels-1]; - double after_alpha = after.channel[channels-1]; - double total_alpha = before_alpha * (1-pos) + after_alpha * pos; - - for (ch = 0; ch < channels-1; ++ch) { - double out_level = ((1-pos) * before.channel[ch] * before_alpha + - pos * after.channel[ch] * after_alpha) / total_alpha; - - out.channel[ch] = I_LIMIT_DOUBLE(out_level); - } - } - - out.channel[channels-1] = total_cover; - } - - return out; -} - -i_img *i_matrix_transform_bg(i_img *src, i_img_dim xsize, i_img_dim ysize, const double *matrix, - const i_color *backp, const i_fcolor *fbackp) { - i_img *result = i_sametype(src, xsize, ysize); - i_img_dim x, y; - int ch; - i_img_dim i, j; - double sx, sy, sz; - - if (src->type == i_direct_type) { - if (src->bits == i_8_bits) { - i_color *vals = mymalloc(xsize * sizeof(i_color)); - i_color back; - i_fsample_t fsamp; - - if (backp) { - back = *backp; - } - else if (fbackp) { - for (ch = 0; ch < src->channels; ++ch) { - fsamp = fbackp->channel[ch]; - back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255; - } - } - else { - for (ch = 0; ch < src->channels; ++ch) - back.channel[ch] = 0; - } - - for (y = 0; y < ysize; ++y) { - for (x = 0; x < xsize; ++x) { - /* dividing by sz gives us the ability to do perspective - transforms */ - sz = x * matrix[6] + y * matrix[7] + matrix[8]; - if (fabs(sz) > 0.0000001) { - sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz; - sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz; - } - else { - sx = sy = 0; - } - - /* anything outside these ranges is either a broken co-ordinate - or outside the source */ - if (fabs(sz) > 0.0000001 - && sx >= -1 && sx < src->xsize - && sy >= -1 && sy < src->ysize) { - - if (sx != (i_img_dim)sx) { - if (sy != (i_img_dim)sy) { - i_color c[2][2]; - i_color ci2[2]; - for (i = 0; i < 2; ++i) - for (j = 0; j < 2; ++j) - if (i_gpix(src, floor(sx)+i, floor(sy)+j, &c[j][i])) - c[j][i] = back; - for (j = 0; j < 2; ++j) - ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels); - vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels); - } - else { - i_color ci2[2]; - for (i = 0; i < 2; ++i) - if (i_gpix(src, floor(sx)+i, sy, ci2+i)) - ci2[i] = back; - vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels); - } - } - else { - if (sy != (i_img_dim)sy) { - i_color ci2[2]; - for (i = 0; i < 2; ++i) - if (i_gpix(src, sx, floor(sy)+i, ci2+i)) - ci2[i] = back; - vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels); - } - else { - /* all the world's an integer */ - if (i_gpix(src, sx, sy, vals+x)) - vals[x] = back; - } - } - } - else { - vals[x] = back; - } - } - i_plin(result, 0, xsize, y, vals); - } - myfree(vals); - } - else { - i_fcolor *vals = mymalloc(xsize * sizeof(i_fcolor)); - i_fcolor back; - - if (fbackp) { - back = *fbackp; - } - else if (backp) { - for (ch = 0; ch < src->channels; ++ch) - back.channel[ch] = backp->channel[ch] / 255.0; - } - else { - for (ch = 0; ch < src->channels; ++ch) - back.channel[ch] = 0; - } - - for (y = 0; y < ysize; ++y) { - for (x = 0; x < xsize; ++x) { - /* dividing by sz gives us the ability to do perspective - transforms */ - sz = x * matrix[6] + y * matrix[7] + matrix[8]; - if (fabs(sz) > 0.0000001) { - sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz; - sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz; - } - else { - sx = sy = 0; - } - - /* anything outside these ranges is either a broken co-ordinate - or outside the source */ - if (fabs(sz) > 0.0000001 - && sx >= -1 && sx < src->xsize - && sy >= -1 && sy < src->ysize) { - - if (sx != (i_img_dim)sx) { - if (sy != (i_img_dim)sy) { - i_fcolor c[2][2]; - i_fcolor ci2[2]; - for (i = 0; i < 2; ++i) - for (j = 0; j < 2; ++j) - if (i_gpixf(src, floor(sx)+i, floor(sy)+j, &c[j][i])) - c[j][i] = back; - for (j = 0; j < 2; ++j) - ci2[j] = interp_i_fcolor(c[j][0], c[j][1], sx, src->channels); - vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels); - } - else { - i_fcolor ci2[2]; - for (i = 0; i < 2; ++i) - if (i_gpixf(src, floor(sx)+i, sy, ci2+i)) - ci2[i] = back; - vals[x] = interp_i_fcolor(ci2[0], ci2[1], sx, src->channels); - } - } - else { - if (sy != (i_img_dim)sy) { - i_fcolor ci2[2]; - for (i = 0; i < 2; ++i) - if (i_gpixf(src, sx, floor(sy)+i, ci2+i)) - ci2[i] = back; - vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels); - } - else { - /* all the world's an integer */ - if (i_gpixf(src, sx, sy, vals+x)) - vals[x] = back; - } - } - } - else { - vals[x] = back; - } - } - i_plinf(result, 0, xsize, y, vals); - } - myfree(vals); - } - } - else { - /* don't interpolate for a palette based image */ - i_palidx *vals = mymalloc(xsize * sizeof(i_palidx)); - i_palidx back = 0; - i_color min; - int minval = 256 * 4; - i_img_dim ix, iy; - i_color want_back; - i_fsample_t fsamp; - - if (backp) { - want_back = *backp; - } - else if (fbackp) { - for (ch = 0; ch < src->channels; ++ch) { - fsamp = fbackp->channel[ch]; - want_back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255; - } - } - else { - for (ch = 0; ch < src->channels; ++ch) - want_back.channel[ch] = 0; - } - - /* find the closest color */ - for (i = 0; i < i_colorcount(src); ++i) { - i_color temp; - int tempval; - i_getcolors(src, i, &temp, 1); - tempval = 0; - for (ch = 0; ch < src->channels; ++ch) { - tempval += abs(want_back.channel[ch] - temp.channel[ch]); - } - if (tempval < minval) { - back = i; - min = temp; - minval = tempval; - } - } - - for (y = 0; y < ysize; ++y) { - for (x = 0; x < xsize; ++x) { - /* dividing by sz gives us the ability to do perspective - transforms */ - sz = x * matrix[6] + y * matrix[7] + matrix[8]; - if (abs(sz) > 0.0000001) { - sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz; - sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz; - } - else { - sx = sy = 0; - } - - /* anything outside these ranges is either a broken co-ordinate - or outside the source */ - if (abs(sz) > 0.0000001 - && sx >= -0.5 && sx < src->xsize-0.5 - && sy >= -0.5 && sy < src->ysize-0.5) { - - /* all the world's an integer */ - ix = (i_img_dim)(sx+0.5); - iy = (i_img_dim)(sy+0.5); - if (!i_gpal(src, ix, ix+1, iy, vals+x)) - vals[i] = back; - } - else { - vals[x] = back; - } - } - i_ppal(result, 0, xsize, y, vals); - } - myfree(vals); - } - - return result; -} - -i_img *i_matrix_transform(i_img *src, i_img_dim xsize, i_img_dim ysize, const double *matrix) { - return i_matrix_transform_bg(src, xsize, ysize, matrix, NULL, NULL); -} - -static void -i_matrix_mult(double *dest, const double *left, const double *right) { - int i, j, k; - double accum; - - for (i = 0; i < 3; ++i) { - for (j = 0; j < 3; ++j) { - accum = 0.0; - for (k = 0; k < 3; ++k) { - accum += left[3*i+k] * right[3*k+j]; - } - dest[3*i+j] = accum; - } - } -} - -i_img *i_rotate_exact_bg(i_img *src, double amount, - const i_color *backp, const i_fcolor *fbackp) { - double xlate1[9] = { 0 }; - double rotate[9]; - double xlate2[9] = { 0 }; - double temp[9], matrix[9]; - i_img_dim x1, x2, y1, y2, newxsize, newysize; - - /* first translate the centre of the image to (0,0) */ - xlate1[0] = 1; - xlate1[2] = src->xsize/2.0; - xlate1[4] = 1; - xlate1[5] = src->ysize/2.0; - xlate1[8] = 1; - - /* rotate around (0.0) */ - rotate[0] = cos(amount); - rotate[1] = sin(amount); - rotate[2] = 0; - rotate[3] = -rotate[1]; - rotate[4] = rotate[0]; - rotate[5] = 0; - rotate[6] = 0; - rotate[7] = 0; - rotate[8] = 1; - - x1 = ceil(i_abs(src->xsize * rotate[0] + src->ysize * rotate[1])); - x2 = ceil(i_abs(src->xsize * rotate[0] - src->ysize * rotate[1])); - y1 = ceil(i_abs(src->xsize * rotate[3] + src->ysize * rotate[4])); - y2 = ceil(i_abs(src->xsize * rotate[3] - src->ysize * rotate[4])); - newxsize = x1 > x2 ? x1 : x2; - newysize = y1 > y2 ? y1 : y2; - /* translate the centre back to the center of the image */ - xlate2[0] = 1; - xlate2[2] = -newxsize/2.0; - xlate2[4] = 1; - xlate2[5] = -newysize/2.0; - xlate2[8] = 1; - i_matrix_mult(temp, xlate1, rotate); - i_matrix_mult(matrix, temp, xlate2); - - return i_matrix_transform_bg(src, newxsize, newysize, matrix, backp, fbackp); -} - -i_img *i_rotate_exact(i_img *src, double amount) { - return i_rotate_exact_bg(src, amount, NULL, NULL); -} - - -/* -=back - -=head1 AUTHOR - -Tony Cook - -=head1 SEE ALSO - -Imager(3) - -=cut -*/ diff --git a/rotate.im b/rotate.im new file mode 100644 index 00000000..e6d1d114 --- /dev/null +++ b/rotate.im @@ -0,0 +1,457 @@ +/* +=head1 NAME + + rotate.im - implements image rotations + +=head1 SYNOPSIS + + i_img *i_rotate90(i_img *src, int degrees) + +=head1 DESCRIPTION + +Implements basic 90 degree rotations of an image. + +Other rotations will be added as tuits become available. + +=cut +*/ + +#include "imager.h" +#include "imageri.h" +#include /* for floor() */ + +i_img *i_rotate90(i_img *src, int degrees) { + i_img *targ; + i_img_dim x, y; + + i_clear_error(); + + if (degrees == 180) { + /* essentially the same as flipxy(..., 2) except that it's not + done in place */ + targ = i_sametype(src, src->xsize, src->ysize); + if (src->type == i_direct_type) { +#code src->bits <= 8 + IM_COLOR *vals = mymalloc(src->xsize * sizeof(IM_COLOR)); + for (y = 0; y < src->ysize; ++y) { + IM_COLOR tmp; + IM_GLIN(src, 0, src->xsize, y, vals); + for (x = 0; x < src->xsize/2; ++x) { + tmp = vals[x]; + vals[x] = vals[src->xsize - x - 1]; + vals[src->xsize - x - 1] = tmp; + } + IM_PLIN(targ, 0, src->xsize, src->ysize - y - 1, vals); + } + myfree(vals); +#/code + } + else { + i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx)); + + for (y = 0; y < src->ysize; ++y) { + i_palidx tmp; + i_gpal(src, 0, src->xsize, y, vals); + for (x = 0; x < src->xsize/2; ++x) { + tmp = vals[x]; + vals[x] = vals[src->xsize - x - 1]; + vals[src->xsize - x - 1] = tmp; + } + i_ppal(targ, 0, src->xsize, src->ysize - y - 1, vals); + } + + myfree(vals); + } + + return targ; + } + else if (degrees == 270 || degrees == 90) { + i_img_dim tx, txstart, txinc; + i_img_dim ty, tystart, tyinc; + + if (degrees == 270) { + txstart = 0; + txinc = 1; + tystart = src->xsize-1; + tyinc = -1; + } + else { + txstart = src->ysize-1; + txinc = -1; + tystart = 0; + tyinc = 1; + } + targ = i_sametype(src, src->ysize, src->xsize); + if (src->type == i_direct_type) { +#code src->bits <= 8 + IM_COLOR *vals = mymalloc(src->xsize * sizeof(IM_COLOR)); + + tx = txstart; + for (y = 0; y < src->ysize; ++y) { + IM_GLIN(src, 0, src->xsize, y, vals); + ty = tystart; + for (x = 0; x < src->xsize; ++x) { + IM_PPIX(targ, tx, ty, vals+x); + ty += tyinc; + } + tx += txinc; + } + myfree(vals); +#/code + } + else { + i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx)); + + tx = txstart; + for (y = 0; y < src->ysize; ++y) { + i_gpal(src, 0, src->xsize, y, vals); + ty = tystart; + for (x = 0; x < src->xsize; ++x) { + i_ppal(targ, tx, tx+1, ty, vals+x); + ty += tyinc; + } + tx += txinc; + } + myfree(vals); + } + return targ; + } + else { + i_push_error(0, "i_rotate90() only rotates at 90, 180, or 270 degrees"); + return NULL; + } +} + +/* linear interpolation */ +static i_color interp_i_color(i_color before, i_color after, double pos, + int channels) { + i_color out; + int ch; + + pos -= floor(pos); + if (channels == 1 || channels == 3) { + for (ch = 0; ch < channels; ++ch) + out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; + } + else { + int total_cover = (1-pos) * before.channel[channels-1] + + pos * after.channel[channels-1]; + + total_cover = I_LIMIT_8(total_cover); + if (total_cover) { + double before_alpha = before.channel[channels-1] / 255.0; + double after_alpha = after.channel[channels-1] / 255.0; + double total_alpha = before_alpha * (1-pos) + after_alpha * pos; + + for (ch = 0; ch < channels-1; ++ch) { + int out_level = ((1-pos) * before.channel[ch] * before_alpha + + pos * after.channel[ch] * after_alpha + 0.5) / total_alpha; + + out.channel[ch] = I_LIMIT_8(out_level); + } + } + + out.channel[channels-1] = total_cover; + } + + return out; +} + +/* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */ +/* linear interpolation */ +static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos, + int channels) { + i_fcolor out; + int ch; + + pos -= floor(pos); + if (channels == 1 || channels == 3) { + for (ch = 0; ch < channels; ++ch) + out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; + } + else { + double total_cover = (1-pos) * before.channel[channels-1] + + pos * after.channel[channels-1]; + + total_cover = I_LIMIT_DOUBLE(total_cover); + if (total_cover) { + double before_alpha = before.channel[channels-1]; + double after_alpha = after.channel[channels-1]; + double total_alpha = before_alpha * (1-pos) + after_alpha * pos; + + for (ch = 0; ch < channels-1; ++ch) { + double out_level = ((1-pos) * before.channel[ch] * before_alpha + + pos * after.channel[ch] * after_alpha) / total_alpha; + + out.channel[ch] = I_LIMIT_DOUBLE(out_level); + } + } + + out.channel[channels-1] = total_cover; + } + + return out; +} + +i_img *i_matrix_transform_bg(i_img *src, i_img_dim xsize, i_img_dim ysize, const double *matrix, + const i_color *backp, const i_fcolor *fbackp) { + i_img *result = i_sametype(src, xsize, ysize); + i_img_dim x, y; + int ch; + i_img_dim i, j; + double sx, sy, sz; + + if (src->type == i_direct_type) { +#code src->bits <= 8 + IM_COLOR *vals = mymalloc(xsize * sizeof(IM_COLOR)); + IM_COLOR back; + i_fsample_t fsamp; + +#ifdef IM_EIGHT_BIT + if (backp) { + back = *backp; + } + else if (fbackp) { + for (ch = 0; ch < src->channels; ++ch) { + fsamp = fbackp->channel[ch]; + back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255; + } + } +#else +#define interp_i_color interp_i_fcolor + if (fbackp) { + back = *fbackp; + } + else if (backp) { + for (ch = 0; ch < src->channels; ++ch) + back.channel[ch] = backp->channel[ch] / 255.0; + } +#endif + else { + for (ch = 0; ch < src->channels; ++ch) + back.channel[ch] = 0; + } + + for (y = 0; y < ysize; ++y) { + for (x = 0; x < xsize; ++x) { + /* dividing by sz gives us the ability to do perspective + transforms */ + sz = x * matrix[6] + y * matrix[7] + matrix[8]; + if (fabs(sz) > 0.0000001) { + sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz; + sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz; + } + else { + sx = sy = 0; + } + + /* anything outside these ranges is either a broken co-ordinate + or outside the source */ + if (fabs(sz) > 0.0000001 + && sx >= -1 && sx < src->xsize + && sy >= -1 && sy < src->ysize) { + + if (sx != (i_img_dim)sx) { + if (sy != (i_img_dim)sy) { + IM_COLOR c[2][2]; + IM_COLOR ci2[2]; + for (i = 0; i < 2; ++i) + for (j = 0; j < 2; ++j) + if (IM_GPIX(src, floor(sx)+i, floor(sy)+j, &c[j][i])) + c[j][i] = back; + for (j = 0; j < 2; ++j) + ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels); + vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels); + } + else { + IM_COLOR ci2[2]; + for (i = 0; i < 2; ++i) + if (IM_GPIX(src, floor(sx)+i, sy, ci2+i)) + ci2[i] = back; + vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels); + } + } + else { + if (sy != (i_img_dim)sy) { + IM_COLOR ci2[2]; + for (i = 0; i < 2; ++i) + if (IM_GPIX(src, sx, floor(sy)+i, ci2+i)) + ci2[i] = back; + vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels); + } + else { + /* all the world's an integer */ + if (IM_GPIX(src, sx, sy, vals+x)) + vals[x] = back; + } + } + } + else { + vals[x] = back; + } + } + IM_PLIN(result, 0, xsize, y, vals); + } + myfree(vals); +#undef interp_i_color +#/code + } + else { + /* don't interpolate for a palette based image */ + i_palidx *vals = mymalloc(xsize * sizeof(i_palidx)); + i_palidx back = 0; + i_color min; + int minval = 256 * 4; + i_img_dim ix, iy; + i_color want_back; + i_fsample_t fsamp; + + if (backp) { + want_back = *backp; + } + else if (fbackp) { + for (ch = 0; ch < src->channels; ++ch) { + fsamp = fbackp->channel[ch]; + want_back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255; + } + } + else { + for (ch = 0; ch < src->channels; ++ch) + want_back.channel[ch] = 0; + } + + /* find the closest color */ + for (i = 0; i < i_colorcount(src); ++i) { + i_color temp; + int tempval; + i_getcolors(src, i, &temp, 1); + tempval = 0; + for (ch = 0; ch < src->channels; ++ch) { + tempval += abs(want_back.channel[ch] - temp.channel[ch]); + } + if (tempval < minval) { + back = i; + min = temp; + minval = tempval; + } + } + + for (y = 0; y < ysize; ++y) { + for (x = 0; x < xsize; ++x) { + /* dividing by sz gives us the ability to do perspective + transforms */ + sz = x * matrix[6] + y * matrix[7] + matrix[8]; + if (abs(sz) > 0.0000001) { + sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz; + sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz; + } + else { + sx = sy = 0; + } + + /* anything outside these ranges is either a broken co-ordinate + or outside the source */ + if (abs(sz) > 0.0000001 + && sx >= -0.5 && sx < src->xsize-0.5 + && sy >= -0.5 && sy < src->ysize-0.5) { + + /* all the world's an integer */ + ix = (i_img_dim)(sx+0.5); + iy = (i_img_dim)(sy+0.5); + if (!i_gpal(src, ix, ix+1, iy, vals+x)) + vals[i] = back; + } + else { + vals[x] = back; + } + } + i_ppal(result, 0, xsize, y, vals); + } + myfree(vals); + } + + return result; +} + +i_img *i_matrix_transform(i_img *src, i_img_dim xsize, i_img_dim ysize, const double *matrix) { + return i_matrix_transform_bg(src, xsize, ysize, matrix, NULL, NULL); +} + +static void +i_matrix_mult(double *dest, const double *left, const double *right) { + int i, j, k; + double accum; + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 3; ++j) { + accum = 0.0; + for (k = 0; k < 3; ++k) { + accum += left[3*i+k] * right[3*k+j]; + } + dest[3*i+j] = accum; + } + } +} + +i_img *i_rotate_exact_bg(i_img *src, double amount, + const i_color *backp, const i_fcolor *fbackp) { + double xlate1[9] = { 0 }; + double rotate[9]; + double xlate2[9] = { 0 }; + double temp[9], matrix[9]; + i_img_dim x1, x2, y1, y2, newxsize, newysize; + + /* first translate the centre of the image to (0,0) */ + xlate1[0] = 1; + xlate1[2] = src->xsize/2.0; + xlate1[4] = 1; + xlate1[5] = src->ysize/2.0; + xlate1[8] = 1; + + /* rotate around (0.0) */ + rotate[0] = cos(amount); + rotate[1] = sin(amount); + rotate[2] = 0; + rotate[3] = -rotate[1]; + rotate[4] = rotate[0]; + rotate[5] = 0; + rotate[6] = 0; + rotate[7] = 0; + rotate[8] = 1; + + x1 = ceil(i_abs(src->xsize * rotate[0] + src->ysize * rotate[1])); + x2 = ceil(i_abs(src->xsize * rotate[0] - src->ysize * rotate[1])); + y1 = ceil(i_abs(src->xsize * rotate[3] + src->ysize * rotate[4])); + y2 = ceil(i_abs(src->xsize * rotate[3] - src->ysize * rotate[4])); + newxsize = x1 > x2 ? x1 : x2; + newysize = y1 > y2 ? y1 : y2; + /* translate the centre back to the center of the image */ + xlate2[0] = 1; + xlate2[2] = -newxsize/2.0; + xlate2[4] = 1; + xlate2[5] = -newysize/2.0; + xlate2[8] = 1; + i_matrix_mult(temp, xlate1, rotate); + i_matrix_mult(matrix, temp, xlate2); + + return i_matrix_transform_bg(src, newxsize, newysize, matrix, backp, fbackp); +} + +i_img *i_rotate_exact(i_img *src, double amount) { + return i_rotate_exact_bg(src, amount, NULL, NULL); +} + + +/* +=back + +=head1 AUTHOR + +Tony Cook + +=head1 SEE ALSO + +Imager(3) + +=cut +*/ diff --git a/t/t64copyflip.t b/t/t64copyflip.t index 15526e3b..5a48c058 100644 --- a/t/t64copyflip.t +++ b/t/t64copyflip.t @@ -1,6 +1,6 @@ #!perl -w use strict; -use Test::More tests => 77; +use Test::More tests => 83; use Imager; use Imager::Test qw(is_color3 is_image is_imaged test_image_double test_image isnt_image); @@ -106,6 +106,31 @@ if (!$rimg->write(file=>"testout/t64_rot10_back.ppm")) { ok(!$rimg, "should fail due to bad back color"); cmp_ok($img->errstr, '=~', "^No color named ", "check error message"); } +SKIP: +{ # rotate in double mode + my $dimg = $img->to_rgb16; + my $rimg = $dimg->rotate(degrees => 10); + ok($rimg, "rotate 16-bit image gave us an image") + or skip("could not rotate", 3); + ok($rimg->write(file => "testout/t64_rotf10.ppm", pnm_write_wide_data => 1), + "save wide data rotated") + or diag($rimg->errstr); + + # with a background color + my $rimgb = $dimg->rotate(degrees => 10, back => "#FF8000"); + ok($rimgb, "rotate 16-bit image with back gave us an image") + or skip("could not rotate", 1); + ok($rimgb->write(file => "testout/t64_rotfb10.ppm", pnm_write_wide_data => 1), + "save wide data rotated") + or diag($rimgb->errstr); +} +{ # rotate in paletted mode + my $rimg = $pimg->rotate(degrees => 10); + ok($rimg, "rotated paletted image 10 degrees"); + ok($rimg->write(file => "testout/t64_rotp10.ppm"), + "save paletted rotated") + or diag($rimg->errstr); +} my $trimg = $img->matrix_transform(matrix=>[ 1.2, 0, 0, 0, 1, 0,