]> git.itanic.dy.fi Git - maemo-mapper/blob - src/gdk-pixbuf-rotate.c
Administrative changes in preparation for release of Maemo Mapper v2.6.2.
[maemo-mapper] / src / gdk-pixbuf-rotate.c
1 /* GdkPixbuf library - image transformation using 2x2 arbitrary matrix
2  *
3  * Copyright (C) 1999 The Free Software Foundation
4  *
5  * Authors: Oleg Klimov <quif@land.ru>, John Costigan
6  *
7  * This file is part of Maemo Mapper.
8  *
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.
13  *
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.
18  *
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/>.
21  */
22
23 /* 1997-1999  original code was written
24  * 2001-08-12 ported to GdkImage
25  * 2003-01-31 ported to GdkPixbuf
26  */
27
28 #define _GNU_SOURCE
29
30 #include <math.h>
31 #include "gdk-pixbuf-rotate.h"
32
33 /**
34  * gdk_pixbuf_rotate_matrix_new:
35  *
36  * Creates a new 2x2 matrix. Allocates 4*sizeof(gfloat) buffer,
37  * fills it with 1 on the main dialonal.
38  * | 1 0 |
39  * | 0 1 |
40  * Static gfloat[4] buffer can be used instead.
41  *
42  * Return value: initialized matrix, destroy with g_free()
43  **/
44 gfloat*
45 gdk_pixbuf_rotate_matrix_new()
46 {
47     gfloat* matrix;
48     matrix = g_malloc(sizeof(gfloat)*4);
49     matrix[0] = 1; matrix[1] = 0;
50     matrix[2] = 0; matrix[3] = 1;
51     return matrix;
52 }
53
54 /**
55  * gdk_pixbuf_rotate_matrix_fill_for_rotation:
56  * @matrix: destination matrix
57  * @angle: angle (radian)
58  *
59  * Prepare 2x2 matrix for rotation.
60  * | cosf(a) -sinf(a) |
61  * | sinf(a)  cosf(a) |
62  **/
63 void
64 gdk_pixbuf_rotate_matrix_fill_for_rotation(gfloat* matrix, gfloat angle)
65 {
66     gfloat sa = sinf(angle), ca = cosf(angle);
67     matrix[0] = ca; matrix[1] = -sa;
68     matrix[2] = sa; matrix[3] = ca;
69 }
70
71 /**
72  * gdk_pixbuf_rotate_matrix_mult_number:
73  * @matrix: matrix to multiply
74  * @mult: multiplier
75  *
76  * Multiply 2x2 matrix.
77  * | *=mult *=mult |
78  * | *=mult *=mult |
79  * Use to scale image.
80  **/
81 void
82 gdk_pixbuf_rotate_matrix_mult_number(gfloat* matrix, gfloat mult)
83 {
84     matrix[0] *= mult; matrix[1] *= mult;
85     matrix[2] *= mult; matrix[3] *= mult;
86 }
87
88 /**
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
93  *
94  * Multiply two 2x2 matrixes. Destination and any of the
95  * sources can be one the same.
96  * Use to combine two transformations.
97  **/
98 void
99 gdk_pixbuf_rotate_matrix_mult_matrix(
100     gfloat* dst,
101     const gfloat* src1, const gfloat* src2)
102 {
103     gfloat ans[4];
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];
108     dst[0] = ans[0];
109     dst[1] = ans[1];
110     dst[2] = ans[2];
111     dst[3] = ans[3];
112 }
113
114 /**
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
121  *
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.
125  **/
126 void
127 gdk_pixbuf_rotate_vector(
128     gfloat* dst_x, gfloat* dst_y,
129     const gfloat* matrix,
130     gfloat src_x, gfloat src_y)
131 {
132     *dst_x = matrix[0]*src_x + matrix[1]*src_y;
133     *dst_y = matrix[2]*src_x + matrix[3]*src_y;
134 }
135
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; } 
139
140 /**
141  * gdk_pixbuf_rotate_matrix_reverse:
142  * @dest: matrix to store the result
143  * @src: matrix to reverse
144  *
145  * Calculates reversed matrix.
146  *
147  * Return value: TRUE on success, FALSE when reverse
148  * matrix doen't exist.
149  **/
150 gboolean
151 gdk_pixbuf_rotate_matrix_reverse(gfloat* dest, const gfloat* src)
152 {
153     gfloat ans[4];
154     gfloat det;
155     gfloat consider_as_zero;
156     det = gdk_pixbuf_rotate_matrix_determinant(src);
157     consider_as_zero =
158         (gfloat_abs(src[0]) +
159          gfloat_abs(src[1]) +
160          gfloat_abs(src[2]) +
161          gfloat_abs(src[3])) * 1e-11;
162     if (gfloat_abs(det)<consider_as_zero) return FALSE;
163     ans[0] = src[3];
164     ans[1] = -src[1];
165     ans[2] = -src[2];
166     ans[3] = src[0];
167     gdk_pixbuf_rotate_matrix_mult_number(ans, 1.0/det);
168     dest[0] = ans[0];
169     dest[1] = ans[1];
170     dest[2] = ans[2];
171     dest[3] = ans[3];
172     return TRUE;
173 }
174
175 /**
176  * gdk_pixbuf_rotate_matrix_transpose:
177  * @dest: matrix to store the result
178  * @src: matrix to transpose
179  *
180  * Calculates transposed matrix.
181  **/
182 void
183 gdk_pixbuf_rotate_matrix_transpose(
184     gfloat* dest,
185     const gfloat* src)
186 {
187     gfloat t;
188     t = dest[2];
189     dest[2] = dest[1];
190     dest[1] = t;
191 }
192
193 /**
194  * gdk_pixbuf_rotate_matrix_determinant:
195  * @matrix: matrix
196  *
197  * Calculates determinant.
198  *
199  * Return value: determinant.
200  **/
201 gfloat
202 gdk_pixbuf_rotate_matrix_determinant(const gfloat* matrix)
203 {
204     return matrix[0]*matrix[3] - matrix[1]*matrix[2];
205 }
206
207
208 #define ROTATE_CUT_EDGES (0.0) /* cut small part of edges, to avoid artefacts*/
209
210 /**
211  * gdk_pixbuf_rotate:
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
224  *
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)
230  **/
231 void
232 gdk_pixbuf_rotate(
233     GdkPixbuf* dst_pixbuf, gfloat dst_x, gfloat dst_y,
234     gfloat* matrix,
235     GdkPixbuf* src_pixbuf, gfloat src_center_x, gfloat src_center_y,
236     gfloat src_width, gfloat src_height,
237     gint* result_rect_x,
238     gint* result_rect_y,
239     gint* result_rect_width,
240     gint* result_rect_height)
241 {
242     guint dst_width, dst_height;
243     gfloat reverse[4];
244     guchar *dst_buf, *dst;
245     guint dst_n_channels;
246     guchar *src_buf, *src;
247     guint src_n_channels;
248     gint src_bpl;
249     gint dst_bpl;
250
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] */
259
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 */
263
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 */
268
269     gint c, x, y; /* bunch of temprorary variables */
270     gfloat tmpd;
271     gint tmpi;
272
273
274 #if 0
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);
280 #endif
281
282 dst_width = gdk_pixbuf_get_width(dst_pixbuf);
283 dst_height = gdk_pixbuf_get_height(dst_pixbuf);
284
285     if (!gdk_pixbuf_rotate_matrix_reverse(reverse, matrix)) return;
286     /* No reverse matrix means we have image collapsed into
287      * line or point
288      */
289
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);
296
297     /* 01  map source edge points to dest
298      * 32
299      */
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));
308 #if 0
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);
321 #endif
322
323     for (c=0; c<4; c++) {
324         tmpi = (c+1)%4;
325         tmpd = py[tmpi] - py[c];
326         if (gfloat_abs(tmpd)<1e-4) {
327             lines4 = FALSE;
328             ili = c;
329         } else {
330             k[c] = (px[tmpi]-px[c])/tmpd;
331             b[c] = px[c] - k[c]*py[c];
332         }
333     }
334
335     left = px[0];
336     right = px[0];
337     top = py[0];
338     bottom = py[0];
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; }
344     }
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;
357
358     if (int_right - int_left == 0 || int_bottom - int_top == 0) return;
359
360     if (lines4) {
361         line_l1 = li;
362         line_r1 = ri;
363         /*    ti
364          *  li    ri
365          *      bi
366          */
367         if (((ti+1)%4)==ri) {
368             line_l2 = bi; /* clockwise */
369             line_r2 = ti;
370             /*   line_l1 /\  line_r2
371              *       \  \ 
372              *    line_l2  \/  line_r1
373              */
374         } else {
375             line_l2 = ti; /* anticlockwise */
376             line_r2 = bi;
377             /*   line_l2 /\  line_r1
378              *       \  \ 
379              *    line_l1  \/  line_r2
380              */
381         }
382     } else {
383         /* 2 lines case
384          *      |   |
385          * line_l1  |   |  line_r1
386          */
387         tmpd = (px[0]+px[1]+px[2]+px[3])/4.0; /* middle x */
388         tmpi = (ili+1)%4; /* valid for sure */
389         if (px[tmpi]<tmpd) {
390             line_l1 = tmpi;
391             line_r1 = (ili+3)%4;
392         } else {
393             line_r1 = tmpi;
394             line_l1 = (ili+3)%4;
395         }
396     }
397
398     gdk_pixbuf_rotate_vector(
399         &pixel_right_x, &pixel_right_y,
400         reverse,
401         1, 0);
402     pixel_right_ix = (gint32) (pixel_right_x*0x10000);
403     pixel_right_iy = (gint32) (pixel_right_y*0x10000);
404
405     for (y=int_top; y<int_bottom; y++) {
406         gfloat x1r, x2r;
407         gint x1, x2;
408         gint32 pixel_ix, pixel_iy;
409
410         /* calc line x1..x2 on dest */
411         tmpd = y - dst_y;
412         if (lines4) {
413             x1r = gfloat_max(
414                 k[line_l1]*(tmpd+0.5) + b[line_l1],
415                 k[line_l2]*(tmpd+0.5) + b[line_l2] );
416             x2r = gfloat_min(
417                 k[line_r1]*(tmpd+0.5) + b[line_r1],
418                 k[line_r2]*(tmpd+0.5) + b[line_r2] );
419         } else {
420             x1r = k[line_l1]*tmpd + b[line_l1];
421             x2r = k[line_r1]*tmpd + b[line_r1];
422         }
423         x1r += dst_x;
424         x2r += dst_x;
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;
430
431         /* calc y:x1 point on source */
432         gdk_pixbuf_rotate_vector(
433             &src_x, &src_y,
434             reverse,
435             x1-dst_x + 0.5,
436             tmpd + 0.5);
437         src_x += src_center_x;
438         src_y += src_center_y;
439
440         /* fixed point */
441         pixel_ix = (gint32) (src_x*0x10000);
442         pixel_iy = (gint32) (src_y*0x10000);
443
444         dst = dst_buf + (y*dst_bpl + x1*dst_n_channels);
445
446 #define INNER_LOOP(DST_CHANNELS, SRC_CHANNELS) \
447         for (x=x1; x<x2; x++) { \
448             src = src_buf \
449                 + (pixel_iy>>16)*src_bpl \
450                 + (pixel_ix>>16)*SRC_CHANNELS; \
451             if (DST_CHANNELS==3 && SRC_CHANNELS==3) { \
452                 dst[0] = src[0]; \
453                 dst[1] = src[1]; \
454                 dst[2] = src[2]; \
455             } \
456             if (DST_CHANNELS==4 && SRC_CHANNELS==3) { \
457                 dst[0] = src[0]; \
458                 dst[1] = src[1]; \
459                 dst[2] = src[2]; \
460                 dst[3] = 0xFF; \
461             } \
462             if (DST_CHANNELS==3 && SRC_CHANNELS==4) { \
463                 guint a0, a1, t; \
464                 a0 = (guint) src[3]; \
465                 a1 = 0xff - a0; \
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; \
472             } \
473             if (DST_CHANNELS==4 && SRC_CHANNELS==4) { \
474                 guint a0 = (guint) src[3]; \
475                 if (a0==255) { \
476                     dst[0] = src[0]; \
477                     dst[1] = src[1]; \
478                     dst[2] = src[2]; \
479                     dst[3] = 0xFF; \
480                 } if (a0>0) { \
481                     guint w0 = 0xff * a0; \
482                     guint w1 = (0xff - a0) * dst[3]; \
483                     guint w = w0 + w1; \
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; \
487                     dst[3] = w / 0xff; \
488                 } \
489             } \
490             pixel_ix += pixel_right_ix; \
491             pixel_iy += pixel_right_iy; \
492             dst += DST_CHANNELS; \
493         }
494
495         if (dst_n_channels==3 && src_n_channels==3) {
496             for (x=x1; x<x2; x++) {
497                 src = src_buf
498                     + (pixel_iy>>16)*src_bpl
499                     + (pixel_ix>>16)*3;
500                 dst[0] = src[0];
501                 dst[1] = src[1];
502                 dst[2] = src[2];
503                 pixel_ix += pixel_right_ix;
504                 pixel_iy += pixel_right_iy;
505                 dst += 3;
506             }
507         }
508         else if (dst_n_channels==3 && src_n_channels==4) {
509             for (x=x1; x<x2; x++) {
510                 guint a0, a1, t;
511                 src = src_buf
512                     + (pixel_iy>>16)*src_bpl
513                     + (pixel_ix>>16)*4;
514                 a0 = (guint) src[3];
515                 a1 = 0xff - a0;
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;
524                 dst += 3;
525             }
526         }
527         else if (dst_n_channels==4 && src_n_channels==3)
528         {
529             for (x=x1; x<x2; x++) {
530                 src = src_buf
531                     + (pixel_iy>>16)*src_bpl
532                     + (pixel_ix>>16)*3;
533                 dst[0] = src[0];
534                 dst[1] = src[1];
535                 dst[2] = src[2];
536                 dst[3] = 0xFF;
537                 pixel_ix += pixel_right_ix;
538                 pixel_iy += pixel_right_iy;
539                 dst += 4;
540             }
541         }
542         else if (dst_n_channels==4 && src_n_channels==4)
543         {
544             for (x=x1; x<x2; x++) {
545                 guint a0;
546                 src = src_buf
547                     + (pixel_iy>>16)*src_bpl
548                     + (pixel_ix>>16)*4;
549                 a0 = (guint) src[3];
550                 if (a0==255) {
551                     dst[0] = src[0];
552                     dst[1] = src[1];
553                     dst[2] = src[2];
554                     dst[3] = 0xFF;
555                 } if (a0>0) {
556                     guint w0 = 0xff * a0;
557                     guint w1 = (0xff - a0) * dst[3];
558                     guint w = w0 + w1;
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;
562                     dst[3] = w / 0xff;
563                 }
564                 pixel_ix += pixel_right_ix;
565                 pixel_iy += pixel_right_iy;
566                 dst += 4;
567             }
568         }
569     }
570 }
571