Upload tizen 2.0 beta source
[external/pango1.0.git] / pango / pango-matrix.c
1 /* Pango
2  * pango-matrix.c: Matrix manipulation routines
3  *
4  * Copyright (C) 2000, 2006 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23 #include <stdlib.h>
24 #include <math.h>
25
26 #include "pango-matrix.h"
27 #include "pango-impl-utils.h"
28
29 GType
30 pango_matrix_get_type (void)
31 {
32   static GType our_type = 0;
33
34   if (G_UNLIKELY (our_type == 0))
35     our_type = g_boxed_type_register_static (I_("PangoMatrix"),
36                                              (GBoxedCopyFunc) pango_matrix_copy,
37                                              (GBoxedFreeFunc) pango_matrix_free);
38
39   return our_type;
40 }
41
42 /**
43  * pango_matrix_copy:
44  * @matrix: a #PangoMatrix, may be %NULL
45  *
46  * Copies a #PangoMatrix.
47  *
48  * Return value: the newly allocated #PangoMatrix, which should
49  *               be freed with pango_matrix_free(), or %NULL if
50  *               @matrix was %NULL.
51  *
52  * Since: 1.6
53  **/
54 PangoMatrix *
55 pango_matrix_copy (const PangoMatrix *matrix)
56 {
57   PangoMatrix *new_matrix;
58
59   if (matrix == NULL)
60     return NULL;
61
62   new_matrix = g_slice_new (PangoMatrix);
63
64   *new_matrix = *matrix;
65
66   return new_matrix;
67 }
68
69 /**
70  * pango_matrix_free:
71  * @matrix: a #PangoMatrix, may be %NULL
72  *
73  * Free a #PangoMatrix created with pango_matrix_copy().
74  *
75  * Since: 1.6
76  **/
77 void
78 pango_matrix_free (PangoMatrix *matrix)
79 {
80   if (matrix == NULL)
81     return;
82
83   g_slice_free (PangoMatrix, matrix);
84 }
85
86 /**
87  * pango_matrix_translate:
88  * @matrix: a #PangoMatrix
89  * @tx: amount to translate in the X direction
90  * @ty: amount to translate in the Y direction
91  *
92  * Changes the transformation represented by @matrix to be the
93  * transformation given by first translating by (@tx, @ty)
94  * then applying the original transformation.
95  *
96  * Since: 1.6
97  **/
98 void
99 pango_matrix_translate (PangoMatrix *matrix,
100                         double       tx,
101                         double       ty)
102 {
103   g_return_if_fail (matrix != NULL);
104
105   matrix->x0  = matrix->xx * tx + matrix->xy * ty + matrix->x0;
106   matrix->y0  = matrix->yx * tx + matrix->yy * ty + matrix->y0;
107 }
108
109 /**
110  * pango_matrix_scale:
111  * @matrix: a #PangoMatrix
112  * @scale_x: amount to scale by in X direction
113  * @scale_y: amount to scale by in Y direction
114  *
115  * Changes the transformation represented by @matrix to be the
116  * transformation given by first scaling by @sx in the X direction
117  * and @sy in the Y direction then applying the original
118  * transformation.
119  *
120  * Since: 1.6
121  **/
122 void
123 pango_matrix_scale (PangoMatrix *matrix,
124                     double       scale_x,
125                     double       scale_y)
126 {
127   g_return_if_fail (matrix != NULL);
128
129   matrix->xx *= scale_x;
130   matrix->xy *= scale_y;
131   matrix->yx *= scale_x;
132   matrix->yy *= scale_y;
133 }
134
135 /**
136  * pango_matrix_rotate:
137  * @matrix: a #PangoMatrix
138  * @degrees: degrees to rotate counter-clockwise
139  *
140  * Changes the transformation represented by @matrix to be the
141  * transformation given by first rotating by @degrees degrees
142  * counter-clockwise then applying the original transformation.
143  *
144  * Since: 1.6
145  **/
146 void
147 pango_matrix_rotate (PangoMatrix *matrix,
148                      double       degrees)
149 {
150   PangoMatrix tmp;
151   gdouble r, s, c;
152
153   g_return_if_fail (matrix != NULL);
154
155   r = degrees * (G_PI / 180.);
156   s = sin (r);
157   c = cos (r);
158
159   tmp.xx = c;
160   tmp.xy = s;
161   tmp.yx = -s;
162   tmp.yy = c;
163   tmp.x0 = 0;
164   tmp.y0 = 0;
165
166   pango_matrix_concat (matrix, &tmp);
167 }
168
169 /**
170  * pango_matrix_concat:
171  * @matrix: a #PangoMatrix
172  * @new_matrix: a #PangoMatrix
173  *
174  * Changes the transformation represented by @matrix to be the
175  * transformation given by first applying transformation
176  * given by @new_matrix then applying the original transformation.
177  *
178  * Since: 1.6
179  **/
180 void
181 pango_matrix_concat (PangoMatrix       *matrix,
182                      const PangoMatrix *new_matrix)
183 {
184   PangoMatrix tmp;
185
186   g_return_if_fail (matrix != NULL);
187
188   tmp = *matrix;
189
190   matrix->xx = tmp.xx * new_matrix->xx + tmp.xy * new_matrix->yx;
191   matrix->xy = tmp.xx * new_matrix->xy + tmp.xy * new_matrix->yy;
192   matrix->yx = tmp.yx * new_matrix->xx + tmp.yy * new_matrix->yx;
193   matrix->yy = tmp.yx * new_matrix->xy + tmp.yy * new_matrix->yy;
194   matrix->x0  = tmp.xx * new_matrix->x0 + tmp.xy * new_matrix->y0 + tmp.x0;
195   matrix->y0  = tmp.yx * new_matrix->x0 + tmp.yy * new_matrix->y0 + tmp.y0;
196 }
197
198 /**
199  * pango_matrix_get_font_scale_factor:
200  * @matrix: a #PangoMatrix, may be %NULL
201  *
202  * Returns the scale factor of a matrix on the height of the font.
203  * That is, the scale factor in the direction perpendicular to the
204  * vector that the X coordinate is mapped to.
205  *
206  * Return value: the scale factor of @matrix on the height of the font,
207  * or 1.0 if @matrix is %NULL.
208  *
209  * Since: 1.12
210  **/
211 double
212 pango_matrix_get_font_scale_factor (const PangoMatrix *matrix)
213 {
214 /*
215  * Based on cairo-matrix.c:_cairo_matrix_compute_scale_factors()
216  *
217  * Copyright 2005, Keith Packard
218  */
219   double det;
220
221   if (!matrix)
222     return 1.0;
223
224   det = matrix->xx * matrix->yy - matrix->yx * matrix->xy;
225
226   if (det == 0)
227     {
228       return 0.0;
229     }
230   else
231     {
232       double x = matrix->xx;
233       double y = matrix->yx;
234       double major, minor;
235
236       major = sqrt (x*x + y*y);
237
238       /*
239        * ignore mirroring
240        */
241       if (det < 0)
242         det = - det;
243
244       if (major)
245         minor = det / major;
246       else
247         minor = 0.0;
248
249       return minor;
250     }
251 }
252
253 /**
254  * pango_matrix_transform_distance:
255  * @matrix: a #PangoMatrix, or %NULL
256  * @dx: in/out X component of a distance vector
257  * @dy: yn/out Y component of a distance vector
258  *
259  * Transforms the distance vector (@dx,@dy) by @matrix. This is
260  * similar to pango_matrix_transform_point() except that the translation
261  * components of the transformation are ignored. The calculation of
262  * the returned vector is as follows:
263  *
264  * <programlisting>
265  * dx2 = dx1 * xx + dy1 * xy;
266  * dy2 = dx1 * yx + dy1 * yy;
267  * </programlisting>
268  *
269  * Affine transformations are position invariant, so the same vector
270  * always transforms to the same vector. If (@x1,@y1) transforms
271  * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to
272  * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2.
273  *
274  * Since: 1.16
275  **/
276 void
277 pango_matrix_transform_distance (const PangoMatrix *matrix,
278                                  double            *dx,
279                                  double            *dy)
280 {
281   if (matrix)
282     {
283       double new_x, new_y;
284
285       new_x = (matrix->xx * *dx + matrix->xy * *dy);
286       new_y = (matrix->yx * *dx + matrix->yy * *dy);
287
288       *dx = new_x;
289       *dy = new_y;
290     }
291 }
292
293 /**
294  * pango_matrix_transform_point:
295  * @matrix: a #PangoMatrix, or %NULL
296  * @x: in/out X position
297  * @y: in/out Y position
298  *
299  * Transforms the point (@x, @y) by @matrix.
300  *
301  * Since: 1.16
302  **/
303 void
304 pango_matrix_transform_point (const PangoMatrix *matrix,
305                               double            *x,
306                               double            *y)
307 {
308   if (matrix)
309     {
310       pango_matrix_transform_distance (matrix, x, y);
311
312       *x += matrix->x0;
313       *y += matrix->y0;
314     }
315 }
316
317 /**
318  * pango_matrix_transform_rectangle:
319  * @matrix: a #PangoMatrix, or %NULL
320  * @rect: in/out bounding box in Pango units, or %NULL
321  *
322  * First transforms @rect using @matrix, then calculates the bounding box
323  * of the transformed rectangle.  The rectangle should be in Pango units.
324  *
325  * This function is useful for example when you want to draw a rotated
326  * @PangoLayout to an image buffer, and want to know how large the image
327  * should be and how much you should shift the layout when rendering.
328  *
329  * If you have a rectangle in device units (pixels), use
330  * pango_matrix_transform_pixel_rectangle().
331  *
332  * If you have the rectangle in Pango units and want to convert to
333  * transformed pixel bounding box, it is more accurate to transform it first
334  * (using this function) and pass the result to pango_extents_to_pixels(),
335  * first argument, for an inclusive rounded rectangle.
336  * However, there are valid reasons that you may want to convert
337  * to pixels first and then transform, for example when the transformed
338  * coordinates may overflow in Pango units (large matrix translation for
339  * example).
340  *
341  * Since: 1.16
342  **/
343 void
344 pango_matrix_transform_rectangle (const PangoMatrix *matrix,
345                                   PangoRectangle    *rect)
346 {
347   int i;
348   double quad_x[4], quad_y[4];
349   double dx1, dy1;
350   double dx2, dy2;
351   double min_x, max_x;
352   double min_y, max_y;
353
354   if (!rect || !matrix)
355     return;
356
357   quad_x[0] = pango_units_to_double (rect->x);
358   quad_y[0] = pango_units_to_double (rect->y);
359   pango_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]);
360
361   dx1 = pango_units_to_double (rect->width);
362   dy1 = 0;
363   pango_matrix_transform_distance (matrix, &dx1, &dy1);
364   quad_x[1] = quad_x[0] + dx1;
365   quad_y[1] = quad_y[0] + dy1;
366
367   dx2 = 0;
368   dy2 = pango_units_to_double (rect->height);
369   pango_matrix_transform_distance (matrix, &dx2, &dy2);
370   quad_x[2] = quad_x[0] + dx2;
371   quad_y[2] = quad_y[0] + dy2;
372
373   quad_x[3] = quad_x[0] + dx1 + dx2;
374   quad_y[3] = quad_y[0] + dy1 + dy2;
375
376   min_x = max_x = quad_x[0];
377   min_y = max_y = quad_y[0];
378
379   for (i=1; i < 4; i++) {
380       if (quad_x[i] < min_x)
381           min_x = quad_x[i];
382       else if (quad_x[i] > max_x)
383           max_x = quad_x[i];
384
385       if (quad_y[i] < min_y)
386           min_y = quad_y[i];
387       else if (quad_y[i] > max_y)
388           max_y = quad_y[i];
389   }
390
391   rect->x      = pango_units_from_double (min_x);
392   rect->y      = pango_units_from_double (min_y);
393   rect->width  = pango_units_from_double (max_x) - rect->x;
394   rect->height = pango_units_from_double (max_y) - rect->y;
395 }
396
397 /**
398  * pango_matrix_transform_pixel_rectangle:
399  * @matrix: a #PangoMatrix, or %NULL
400  * @rect: in/out bounding box in device units, or %NULL
401  *
402  * First transforms the @rect using @matrix, then calculates the bounding box
403  * of the transformed rectangle.  The rectangle should be in device units
404  * (pixels).
405  *
406  * This function is useful for example when you want to draw a rotated
407  * @PangoLayout to an image buffer, and want to know how large the image
408  * should be and how much you should shift the layout when rendering.
409  *
410  * For better accuracy, you should use pango_matrix_transform_rectangle() on
411  * original rectangle in Pango units and convert to pixels afterward
412  * using pango_extents_to_pixels()'s first argument.
413  *
414  * Since: 1.16
415  **/
416 void
417 pango_matrix_transform_pixel_rectangle (const PangoMatrix *matrix,
418                                         PangoRectangle    *rect)
419 {
420   int i;
421   double quad_x[4], quad_y[4];
422   double dx1, dy1;
423   double dx2, dy2;
424   double min_x, max_x;
425   double min_y, max_y;
426
427   if (!rect || !matrix)
428     return;
429
430   quad_x[0] = rect->x;
431   quad_y[0] = rect->y;
432   pango_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]);
433
434   dx1 = rect->width;
435   dy1 = 0;
436   pango_matrix_transform_distance (matrix, &dx1, &dy1);
437   quad_x[1] = quad_x[0] + dx1;
438   quad_y[1] = quad_y[0] + dy1;
439
440   dx2 = 0;
441   dy2 = rect->height;
442   pango_matrix_transform_distance (matrix, &dx2, &dy2);
443   quad_x[2] = quad_x[0] + dx2;
444   quad_y[2] = quad_y[0] + dy2;
445
446   quad_x[3] = quad_x[0] + dx1 + dx2;
447   quad_y[3] = quad_y[0] + dy1 + dy2;
448
449   min_x = max_x = quad_x[0];
450   min_y = max_y = quad_y[0];
451
452   for (i=1; i < 4; i++) {
453       if (quad_x[i] < min_x)
454           min_x = quad_x[i];
455       else if (quad_x[i] > max_x)
456           max_x = quad_x[i];
457
458       if (quad_y[i] < min_y)
459           min_y = quad_y[i];
460       else if (quad_y[i] > max_y)
461           max_y = quad_y[i];
462   }
463
464   rect->x      = floor (min_x);
465   rect->y      = floor (min_y);
466   rect->width  = ceil (max_x - rect->x);
467   rect->height = ceil (max_y - rect->y);
468 }