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 *
pango_matrix_copy(const PangoMatrix * matrix)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
pango_matrix_free(PangoMatrix * matrix)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
pango_matrix_translate(PangoMatrix * matrix,double tx,double ty)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
pango_matrix_scale(PangoMatrix * matrix,double scale_x,double scale_y)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
pango_matrix_rotate(PangoMatrix * matrix,double degrees)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 (r);
146 c = cos (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, &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
pango_matrix_concat(PangoMatrix * matrix,const PangoMatrix * new_matrix)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
pango_matrix_get_font_scale_factor(const PangoMatrix * matrix)203 pango_matrix_get_font_scale_factor (const PangoMatrix *matrix)
204 {
205 double yscale;
206 pango_matrix_get_font_scale_factors (matrix, NULL, &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
pango_matrix_get_font_scale_factors(const PangoMatrix * matrix,double * xscale,double * yscale)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 + 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_transform_distance:
267 * @matrix: (nullable): a `PangoMatrix`
268 * @dx: (inout): in/out X component of a distance vector
269 * @dy: (inout): in/out Y component of a distance vector
270 *
271 * Transforms the distance vector (@dx,@dy) by @matrix.
272 *
273 * This is similar to [method@Pango.Matrix.transform_point],
274 * except that the translation components of the transformation
275 * are ignored. The calculation of the returned vector is as follows:
276 *
277 * ```
278 * dx2 = dx1 * xx + dy1 * xy;
279 * dy2 = dx1 * yx + dy1 * yy;
280 * ```
281 *
282 * Affine transformations are position invariant, so the same vector
283 * always transforms to the same vector. If (@x1,@y1) transforms
284 * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to
285 * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2.
286 *
287 * Since: 1.16
288 */
289 void
pango_matrix_transform_distance(const PangoMatrix * matrix,double * dx,double * dy)290 pango_matrix_transform_distance (const PangoMatrix *matrix,
291 double *dx,
292 double *dy)
293 {
294 if (matrix)
295 {
296 double new_x, new_y;
297
298 new_x = (matrix->xx * *dx + matrix->xy * *dy);
299 new_y = (matrix->yx * *dx + matrix->yy * *dy);
300
301 *dx = new_x;
302 *dy = new_y;
303 }
304 }
305
306 /**
307 * pango_matrix_transform_point:
308 * @matrix: (nullable): a `PangoMatrix`
309 * @x: (inout): in/out X position
310 * @y: (inout): in/out Y position
311 *
312 * Transforms the point (@x, @y) by @matrix.
313 *
314 * Since: 1.16
315 **/
316 void
pango_matrix_transform_point(const PangoMatrix * matrix,double * x,double * y)317 pango_matrix_transform_point (const PangoMatrix *matrix,
318 double *x,
319 double *y)
320 {
321 if (matrix)
322 {
323 pango_matrix_transform_distance (matrix, x, y);
324
325 *x += matrix->x0;
326 *y += matrix->y0;
327 }
328 }
329
330 /**
331 * pango_matrix_transform_rectangle:
332 * @matrix: (nullable): a `PangoMatrix`
333 * @rect: (inout) (optional): in/out bounding box in Pango units
334 *
335 * First transforms @rect using @matrix, then calculates the bounding box
336 * of the transformed rectangle.
337 *
338 * This function is useful for example when you want to draw a rotated
339 * @PangoLayout to an image buffer, and want to know how large the image
340 * should be and how much you should shift the layout when rendering.
341 *
342 * If you have a rectangle in device units (pixels), use
343 * [method@Pango.Matrix.transform_pixel_rectangle].
344 *
345 * If you have the rectangle in Pango units and want to convert to
346 * transformed pixel bounding box, it is more accurate to transform it first
347 * (using this function) and pass the result to pango_extents_to_pixels(),
348 * first argument, for an inclusive rounded rectangle.
349 * However, there are valid reasons that you may want to convert
350 * to pixels first and then transform, for example when the transformed
351 * coordinates may overflow in Pango units (large matrix translation for
352 * example).
353 *
354 * Since: 1.16
355 */
356 void
pango_matrix_transform_rectangle(const PangoMatrix * matrix,PangoRectangle * rect)357 pango_matrix_transform_rectangle (const PangoMatrix *matrix,
358 PangoRectangle *rect)
359 {
360 int i;
361 double quad_x[4], quad_y[4];
362 double dx1, dy1;
363 double dx2, dy2;
364 double min_x, max_x;
365 double min_y, max_y;
366
367 if (!rect || !matrix)
368 return;
369
370 quad_x[0] = pango_units_to_double (rect->x);
371 quad_y[0] = pango_units_to_double (rect->y);
372 pango_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]);
373
374 dx1 = pango_units_to_double (rect->width);
375 dy1 = 0;
376 pango_matrix_transform_distance (matrix, &dx1, &dy1);
377 quad_x[1] = quad_x[0] + dx1;
378 quad_y[1] = quad_y[0] + dy1;
379
380 dx2 = 0;
381 dy2 = pango_units_to_double (rect->height);
382 pango_matrix_transform_distance (matrix, &dx2, &dy2);
383 quad_x[2] = quad_x[0] + dx2;
384 quad_y[2] = quad_y[0] + dy2;
385
386 quad_x[3] = quad_x[0] + dx1 + dx2;
387 quad_y[3] = quad_y[0] + dy1 + dy2;
388
389 min_x = max_x = quad_x[0];
390 min_y = max_y = quad_y[0];
391
392 for (i=1; i < 4; i++) {
393 if (quad_x[i] < min_x)
394 min_x = quad_x[i];
395 else if (quad_x[i] > max_x)
396 max_x = quad_x[i];
397
398 if (quad_y[i] < min_y)
399 min_y = quad_y[i];
400 else if (quad_y[i] > max_y)
401 max_y = quad_y[i];
402 }
403
404 rect->x = pango_units_from_double (min_x);
405 rect->y = pango_units_from_double (min_y);
406 rect->width = pango_units_from_double (max_x) - rect->x;
407 rect->height = pango_units_from_double (max_y) - rect->y;
408 }
409
410 /**
411 * pango_matrix_transform_pixel_rectangle:
412 * @matrix: (nullable): a `PangoMatrix`
413 * @rect: (inout) (optional): in/out bounding box in device units
414 *
415 * First transforms the @rect using @matrix, then calculates the bounding box
416 * of the transformed rectangle.
417 *
418 * This function is useful for example when you want to draw a rotated
419 * @PangoLayout to an image buffer, and want to know how large the image
420 * should be and how much you should shift the layout when rendering.
421 *
422 * For better accuracy, you should use [method@Pango.Matrix.transform_rectangle]
423 * on original rectangle in Pango units and convert to pixels afterward
424 * using [func@extents_to_pixels]'s first argument.
425 *
426 * Since: 1.16
427 */
428 void
pango_matrix_transform_pixel_rectangle(const PangoMatrix * matrix,PangoRectangle * rect)429 pango_matrix_transform_pixel_rectangle (const PangoMatrix *matrix,
430 PangoRectangle *rect)
431 {
432 int i;
433 double quad_x[4], quad_y[4];
434 double dx1, dy1;
435 double dx2, dy2;
436 double min_x, max_x;
437 double min_y, max_y;
438
439 if (!rect || !matrix)
440 return;
441
442 quad_x[0] = rect->x;
443 quad_y[0] = rect->y;
444 pango_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]);
445
446 dx1 = rect->width;
447 dy1 = 0;
448 pango_matrix_transform_distance (matrix, &dx1, &dy1);
449 quad_x[1] = quad_x[0] + dx1;
450 quad_y[1] = quad_y[0] + dy1;
451
452 dx2 = 0;
453 dy2 = rect->height;
454 pango_matrix_transform_distance (matrix, &dx2, &dy2);
455 quad_x[2] = quad_x[0] + dx2;
456 quad_y[2] = quad_y[0] + dy2;
457
458 quad_x[3] = quad_x[0] + dx1 + dx2;
459 quad_y[3] = quad_y[0] + dy1 + dy2;
460
461 min_x = max_x = quad_x[0];
462 min_y = max_y = quad_y[0];
463
464 for (i=1; i < 4; i++)
465 {
466 if (quad_x[i] < min_x)
467 min_x = quad_x[i];
468 else if (quad_x[i] > max_x)
469 max_x = quad_x[i];
470
471 if (quad_y[i] < min_y)
472 min_y = quad_y[i];
473 else if (quad_y[i] > max_y)
474 max_y = quad_y[i];
475 }
476
477 rect->x = floor (min_x);
478 rect->y = floor (min_y);
479 rect->width = ceil (max_x - rect->x);
480 rect->height = ceil (max_y - rect->y);
481 }
482