comparison mupdf-source/thirdparty/leptonica/src/affinecompose.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
27 /*!
28 * \file affinecompose.c
29 * <pre>
30 *
31 * Composable coordinate transforms
32 * l_float32 *createMatrix2dTranslate()
33 * l_float32 *createMatrix2dScale()
34 * l_float32 *createMatrix2dRotate()
35 *
36 * Special coordinate transforms on pta
37 * PTA *ptaTranslate()
38 * PTA *ptaScale()
39 * PTA *ptaRotate()
40 *
41 * Special coordinate transforms on boxa
42 * BOXA *boxaTranslate()
43 * BOXA *boxaScale()
44 * BOXA *boxaRotate()
45 *
46 * General coordinate transform on pta and boxa
47 * PTA *ptaAffineTransform()
48 * BOXA *boxaAffineTransform()
49 *
50 * Matrix operations
51 * l_int32 l_productMatVec()
52 * l_int32 l_productMat2()
53 * l_int32 l_productMat3()
54 * l_int32 l_productMat4()
55 * </pre>
56 */
57
58 #ifdef HAVE_CONFIG_H
59 #include <config_auto.h>
60 #endif /* HAVE_CONFIG_H */
61
62 #include <math.h>
63 #include "allheaders.h"
64
65 /*-------------------------------------------------------------*
66 * Composable coordinate transforms *
67 *-------------------------------------------------------------*/
68 /*!
69 * \brief createMatrix2dTranslate()
70 *
71 * \param[in] transx x component of translation wrt. the origin
72 * \param[in] transy y component of translation wrt. the origin
73 * \return 3x3 transform matrix, or NULL on error
74 *
75 * <pre>
76 * Notes:
77 * (1) The translation is equivalent to:
78 * v' = Av
79 * where v and v' are 1x3 column vectors in the form
80 * v = [x, y, 1]^ ^ denotes transpose
81 * and the affine translation matrix is
82 * A = [ 1 0 tx
83 * 0 1 ty
84 * 0 0 1 ]
85 *
86 * (2) We consider translation as with respect to a fixed origin.
87 * In a clipping operation, the origin moves and the points
88 * are fixed, and you use (-tx, -ty) where (tx, ty) is the
89 * translation vector of the origin.
90 * </pre>
91 */
92 l_float32 *
93 createMatrix2dTranslate(l_float32 transx,
94 l_float32 transy)
95 {
96 l_float32 *mat;
97
98 mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32));
99 mat[0] = mat[4] = mat[8] = 1;
100 mat[2] = transx;
101 mat[5] = transy;
102 return mat;
103 }
104
105
106 /*!
107 * \brief createMatrix2dScale()
108 *
109 * \param[in] scalex horizontal scale factor
110 * \param[in] scaley vertical scale factor
111 * \return 3x3 transform matrix, or NULL on error
112 *
113 * <pre>
114 * Notes:
115 * (1) The scaling is equivalent to:
116 * v' = Av
117 * where v and v' are 1x3 column vectors in the form
118 * v = [x, y, 1]^ ^ denotes transpose
119 * and the affine scaling matrix is
120 * A = [ sx 0 0
121 * 0 sy 0
122 * 0 0 1 ]
123 *
124 * (2) We consider scaling as with respect to a fixed origin.
125 * In other words, the origin is the only point that doesn't
126 * move in the scaling transform.
127 * </pre>
128 */
129 l_float32 *
130 createMatrix2dScale(l_float32 scalex,
131 l_float32 scaley)
132 {
133 l_float32 *mat;
134
135 mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32));
136 mat[0] = scalex;
137 mat[4] = scaley;
138 mat[8] = 1;
139 return mat;
140 }
141
142
143 /*!
144 * \brief createMatrix2dRotate()
145 *
146 * \param[in] xc, yc location of center of rotation
147 * \param[in] angle rotation in radians; clockwise is positive
148 * \return 3x3 transform matrix, or NULL on error
149 *
150 * <pre>
151 * Notes:
152 * (1) The rotation is equivalent to:
153 * v' = Av
154 * where v and v' are 1x3 column vectors in the form
155 * v = [x, y, 1]^ ^ denotes transpose
156 * and the affine rotation matrix is
157 * A = [ cosa -sina xc*1-cosa + yc*sina
158 * sina cosa yc*1-cosa - xc*sina
159 * 0 0 1 ]
160 *
161 * If the rotation is about the origin, xc, yc) = (0, 0 and
162 * this simplifies to
163 * A = [ cosa -sina 0
164 * sina cosa 0
165 * 0 0 1 ]
166 *
167 * These relations follow from the following equations, which
168 * you can convince yourself are correct as follows. Draw a
169 * circle centered on xc,yc) and passing through (x,y), with
170 * (x',y') on the arc at an angle 'a' clockwise from (x,y).
171 * [ Hint: cosa + b = cosa * cosb - sina * sinb
172 * sina + b = sina * cosb + cosa * sinb ]
173 *
174 * x' - xc = x - xc) * cosa - (y - yc * sina
175 * y' - yc = x - xc) * sina + (y - yc * cosa
176 * </pre>
177 */
178 l_float32 *
179 createMatrix2dRotate(l_float32 xc,
180 l_float32 yc,
181 l_float32 angle)
182 {
183 l_float32 sina, cosa;
184 l_float32 *mat;
185
186 mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32));
187 sina = sin(angle);
188 cosa = cos(angle);
189 mat[0] = mat[4] = cosa;
190 mat[1] = -sina;
191 mat[2] = xc * (1.0 - cosa) + yc * sina;
192 mat[3] = sina;
193 mat[5] = yc * (1.0 - cosa) - xc * sina;
194 mat[8] = 1;
195 return mat;
196 }
197
198
199
200 /*-------------------------------------------------------------*
201 * Special coordinate transforms on pta *
202 *-------------------------------------------------------------*/
203 /*!
204 * \brief ptaTranslate()
205 *
206 * \param[in] ptas for initial points
207 * \param[in] transx x component of translation wrt. the origin
208 * \param[in] transy y component of translation wrt. the origin
209 * \return ptad translated points, or NULL on error
210 *
211 * <pre>
212 * Notes:
213 * (1) See createMatrix2dTranslate() for details of transform.
214 * </pre>
215 */
216 PTA *
217 ptaTranslate(PTA *ptas,
218 l_float32 transx,
219 l_float32 transy)
220 {
221 l_int32 i, npts;
222 l_float32 x, y;
223 PTA *ptad;
224
225 if (!ptas)
226 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL);
227
228 npts = ptaGetCount(ptas);
229 if ((ptad = ptaCreate(npts)) == NULL)
230 return (PTA *)ERROR_PTR("ptad not made", __func__, NULL);
231 for (i = 0; i < npts; i++) {
232 ptaGetPt(ptas, i, &x, &y);
233 ptaAddPt(ptad, x + transx, y + transy);
234 }
235
236 return ptad;
237 }
238
239
240 /*!
241 * \brief ptaScale()
242 *
243 * \param[in] ptas for initial points
244 * \param[in] scalex horizontal scale factor
245 * \param[in] scaley vertical scale factor
246 * \return 0 if OK; 1 on error
247 *
248 * <pre>
249 * Notes:
250 * (1) See createMatrix2dScale() for details of transform.
251 * </pre>
252 */
253 PTA *
254 ptaScale(PTA *ptas,
255 l_float32 scalex,
256 l_float32 scaley)
257 {
258 l_int32 i, npts;
259 l_float32 x, y;
260 PTA *ptad;
261
262 if (!ptas)
263 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL);
264
265 npts = ptaGetCount(ptas);
266 if ((ptad = ptaCreate(npts)) == NULL)
267 return (PTA *)ERROR_PTR("ptad not made", __func__, NULL);
268 for (i = 0; i < npts; i++) {
269 ptaGetPt(ptas, i, &x, &y);
270 ptaAddPt(ptad, scalex * x, scaley * y);
271 }
272
273 return ptad;
274 }
275
276
277 /*!
278 * \brief ptaRotate()
279 *
280 * \param[in] ptas for initial points
281 * \param[in] xc, yc location of center of rotation
282 * \param[in] angle rotation in radians; clockwise is positive
283 * \return ptad rotated pta, or NULL on error
284 *
285 * <pre>
286 * Notes;
287 * (1) See createMatrix2dRotate() for details of transform.
288 * (2) This transform can be thought of as composed of the
289 * sum of two parts:
290 * a) an (x,y)-dependent rotation about the origin:
291 * xr = x * cosa - y * sina
292 * yr = x * sina + y * cosa
293 * b) an (x,y)-independent translation that depends on the
294 * rotation center and the angle:
295 * xt = xc - xc * cosa + yc * sina
296 * yt = yc - xc * sina - yc * cosa
297 * The translation part (xt,yt) is equal to the difference
298 * between the center (xc,yc) and the location of the
299 * center after it is rotated about the origin.
300 * </pre>
301 */
302 PTA *
303 ptaRotate(PTA *ptas,
304 l_float32 xc,
305 l_float32 yc,
306 l_float32 angle)
307 {
308 l_int32 i, npts;
309 l_float32 x, y, xp, yp, sina, cosa;
310 PTA *ptad;
311
312 if (!ptas)
313 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL);
314
315 npts = ptaGetCount(ptas);
316 if ((ptad = ptaCreate(npts)) == NULL)
317 return (PTA *)ERROR_PTR("ptad not made", __func__, NULL);
318 sina = sin(angle);
319 cosa = cos(angle);
320 for (i = 0; i < npts; i++) {
321 ptaGetPt(ptas, i, &x, &y);
322 xp = xc + (x - xc) * cosa - (y - yc) * sina;
323 yp = yc + (x - xc) * sina + (y - yc) * cosa;
324 ptaAddPt(ptad, xp, yp);
325 }
326
327 return ptad;
328 }
329
330
331 /*-------------------------------------------------------------*
332 * Special coordinate transforms on boxa *
333 *-------------------------------------------------------------*/
334 /*!
335 * \brief boxaTranslate()
336 *
337 * \param[in] boxas
338 * \param[in] transx x component of translation wrt. the origin
339 * \param[in] transy y component of translation wrt. the origin
340 * \return boxad translated boxas, or NULL on error
341 *
342 * Notes:
343 * (1) See createMatrix2dTranslate() for details of transform.
344 */
345 BOXA *
346 boxaTranslate(BOXA *boxas,
347 l_float32 transx,
348 l_float32 transy)
349 {
350 PTA *ptas, *ptad;
351 BOXA *boxad;
352
353 if (!boxas)
354 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
355
356 ptas = boxaConvertToPta(boxas, 4);
357 ptad = ptaTranslate(ptas, transx, transy);
358 boxad = ptaConvertToBoxa(ptad, 4);
359 ptaDestroy(&ptas);
360 ptaDestroy(&ptad);
361 return boxad;
362 }
363
364
365 /*!
366 * \brief boxaScale()
367 *
368 * \param[in] boxas
369 * \param[in] scalex horizontal scale factor
370 * \param[in] scaley vertical scale factor
371 * \return boxad scaled boxas, or NULL on error
372 *
373 * Notes:
374 * (1) See createMatrix2dScale() for details of transform.
375 */
376 BOXA *
377 boxaScale(BOXA *boxas,
378 l_float32 scalex,
379 l_float32 scaley)
380 {
381 PTA *ptas, *ptad;
382 BOXA *boxad;
383
384 if (!boxas)
385 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
386
387 ptas = boxaConvertToPta(boxas, 4);
388 ptad = ptaScale(ptas, scalex, scaley);
389 boxad = ptaConvertToBoxa(ptad, 4);
390 ptaDestroy(&ptas);
391 ptaDestroy(&ptad);
392 return boxad;
393 }
394
395
396 /*!
397 * \brief boxaRotate()
398 *
399 * \param[in] boxas
400 * \param[in] xc, yc location of center of rotation
401 * \param[in] angle rotation in radians; clockwise is positive
402 * \return boxad rotated boxas, or NULL on error
403 *
404 * Notes:
405 * (1) See createMatrix2dRotate() for details of transform.
406 */
407 BOXA *
408 boxaRotate(BOXA *boxas,
409 l_float32 xc,
410 l_float32 yc,
411 l_float32 angle)
412 {
413 PTA *ptas, *ptad;
414 BOXA *boxad;
415
416 if (!boxas)
417 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
418
419 ptas = boxaConvertToPta(boxas, 4);
420 ptad = ptaRotate(ptas, xc, yc, angle);
421 boxad = ptaConvertToBoxa(ptad, 4);
422 ptaDestroy(&ptas);
423 ptaDestroy(&ptad);
424 return boxad;
425 }
426
427
428 /*-------------------------------------------------------------*
429 * General affine coordinate transform *
430 *-------------------------------------------------------------*/
431 /*!
432 * \brief ptaAffineTransform()
433 *
434 * \param[in] ptas for initial points
435 * \param[in] mat 3x3 transform matrix; canonical form
436 * \return ptad transformed points, or NULL on error
437 */
438 PTA *
439 ptaAffineTransform(PTA *ptas,
440 l_float32 *mat)
441 {
442 l_int32 i, npts;
443 l_float32 vecs[3], vecd[3];
444 PTA *ptad;
445
446 if (!ptas)
447 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL);
448 if (!mat)
449 return (PTA *)ERROR_PTR("transform not defined", __func__, NULL);
450
451 vecs[2] = 1;
452 npts = ptaGetCount(ptas);
453 if ((ptad = ptaCreate(npts)) == NULL)
454 return (PTA *)ERROR_PTR("ptad not made", __func__, NULL);
455 for (i = 0; i < npts; i++) {
456 ptaGetPt(ptas, i, &vecs[0], &vecs[1]);
457 l_productMatVec(mat, vecs, vecd, 3);
458 ptaAddPt(ptad, vecd[0], vecd[1]);
459 }
460
461 return ptad;
462 }
463
464
465 /*!
466 * \brief boxaAffineTransform()
467 *
468 * \param[in] boxas
469 * \param[in] mat 3x3 transform matrix; canonical form
470 * \return boxad transformed boxas, or NULL on error
471 */
472 BOXA *
473 boxaAffineTransform(BOXA *boxas,
474 l_float32 *mat)
475 {
476 PTA *ptas, *ptad;
477 BOXA *boxad;
478
479 if (!boxas)
480 return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL);
481 if (!mat)
482 return (BOXA *)ERROR_PTR("transform not defined", __func__, NULL);
483
484 ptas = boxaConvertToPta(boxas, 4);
485 ptad = ptaAffineTransform(ptas, mat);
486 boxad = ptaConvertToBoxa(ptad, 4);
487 ptaDestroy(&ptas);
488 ptaDestroy(&ptad);
489 return boxad;
490 }
491
492
493 /*-------------------------------------------------------------*
494 * Matrix operations *
495 *-------------------------------------------------------------*/
496 /*!
497 * \brief l_productMatVec()
498 *
499 * \param[in] mat square matrix, as a 1-dimensional %size^2 array
500 * \param[in] vecs input column vector of length %size
501 * \param[in] vecd result column vector
502 * \param[in] size matrix is %size x %size; vectors are length %size
503 * \return 0 if OK, 1 on error
504 */
505 l_ok
506 l_productMatVec(l_float32 *mat,
507 l_float32 *vecs,
508 l_float32 *vecd,
509 l_int32 size)
510 {
511 l_int32 i, j;
512
513 if (!mat)
514 return ERROR_INT("matrix not defined", __func__, 1);
515 if (!vecs)
516 return ERROR_INT("input vector not defined", __func__, 1);
517 if (!vecd)
518 return ERROR_INT("result vector not defined", __func__, 1);
519
520 for (i = 0; i < size; i++) {
521 vecd[i] = 0;
522 for (j = 0; j < size; j++) {
523 vecd[i] += mat[size * i + j] * vecs[j];
524 }
525 }
526 return 0;
527 }
528
529
530 /*!
531 * \brief l_productMat2()
532 *
533 * \param[in] mat1 square matrix, as a 1-dimensional size^2 array
534 * \param[in] mat2 square matrix, as a 1-dimensional size^2 array
535 * \param[in] matd square matrix; product stored here
536 * \param[in] size of matrices
537 * \return 0 if OK, 1 on error
538 */
539 l_ok
540 l_productMat2(l_float32 *mat1,
541 l_float32 *mat2,
542 l_float32 *matd,
543 l_int32 size)
544 {
545 l_int32 i, j, k, index;
546
547 if (!mat1)
548 return ERROR_INT("matrix 1 not defined", __func__, 1);
549 if (!mat2)
550 return ERROR_INT("matrix 2 not defined", __func__, 1);
551 if (!matd)
552 return ERROR_INT("result matrix not defined", __func__, 1);
553
554 for (i = 0; i < size; i++) {
555 for (j = 0; j < size; j++) {
556 index = size * i + j;
557 matd[index] = 0;
558 for (k = 0; k < size; k++)
559 matd[index] += mat1[size * i + k] * mat2[size * k + j];
560 }
561 }
562 return 0;
563 }
564
565
566 /*!
567 * \brief l_productMat3()
568 *
569 * \param[in] mat1 square matrix, as a 1-dimensional size^2 array
570 * \param[in] mat2 square matrix, as a 1-dimensional size^2 array
571 * \param[in] mat3 square matrix, as a 1-dimensional size^2 array
572 * \param[in] matd square matrix; product stored here
573 * \param[in] size of matrices
574 * \return 0 if OK, 1 on error
575 */
576 l_ok
577 l_productMat3(l_float32 *mat1,
578 l_float32 *mat2,
579 l_float32 *mat3,
580 l_float32 *matd,
581 l_int32 size)
582 {
583 l_float32 *matt;
584
585 if (!mat1)
586 return ERROR_INT("matrix 1 not defined", __func__, 1);
587 if (!mat2)
588 return ERROR_INT("matrix 2 not defined", __func__, 1);
589 if (!mat3)
590 return ERROR_INT("matrix 3 not defined", __func__, 1);
591 if (!matd)
592 return ERROR_INT("result matrix not defined", __func__, 1);
593
594 if ((matt = (l_float32 *)LEPT_CALLOC((size_t)size * size,
595 sizeof(l_float32))) == NULL)
596 return ERROR_INT("matt not made", __func__, 1);
597 l_productMat2(mat1, mat2, matt, size);
598 l_productMat2(matt, mat3, matd, size);
599 LEPT_FREE(matt);
600 return 0;
601 }
602
603
604 /*!
605 * \brief l_productMat4()
606 *
607 * \param[in] mat1 square matrix, as a 1-dimensional size^2 array
608 * \param[in] mat2 square matrix, as a 1-dimensional size^2 array
609 * \param[in] mat3 square matrix, as a 1-dimensional size^2 array
610 * \param[in] mat4 square matrix, as a 1-dimensional size^2 array
611 * \param[in] matd square matrix; product stored here
612 * \param[in] size of matrices
613 * \return 0 if OK, 1 on error
614 */
615 l_ok
616 l_productMat4(l_float32 *mat1,
617 l_float32 *mat2,
618 l_float32 *mat3,
619 l_float32 *mat4,
620 l_float32 *matd,
621 l_int32 size)
622 {
623 l_float32 *matt;
624
625 if (!mat1)
626 return ERROR_INT("matrix 1 not defined", __func__, 1);
627 if (!mat2)
628 return ERROR_INT("matrix 2 not defined", __func__, 1);
629 if (!mat3)
630 return ERROR_INT("matrix 3 not defined", __func__, 1);
631 if (!matd)
632 return ERROR_INT("result matrix not defined", __func__, 1);
633
634 if ((matt = (l_float32 *)LEPT_CALLOC((size_t)size * size,
635 sizeof(l_float32))) == NULL)
636 return ERROR_INT("matt not made", __func__, 1);
637 l_productMat3(mat1, mat2, mat3, matt, size);
638 l_productMat2(matt, mat4, matd, size);
639 LEPT_FREE(matt);
640 return 0;
641 }