11cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
21cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger/*
31cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Copyright 2011 Google Inc.
41cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger *
51cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Use of this source code is governed by a BSD-style license that can be
61cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * found in the LICENSE file.
71cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger */
81cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
91cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#ifndef SkMatrix44_DEFINED
121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#define SkMatrix44_DEFINED
131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "SkMatrix.h"
151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "SkScalar.h"
161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#ifdef SK_MSCALAR_IS_DOUBLE
181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    typedef double SkMScalar;
191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static inline double SkFloatToMScalar(float x) {
201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return static_cast<double>(x);
211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static inline float SkMScalarToFloat(double x) {
231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return static_cast<float>(x);
241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static inline double SkDoubleToMScalar(double x) {
261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return x;
271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static inline double SkMScalarToDouble(double x) {
291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return x;
301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static const SkMScalar SK_MScalarPI = 3.141592653589793;
321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#else
331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    typedef float SkMScalar;
341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static inline float SkFloatToMScalar(float x) {
351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return x;
361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static inline float SkMScalarToFloat(float x) {
381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return x;
391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
401cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static inline float SkDoubleToMScalar(double x) {
411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return static_cast<float>(x);
421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
431cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static inline double SkMScalarToDouble(float x) {
441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return static_cast<double>(x);
451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
461cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    static const SkMScalar SK_MScalarPI = 3.14159265f;
471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#endif
481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#ifdef SK_SCALAR_IS_FLOAT
501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    #define SkMScalarToScalar SkMScalarToFloat
511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    #define SkScalarToMScalar SkFloatToMScalar
521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#else
531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    #if SK_MSCALAR_IS_DOUBLE
541cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        // we don't have fixed <-> double macros, use double<->scalar macros
551cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        #define SkMScalarToScalar SkDoubleToScalar
561cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        #define SkScalarToMScalar SkScalarToDouble
571cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    #else
581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        #define SkMScalarToScalar SkFloatToFixed
591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        #define SkScalarToMScalar SkFixedToFloat
601cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    #endif
611cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#endif
621cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstatic const SkMScalar SK_MScalar1 = 1;
641cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger///////////////////////////////////////////////////////////////////////////////
661cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
671cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstruct SkVector4 {
681cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkScalar fData[4];
691cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkVector4() {
711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        this->set(0, 0, 0, 1);
721cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkVector4(const SkVector4& src) {
741cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        memcpy(fData, src.fData, sizeof(fData));
751cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
761cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkVector4(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
771cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        fData[0] = x;
781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        fData[1] = y;
791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        fData[2] = z;
801cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        fData[3] = w;
811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkVector4& operator=(const SkVector4& src) {
841cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        memcpy(fData, src.fData, sizeof(fData));
851cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return *this;
861cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool operator==(const SkVector4& v) {
891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return fData[0] == v.fData[0] && fData[1] == v.fData[1] &&
901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger               fData[2] == v.fData[2] && fData[3] == v.fData[3];
911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool operator!=(const SkVector4& v) {
931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return !(*this == v);
941cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool equals(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return fData[0] == x && fData[1] == y &&
971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger               fData[2] == z && fData[3] == w;
981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void set(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
1011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        fData[0] = x;
1021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        fData[1] = y;
1031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        fData[2] = z;
1041cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        fData[3] = w;
1051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger};
1071cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerclass SK_API SkMatrix44 {
1091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerpublic:
1101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkMatrix44();
1111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkMatrix44(const SkMatrix44&);
1121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkMatrix44(const SkMatrix44& a, const SkMatrix44& b);
1131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkMatrix44& operator=(const SkMatrix44& src) {
1151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        memcpy(this, &src, sizeof(*this));
1161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return *this;
1171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool operator==(const SkMatrix44& other) const {
1201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return !memcmp(this, &other, sizeof(*this));
1211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool operator!=(const SkMatrix44& other) const {
1231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return !!memcmp(this, &other, sizeof(*this));
1241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkMatrix44(const SkMatrix&);
1271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkMatrix44& operator=(const SkMatrix& src);
1281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    operator SkMatrix() const;
1291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkMScalar get(int row, int col) const;
1311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void set(int row, int col, const SkMScalar& value);
1321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void asColMajorf(float[]) const;
1341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void asColMajord(double[]) const;
1351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void asRowMajorf(float[]) const;
1361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void asRowMajord(double[]) const;
1371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool isIdentity() const;
1391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void setIdentity();
1401cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void reset() { this->setIdentity();}
1411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void set3x3(SkMScalar m00, SkMScalar m01, SkMScalar m02,
1431cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                SkMScalar m10, SkMScalar m11, SkMScalar m12,
1441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                SkMScalar m20, SkMScalar m21, SkMScalar m22);
1451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1461cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void setTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
1471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
1481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
1491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
1511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
1521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
1531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1541cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void setScale(SkMScalar scale) {
1551cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        this->setScale(scale, scale, scale);
1561cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1571cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void preScale(SkMScalar scale) {
1581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        this->preScale(scale, scale, scale);
1591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1601cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void postScale(SkMScalar scale) {
1611cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        this->postScale(scale, scale, scale);
1621cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1641cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void setRotateDegreesAbout(SkMScalar x, SkMScalar y, SkMScalar z,
1651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                               SkMScalar degrees) {
1661cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        this->setRotateAbout(x, y, z, degrees * SK_MScalarPI / 180);
1671cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1681cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1691cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    /** Rotate about the vector [x,y,z]. If that vector is not unit-length,
1701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        it will be automatically resized.
1711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger     */
1721cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void setRotateAbout(SkMScalar x, SkMScalar y, SkMScalar z,
1731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                        SkMScalar radians);
1741cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    /** Rotate about the vector [x,y,z]. Does not check the length of the
1751cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        vector, assuming it is unit-length.
1761cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger     */
1771cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void setRotateAboutUnit(SkMScalar x, SkMScalar y, SkMScalar z,
1781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger                            SkMScalar radians);
1791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1801cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void setConcat(const SkMatrix44& a, const SkMatrix44& b);
1811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void preConcat(const SkMatrix44& m) {
1821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        this->setConcat(*this, m);
1831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1841cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void postConcat(const SkMatrix44& m) {
1851cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        this->setConcat(m, *this);
1861cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    friend SkMatrix44 operator*(const SkMatrix44& a, const SkMatrix44& b) {
1891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return SkMatrix44(a, b);
1901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
1911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    /** If this is invertible, return that in inverse and return true. If it is
1931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        not invertible, return false and ignore the inverse parameter.
1941cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger     */
1951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    bool invert(SkMatrix44* inverse) const;
1961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
1971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    /** Apply the matrix to the src vector, returning the new vector in dst.
1981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        It is legal for src and dst to point to the same memory.
1991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger     */
2001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void map(const SkScalar src[4], SkScalar dst[4]) const;
2011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void map(SkScalar vec[4]) const {
2021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        this->map(vec, vec);
2031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
2041cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
2051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    friend SkVector4 operator*(const SkMatrix44& m, const SkVector4& src) {
2061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        SkVector4 dst;
2071cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        m.map(src.fData, dst.fData);
2081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        return dst;
2091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    }
2101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
2111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    void dump() const;
2121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
2131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerprivate:
2141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    /*  Stored in the same order as opengl:
2151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger         [3][0] = tx
2161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger         [3][1] = ty
2171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger         [3][2] = tz
2181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger     */
2191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    SkMScalar fMat[4][4];
2201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
2211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger    double determinant() const;
2221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger};
2231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
2241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#endif
225