1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
21 #include <drawinglayer/animation/animationtiming.hxx>
22 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
23 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
25 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
26 #include <basegfx/polygon/b2dpolygon.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <drawinglayer/primitive2d/cropprimitive2d.hxx>
29 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
30 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
32 //////////////////////////////////////////////////////////////////////////////
33 // helper class for animated graphics
35 #include <vcl/animate.hxx>
36 #include <vcl/graph.hxx>
37 #include <vcl/virdev.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/metaact.hxx>
41 //////////////////////////////////////////////////////////////////////////////
42 // includes for testing MetafilePrimitive2D::create2DDecomposition
44 //////////////////////////////////////////////////////////////////////////////
54 class animatedBitmapExPreparator
56 ::Animation maAnimation
;
57 ::std::vector
< animationStep
> maSteps
;
59 sal_uInt32
generateStepTime(sal_uInt32 nIndex
) const;
62 explicit animatedBitmapExPreparator(const Graphic
& rGraphic
);
64 sal_uInt32
count() const { return maSteps
.size(); }
65 sal_uInt32
loopCount() const { return (sal_uInt32
)maAnimation
.GetLoopCount(); }
66 sal_uInt32
stepTime(sal_uInt32 a
) const { return maSteps
[a
].mnTime
; }
67 const BitmapEx
& stepBitmapEx(sal_uInt32 a
) const { return maSteps
[a
].maBitmapEx
; }
70 sal_uInt32
animatedBitmapExPreparator::generateStepTime(sal_uInt32 nIndex
) const
72 const AnimationBitmap
& rAnimBitmap
= maAnimation
.Get(sal_uInt16(nIndex
));
73 sal_uInt32
nWaitTime(rAnimBitmap
.nWait
* 10);
76 // Take care of special value for MultiPage TIFFs. ATM these shall just
77 // show their first page. Later we will offer some switching when object
79 if(ANIMATION_TIMEOUT_ON_CLICK
== rAnimBitmap
.nWait
)
81 // ATM the huge value would block the timer, so
82 // use a long time to show first page (whole day)
83 nWaitTime
= 100 * 60 * 60 * 24;
86 // Bad trap: There are animated gifs with no set WaitTime (!).
87 // In that case use a default value.
96 animatedBitmapExPreparator::animatedBitmapExPreparator(const Graphic
& rGraphic
)
97 : maAnimation(rGraphic
.GetAnimation())
99 OSL_ENSURE(GRAPHIC_BITMAP
== rGraphic
.GetType() && rGraphic
.IsAnimated(), "animatedBitmapExPreparator: graphic is not animated (!)");
101 // #128539# secure access to Animation, looks like there exist animated GIFs out there
102 // with a step count of zero
103 if(maAnimation
.Count())
105 VirtualDevice
aVirtualDevice(*Application::GetDefaultDevice());
106 VirtualDevice
aVirtualDeviceMask(*Application::GetDefaultDevice(), 1L);
108 // Prepare VirtualDevices and their states
109 aVirtualDevice
.EnableMapMode(sal_False
);
110 aVirtualDeviceMask
.EnableMapMode(sal_False
);
111 aVirtualDevice
.SetOutputSizePixel(maAnimation
.GetDisplaySizePixel());
112 aVirtualDeviceMask
.SetOutputSizePixel(maAnimation
.GetDisplaySizePixel());
113 aVirtualDevice
.Erase();
114 aVirtualDeviceMask
.Erase();
116 for(sal_uInt16
a(0L); a
< maAnimation
.Count(); a
++)
118 animationStep aNextStep
;
119 aNextStep
.mnTime
= generateStepTime(a
);
122 const AnimationBitmap
& rAnimBitmap
= maAnimation
.Get(sal_uInt16(a
));
124 switch(rAnimBitmap
.eDisposal
)
128 aVirtualDevice
.DrawBitmapEx(rAnimBitmap
.aPosPix
, rAnimBitmap
.aBmpEx
);
129 Bitmap aMask
= rAnimBitmap
.aBmpEx
.GetMask();
134 const Rectangle
aRect(aEmpty
, aVirtualDeviceMask
.GetOutputSizePixel());
135 const Wallpaper
aWallpaper(COL_BLACK
);
136 aVirtualDeviceMask
.DrawWallpaper(aRect
, aWallpaper
);
140 BitmapEx aExpandVisibilityMask
= BitmapEx(aMask
, aMask
);
141 aVirtualDeviceMask
.DrawBitmapEx(rAnimBitmap
.aPosPix
, aExpandVisibilityMask
);
148 // #i70772# react on no mask, for primitives, too.
149 const Bitmap
aMask(rAnimBitmap
.aBmpEx
.GetMask());
150 const Bitmap
aContent(rAnimBitmap
.aBmpEx
.GetBitmap());
152 aVirtualDeviceMask
.Erase();
153 aVirtualDevice
.DrawBitmap(rAnimBitmap
.aPosPix
, aContent
);
157 const Rectangle
aRect(rAnimBitmap
.aPosPix
, aContent
.GetSizePixel());
158 aVirtualDeviceMask
.SetFillColor(COL_BLACK
);
159 aVirtualDeviceMask
.SetLineColor();
160 aVirtualDeviceMask
.DrawRect(aRect
);
164 aVirtualDeviceMask
.DrawBitmap(rAnimBitmap
.aPosPix
, aMask
);
171 aVirtualDevice
.DrawBitmapEx(rAnimBitmap
.aPosPix
, rAnimBitmap
.aBmpEx
);
174 case DISPOSE_PREVIOUS
:
176 aVirtualDevice
.DrawBitmapEx(rAnimBitmap
.aPosPix
, rAnimBitmap
.aBmpEx
);
177 aVirtualDeviceMask
.DrawBitmap(rAnimBitmap
.aPosPix
, rAnimBitmap
.aBmpEx
.GetMask());
183 Bitmap aMainBitmap
= aVirtualDevice
.GetBitmap(Point(), aVirtualDevice
.GetOutputSizePixel());
184 Bitmap aMaskBitmap
= aVirtualDeviceMask
.GetBitmap(Point(), aVirtualDeviceMask
.GetOutputSizePixel());
185 aNextStep
.maBitmapEx
= BitmapEx(aMainBitmap
, aMaskBitmap
);
188 maSteps
.push_back(aNextStep
);
192 } // end of anonymous namespace
194 //////////////////////////////////////////////////////////////////////////////
196 namespace drawinglayer
198 namespace primitive2d
200 Primitive2DSequence
GraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
&
203 Primitive2DSequence aRetval
;
205 if(255L != getGraphicAttr().GetTransparency())
207 Primitive2DReference xPrimitive
;
209 // do not apply mirroring from GraphicAttr to the Metafile by calling
210 // GetTransformedGraphic, this will try to mirror the Metafile using Scale()
211 // at the Metafile. This again calls Scale at the single MetaFile actions,
212 // but this implementation never worked. I reworked that implementations,
213 // but for security reasons i will try not to use it.
214 basegfx::B2DHomMatrix
aTransform(getTransform());
216 if(getGraphicAttr().IsMirrored())
218 // content needs mirroring
219 const bool bHMirr(getGraphicAttr().GetMirrorFlags() & BMP_MIRROR_HORZ
);
220 const bool bVMirr(getGraphicAttr().GetMirrorFlags() & BMP_MIRROR_VERT
);
222 // mirror by applying negative scale to the unit primitive and
223 // applying the object transformation on it.
224 aTransform
= basegfx::tools::createScaleB2DHomMatrix(
226 bVMirr
? -1.0 : 1.0);
227 aTransform
.translate(
230 aTransform
= getTransform() * aTransform
;
233 // Get transformed graphic. Suppress rotation and cropping, only filtering is needed
234 // here (and may be replaced later on). Cropping is handled below as mask primitive (if set).
235 // Also need to suppress mirroring, it is part of the transformation now (see above).
236 GraphicAttr
aSuppressGraphicAttr(getGraphicAttr());
237 aSuppressGraphicAttr
.SetCrop(0, 0, 0, 0);
238 aSuppressGraphicAttr
.SetRotation(0);
239 aSuppressGraphicAttr
.SetMirrorFlags(0);
241 const GraphicObject
& rGraphicObject
= getGraphicObject();
242 const Graphic
aTransformedGraphic(rGraphicObject
.GetTransformedGraphic(&aSuppressGraphicAttr
));
244 switch(aTransformedGraphic
.GetType())
246 case GRAPHIC_BITMAP
:
248 if(aTransformedGraphic
.IsAnimated())
250 // prepare animation data
251 animatedBitmapExPreparator
aData(aTransformedGraphic
);
255 // create sub-primitives for animated bitmap and the needed animation loop
256 animation::AnimationEntryLoop
aAnimationLoop(aData
.loopCount() ? aData
.loopCount() : 0xffff);
257 Primitive2DSequence
aBitmapPrimitives(aData
.count());
259 for(sal_uInt32
a(0L); a
< aData
.count(); a
++)
261 animation::AnimationEntryFixed
aTime((double)aData
.stepTime(a
), (double)a
/ (double)aData
.count());
262 aAnimationLoop
.append(aTime
);
263 const Primitive2DReference
xRef(new BitmapPrimitive2D(aData
.stepBitmapEx(a
), aTransform
));
264 aBitmapPrimitives
[a
] = xRef
;
267 // prepare animation list
268 animation::AnimationEntryList aAnimationList
;
269 aAnimationList
.append(aAnimationLoop
);
271 // create and add animated switch primitive
272 xPrimitive
= Primitive2DReference(new AnimatedSwitchPrimitive2D(aAnimationList
, aBitmapPrimitives
, false));
275 else if(aTransformedGraphic
.getSvgData().get())
277 // embedded Svg fill, create embed transform
278 const basegfx::B2DRange
& rSvgRange(aTransformedGraphic
.getSvgData()->getRange());
280 if(basegfx::fTools::more(rSvgRange
.getWidth(), 0.0) && basegfx::fTools::more(rSvgRange
.getHeight(), 0.0))
282 // translate back to origin, scale to unit coordinates
283 basegfx::B2DHomMatrix
aEmbedSvg(
284 basegfx::tools::createTranslateB2DHomMatrix(
285 -rSvgRange
.getMinX(),
286 -rSvgRange
.getMinY()));
289 1.0 / rSvgRange
.getWidth(),
290 1.0 / rSvgRange
.getHeight());
292 // apply created object transformation
293 aEmbedSvg
= aTransform
* aEmbedSvg
;
295 // add Svg primitives embedded
296 xPrimitive
= new TransformPrimitive2D(
298 aTransformedGraphic
.getSvgData()->getPrimitive2DSequence());
303 xPrimitive
= Primitive2DReference(new BitmapPrimitive2D(aTransformedGraphic
.GetBitmapEx(), aTransform
));
309 case GRAPHIC_GDIMETAFILE
:
311 // create MetafilePrimitive2D
312 const GDIMetaFile
& rMetafile
= aTransformedGraphic
.GetGDIMetaFile();
314 xPrimitive
= Primitive2DReference(
315 new MetafilePrimitive2D( aTransform
, rMetafile
) );
317 // #i100357# find out if clipping is needed for this primitive. Unfortunately,
318 // there exist Metafiles who's content is bigger than the proposed PrefSize set
319 // at them. This is an error, but we need to work around this
320 const Size
aMetaFilePrefSize(rMetafile
.GetPrefSize());
321 const Size
aMetaFileRealSize(
322 const_cast< GDIMetaFile
& >(rMetafile
).GetBoundRect(
323 *Application::GetDefaultDevice()).GetSize());
325 if(aMetaFileRealSize
.getWidth() > aMetaFilePrefSize
.getWidth()
326 || aMetaFileRealSize
.getHeight() > aMetaFilePrefSize
.getHeight())
328 // clipping needed. Embed to MaskPrimitive2D. Create childs and mask polygon
329 const primitive2d::Primitive2DSequence
aChildContent(&xPrimitive
, 1);
330 basegfx::B2DPolygon
aMaskPolygon(basegfx::tools::createUnitPolygon());
331 aMaskPolygon
.transform(aTransform
);
333 xPrimitive
= Primitive2DReference(
335 basegfx::B2DPolyPolygon(aMaskPolygon
),
350 // check for cropping
351 if(getGraphicAttr().IsCropped())
353 // calculate scalings between real image size and logic object size. This
354 // is necessary since the crop values are relative to original bitmap size
355 double fFactorX(1.0);
356 double fFactorY(1.0);
359 const MapMode
aMapMode100thmm(MAP_100TH_MM
);
360 Size
aBitmapSize(rGraphicObject
.GetPrefSize());
362 // #i95968# better support PrefMapMode; special for MAP_PIXEL was missing
363 if(MAP_PIXEL
== rGraphicObject
.GetPrefMapMode().GetMapUnit())
365 aBitmapSize
= Application::GetDefaultDevice()->PixelToLogic(aBitmapSize
, aMapMode100thmm
);
369 aBitmapSize
= Application::GetDefaultDevice()->LogicToLogic(aBitmapSize
, rGraphicObject
.GetPrefMapMode(), aMapMode100thmm
);
372 const double fDivX(aBitmapSize
.Width() - getGraphicAttr().GetLeftCrop() - getGraphicAttr().GetRightCrop());
373 const double fDivY(aBitmapSize
.Height() - getGraphicAttr().GetTopCrop() - getGraphicAttr().GetBottomCrop());
374 const basegfx::B2DVector
aScale(aTransform
* basegfx::B2DVector(1.0, 1.0));
376 if(!basegfx::fTools::equalZero(fDivX
))
378 fFactorX
= fabs(aScale
.getX()) / fDivX
;
381 if(!basegfx::fTools::equalZero(fDivY
))
383 fFactorY
= fabs(aScale
.getY()) / fDivY
;
387 // embed content in cropPrimitive
388 xPrimitive
= new CropPrimitive2D(
389 Primitive2DSequence(&xPrimitive
, 1),
391 getGraphicAttr().GetLeftCrop() * fFactorX
,
392 getGraphicAttr().GetTopCrop() * fFactorY
,
393 getGraphicAttr().GetRightCrop() * fFactorX
,
394 getGraphicAttr().GetBottomCrop() * fFactorY
);
397 // add to decomposition
398 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval
, xPrimitive
);
405 GraphicPrimitive2D::GraphicPrimitive2D(
406 const basegfx::B2DHomMatrix
& rTransform
,
407 const GraphicObject
& rGraphicObject
,
408 const GraphicAttr
& rGraphicAttr
)
409 : BufferedDecompositionPrimitive2D(),
410 maTransform(rTransform
),
411 maGraphicObject(rGraphicObject
),
412 maGraphicAttr(rGraphicAttr
)
416 bool GraphicPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
418 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
420 const GraphicPrimitive2D
& rCompare
= (GraphicPrimitive2D
&)rPrimitive
;
422 return (getTransform() == rCompare
.getTransform()
423 && getGraphicObject() == rCompare
.getGraphicObject()
424 && getGraphicAttr() == rCompare
.getGraphicAttr());
430 basegfx::B2DRange
GraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
432 basegfx::B2DRange
aRetval(0.0, 0.0, 1.0, 1.0);
433 aRetval
.transform(getTransform());
438 ImplPrimitive2DIDBlock(GraphicPrimitive2D
, PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D
)
440 } // end of namespace primitive2d
441 } // end of namespace drawinglayer
443 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */