Mercurial > hgrepos > Python2 > PyMuPDF
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/affinecompose.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,641 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions + - are met: + - 1. Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - 2. Redistributions in binary form must reproduce the above + - copyright notice, this list of conditions and the following + - disclaimer in the documentation and/or other materials + - provided with the distribution. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY + - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *====================================================================*/ + +/*! + * \file affinecompose.c + * <pre> + * + * Composable coordinate transforms + * l_float32 *createMatrix2dTranslate() + * l_float32 *createMatrix2dScale() + * l_float32 *createMatrix2dRotate() + * + * Special coordinate transforms on pta + * PTA *ptaTranslate() + * PTA *ptaScale() + * PTA *ptaRotate() + * + * Special coordinate transforms on boxa + * BOXA *boxaTranslate() + * BOXA *boxaScale() + * BOXA *boxaRotate() + * + * General coordinate transform on pta and boxa + * PTA *ptaAffineTransform() + * BOXA *boxaAffineTransform() + * + * Matrix operations + * l_int32 l_productMatVec() + * l_int32 l_productMat2() + * l_int32 l_productMat3() + * l_int32 l_productMat4() + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <math.h> +#include "allheaders.h" + +/*-------------------------------------------------------------* + * Composable coordinate transforms * + *-------------------------------------------------------------*/ +/*! + * \brief createMatrix2dTranslate() + * + * \param[in] transx x component of translation wrt. the origin + * \param[in] transy y component of translation wrt. the origin + * \return 3x3 transform matrix, or NULL on error + * + * <pre> + * Notes: + * (1) The translation is equivalent to: + * v' = Av + * where v and v' are 1x3 column vectors in the form + * v = [x, y, 1]^ ^ denotes transpose + * and the affine translation matrix is + * A = [ 1 0 tx + * 0 1 ty + * 0 0 1 ] + * + * (2) We consider translation as with respect to a fixed origin. + * In a clipping operation, the origin moves and the points + * are fixed, and you use (-tx, -ty) where (tx, ty) is the + * translation vector of the origin. + * </pre> + */ +l_float32 * +createMatrix2dTranslate(l_float32 transx, + l_float32 transy) +{ +l_float32 *mat; + + mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); + mat[0] = mat[4] = mat[8] = 1; + mat[2] = transx; + mat[5] = transy; + return mat; +} + + +/*! + * \brief createMatrix2dScale() + * + * \param[in] scalex horizontal scale factor + * \param[in] scaley vertical scale factor + * \return 3x3 transform matrix, or NULL on error + * + * <pre> + * Notes: + * (1) The scaling is equivalent to: + * v' = Av + * where v and v' are 1x3 column vectors in the form + * v = [x, y, 1]^ ^ denotes transpose + * and the affine scaling matrix is + * A = [ sx 0 0 + * 0 sy 0 + * 0 0 1 ] + * + * (2) We consider scaling as with respect to a fixed origin. + * In other words, the origin is the only point that doesn't + * move in the scaling transform. + * </pre> + */ +l_float32 * +createMatrix2dScale(l_float32 scalex, + l_float32 scaley) +{ +l_float32 *mat; + + mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); + mat[0] = scalex; + mat[4] = scaley; + mat[8] = 1; + return mat; +} + + +/*! + * \brief createMatrix2dRotate() + * + * \param[in] xc, yc location of center of rotation + * \param[in] angle rotation in radians; clockwise is positive + * \return 3x3 transform matrix, or NULL on error + * + * <pre> + * Notes: + * (1) The rotation is equivalent to: + * v' = Av + * where v and v' are 1x3 column vectors in the form + * v = [x, y, 1]^ ^ denotes transpose + * and the affine rotation matrix is + * A = [ cosa -sina xc*1-cosa + yc*sina + * sina cosa yc*1-cosa - xc*sina + * 0 0 1 ] + * + * If the rotation is about the origin, xc, yc) = (0, 0 and + * this simplifies to + * A = [ cosa -sina 0 + * sina cosa 0 + * 0 0 1 ] + * + * These relations follow from the following equations, which + * you can convince yourself are correct as follows. Draw a + * circle centered on xc,yc) and passing through (x,y), with + * (x',y') on the arc at an angle 'a' clockwise from (x,y). + * [ Hint: cosa + b = cosa * cosb - sina * sinb + * sina + b = sina * cosb + cosa * sinb ] + * + * x' - xc = x - xc) * cosa - (y - yc * sina + * y' - yc = x - xc) * sina + (y - yc * cosa + * </pre> + */ +l_float32 * +createMatrix2dRotate(l_float32 xc, + l_float32 yc, + l_float32 angle) +{ +l_float32 sina, cosa; +l_float32 *mat; + + mat = (l_float32 *)LEPT_CALLOC(9, sizeof(l_float32)); + sina = sin(angle); + cosa = cos(angle); + mat[0] = mat[4] = cosa; + mat[1] = -sina; + mat[2] = xc * (1.0 - cosa) + yc * sina; + mat[3] = sina; + mat[5] = yc * (1.0 - cosa) - xc * sina; + mat[8] = 1; + return mat; +} + + + +/*-------------------------------------------------------------* + * Special coordinate transforms on pta * + *-------------------------------------------------------------*/ +/*! + * \brief ptaTranslate() + * + * \param[in] ptas for initial points + * \param[in] transx x component of translation wrt. the origin + * \param[in] transy y component of translation wrt. the origin + * \return ptad translated points, or NULL on error + * + * <pre> + * Notes: + * (1) See createMatrix2dTranslate() for details of transform. + * </pre> + */ +PTA * +ptaTranslate(PTA *ptas, + l_float32 transx, + l_float32 transy) +{ +l_int32 i, npts; +l_float32 x, y; +PTA *ptad; + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); + + npts = ptaGetCount(ptas); + if ((ptad = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", __func__, NULL); + for (i = 0; i < npts; i++) { + ptaGetPt(ptas, i, &x, &y); + ptaAddPt(ptad, x + transx, y + transy); + } + + return ptad; +} + + +/*! + * \brief ptaScale() + * + * \param[in] ptas for initial points + * \param[in] scalex horizontal scale factor + * \param[in] scaley vertical scale factor + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) See createMatrix2dScale() for details of transform. + * </pre> + */ +PTA * +ptaScale(PTA *ptas, + l_float32 scalex, + l_float32 scaley) +{ +l_int32 i, npts; +l_float32 x, y; +PTA *ptad; + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); + + npts = ptaGetCount(ptas); + if ((ptad = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", __func__, NULL); + for (i = 0; i < npts; i++) { + ptaGetPt(ptas, i, &x, &y); + ptaAddPt(ptad, scalex * x, scaley * y); + } + + return ptad; +} + + +/*! + * \brief ptaRotate() + * + * \param[in] ptas for initial points + * \param[in] xc, yc location of center of rotation + * \param[in] angle rotation in radians; clockwise is positive + * \return ptad rotated pta, or NULL on error + * + * <pre> + * Notes; + * (1) See createMatrix2dRotate() for details of transform. + * (2) This transform can be thought of as composed of the + * sum of two parts: + * a) an (x,y)-dependent rotation about the origin: + * xr = x * cosa - y * sina + * yr = x * sina + y * cosa + * b) an (x,y)-independent translation that depends on the + * rotation center and the angle: + * xt = xc - xc * cosa + yc * sina + * yt = yc - xc * sina - yc * cosa + * The translation part (xt,yt) is equal to the difference + * between the center (xc,yc) and the location of the + * center after it is rotated about the origin. + * </pre> + */ +PTA * +ptaRotate(PTA *ptas, + l_float32 xc, + l_float32 yc, + l_float32 angle) +{ +l_int32 i, npts; +l_float32 x, y, xp, yp, sina, cosa; +PTA *ptad; + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); + + npts = ptaGetCount(ptas); + if ((ptad = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", __func__, NULL); + sina = sin(angle); + cosa = cos(angle); + for (i = 0; i < npts; i++) { + ptaGetPt(ptas, i, &x, &y); + xp = xc + (x - xc) * cosa - (y - yc) * sina; + yp = yc + (x - xc) * sina + (y - yc) * cosa; + ptaAddPt(ptad, xp, yp); + } + + return ptad; +} + + +/*-------------------------------------------------------------* + * Special coordinate transforms on boxa * + *-------------------------------------------------------------*/ +/*! + * \brief boxaTranslate() + * + * \param[in] boxas + * \param[in] transx x component of translation wrt. the origin + * \param[in] transy y component of translation wrt. the origin + * \return boxad translated boxas, or NULL on error + * + * Notes: + * (1) See createMatrix2dTranslate() for details of transform. + */ +BOXA * +boxaTranslate(BOXA *boxas, + l_float32 transx, + l_float32 transy) +{ +PTA *ptas, *ptad; +BOXA *boxad; + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); + + ptas = boxaConvertToPta(boxas, 4); + ptad = ptaTranslate(ptas, transx, transy); + boxad = ptaConvertToBoxa(ptad, 4); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return boxad; +} + + +/*! + * \brief boxaScale() + * + * \param[in] boxas + * \param[in] scalex horizontal scale factor + * \param[in] scaley vertical scale factor + * \return boxad scaled boxas, or NULL on error + * + * Notes: + * (1) See createMatrix2dScale() for details of transform. + */ +BOXA * +boxaScale(BOXA *boxas, + l_float32 scalex, + l_float32 scaley) +{ +PTA *ptas, *ptad; +BOXA *boxad; + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); + + ptas = boxaConvertToPta(boxas, 4); + ptad = ptaScale(ptas, scalex, scaley); + boxad = ptaConvertToBoxa(ptad, 4); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return boxad; +} + + +/*! + * \brief boxaRotate() + * + * \param[in] boxas + * \param[in] xc, yc location of center of rotation + * \param[in] angle rotation in radians; clockwise is positive + * \return boxad rotated boxas, or NULL on error + * + * Notes: + * (1) See createMatrix2dRotate() for details of transform. + */ +BOXA * +boxaRotate(BOXA *boxas, + l_float32 xc, + l_float32 yc, + l_float32 angle) +{ +PTA *ptas, *ptad; +BOXA *boxad; + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); + + ptas = boxaConvertToPta(boxas, 4); + ptad = ptaRotate(ptas, xc, yc, angle); + boxad = ptaConvertToBoxa(ptad, 4); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return boxad; +} + + +/*-------------------------------------------------------------* + * General affine coordinate transform * + *-------------------------------------------------------------*/ +/*! + * \brief ptaAffineTransform() + * + * \param[in] ptas for initial points + * \param[in] mat 3x3 transform matrix; canonical form + * \return ptad transformed points, or NULL on error + */ +PTA * +ptaAffineTransform(PTA *ptas, + l_float32 *mat) +{ +l_int32 i, npts; +l_float32 vecs[3], vecd[3]; +PTA *ptad; + + if (!ptas) + return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); + if (!mat) + return (PTA *)ERROR_PTR("transform not defined", __func__, NULL); + + vecs[2] = 1; + npts = ptaGetCount(ptas); + if ((ptad = ptaCreate(npts)) == NULL) + return (PTA *)ERROR_PTR("ptad not made", __func__, NULL); + for (i = 0; i < npts; i++) { + ptaGetPt(ptas, i, &vecs[0], &vecs[1]); + l_productMatVec(mat, vecs, vecd, 3); + ptaAddPt(ptad, vecd[0], vecd[1]); + } + + return ptad; +} + + +/*! + * \brief boxaAffineTransform() + * + * \param[in] boxas + * \param[in] mat 3x3 transform matrix; canonical form + * \return boxad transformed boxas, or NULL on error + */ +BOXA * +boxaAffineTransform(BOXA *boxas, + l_float32 *mat) +{ +PTA *ptas, *ptad; +BOXA *boxad; + + if (!boxas) + return (BOXA *)ERROR_PTR("boxas not defined", __func__, NULL); + if (!mat) + return (BOXA *)ERROR_PTR("transform not defined", __func__, NULL); + + ptas = boxaConvertToPta(boxas, 4); + ptad = ptaAffineTransform(ptas, mat); + boxad = ptaConvertToBoxa(ptad, 4); + ptaDestroy(&ptas); + ptaDestroy(&ptad); + return boxad; +} + + +/*-------------------------------------------------------------* + * Matrix operations * + *-------------------------------------------------------------*/ +/*! + * \brief l_productMatVec() + * + * \param[in] mat square matrix, as a 1-dimensional %size^2 array + * \param[in] vecs input column vector of length %size + * \param[in] vecd result column vector + * \param[in] size matrix is %size x %size; vectors are length %size + * \return 0 if OK, 1 on error + */ +l_ok +l_productMatVec(l_float32 *mat, + l_float32 *vecs, + l_float32 *vecd, + l_int32 size) +{ +l_int32 i, j; + + if (!mat) + return ERROR_INT("matrix not defined", __func__, 1); + if (!vecs) + return ERROR_INT("input vector not defined", __func__, 1); + if (!vecd) + return ERROR_INT("result vector not defined", __func__, 1); + + for (i = 0; i < size; i++) { + vecd[i] = 0; + for (j = 0; j < size; j++) { + vecd[i] += mat[size * i + j] * vecs[j]; + } + } + return 0; +} + + +/*! + * \brief l_productMat2() + * + * \param[in] mat1 square matrix, as a 1-dimensional size^2 array + * \param[in] mat2 square matrix, as a 1-dimensional size^2 array + * \param[in] matd square matrix; product stored here + * \param[in] size of matrices + * \return 0 if OK, 1 on error + */ +l_ok +l_productMat2(l_float32 *mat1, + l_float32 *mat2, + l_float32 *matd, + l_int32 size) +{ +l_int32 i, j, k, index; + + if (!mat1) + return ERROR_INT("matrix 1 not defined", __func__, 1); + if (!mat2) + return ERROR_INT("matrix 2 not defined", __func__, 1); + if (!matd) + return ERROR_INT("result matrix not defined", __func__, 1); + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + index = size * i + j; + matd[index] = 0; + for (k = 0; k < size; k++) + matd[index] += mat1[size * i + k] * mat2[size * k + j]; + } + } + return 0; +} + + +/*! + * \brief l_productMat3() + * + * \param[in] mat1 square matrix, as a 1-dimensional size^2 array + * \param[in] mat2 square matrix, as a 1-dimensional size^2 array + * \param[in] mat3 square matrix, as a 1-dimensional size^2 array + * \param[in] matd square matrix; product stored here + * \param[in] size of matrices + * \return 0 if OK, 1 on error + */ +l_ok +l_productMat3(l_float32 *mat1, + l_float32 *mat2, + l_float32 *mat3, + l_float32 *matd, + l_int32 size) +{ +l_float32 *matt; + + if (!mat1) + return ERROR_INT("matrix 1 not defined", __func__, 1); + if (!mat2) + return ERROR_INT("matrix 2 not defined", __func__, 1); + if (!mat3) + return ERROR_INT("matrix 3 not defined", __func__, 1); + if (!matd) + return ERROR_INT("result matrix not defined", __func__, 1); + + if ((matt = (l_float32 *)LEPT_CALLOC((size_t)size * size, + sizeof(l_float32))) == NULL) + return ERROR_INT("matt not made", __func__, 1); + l_productMat2(mat1, mat2, matt, size); + l_productMat2(matt, mat3, matd, size); + LEPT_FREE(matt); + return 0; +} + + +/*! + * \brief l_productMat4() + * + * \param[in] mat1 square matrix, as a 1-dimensional size^2 array + * \param[in] mat2 square matrix, as a 1-dimensional size^2 array + * \param[in] mat3 square matrix, as a 1-dimensional size^2 array + * \param[in] mat4 square matrix, as a 1-dimensional size^2 array + * \param[in] matd square matrix; product stored here + * \param[in] size of matrices + * \return 0 if OK, 1 on error + */ +l_ok +l_productMat4(l_float32 *mat1, + l_float32 *mat2, + l_float32 *mat3, + l_float32 *mat4, + l_float32 *matd, + l_int32 size) +{ +l_float32 *matt; + + if (!mat1) + return ERROR_INT("matrix 1 not defined", __func__, 1); + if (!mat2) + return ERROR_INT("matrix 2 not defined", __func__, 1); + if (!mat3) + return ERROR_INT("matrix 3 not defined", __func__, 1); + if (!matd) + return ERROR_INT("result matrix not defined", __func__, 1); + + if ((matt = (l_float32 *)LEPT_CALLOC((size_t)size * size, + sizeof(l_float32))) == NULL) + return ERROR_INT("matt not made", __func__, 1); + l_productMat3(mat1, mat2, mat3, matt, size); + l_productMat2(matt, mat4, matd, size); + LEPT_FREE(matt); + return 0; +}
