4 rotate.im - implements image rotations
8 i_img *i_rotate90(i_img *src, int degrees)
12 Implements basic 90 degree rotations of an image.
14 Other rotations will be added as tuits become available.
21 #include <math.h> /* for floor() */
25 i_img *i_rotate90(i_img *src, int degrees) {
32 /* essentially the same as flipxy(..., 2) except that it's not
34 targ = i_sametype(src, src->xsize, src->ysize);
35 if (src->type == i_direct_type) {
37 IM_COLOR *vals = mymalloc(src->xsize * sizeof(IM_COLOR));
38 for (y = 0; y < src->ysize; ++y) {
40 IM_GLIN(src, 0, src->xsize, y, vals);
41 for (x = 0; x < src->xsize/2; ++x) {
43 vals[x] = vals[src->xsize - x - 1];
44 vals[src->xsize - x - 1] = tmp;
46 IM_PLIN(targ, 0, src->xsize, src->ysize - y - 1, vals);
52 i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx));
54 for (y = 0; y < src->ysize; ++y) {
56 i_gpal(src, 0, src->xsize, y, vals);
57 for (x = 0; x < src->xsize/2; ++x) {
59 vals[x] = vals[src->xsize - x - 1];
60 vals[src->xsize - x - 1] = tmp;
62 i_ppal(targ, 0, src->xsize, src->ysize - y - 1, vals);
70 else if (degrees == 270 || degrees == 90) {
71 i_img_dim tx, txstart, txinc;
72 i_img_dim ty, tystart, tyinc;
77 tystart = src->xsize-1;
81 txstart = src->ysize-1;
86 targ = i_sametype(src, src->ysize, src->xsize);
87 if (src->type == i_direct_type) {
89 IM_COLOR *vals = mymalloc(src->xsize * sizeof(IM_COLOR));
92 for (y = 0; y < src->ysize; ++y) {
93 IM_GLIN(src, 0, src->xsize, y, vals);
95 for (x = 0; x < src->xsize; ++x) {
96 IM_PPIX(targ, tx, ty, vals+x);
105 i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx));
108 for (y = 0; y < src->ysize; ++y) {
109 i_gpal(src, 0, src->xsize, y, vals);
111 for (x = 0; x < src->xsize; ++x) {
112 i_ppal(targ, tx, tx+1, ty, vals+x);
122 i_push_error(0, "i_rotate90() only rotates at 90, 180, or 270 degrees");
127 /* linear interpolation */
128 static i_color interp_i_color(i_color before, i_color after, double pos,
134 if (channels == 1 || channels == 3) {
135 for (ch = 0; ch < channels; ++ch)
136 out.channel[ch] = ((1-pos) * before.channel[ch] + pos * after.channel[ch]) + 0.5;
139 int total_cover = (1-pos) * before.channel[channels-1]
140 + pos * after.channel[channels-1];
142 total_cover = I_LIMIT_8(total_cover);
144 double before_alpha = before.channel[channels-1] / 255.0;
145 double after_alpha = after.channel[channels-1] / 255.0;
146 double total_alpha = before_alpha * (1-pos) + after_alpha * pos;
148 for (ch = 0; ch < channels-1; ++ch) {
149 int out_level = ((1-pos) * before.channel[ch] * before_alpha +
150 pos * after.channel[ch] * after_alpha) / total_alpha + 0.5;
152 out.channel[ch] = I_LIMIT_8(out_level);
156 out.channel[channels-1] = total_cover;
162 /* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */
163 /* linear interpolation */
164 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
170 if (channels == 1 || channels == 3) {
171 for (ch = 0; ch < channels; ++ch)
172 out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
175 double total_cover = (1-pos) * before.channel[channels-1]
176 + pos * after.channel[channels-1];
178 total_cover = I_LIMIT_DOUBLE(total_cover);
180 double before_alpha = before.channel[channels-1];
181 double after_alpha = after.channel[channels-1];
182 double total_alpha = before_alpha * (1-pos) + after_alpha * pos;
184 for (ch = 0; ch < channels-1; ++ch) {
185 double out_level = ((1-pos) * before.channel[ch] * before_alpha +
186 pos * after.channel[ch] * after_alpha) / total_alpha;
188 out.channel[ch] = I_LIMIT_DOUBLE(out_level);
192 out.channel[channels-1] = total_cover;
198 i_img *i_matrix_transform_bg(i_img *src, i_img_dim xsize, i_img_dim ysize, const double *matrix,
199 const i_color *backp, const i_fcolor *fbackp) {
200 i_img *result = i_sametype(src, xsize, ysize);
206 if (src->type == i_direct_type) {
208 IM_COLOR *vals = mymalloc(xsize * sizeof(IM_COLOR));
217 for (ch = 0; ch < src->channels; ++ch) {
218 fsamp = fbackp->channel[ch];
219 back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255;
223 #define interp_i_color interp_i_fcolor
228 for (ch = 0; ch < src->channels; ++ch)
229 back.channel[ch] = backp->channel[ch] / 255.0;
233 for (ch = 0; ch < src->channels; ++ch)
234 back.channel[ch] = 0;
237 for (y = 0; y < ysize; ++y) {
238 for (x = 0; x < xsize; ++x) {
239 /* dividing by sz gives us the ability to do perspective
241 sz = x * matrix[6] + y * matrix[7] + matrix[8];
242 if (fabs(sz) > 0.0000001) {
243 sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
244 sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
250 /* anything outside these ranges is either a broken co-ordinate
251 or outside the source */
252 if (fabs(sz) > 0.0000001
253 && sx >= -1 && sx < src->xsize
254 && sy >= -1 && sy < src->ysize) {
256 ROT_DEBUG(fprintf(stderr, "map " i_DFp " to %g,%g\n", i_DFcp(x, y), sx, sy));
257 if (sx != (i_img_dim)sx) {
258 if (sy != (i_img_dim)sy) {
261 ROT_DEBUG(fprintf(stderr, " both non-int\n"));
262 for (i = 0; i < 2; ++i)
263 for (j = 0; j < 2; ++j)
264 if (IM_GPIX(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
266 for (j = 0; j < 2; ++j)
267 ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels);
268 vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
272 ROT_DEBUG(fprintf(stderr, " y int, x non-int\n"));
273 for (i = 0; i < 2; ++i)
274 if (IM_GPIX(src, floor(sx)+i, sy, ci2+i))
276 vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels);
280 if (sy != (i_img_dim)sy) {
282 ROT_DEBUG(fprintf(stderr, " x int, y non-int\n"));
283 for (i = 0; i < 2; ++i)
284 if (IM_GPIX(src, sx, floor(sy)+i, ci2+i))
286 vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
289 ROT_DEBUG(fprintf(stderr, " both int\n"));
290 /* all the world's an integer */
291 if (IM_GPIX(src, sx, sy, vals+x))
300 IM_PLIN(result, 0, xsize, y, vals);
303 #undef interp_i_color
307 /* don't interpolate for a palette based image */
308 i_palidx *vals = mymalloc(xsize * sizeof(i_palidx));
311 int minval = 256 * 4;
320 for (ch = 0; ch < src->channels; ++ch) {
321 fsamp = fbackp->channel[ch];
322 want_back.channel[ch] = fsamp < 0 ? 0 : fsamp > 1 ? 255 : fsamp * 255;
326 for (ch = 0; ch < src->channels; ++ch)
327 want_back.channel[ch] = 0;
330 /* find the closest color */
331 for (i = 0; i < i_colorcount(src); ++i) {
334 i_getcolors(src, i, &temp, 1);
336 for (ch = 0; ch < src->channels; ++ch) {
337 tempval += abs(want_back.channel[ch] - temp.channel[ch]);
339 if (tempval < minval) {
346 for (y = 0; y < ysize; ++y) {
347 for (x = 0; x < xsize; ++x) {
348 /* dividing by sz gives us the ability to do perspective
350 sz = x * matrix[6] + y * matrix[7] + matrix[8];
351 if (abs(sz) > 0.0000001) {
352 sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
353 sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
359 /* anything outside these ranges is either a broken co-ordinate
360 or outside the source */
361 if (abs(sz) > 0.0000001
362 && sx >= -0.5 && sx < src->xsize-0.5
363 && sy >= -0.5 && sy < src->ysize-0.5) {
365 /* all the world's an integer */
366 ix = (i_img_dim)(sx+0.5);
367 iy = (i_img_dim)(sy+0.5);
368 if (!i_gpal(src, ix, ix+1, iy, vals+x))
375 i_ppal(result, 0, xsize, y, vals);
383 i_img *i_matrix_transform(i_img *src, i_img_dim xsize, i_img_dim ysize, const double *matrix) {
384 return i_matrix_transform_bg(src, xsize, ysize, matrix, NULL, NULL);
388 i_matrix_mult(double *dest, const double *left, const double *right) {
392 for (i = 0; i < 3; ++i) {
393 for (j = 0; j < 3; ++j) {
395 for (k = 0; k < 3; ++k) {
396 accum += left[3*i+k] * right[3*k+j];
403 #define numfmt "%23g"
405 ROT_DEBUG(static dump_mat(const char *name, double *f) {
406 fprintf(stderr, "%s:\n " numfmt " " numfmt " " numfmt "\n"
407 " " numfmt " " numfmt " " numfmt "\n"
408 " " numfmt " " numfmt " " numfmt "\n",
409 name, f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8]);
412 i_img *i_rotate_exact_bg(i_img *src, double amount,
413 const i_color *backp, const i_fcolor *fbackp) {
414 double xlate1[9] = { 0 };
416 double xlate2[9] = { 0 };
417 double temp[9], matrix[9];
418 i_img_dim x1, x2, y1, y2, newxsize, newysize;
420 /* first translate the centre of the image to (0,0) */
422 xlate1[2] = (src->xsize-1)/2.0;
424 xlate1[5] = (src->ysize-1)/2.0;
427 ROT_DEBUG(dump_mat("xlate1", xlate1));
429 /* rotate around (0.0) */
430 rotate[0] = cos(amount);
431 rotate[1] = sin(amount);
433 rotate[3] = -rotate[1];
434 rotate[4] = rotate[0];
440 ROT_DEBUG(dump_mat("rotate", rotate));
442 ROT_DEBUG(fprintf(stderr, "cos %g sin %g\n", rotate[0], rotate[1]));
444 x1 = ceil(fabs(src->xsize * rotate[0] + src->ysize * rotate[1]) - 0.0001);
445 x2 = ceil(fabs(src->xsize * rotate[0] - src->ysize * rotate[1]) - 0.0001);
446 y1 = ceil(fabs(src->xsize * rotate[3] + src->ysize * rotate[4]) - 0.0001);
447 y2 = ceil(fabs(src->xsize * rotate[3] - src->ysize * rotate[4]) - 0.0001);
448 ROT_DEBUG(fprintf(stderr, "x1 y1 " i_DFp " x2 y2 " i_DFp "\n", i_DFcp(x1, y1), i_DFcp(x2, y2)));
449 newxsize = x1 > x2 ? x1 : x2;
450 newysize = y1 > y2 ? y1 : y2;
451 /* translate the centre back to the center of the image */
453 xlate2[2] = -(newxsize-1)/2.0;
455 xlate2[5] = -(newysize-1)/2.0;
458 ROT_DEBUG(dump_mat("xlate2", xlate2));
460 i_matrix_mult(temp, xlate1, rotate);
461 i_matrix_mult(matrix, temp, xlate2);
463 ROT_DEBUG(dump_mat("matrxi", matrix));
465 return i_matrix_transform_bg(src, newxsize, newysize, matrix, backp, fbackp);
468 i_img *i_rotate_exact(i_img *src, double amount) {
469 return i_rotate_exact_bg(src, amount, NULL, NULL);
478 Tony Cook <tony@develop-help.com>