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 | G_DEFINE_BOXED_TYPE (PangoMatrix, pango_matrix, |
30 | pango_matrix_copy, |
31 | pango_matrix_free); |
32 | |
33 | /** |
34 | * pango_matrix_copy: |
35 | * @matrix: (nullable): a `PangoMatrix` |
36 | * |
37 | * Copies a `PangoMatrix`. |
38 | * |
39 | * Return value: (nullable): the newly allocated `PangoMatrix` |
40 | * |
41 | * Since: 1.6 |
42 | */ |
43 | PangoMatrix * |
44 | pango_matrix_copy (const PangoMatrix *matrix) |
45 | { |
46 | PangoMatrix *new_matrix; |
47 | |
48 | if (matrix == NULL) |
49 | return NULL; |
50 | |
51 | new_matrix = g_slice_new (PangoMatrix); |
52 | |
53 | *new_matrix = *matrix; |
54 | |
55 | return new_matrix; |
56 | } |
57 | |
58 | /** |
59 | * pango_matrix_free: |
60 | * @matrix: (nullable): a `PangoMatrix`, may be %NULL |
61 | * |
62 | * Free a `PangoMatrix`. |
63 | * |
64 | * Since: 1.6 |
65 | */ |
66 | void |
67 | pango_matrix_free (PangoMatrix *matrix) |
68 | { |
69 | if (matrix == NULL) |
70 | return; |
71 | |
72 | g_slice_free (PangoMatrix, matrix); |
73 | } |
74 | |
75 | /** |
76 | * pango_matrix_translate: |
77 | * @matrix: a `PangoMatrix` |
78 | * @tx: amount to translate in the X direction |
79 | * @ty: amount to translate in the Y direction |
80 | * |
81 | * Changes the transformation represented by @matrix to be the |
82 | * transformation given by first translating by (@tx, @ty) |
83 | * then applying the original transformation. |
84 | * |
85 | * Since: 1.6 |
86 | */ |
87 | void |
88 | pango_matrix_translate (PangoMatrix *matrix, |
89 | double tx, |
90 | double ty) |
91 | { |
92 | g_return_if_fail (matrix != NULL); |
93 | |
94 | matrix->x0 = matrix->xx * tx + matrix->xy * ty + matrix->x0; |
95 | matrix->y0 = matrix->yx * tx + matrix->yy * ty + matrix->y0; |
96 | } |
97 | |
98 | /** |
99 | * pango_matrix_scale: |
100 | * @matrix: a `PangoMatrix` |
101 | * @scale_x: amount to scale by in X direction |
102 | * @scale_y: amount to scale by in Y direction |
103 | * |
104 | * Changes the transformation represented by @matrix to be the |
105 | * transformation given by first scaling by @sx in the X direction |
106 | * and @sy in the Y direction then applying the original |
107 | * transformation. |
108 | * |
109 | * Since: 1.6 |
110 | */ |
111 | void |
112 | pango_matrix_scale (PangoMatrix *matrix, |
113 | double scale_x, |
114 | double scale_y) |
115 | { |
116 | g_return_if_fail (matrix != NULL); |
117 | |
118 | matrix->xx *= scale_x; |
119 | matrix->xy *= scale_y; |
120 | matrix->yx *= scale_x; |
121 | matrix->yy *= scale_y; |
122 | } |
123 | |
124 | /** |
125 | * pango_matrix_rotate: |
126 | * @matrix: a `PangoMatrix` |
127 | * @degrees: degrees to rotate counter-clockwise |
128 | * |
129 | * Changes the transformation represented by @matrix to be the |
130 | * transformation given by first rotating by @degrees degrees |
131 | * counter-clockwise then applying the original transformation. |
132 | * |
133 | * Since: 1.6 |
134 | */ |
135 | void |
136 | pango_matrix_rotate (PangoMatrix *matrix, |
137 | double degrees) |
138 | { |
139 | PangoMatrix tmp; |
140 | gdouble r, s, c; |
141 | |
142 | g_return_if_fail (matrix != NULL); |
143 | |
144 | r = degrees * (G_PI / 180.); |
145 | s = sin (x: r); |
146 | c = cos (x: r); |
147 | |
148 | tmp.xx = c; |
149 | tmp.xy = s; |
150 | tmp.yx = -s; |
151 | tmp.yy = c; |
152 | tmp.x0 = 0; |
153 | tmp.y0 = 0; |
154 | |
155 | pango_matrix_concat (matrix, new_matrix: &tmp); |
156 | } |
157 | |
158 | /** |
159 | * pango_matrix_concat: |
160 | * @matrix: a `PangoMatrix` |
161 | * @new_matrix: a `PangoMatrix` |
162 | * |
163 | * Changes the transformation represented by @matrix to be the |
164 | * transformation given by first applying transformation |
165 | * given by @new_matrix then applying the original transformation. |
166 | * |
167 | * Since: 1.6 |
168 | */ |
169 | void |
170 | pango_matrix_concat (PangoMatrix *matrix, |
171 | const PangoMatrix *new_matrix) |
172 | { |
173 | PangoMatrix tmp; |
174 | |
175 | g_return_if_fail (matrix != NULL); |
176 | |
177 | tmp = *matrix; |
178 | |
179 | matrix->xx = tmp.xx * new_matrix->xx + tmp.xy * new_matrix->yx; |
180 | matrix->xy = tmp.xx * new_matrix->xy + tmp.xy * new_matrix->yy; |
181 | matrix->yx = tmp.yx * new_matrix->xx + tmp.yy * new_matrix->yx; |
182 | matrix->yy = tmp.yx * new_matrix->xy + tmp.yy * new_matrix->yy; |
183 | matrix->x0 = tmp.xx * new_matrix->x0 + tmp.xy * new_matrix->y0 + tmp.x0; |
184 | matrix->y0 = tmp.yx * new_matrix->x0 + tmp.yy * new_matrix->y0 + tmp.y0; |
185 | } |
186 | |
187 | /** |
188 | * pango_matrix_get_font_scale_factor: |
189 | * @matrix: (nullable): a `PangoMatrix`, may be %NULL |
190 | * |
191 | * Returns the scale factor of a matrix on the height of the font. |
192 | * |
193 | * That is, the scale factor in the direction perpendicular to the |
194 | * vector that the X coordinate is mapped to. If the scale in the X |
195 | * coordinate is needed as well, use [method@Pango.Matrix.get_font_scale_factors]. |
196 | * |
197 | * Return value: the scale factor of @matrix on the height of the font, |
198 | * or 1.0 if @matrix is %NULL. |
199 | * |
200 | * Since: 1.12 |
201 | */ |
202 | double |
203 | pango_matrix_get_font_scale_factor (const PangoMatrix *matrix) |
204 | { |
205 | double yscale; |
206 | pango_matrix_get_font_scale_factors (matrix, NULL, yscale: &yscale); |
207 | return yscale; |
208 | } |
209 | |
210 | /** |
211 | * pango_matrix_get_font_scale_factors: |
212 | * @matrix: (nullable): a `PangoMatrix` |
213 | * @xscale: (out) (optional): output scale factor in the x direction |
214 | * @yscale: (out) (optional): output scale factor perpendicular to the x direction |
215 | * |
216 | * Calculates the scale factor of a matrix on the width and height of the font. |
217 | * |
218 | * That is, @xscale is the scale factor in the direction of the X coordinate, |
219 | * and @yscale is the scale factor in the direction perpendicular to the |
220 | * vector that the X coordinate is mapped to. |
221 | * |
222 | * Note that output numbers will always be non-negative. |
223 | * |
224 | * Since: 1.38 |
225 | **/ |
226 | void |
227 | pango_matrix_get_font_scale_factors (const PangoMatrix *matrix, |
228 | double *xscale, double *yscale) |
229 | { |
230 | /* |
231 | * Based on cairo-matrix.c:_cairo_matrix_compute_scale_factors() |
232 | * |
233 | * Copyright 2005, Keith Packard |
234 | */ |
235 | double major = 1., minor = 1.; |
236 | |
237 | if (matrix) |
238 | { |
239 | double x = matrix->xx; |
240 | double y = matrix->yx; |
241 | major = sqrt (x: x*x + y*y); |
242 | |
243 | if (major) |
244 | { |
245 | double det = matrix->xx * matrix->yy - matrix->yx * matrix->xy; |
246 | |
247 | /* |
248 | * ignore mirroring |
249 | */ |
250 | if (det < 0) |
251 | det = - det; |
252 | |
253 | minor = det / major; |
254 | } |
255 | else |
256 | minor = 0.; |
257 | } |
258 | |
259 | if (xscale) |
260 | *xscale = major; |
261 | if (yscale) |
262 | *yscale = minor; |
263 | } |
264 | |
265 | /** |
266 | * pango_matrix_get_slant_ratio: |
267 | * @matrix: a `PangoMatrix` |
268 | * |
269 | * Gets the slant ratio of a matrix. |
270 | * |
271 | * For a simple shear matrix in the form: |
272 | * |
273 | * 1 λ |
274 | * 0 1 |
275 | * |
276 | * this is simply λ. |
277 | * |
278 | * Returns: the slant ratio of @matrix |
279 | * |
280 | * Since: 1.50 |
281 | */ |
282 | double |
283 | pango_matrix_get_slant_ratio (const PangoMatrix *matrix) |
284 | { |
285 | double x0, y0; |
286 | double x1, y1; |
287 | |
288 | x0 = 0; |
289 | y0 = 1; |
290 | pango_matrix_transform_distance (matrix, dx: &x0, dy: &y0); |
291 | |
292 | x1 = 1; |
293 | y1 = 0; |
294 | pango_matrix_transform_distance (matrix, dx: &x1, dy: &y1); |
295 | |
296 | return (x0 * x1 + y0 * y1) / (x0 * x0 + y0 * y0); |
297 | } |
298 | |
299 | /** |
300 | * pango_matrix_transform_distance: |
301 | * @matrix: (nullable): a `PangoMatrix` |
302 | * @dx: (inout): in/out X component of a distance vector |
303 | * @dy: (inout): in/out Y component of a distance vector |
304 | * |
305 | * Transforms the distance vector (@dx,@dy) by @matrix. |
306 | * |
307 | * This is similar to [method@Pango.Matrix.transform_point], |
308 | * except that the translation components of the transformation |
309 | * are ignored. The calculation of the returned vector is as follows: |
310 | * |
311 | * ``` |
312 | * dx2 = dx1 * xx + dy1 * xy; |
313 | * dy2 = dx1 * yx + dy1 * yy; |
314 | * ``` |
315 | * |
316 | * Affine transformations are position invariant, so the same vector |
317 | * always transforms to the same vector. If (@x1,@y1) transforms |
318 | * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to |
319 | * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. |
320 | * |
321 | * Since: 1.16 |
322 | */ |
323 | void |
324 | pango_matrix_transform_distance (const PangoMatrix *matrix, |
325 | double *dx, |
326 | double *dy) |
327 | { |
328 | if (matrix) |
329 | { |
330 | double new_x, new_y; |
331 | |
332 | new_x = (matrix->xx * *dx + matrix->xy * *dy); |
333 | new_y = (matrix->yx * *dx + matrix->yy * *dy); |
334 | |
335 | *dx = new_x; |
336 | *dy = new_y; |
337 | } |
338 | } |
339 | |
340 | /** |
341 | * pango_matrix_transform_point: |
342 | * @matrix: (nullable): a `PangoMatrix` |
343 | * @x: (inout): in/out X position |
344 | * @y: (inout): in/out Y position |
345 | * |
346 | * Transforms the point (@x, @y) by @matrix. |
347 | * |
348 | * Since: 1.16 |
349 | **/ |
350 | void |
351 | pango_matrix_transform_point (const PangoMatrix *matrix, |
352 | double *x, |
353 | double *y) |
354 | { |
355 | if (matrix) |
356 | { |
357 | pango_matrix_transform_distance (matrix, dx: x, dy: y); |
358 | |
359 | *x += matrix->x0; |
360 | *y += matrix->y0; |
361 | } |
362 | } |
363 | |
364 | /** |
365 | * pango_matrix_transform_rectangle: |
366 | * @matrix: (nullable): a `PangoMatrix` |
367 | * @rect: (inout) (optional): in/out bounding box in Pango units |
368 | * |
369 | * First transforms @rect using @matrix, then calculates the bounding box |
370 | * of the transformed rectangle. |
371 | * |
372 | * This function is useful for example when you want to draw a rotated |
373 | * @PangoLayout to an image buffer, and want to know how large the image |
374 | * should be and how much you should shift the layout when rendering. |
375 | * |
376 | * If you have a rectangle in device units (pixels), use |
377 | * [method@Pango.Matrix.transform_pixel_rectangle]. |
378 | * |
379 | * If you have the rectangle in Pango units and want to convert to |
380 | * transformed pixel bounding box, it is more accurate to transform it first |
381 | * (using this function) and pass the result to pango_extents_to_pixels(), |
382 | * first argument, for an inclusive rounded rectangle. |
383 | * However, there are valid reasons that you may want to convert |
384 | * to pixels first and then transform, for example when the transformed |
385 | * coordinates may overflow in Pango units (large matrix translation for |
386 | * example). |
387 | * |
388 | * Since: 1.16 |
389 | */ |
390 | void |
391 | pango_matrix_transform_rectangle (const PangoMatrix *matrix, |
392 | PangoRectangle *rect) |
393 | { |
394 | int i; |
395 | double quad_x[4], quad_y[4]; |
396 | double dx1, dy1; |
397 | double dx2, dy2; |
398 | double min_x, max_x; |
399 | double min_y, max_y; |
400 | |
401 | if (!rect || !matrix) |
402 | return; |
403 | |
404 | quad_x[0] = pango_units_to_double (i: rect->x); |
405 | quad_y[0] = pango_units_to_double (i: rect->y); |
406 | pango_matrix_transform_point (matrix, x: &quad_x[0], y: &quad_y[0]); |
407 | |
408 | dx1 = pango_units_to_double (i: rect->width); |
409 | dy1 = 0; |
410 | pango_matrix_transform_distance (matrix, dx: &dx1, dy: &dy1); |
411 | quad_x[1] = quad_x[0] + dx1; |
412 | quad_y[1] = quad_y[0] + dy1; |
413 | |
414 | dx2 = 0; |
415 | dy2 = pango_units_to_double (i: rect->height); |
416 | pango_matrix_transform_distance (matrix, dx: &dx2, dy: &dy2); |
417 | quad_x[2] = quad_x[0] + dx2; |
418 | quad_y[2] = quad_y[0] + dy2; |
419 | |
420 | quad_x[3] = quad_x[0] + dx1 + dx2; |
421 | quad_y[3] = quad_y[0] + dy1 + dy2; |
422 | |
423 | min_x = max_x = quad_x[0]; |
424 | min_y = max_y = quad_y[0]; |
425 | |
426 | for (i=1; i < 4; i++) { |
427 | if (quad_x[i] < min_x) |
428 | min_x = quad_x[i]; |
429 | else if (quad_x[i] > max_x) |
430 | max_x = quad_x[i]; |
431 | |
432 | if (quad_y[i] < min_y) |
433 | min_y = quad_y[i]; |
434 | else if (quad_y[i] > max_y) |
435 | max_y = quad_y[i]; |
436 | } |
437 | |
438 | rect->x = pango_units_from_double (d: min_x); |
439 | rect->y = pango_units_from_double (d: min_y); |
440 | rect->width = pango_units_from_double (d: max_x) - rect->x; |
441 | rect->height = pango_units_from_double (d: max_y) - rect->y; |
442 | } |
443 | |
444 | /** |
445 | * pango_matrix_transform_pixel_rectangle: |
446 | * @matrix: (nullable): a `PangoMatrix` |
447 | * @rect: (inout) (optional): in/out bounding box in device units |
448 | * |
449 | * First transforms the @rect using @matrix, then calculates the bounding box |
450 | * of the transformed rectangle. |
451 | * |
452 | * This function is useful for example when you want to draw a rotated |
453 | * @PangoLayout to an image buffer, and want to know how large the image |
454 | * should be and how much you should shift the layout when rendering. |
455 | * |
456 | * For better accuracy, you should use [method@Pango.Matrix.transform_rectangle] |
457 | * on original rectangle in Pango units and convert to pixels afterward |
458 | * using [func@extents_to_pixels]'s first argument. |
459 | * |
460 | * Since: 1.16 |
461 | */ |
462 | void |
463 | pango_matrix_transform_pixel_rectangle (const PangoMatrix *matrix, |
464 | PangoRectangle *rect) |
465 | { |
466 | int i; |
467 | double quad_x[4], quad_y[4]; |
468 | double dx1, dy1; |
469 | double dx2, dy2; |
470 | double min_x, max_x; |
471 | double min_y, max_y; |
472 | |
473 | if (!rect || !matrix) |
474 | return; |
475 | |
476 | quad_x[0] = rect->x; |
477 | quad_y[0] = rect->y; |
478 | pango_matrix_transform_point (matrix, x: &quad_x[0], y: &quad_y[0]); |
479 | |
480 | dx1 = rect->width; |
481 | dy1 = 0; |
482 | pango_matrix_transform_distance (matrix, dx: &dx1, dy: &dy1); |
483 | quad_x[1] = quad_x[0] + dx1; |
484 | quad_y[1] = quad_y[0] + dy1; |
485 | |
486 | dx2 = 0; |
487 | dy2 = rect->height; |
488 | pango_matrix_transform_distance (matrix, dx: &dx2, dy: &dy2); |
489 | quad_x[2] = quad_x[0] + dx2; |
490 | quad_y[2] = quad_y[0] + dy2; |
491 | |
492 | quad_x[3] = quad_x[0] + dx1 + dx2; |
493 | quad_y[3] = quad_y[0] + dy1 + dy2; |
494 | |
495 | min_x = max_x = quad_x[0]; |
496 | min_y = max_y = quad_y[0]; |
497 | |
498 | for (i=1; i < 4; i++) |
499 | { |
500 | if (quad_x[i] < min_x) |
501 | min_x = quad_x[i]; |
502 | else if (quad_x[i] > max_x) |
503 | max_x = quad_x[i]; |
504 | |
505 | if (quad_y[i] < min_y) |
506 | min_y = quad_y[i]; |
507 | else if (quad_y[i] > max_y) |
508 | max_y = quad_y[i]; |
509 | } |
510 | |
511 | rect->x = floor (x: min_x); |
512 | rect->y = floor (x: min_y); |
513 | rect->width = ceil (x: max_x - rect->x); |
514 | rect->height = ceil (x: max_y - rect->y); |
515 | } |
516 | |