1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_drawinglayer.hxx"
24 
25 #include <drawinglayer/primitive2d/cropprimitive2d.hxx>
26 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
27 #include <basegfx/matrix/b2dhommatrix.hxx>
28 #include <basegfx/matrix/b2dhommatrixtools.hxx>
29 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
30 #include <basegfx/polygon/b2dpolygon.hxx>
31 #include <basegfx/polygon/b2dpolygontools.hxx>
32 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
33 
34 //////////////////////////////////////////////////////////////////////////////
35 
36 using namespace com::sun::star;
37 
38 //////////////////////////////////////////////////////////////////////////////
39 
40 namespace drawinglayer
41 {
42     namespace primitive2d
43     {
44         CropPrimitive2D::CropPrimitive2D(
45             const Primitive2DSequence& rChildren,
46             const basegfx::B2DHomMatrix& rTransformation,
47             double fCropLeft,
48             double fCropTop,
49             double fCropRight,
50             double fCropBottom)
51         :   GroupPrimitive2D(rChildren),
52             maTransformation(rTransformation),
53             mfCropLeft(fCropLeft),
54             mfCropTop(fCropTop),
55             mfCropRight(fCropRight),
56             mfCropBottom(fCropBottom)
57         {
58         }
59 
60         bool CropPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
61         {
62             if(GroupPrimitive2D::operator==(rPrimitive))
63             {
64                 const CropPrimitive2D& rCompare = static_cast< const CropPrimitive2D& >(rPrimitive);
65 
66                 return (getTransformation() == rCompare.getTransformation()
67                     && getCropLeft() == rCompare.getCropLeft()
68                     && getCropTop() == rCompare.getCropTop()
69                     && getCropRight() == rCompare.getCropRight()
70                     && getCropBottom() == rCompare.getCropBottom());
71             }
72 
73             return false;
74         }
75 
76         Primitive2DSequence CropPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
77         {
78             Primitive2DSequence xRetval;
79 
80             if(getChildren().hasElements())
81             {
82                 // decompose to have current translate and scale
83                 basegfx::B2DVector aScale, aTranslate;
84                 double fRotate, fShearX;
85 
86                 getTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
87 
88                 // detect 180 degree rotation, this is the same as mirrored in X and Y,
89                 // thus change to mirroring. Prefer mirroring here. Use the equal call
90                 // with getSmallValue here, the original which uses rtl::math::approxEqual
91                 // is too correct here. Maybe this changes with enhanced precision in aw080
92                 // to the better so that this can be reduced to the more precise call again
93                 if(basegfx::fTools::equal(fRotate, F_PI, 0.000000001))
94                 {
95                     aScale.setX(aScale.getX() * -1.0);
96                     aScale.setY(aScale.getY() * -1.0);
97                     fRotate = 0.0;
98                 }
99 
100                 // create target translate and scale
101                 const bool bMirroredX(aScale.getX() < 0.0);
102                 const bool bMirroredY(aScale.getY() < 0.0);
103                 basegfx::B2DVector aTargetScale(aScale);
104                 basegfx::B2DVector aTargetTranslate(aTranslate);
105 
106                 if(bMirroredX)
107                 {
108                     aTargetTranslate.setX(aTargetTranslate.getX() + getCropRight());
109                     aTargetScale.setX(aTargetScale.getX() - getCropLeft() - getCropRight());
110                 }
111                 else
112                 {
113                     aTargetTranslate.setX(aTargetTranslate.getX() - getCropLeft());
114                     aTargetScale.setX(aTargetScale.getX() + getCropRight() + getCropLeft());
115                 }
116 
117                 if(bMirroredY)
118                 {
119                     aTargetTranslate.setY(aTargetTranslate.getY() + getCropBottom());
120                     aTargetScale.setY(aTargetScale.getY() - getCropTop() - getCropBottom());
121                 }
122                 else
123                 {
124                     aTargetTranslate.setY(aTargetTranslate.getY() - getCropTop());
125                     aTargetScale.setY(aTargetScale.getY() + getCropBottom() + getCropTop());
126                 }
127 
128                 // create ranges to make comparisons
129                 const basegfx::B2DRange aCurrent(
130                     aTranslate.getX(), aTranslate.getY(),
131                     aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY());
132                 const basegfx::B2DRange aCropped(
133                     aTargetTranslate.getX(), aTargetTranslate.getY(),
134                     aTargetTranslate.getX() + aTargetScale.getX(), aTargetTranslate.getY() + aTargetScale.getY());
135 
136                 if(aCropped.isEmpty())
137                 {
138                     // nothing to return since cropped content is completely empty
139                 }
140                 else if(aCurrent.equal(aCropped))
141                 {
142                     // no crop, just use content
143                     xRetval = getChildren();
144                 }
145                 else
146                 {
147                     // build new combined content transformation
148                     basegfx::B2DHomMatrix aNewObjectTransform(getTransformation());
149 
150                     // remove content transform by inverting
151                     aNewObjectTransform.invert();
152 
153                     // add target values and original shear/rotate
154                     aNewObjectTransform = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
155                         aTargetScale.getX(),
156                         aTargetScale.getY(),
157                         fShearX,
158                         fRotate,
159                         aTargetTranslate.getX(),
160                         aTargetTranslate.getY())
161                             * aNewObjectTransform;
162 
163                     // prepare TransformPrimitive2D with xPrimitive
164                     const Primitive2DReference xTransformPrimitive(
165                         new TransformPrimitive2D(
166                             aNewObjectTransform,
167                             getChildren()));
168 
169                     if(aCurrent.isInside(aCropped))
170                     {
171                         // crop just shrunk so that its inside content,
172                         // no need to use a mask since not really cropped.
173                         xRetval = Primitive2DSequence(&xTransformPrimitive, 1);
174                     }
175                     else
176                     {
177                         // mask with original object's bounds
178                         basegfx::B2DPolyPolygon aMaskPolyPolygon(basegfx::tools::createUnitPolygon());
179                         aMaskPolyPolygon.transform(getTransformation());
180 
181                         // create maskPrimitive with aMaskPolyPolygon and aMaskContentVector
182                         const Primitive2DReference xMask(
183                             new MaskPrimitive2D(
184                                 aMaskPolyPolygon,
185                                 Primitive2DSequence(&xTransformPrimitive, 1)));
186 
187                         xRetval = Primitive2DSequence(&xMask, 1);
188                     }
189                 }
190             }
191 
192             return xRetval;
193         }
194 
195         // provide unique ID
196         ImplPrimitrive2DIDBlock(CropPrimitive2D, PRIMITIVE2D_ID_CROPPRIMITIVE2D)
197 
198     } // end of namespace primitive2d
199 } // end of namespace drawinglayer
200 
201 //////////////////////////////////////////////////////////////////////////////
202 // eof
203