gfx/thebes/gfxMatrix.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/thebes/gfxMatrix.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,285 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#ifndef GFX_MATRIX_H
    1.10 +#define GFX_MATRIX_H
    1.11 +
    1.12 +#include "gfxPoint.h"
    1.13 +#include "gfxTypes.h"
    1.14 +#include "gfxRect.h"
    1.15 +
    1.16 +// XX - I don't think this class should use gfxFloat at all,
    1.17 +// but should use 'double' and be called gfxDoubleMatrix;
    1.18 +// we can then typedef that to gfxMatrix where we typedef
    1.19 +// double to be gfxFloat.
    1.20 +
    1.21 +/**
    1.22 + * A matrix that represents an affine transformation. Projective
    1.23 + * transformations are not supported. This matrix looks like:
    1.24 + *
    1.25 + * / a  b  0 \
    1.26 + * | c  d  0 |
    1.27 + * \ tx ty 1 /
    1.28 + *
    1.29 + * So, transforming a point (x, y) results in:
    1.30 + *
    1.31 + *           / a  b  0 \   / a * x + c * y + tx \ T
    1.32 + * (x y 1) * | c  d  0 | = | b * x + d * y + ty |
    1.33 + *           \ tx ty 1 /   \         1          /
    1.34 + *
    1.35 + */
    1.36 +struct gfxMatrix {
    1.37 +    double xx; double yx;
    1.38 +    double xy; double yy;
    1.39 +    double x0; double y0;
    1.40 +
    1.41 +public:
    1.42 +    /**
    1.43 +     * Initializes this matrix as the identity matrix.
    1.44 +     */
    1.45 +    gfxMatrix() { Reset(); }
    1.46 +
    1.47 +    /**
    1.48 +     * Initializes the matrix from individual components. See the class
    1.49 +     * description for the layout of the matrix.
    1.50 +     */
    1.51 +    gfxMatrix(gfxFloat a, gfxFloat b, gfxFloat c, gfxFloat d, gfxFloat tx, gfxFloat ty) :
    1.52 +        xx(a),  yx(b),
    1.53 +        xy(c),  yy(d),
    1.54 +        x0(tx), y0(ty) { }
    1.55 +
    1.56 +    /**
    1.57 +     * Post-multiplies m onto the matrix.
    1.58 +     */
    1.59 +    const gfxMatrix& operator *= (const gfxMatrix& m) {
    1.60 +        return Multiply(m);
    1.61 +    }
    1.62 +
    1.63 +    /**
    1.64 +     * Multiplies *this with m and returns the result.
    1.65 +     */
    1.66 +    gfxMatrix operator * (const gfxMatrix& m) const {
    1.67 +        return gfxMatrix(*this).Multiply(m);
    1.68 +    }
    1.69 +
    1.70 +    /* Returns true if the other matrix is fuzzy-equal to this matrix.
    1.71 +     * Note that this isn't a cheap comparison!
    1.72 +     */
    1.73 +    bool operator==(const gfxMatrix& other) const
    1.74 +    {
    1.75 +      return FuzzyEqual(xx, other.xx) && FuzzyEqual(yx, other.yx) &&
    1.76 +             FuzzyEqual(xy, other.xy) && FuzzyEqual(yy, other.yy) &&
    1.77 +             FuzzyEqual(x0, other.x0) && FuzzyEqual(y0, other.y0);
    1.78 +    }
    1.79 +
    1.80 +    bool operator!=(const gfxMatrix& other) const
    1.81 +    {
    1.82 +      return !(*this == other);
    1.83 +    }
    1.84 +
    1.85 +    // matrix operations
    1.86 +    /**
    1.87 +     * Resets this matrix to the identity matrix.
    1.88 +     */
    1.89 +    const gfxMatrix& Reset();
    1.90 +
    1.91 +    bool IsIdentity() const {
    1.92 +       return xx == 1.0 && yx == 0.0 &&
    1.93 +              xy == 0.0 && yy == 1.0 &&
    1.94 +              x0 == 0.0 && y0 == 0.0;
    1.95 +    }
    1.96 +
    1.97 +    /**
    1.98 +     * Inverts this matrix, if possible. Otherwise, the matrix is left
    1.99 +     * unchanged.
   1.100 +     *
   1.101 +     * XXX should this do something with the return value of
   1.102 +     * cairo_matrix_invert?
   1.103 +     */
   1.104 +    const gfxMatrix& Invert();
   1.105 +
   1.106 +    /**
   1.107 +     * Check if matrix is singular (no inverse exists).
   1.108 +     */
   1.109 +    bool IsSingular() const {
   1.110 +        // if the determinant (ad - bc) is zero it's singular
   1.111 +        return (xx * yy) == (yx * xy);
   1.112 +    }
   1.113 +
   1.114 +    /**
   1.115 +     * Scales this matrix. The scale is pre-multiplied onto this matrix,
   1.116 +     * i.e. the scaling takes place before the other transformations.
   1.117 +     */
   1.118 +    const gfxMatrix& Scale(gfxFloat x, gfxFloat y);
   1.119 +
   1.120 +    /**
   1.121 +     * Translates this matrix. The translation is pre-multiplied onto this matrix,
   1.122 +     * i.e. the translation takes place before the other transformations.
   1.123 +     */
   1.124 +    const gfxMatrix& Translate(const gfxPoint& pt);
   1.125 +
   1.126 +    /**
   1.127 +     * Rotates this matrix. The rotation is pre-multiplied onto this matrix,
   1.128 +     * i.e. the translation takes place after the other transformations.
   1.129 +     *
   1.130 +     * @param radians Angle in radians.
   1.131 +     */
   1.132 +    const gfxMatrix& Rotate(gfxFloat radians);
   1.133 +
   1.134 +     /**
   1.135 +      * Multiplies the current matrix with m.
   1.136 +      * This is a post-multiplication, i.e. the transformations of m are
   1.137 +      * applied _after_ the existing transformations.
   1.138 +      *
   1.139 +      * XXX is that difference (compared to Rotate etc) a good thing?
   1.140 +      */
   1.141 +    const gfxMatrix& Multiply(const gfxMatrix& m);
   1.142 +
   1.143 +    /**
   1.144 +     * Multiplies the current matrix with m.
   1.145 +     * This is a pre-multiplication, i.e. the transformations of m are
   1.146 +     * applied _before_ the existing transformations.
   1.147 +     */
   1.148 +    const gfxMatrix& PreMultiply(const gfxMatrix& m);
   1.149 +
   1.150 +    /**
   1.151 +     * Transforms a point according to this matrix.
   1.152 +     */
   1.153 +    gfxPoint Transform(const gfxPoint& point) const;
   1.154 +
   1.155 +
   1.156 +    /**
   1.157 +     * Transform a distance according to this matrix. This does not apply
   1.158 +     * any translation components.
   1.159 +     */
   1.160 +    gfxSize Transform(const gfxSize& size) const;
   1.161 +
   1.162 +    /**
   1.163 +     * Transforms both the point and distance according to this matrix.
   1.164 +     */
   1.165 +    gfxRect Transform(const gfxRect& rect) const;
   1.166 +
   1.167 +    gfxRect TransformBounds(const gfxRect& rect) const;
   1.168 +
   1.169 +    /**
   1.170 +     * Returns the translation component of this matrix.
   1.171 +     */
   1.172 +    gfxPoint GetTranslation() const {
   1.173 +        return gfxPoint(x0, y0);
   1.174 +    }
   1.175 +
   1.176 +    /**
   1.177 +     * Returns true if the matrix is anything other than a straight
   1.178 +     * translation by integers.
   1.179 +     */
   1.180 +    bool HasNonIntegerTranslation() const {
   1.181 +        return HasNonTranslation() ||
   1.182 +            !FuzzyEqual(x0, floor(x0 + 0.5)) ||
   1.183 +            !FuzzyEqual(y0, floor(y0 + 0.5));
   1.184 +    }
   1.185 +
   1.186 +    /**
   1.187 +     * Returns true if the matrix has any transform other
   1.188 +     * than a straight translation
   1.189 +     */
   1.190 +    bool HasNonTranslation() const {
   1.191 +        return !FuzzyEqual(xx, 1.0) || !FuzzyEqual(yy, 1.0) ||
   1.192 +               !FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0);
   1.193 +    }
   1.194 +
   1.195 +    /**
   1.196 +     * Returns true if the matrix only has an integer translation.
   1.197 +     */
   1.198 +    bool HasOnlyIntegerTranslation() const {
   1.199 +        return !HasNonIntegerTranslation();
   1.200 +    }
   1.201 +
   1.202 +    /**
   1.203 +     * Returns true if the matrix has any transform other
   1.204 +     * than a translation or a -1 y scale (y axis flip)
   1.205 +     */
   1.206 +    bool HasNonTranslationOrFlip() const {
   1.207 +        return !FuzzyEqual(xx, 1.0) ||
   1.208 +               (!FuzzyEqual(yy, 1.0) && !FuzzyEqual(yy, -1.0)) ||
   1.209 +               !FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0);
   1.210 +    }
   1.211 +
   1.212 +    /**
   1.213 +     * Returns true if the matrix has any transform other
   1.214 +     * than a translation or scale; this is, if there is
   1.215 +     * no rotation.
   1.216 +     */
   1.217 +    bool HasNonAxisAlignedTransform() const {
   1.218 +        return !FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0);
   1.219 +    }
   1.220 +
   1.221 +    /**
   1.222 +     * Computes the determinant of this matrix.
   1.223 +     */
   1.224 +    double Determinant() const {
   1.225 +        return xx*yy - yx*xy;
   1.226 +    }
   1.227 +
   1.228 +    /* Computes the scale factors of this matrix; that is,
   1.229 +     * the amounts each basis vector is scaled by.
   1.230 +     * The xMajor parameter indicates if the larger scale is
   1.231 +     * to be assumed to be in the X direction or not.
   1.232 +     */
   1.233 +    gfxSize ScaleFactors(bool xMajor) const {
   1.234 +        double det = Determinant();
   1.235 +
   1.236 +        if (det == 0.0)
   1.237 +            return gfxSize(0.0, 0.0);
   1.238 +
   1.239 +        gfxSize sz = xMajor ? gfxSize(1.0, 0.0) : gfxSize(0.0, 1.0);
   1.240 +        sz = Transform(sz);
   1.241 +
   1.242 +        double major = sqrt(sz.width * sz.width + sz.height * sz.height);
   1.243 +        double minor = 0.0;
   1.244 +
   1.245 +        // ignore mirroring
   1.246 +        if (det < 0.0)
   1.247 +            det = - det;
   1.248 +
   1.249 +        if (major)
   1.250 +            minor = det / major;
   1.251 +
   1.252 +        if (xMajor)
   1.253 +            return gfxSize(major, minor);
   1.254 +
   1.255 +        return gfxSize(minor, major);
   1.256 +    }
   1.257 +
   1.258 +    /**
   1.259 +     * Snap matrix components that are close to integers
   1.260 +     * to integers. In particular, components that are integral when
   1.261 +     * converted to single precision are set to those integers.
   1.262 +     */
   1.263 +    void NudgeToIntegers(void);
   1.264 +
   1.265 +    /**
   1.266 +     * Returns true if matrix is multiple of 90 degrees rotation with flipping,
   1.267 +     * scaling and translation.
   1.268 +     */
   1.269 +    bool PreservesAxisAlignedRectangles() const {
   1.270 +        return ((FuzzyEqual(xx, 0.0) && FuzzyEqual(yy, 0.0))
   1.271 +            || (FuzzyEqual(xy, 0.0) && FuzzyEqual(yx, 0.0)));
   1.272 +    }
   1.273 +
   1.274 +    /**
   1.275 +     * Returns true if the matrix has non-integer scale
   1.276 +     */
   1.277 +    bool HasNonIntegerScale() const {
   1.278 +        return !FuzzyEqual(xx, floor(xx + 0.5)) ||
   1.279 +               !FuzzyEqual(yy, floor(yy + 0.5));
   1.280 +    }
   1.281 +
   1.282 +private:
   1.283 +    static bool FuzzyEqual(gfxFloat aV1, gfxFloat aV2) {
   1.284 +        return fabs(aV2 - aV1) < 1e-6;
   1.285 +    }
   1.286 +};
   1.287 +
   1.288 +#endif /* GFX_MATRIX_H */

mercurial