1 /* GdkPixbuf library - image transformation using 2x2 arbitrary matrix
3 * Copyright (C) 1999 The Free Software Foundation
5 * Authors: Oleg Klimov <quif@land.ru>, John Costigan
7 * This file is part of Maemo Mapper.
9 * Maemo Mapper is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * Maemo Mapper is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with Maemo Mapper. If not, see <http://www.gnu.org/licenses/>.
23 /* 1997-1999 original code was written
24 * 2001-08-12 ported to GdkImage
25 * 2003-01-31 ported to GdkPixbuf
31 #include "gdk-pixbuf-rotate.h"
34 * gdk_pixbuf_rotate_matrix_new:
36 * Creates a new 2x2 matrix. Allocates 4*sizeof(gfloat) buffer,
37 * fills it with 1 on the main dialonal.
40 * Static gfloat[4] buffer can be used instead.
42 * Return value: initialized matrix, destroy with g_free()
45 gdk_pixbuf_rotate_matrix_new()
48 matrix = g_malloc(sizeof(gfloat)*4);
49 matrix[0] = 1; matrix[1] = 0;
50 matrix[2] = 0; matrix[3] = 1;
55 * gdk_pixbuf_rotate_matrix_fill_for_rotation:
56 * @matrix: destination matrix
57 * @angle: angle (radian)
59 * Prepare 2x2 matrix for rotation.
60 * | cosf(a) -sinf(a) |
64 gdk_pixbuf_rotate_matrix_fill_for_rotation(gfloat* matrix, gfloat angle)
66 gfloat sa = sinf(angle), ca = cosf(angle);
67 matrix[0] = ca; matrix[1] = -sa;
68 matrix[2] = sa; matrix[3] = ca;
72 * gdk_pixbuf_rotate_matrix_mult_number:
73 * @matrix: matrix to multiply
76 * Multiply 2x2 matrix.
82 gdk_pixbuf_rotate_matrix_mult_number(gfloat* matrix, gfloat mult)
84 matrix[0] *= mult; matrix[1] *= mult;
85 matrix[2] *= mult; matrix[3] *= mult;
89 * gdk_pixbuf_rotate_matrix_mult_matrix:
90 * @dst: matrix to store the result
91 * @src1: left matrix to multiply
92 * @src2: right matrix to multiply
94 * Multiply two 2x2 matrixes. Destination and any of the
95 * sources can be one the same.
96 * Use to combine two transformations.
99 gdk_pixbuf_rotate_matrix_mult_matrix(
101 const gfloat* src1, const gfloat* src2)
104 ans[0] = src1[0]*src2[0] + src1[1]*src2[2];
105 ans[1] = src1[0]*src2[1] + src1[1]*src2[3];
106 ans[2] = src1[2]*src2[0] + src1[3]*src2[2];
107 ans[3] = src1[2]*src2[1] + src1[3]*src2[3];
115 * gdk_pixbuf_rotate_vector:
116 * @dst_x: pointer to store X coord of the result vector
117 * @dst_y: pointer to store Y coord of the result vector
118 * @matrix: matrix to use
119 * @src_x: X coord of source vector
120 * @src_y: Y coord of source vector
122 * Convenience: the same matrix for transforming vectors.
123 * Imagine you have a spaceship image, you rotate it and
124 * now want to calculate where its spaceguns located.
127 gdk_pixbuf_rotate_vector(
128 gfloat* dst_x, gfloat* dst_y,
129 const gfloat* matrix,
130 gfloat src_x, gfloat src_y)
132 *dst_x = matrix[0]*src_x + matrix[1]*src_y;
133 *dst_y = matrix[2]*src_x + matrix[3]*src_y;
136 inline gfloat gfloat_abs(gfloat d) { return d>0 ? d : -d; }
137 inline gfloat gfloat_min(gfloat a, gfloat b) { return a>b ? b : a; }
138 inline gfloat gfloat_max(gfloat a, gfloat b) { return a>b ? a : b; }
141 * gdk_pixbuf_rotate_matrix_reverse:
142 * @dest: matrix to store the result
143 * @src: matrix to reverse
145 * Calculates reversed matrix.
147 * Return value: TRUE on success, FALSE when reverse
148 * matrix doen't exist.
151 gdk_pixbuf_rotate_matrix_reverse(gfloat* dest, const gfloat* src)
155 gfloat consider_as_zero;
156 det = gdk_pixbuf_rotate_matrix_determinant(src);
158 (gfloat_abs(src[0]) +
161 gfloat_abs(src[3])) * 1e-11;
162 if (gfloat_abs(det)<consider_as_zero) return FALSE;
167 gdk_pixbuf_rotate_matrix_mult_number(ans, 1.0/det);
176 * gdk_pixbuf_rotate_matrix_transpose:
177 * @dest: matrix to store the result
178 * @src: matrix to transpose
180 * Calculates transposed matrix.
183 gdk_pixbuf_rotate_matrix_transpose(
194 * gdk_pixbuf_rotate_matrix_determinant:
197 * Calculates determinant.
199 * Return value: determinant.
202 gdk_pixbuf_rotate_matrix_determinant(const gfloat* matrix)
204 return matrix[0]*matrix[3] - matrix[1]*matrix[2];
208 #define ROTATE_CUT_EDGES (0.0) /* cut small part of edges, to avoid artefacts*/
212 * @dst_pixbuf: destination pixbuf
213 * @dst_x: X coord in @dst_pixbuf
214 * @dst_y: Y coord in @dst_pixbuf
215 * @matrix: transformation matrix
216 * @src_pixbuf: a pixbuf
217 * @src_center_x: X coord of center in @src_pixbuf
218 * @src_center_y: Y coord of center in @src_pixbuf
219 * @result_rect_x: pointer to X coord of rectangle to store result bounds
220 * @result_rect_y: pointer to Y coord of rectangle to store result bounds
221 * @result_rect_width: pointer to Width of rectangle to store result bounds
222 * @result_rect_height: pointer to Height of rectangle to store result bounds
223 * @interp_type: GDK_INTERP_NEAREST
225 * Transform a src_pixbuf into dst_pixbuf using arbitrary matrix.
226 * GDK_INTERP_NEAREST only supported
227 * GDK_COLORSPACE_RGB only supported
228 * only 8 bits per sample supported
229 * only 3 or 4 channels supported (with or without alpha)
233 GdkPixbuf* dst_pixbuf, gfloat dst_x, gfloat dst_y,
235 GdkPixbuf* src_pixbuf, gfloat src_center_x, gfloat src_center_y,
236 gfloat src_width, gfloat src_height,
239 gint* result_rect_width,
240 gint* result_rect_height)
242 guint dst_width, dst_height;
244 guchar *dst_buf, *dst;
245 guint dst_n_channels;
246 guchar *src_buf, *src;
247 guint src_n_channels;
251 gboolean lines4 = TRUE; /* two cases: 4-lines or 2-lines */
252 gfloat px[4], py[4]; /* edge points on dest */
253 gint line_l1, line_l2 = -1; /* line indexes, 2 left + 2 right
254 * or 1 left + 1 right */
255 gint line_r1, line_r2 = -1;
256 gfloat k[4], b[4]; /* x = k*y + b - bounding line equations */
257 gint ili = -1; /* invalid line index - to restore which
258 * 2 lines valid [2-lines case] */
260 gfloat left, right, top, bottom; /* resulting rect */
261 gint int_left, int_right, int_top, int_bottom;
262 gint li =0, ri =0, ti =0, bi =0; /* point indexes */
264 gfloat src_x, src_y; /* first point in a row */
265 gfloat pixel_right_x, pixel_right_y; /* one-pixel-to-the-left
266 * vector on source */
267 gint32 pixel_right_ix, pixel_right_iy; /* fixed-point, the same */
269 gint c, x, y; /* bunch of temprorary variables */
275 printf("%s(%p, %f, %f, [%f, %f, %f, %f], %p, %f, %f, %f, %f)\n",
276 __PRETTY_FUNCTION__, dst_pixbuf, dst_x, dst_y,
277 matrix[0], matrix[1], matrix[2], matrix[3],
278 src_pixbuf, src_center_x, src_center_y,
279 src_width, src_height);
282 dst_width = gdk_pixbuf_get_width(dst_pixbuf);
283 dst_height = gdk_pixbuf_get_height(dst_pixbuf);
285 if (!gdk_pixbuf_rotate_matrix_reverse(reverse, matrix)) return;
286 /* No reverse matrix means we have image collapsed into
290 src_buf = gdk_pixbuf_get_pixels(src_pixbuf);
291 dst_buf = gdk_pixbuf_get_pixels(dst_pixbuf);
292 src_bpl = gdk_pixbuf_get_rowstride(src_pixbuf);
293 dst_bpl = gdk_pixbuf_get_rowstride(dst_pixbuf);
294 src_n_channels = gdk_pixbuf_get_n_channels(src_pixbuf);
295 dst_n_channels = gdk_pixbuf_get_n_channels(dst_pixbuf);
297 /* 01 map source edge points to dest
300 gdk_pixbuf_rotate_vector(&px[0], &py[0], matrix,
301 (src_width / -2.f), (src_height / -2.f));
302 gdk_pixbuf_rotate_vector(&px[1], &py[1], matrix,
303 (src_width / 2.f), (src_height / -2.f));
304 gdk_pixbuf_rotate_vector(&px[2], &py[2], matrix,
305 (src_width / 2.f), (src_height / 2.f));
306 gdk_pixbuf_rotate_vector(&px[3], &py[3], matrix,
307 (src_width / -2.f), (src_height / 2.f));
309 gdk_pixbuf_rotate_vector(&px[0], &py[0], matrix,
310 - src_center_x + ROTATE_CUT_EDGES,
311 - src_center_y + ROTATE_CUT_EDGES);
312 gdk_pixbuf_rotate_vector(&px[1], &py[1], matrix,
313 src_width - src_center_x - ROTATE_CUT_EDGES,
314 - src_center_y + ROTATE_CUT_EDGES);
315 gdk_pixbuf_rotate_vector(&px[2], &py[2], matrix,
316 src_width - src_center_x -ROTATE_CUT_EDGES,
317 src_height - src_center_y - ROTATE_CUT_EDGES);
318 gdk_pixbuf_rotate_vector(&px[3], &py[3], matrix,
319 - src_center_x + ROTATE_CUT_EDGES,
320 src_height - src_center_y - ROTATE_CUT_EDGES);
323 for (c=0; c<4; c++) {
325 tmpd = py[tmpi] - py[c];
326 if (gfloat_abs(tmpd)<1e-4) {
330 k[c] = (px[tmpi]-px[c])/tmpd;
331 b[c] = px[c] - k[c]*py[c];
339 for (c=1; c<4; c++) {
340 if (left>=px[c]) { left = px[c]; li = c; }
341 if (right<=px[c]) { right = px[c]; ri = c; }
342 if (top>=py[c]) { top = py[c]; ti = c; }
343 if (bottom<=py[c]) { bottom = py[c]; bi = c; }
345 int_top = (gint) (dst_y + py[ti] + 0.5);
346 int_top = CLAMP(int_top, 0, (gint)dst_height);
347 int_bottom = (gint) (dst_y + py[bi] + 0.5);
348 int_bottom = CLAMP(int_bottom, 0, (gint)dst_height);
349 int_left = (gint) (dst_x + px[li] + 0.5);
350 int_left = CLAMP(int_left, 0, (gint)dst_width);
351 int_right = (gint) (dst_x + px[ri] + 0.5);
352 int_right = CLAMP(int_right, 0, (gint)dst_width);
353 if (result_rect_x) *result_rect_x = int_left;
354 if (result_rect_y) *result_rect_y = int_top;
355 if (result_rect_width) *result_rect_width = int_right - int_left;
356 if (result_rect_height) *result_rect_height = int_bottom - int_top;
358 if (int_right - int_left == 0 || int_bottom - int_top == 0) return;
367 if (((ti+1)%4)==ri) {
368 line_l2 = bi; /* clockwise */
370 /* line_l1 /\ line_r2
375 line_l2 = ti; /* anticlockwise */
377 /* line_l2 /\ line_r1
385 * line_l1 | | line_r1
387 tmpd = (px[0]+px[1]+px[2]+px[3])/4.0; /* middle x */
388 tmpi = (ili+1)%4; /* valid for sure */
398 gdk_pixbuf_rotate_vector(
399 &pixel_right_x, &pixel_right_y,
402 pixel_right_ix = (gint32) (pixel_right_x*0x10000);
403 pixel_right_iy = (gint32) (pixel_right_y*0x10000);
405 for (y=int_top; y<int_bottom; y++) {
408 gint32 pixel_ix, pixel_iy;
410 /* calc line x1..x2 on dest */
414 k[line_l1]*(tmpd+0.5) + b[line_l1],
415 k[line_l2]*(tmpd+0.5) + b[line_l2] );
417 k[line_r1]*(tmpd+0.5) + b[line_r1],
418 k[line_r2]*(tmpd+0.5) + b[line_r2] );
420 x1r = k[line_l1]*tmpd + b[line_l1];
421 x2r = k[line_r1]*tmpd + b[line_r1];
425 x1r = gfloat_max(x1r, 0);
426 x2r = gfloat_min(x2r, dst_width);
427 x1 = (gint) (x1r+0.5);
428 x2 = (gint) (x2r+0.5);
429 if (x2<=x1) continue;
431 /* calc y:x1 point on source */
432 gdk_pixbuf_rotate_vector(
437 src_x += src_center_x;
438 src_y += src_center_y;
441 pixel_ix = (gint32) (src_x*0x10000);
442 pixel_iy = (gint32) (src_y*0x10000);
444 dst = dst_buf + (y*dst_bpl + x1*dst_n_channels);
446 #define INNER_LOOP(DST_CHANNELS, SRC_CHANNELS) \
447 for (x=x1; x<x2; x++) { \
449 + (pixel_iy>>16)*src_bpl \
450 + (pixel_ix>>16)*SRC_CHANNELS; \
451 if (DST_CHANNELS==3 && SRC_CHANNELS==3) { \
456 if (DST_CHANNELS==4 && SRC_CHANNELS==3) { \
462 if (DST_CHANNELS==3 && SRC_CHANNELS==4) { \
464 a0 = (guint) src[3]; \
466 t = a0*src[0] + a1*dst[0] + 0x80; \
467 dst[0] = (t + (t>>8)) >> 8; \
468 t = a0*src[1] + a1*dst[1] + 0x80; \
469 dst[1] = (t + (t>>8)) >> 8; \
470 t = a0*src[2] + a1*dst[2] + 0x80; \
471 dst[2] = (t + (t>>8)) >> 8; \
473 if (DST_CHANNELS==4 && SRC_CHANNELS==4) { \
474 guint a0 = (guint) src[3]; \
481 guint w0 = 0xff * a0; \
482 guint w1 = (0xff - a0) * dst[3]; \
484 dst[0] = (w0*src[0] + w1*dst[0]) / w; \
485 dst[1] = (w0*src[1] + w1*dst[1]) / w; \
486 dst[2] = (w0*src[2] + w1*dst[2]) / w; \
490 pixel_ix += pixel_right_ix; \
491 pixel_iy += pixel_right_iy; \
492 dst += DST_CHANNELS; \
495 if (dst_n_channels==3 && src_n_channels==3) {
496 for (x=x1; x<x2; x++) {
498 + (pixel_iy>>16)*src_bpl
503 pixel_ix += pixel_right_ix;
504 pixel_iy += pixel_right_iy;
508 else if (dst_n_channels==3 && src_n_channels==4) {
509 for (x=x1; x<x2; x++) {
512 + (pixel_iy>>16)*src_bpl
516 t = a0*src[0] + a1*dst[0] + 0x80;
517 dst[0] = (t + (t>>8)) >> 8;
518 t = a0*src[1] + a1*dst[1] + 0x80;
519 dst[1] = (t + (t>>8)) >> 8;
520 t = a0*src[2] + a1*dst[2] + 0x80;
521 dst[2] = (t + (t>>8)) >> 8;
522 pixel_ix += pixel_right_ix;
523 pixel_iy += pixel_right_iy;
527 else if (dst_n_channels==4 && src_n_channels==3)
529 for (x=x1; x<x2; x++) {
531 + (pixel_iy>>16)*src_bpl
537 pixel_ix += pixel_right_ix;
538 pixel_iy += pixel_right_iy;
542 else if (dst_n_channels==4 && src_n_channels==4)
544 for (x=x1; x<x2; x++) {
547 + (pixel_iy>>16)*src_bpl
556 guint w0 = 0xff * a0;
557 guint w1 = (0xff - a0) * dst[3];
559 dst[0] = (w0*src[0] + w1*dst[0]) / w;
560 dst[1] = (w0*src[1] + w1*dst[1]) / w;
561 dst[2] = (w0*src[2] + w1*dst[2]) / w;
564 pixel_ix += pixel_right_ix;
565 pixel_iy += pixel_right_iy;