summaryrefslogtreecommitdiff
path: root/drawinglayer/source/primitive2d/cropprimitive2d.cxx
blob: 9ce8f82f0b77381b1d52460bba9dbad3a9a6d182 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <drawinglayer/primitive2d/cropprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>

//////////////////////////////////////////////////////////////////////////////

using namespace com::sun::star;

//////////////////////////////////////////////////////////////////////////////

namespace drawinglayer
{
    namespace primitive2d
    {
        CropPrimitive2D::CropPrimitive2D(
            const Primitive2DSequence& rChildren,
            const basegfx::B2DHomMatrix& rTransformation,
            double fCropLeft,
            double fCropTop,
            double fCropRight,
            double fCropBottom)
        :   GroupPrimitive2D(rChildren),
            maTransformation(rTransformation),
            mfCropLeft(fCropLeft),
            mfCropTop(fCropTop),
            mfCropRight(fCropRight),
            mfCropBottom(fCropBottom)
        {
        }

        bool CropPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
        {
            if(GroupPrimitive2D::operator==(rPrimitive))
            {
                const CropPrimitive2D& rCompare = static_cast< const CropPrimitive2D& >(rPrimitive);

                return (getTransformation() == rCompare.getTransformation()
                    && getCropLeft() == rCompare.getCropLeft()
                    && getCropTop() == rCompare.getCropTop()
                    && getCropRight() == rCompare.getCropRight()
                    && getCropBottom() == rCompare.getCropBottom());
            }

            return false;
        }

        Primitive2DSequence CropPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
        {
            Primitive2DSequence xRetval;

            if(getChildren().hasElements())
            {
                // decompose to have current translate and scale
                basegfx::B2DVector aScale, aTranslate;
                double fRotate, fShearX;

                getTransformation().decompose(aScale, aTranslate, fRotate, fShearX);

                // detect 180 degree rotation, this is the same as mirrored in X and Y,
                // thus change to mirroring. Prefer mirroring here. Use the equal call
                // with getSmallValue here, the original which uses rtl::math::approxEqual
                // is too correct here. Maybe this changes with enhanced precision in aw080
                // to the better so that this can be reduced to the more precise call again
                if(basegfx::fTools::equal(fRotate, F_PI, 0.000000001))
                {
                    aScale.setX(aScale.getX() * -1.0);
                    aScale.setY(aScale.getY() * -1.0);
                    fRotate = 0.0;
                }

                // create target translate and scale
                const bool bMirroredX(aScale.getX() < 0.0);
                const bool bMirroredY(aScale.getY() < 0.0);
                basegfx::B2DVector aTargetScale(aScale);
                basegfx::B2DVector aTargetTranslate(aTranslate);

                if(bMirroredX)
                {
                    aTargetTranslate.setX(aTargetTranslate.getX() + getCropRight());
                    aTargetScale.setX(aTargetScale.getX() - getCropLeft() - getCropRight());
                }
                else
                {
                    aTargetTranslate.setX(aTargetTranslate.getX() - getCropLeft());
                    aTargetScale.setX(aTargetScale.getX() + getCropRight() + getCropLeft());
                }

                if(bMirroredY)
                {
                    aTargetTranslate.setY(aTargetTranslate.getY() + getCropBottom());
                    aTargetScale.setY(aTargetScale.getY() - getCropTop() - getCropBottom());
                }
                else
                {
                    aTargetTranslate.setY(aTargetTranslate.getY() - getCropTop());
                    aTargetScale.setY(aTargetScale.getY() + getCropBottom() + getCropTop());
                }

                // create ranges to make comparisons
                const basegfx::B2DRange aCurrent(
                    aTranslate.getX(), aTranslate.getY(),
                    aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY());
                const basegfx::B2DRange aCropped(
                    aTargetTranslate.getX(), aTargetTranslate.getY(),
                    aTargetTranslate.getX() + aTargetScale.getX(), aTargetTranslate.getY() + aTargetScale.getY());

                if(aCropped.isEmpty())
                {
                    // nothing to return since cropped content is completely empty
                }
                else if(aCurrent.equal(aCropped))
                {
                    // no crop, just use content
                    xRetval = getChildren();
                }
                else
                {
                    // build new combined content transformation
                    basegfx::B2DHomMatrix aNewObjectTransform(getTransformation());

                    // remove content transform by inverting
                    aNewObjectTransform.invert();

                    // add target values and original shear/rotate
                    aNewObjectTransform = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
                        aTargetScale.getX(),
                        aTargetScale.getY(),
                        fShearX,
                        fRotate,
                        aTargetTranslate.getX(),
                        aTargetTranslate.getY())
                            * aNewObjectTransform;

                    // prepare TransformPrimitive2D with xPrimitive
                    const Primitive2DReference xTransformPrimitive(
                        new TransformPrimitive2D(
                            aNewObjectTransform,
                            getChildren()));

                    if(aCurrent.isInside(aCropped))
                    {
                        // crop just shrunk so that its inside content,
                        // no need to use a mask since not really cropped.
                        xRetval = Primitive2DSequence(&xTransformPrimitive, 1);
                    }
                    else
                    {
                        // mask with original object's bounds
                        basegfx::B2DPolyPolygon aMaskPolyPolygon(basegfx::tools::createUnitPolygon());
                        aMaskPolyPolygon.transform(getTransformation());

                        // create maskPrimitive with aMaskPolyPolygon and aMaskContentVector
                        const Primitive2DReference xMask(
                            new MaskPrimitive2D(
                                aMaskPolyPolygon,
                                Primitive2DSequence(&xTransformPrimitive, 1)));

                        xRetval = Primitive2DSequence(&xMask, 1);
                    }
                }
            }

            return xRetval;
        }

        // provide unique ID
        ImplPrimitrive2DIDBlock(CropPrimitive2D, PRIMITIVE2D_ID_CROPPRIMITIVE2D)

    } // end of namespace primitive2d
} // end of namespace drawinglayer

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */