Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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>
31 :
32 : //////////////////////////////////////////////////////////////////////////////
33 : // helper class for animated graphics
34 :
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>
40 :
41 : //////////////////////////////////////////////////////////////////////////////
42 : // includes for testing MetafilePrimitive2D::create2DDecomposition
43 :
44 : //////////////////////////////////////////////////////////////////////////////
45 :
46 : namespace
47 : {
48 0 : struct animationStep
49 : {
50 : BitmapEx maBitmapEx;
51 : sal_uInt32 mnTime;
52 : };
53 :
54 0 : class animatedBitmapExPreparator
55 : {
56 : ::Animation maAnimation;
57 : ::std::vector< animationStep > maSteps;
58 :
59 : sal_uInt32 generateStepTime(sal_uInt32 nIndex) const;
60 :
61 : public:
62 : explicit animatedBitmapExPreparator(const Graphic& rGraphic);
63 :
64 0 : sal_uInt32 count() const { return maSteps.size(); }
65 0 : sal_uInt32 loopCount() const { return (sal_uInt32)maAnimation.GetLoopCount(); }
66 0 : sal_uInt32 stepTime(sal_uInt32 a) const { return maSteps[a].mnTime; }
67 0 : const BitmapEx& stepBitmapEx(sal_uInt32 a) const { return maSteps[a].maBitmapEx; }
68 : };
69 :
70 0 : sal_uInt32 animatedBitmapExPreparator::generateStepTime(sal_uInt32 nIndex) const
71 : {
72 0 : const AnimationBitmap& rAnimBitmap = maAnimation.Get(sal_uInt16(nIndex));
73 0 : sal_uInt32 nWaitTime(rAnimBitmap.nWait * 10);
74 :
75 : // #115934#
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
78 : // is selected.
79 0 : if(ANIMATION_TIMEOUT_ON_CLICK == rAnimBitmap.nWait)
80 : {
81 : // ATM the huge value would block the timer, so
82 : // use a long time to show first page (whole day)
83 0 : nWaitTime = 100 * 60 * 60 * 24;
84 : }
85 :
86 : // Bad trap: There are animated gifs with no set WaitTime (!).
87 : // In that case use a default value.
88 0 : if(0L == nWaitTime)
89 : {
90 0 : nWaitTime = 100L;
91 : }
92 :
93 0 : return nWaitTime;
94 : }
95 :
96 0 : animatedBitmapExPreparator::animatedBitmapExPreparator(const Graphic& rGraphic)
97 0 : : maAnimation(rGraphic.GetAnimation())
98 : {
99 : OSL_ENSURE(GRAPHIC_BITMAP == rGraphic.GetType() && rGraphic.IsAnimated(), "animatedBitmapExPreparator: graphic is not animated (!)");
100 :
101 : // #128539# secure access to Animation, looks like there exist animated GIFs out there
102 : // with a step count of zero
103 0 : if(maAnimation.Count())
104 : {
105 0 : VirtualDevice aVirtualDevice(*Application::GetDefaultDevice());
106 0 : VirtualDevice aVirtualDeviceMask(*Application::GetDefaultDevice(), 1L);
107 :
108 : // Prepare VirtualDevices and their states
109 0 : aVirtualDevice.EnableMapMode(sal_False);
110 0 : aVirtualDeviceMask.EnableMapMode(sal_False);
111 0 : aVirtualDevice.SetOutputSizePixel(maAnimation.GetDisplaySizePixel());
112 0 : aVirtualDeviceMask.SetOutputSizePixel(maAnimation.GetDisplaySizePixel());
113 0 : aVirtualDevice.Erase();
114 0 : aVirtualDeviceMask.Erase();
115 :
116 0 : for(sal_uInt16 a(0L); a < maAnimation.Count(); a++)
117 : {
118 0 : animationStep aNextStep;
119 0 : aNextStep.mnTime = generateStepTime(a);
120 :
121 : // prepare step
122 0 : const AnimationBitmap& rAnimBitmap = maAnimation.Get(sal_uInt16(a));
123 :
124 0 : switch(rAnimBitmap.eDisposal)
125 : {
126 : case DISPOSE_NOT:
127 : {
128 0 : aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
129 0 : Bitmap aMask = rAnimBitmap.aBmpEx.GetMask();
130 :
131 0 : if(aMask.IsEmpty())
132 : {
133 0 : const Point aEmpty;
134 0 : const Rectangle aRect(aEmpty, aVirtualDeviceMask.GetOutputSizePixel());
135 0 : const Wallpaper aWallpaper(COL_BLACK);
136 0 : aVirtualDeviceMask.DrawWallpaper(aRect, aWallpaper);
137 : }
138 : else
139 : {
140 0 : BitmapEx aExpandVisibilityMask = BitmapEx(aMask, aMask);
141 0 : aVirtualDeviceMask.DrawBitmapEx(rAnimBitmap.aPosPix, aExpandVisibilityMask);
142 : }
143 :
144 0 : break;
145 : }
146 : case DISPOSE_BACK:
147 : {
148 : // #i70772# react on no mask, for primitives, too.
149 0 : const Bitmap aMask(rAnimBitmap.aBmpEx.GetMask());
150 0 : const Bitmap aContent(rAnimBitmap.aBmpEx.GetBitmap());
151 :
152 0 : aVirtualDeviceMask.Erase();
153 0 : aVirtualDevice.DrawBitmap(rAnimBitmap.aPosPix, aContent);
154 :
155 0 : if(aMask.IsEmpty())
156 : {
157 0 : const Rectangle aRect(rAnimBitmap.aPosPix, aContent.GetSizePixel());
158 0 : aVirtualDeviceMask.SetFillColor(COL_BLACK);
159 0 : aVirtualDeviceMask.SetLineColor();
160 0 : aVirtualDeviceMask.DrawRect(aRect);
161 : }
162 : else
163 : {
164 0 : aVirtualDeviceMask.DrawBitmap(rAnimBitmap.aPosPix, aMask);
165 : }
166 :
167 0 : break;
168 : }
169 : case DISPOSE_FULL:
170 : {
171 0 : aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
172 0 : break;
173 : }
174 : case DISPOSE_PREVIOUS :
175 : {
176 0 : aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
177 0 : aVirtualDeviceMask.DrawBitmap(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx.GetMask());
178 0 : break;
179 : }
180 : }
181 :
182 : // create BitmapEx
183 0 : Bitmap aMainBitmap = aVirtualDevice.GetBitmap(Point(), aVirtualDevice.GetOutputSizePixel());
184 0 : Bitmap aMaskBitmap = aVirtualDeviceMask.GetBitmap(Point(), aVirtualDeviceMask.GetOutputSizePixel());
185 0 : aNextStep.maBitmapEx = BitmapEx(aMainBitmap, aMaskBitmap);
186 :
187 : // add to vector
188 0 : maSteps.push_back(aNextStep);
189 0 : }
190 : }
191 0 : }
192 : } // end of anonymous namespace
193 :
194 : //////////////////////////////////////////////////////////////////////////////
195 :
196 : namespace drawinglayer
197 : {
198 : namespace primitive2d
199 : {
200 8 : Primitive2DSequence GraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D&
201 : ) const
202 : {
203 8 : Primitive2DSequence aRetval;
204 :
205 8 : if(255L != getGraphicAttr().GetTransparency())
206 : {
207 8 : Primitive2DReference xPrimitive;
208 :
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 8 : basegfx::B2DHomMatrix aTransform(getTransform());
215 :
216 8 : if(getGraphicAttr().IsMirrored())
217 : {
218 : // content needs mirroring
219 0 : const bool bHMirr(getGraphicAttr().GetMirrorFlags() & BMP_MIRROR_HORZ);
220 0 : const bool bVMirr(getGraphicAttr().GetMirrorFlags() & BMP_MIRROR_VERT);
221 :
222 : // mirror by applying negative scale to the unit primitive and
223 : // applying the object transformation on it.
224 : aTransform = basegfx::tools::createScaleB2DHomMatrix(
225 : bHMirr ? -1.0 : 1.0,
226 0 : bVMirr ? -1.0 : 1.0);
227 : aTransform.translate(
228 : bHMirr ? 1.0 : 0.0,
229 0 : bVMirr ? 1.0 : 0.0);
230 0 : aTransform = getTransform() * aTransform;
231 : }
232 :
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 8 : GraphicAttr aSuppressGraphicAttr(getGraphicAttr());
237 8 : aSuppressGraphicAttr.SetCrop(0, 0, 0, 0);
238 8 : aSuppressGraphicAttr.SetRotation(0);
239 8 : aSuppressGraphicAttr.SetMirrorFlags(0);
240 :
241 8 : const GraphicObject& rGraphicObject = getGraphicObject();
242 8 : const Graphic aTransformedGraphic(rGraphicObject.GetTransformedGraphic(&aSuppressGraphicAttr));
243 :
244 8 : switch(aTransformedGraphic.GetType())
245 : {
246 : case GRAPHIC_BITMAP :
247 : {
248 7 : if(aTransformedGraphic.IsAnimated())
249 : {
250 : // prepare animation data
251 0 : animatedBitmapExPreparator aData(aTransformedGraphic);
252 :
253 0 : if(aData.count())
254 : {
255 : // create sub-primitives for animated bitmap and the needed animation loop
256 0 : animation::AnimationEntryLoop aAnimationLoop(aData.loopCount() ? aData.loopCount() : 0xffff);
257 0 : Primitive2DSequence aBitmapPrimitives(aData.count());
258 :
259 0 : for(sal_uInt32 a(0L); a < aData.count(); a++)
260 : {
261 0 : animation::AnimationEntryFixed aTime((double)aData.stepTime(a), (double)a / (double)aData.count());
262 0 : aAnimationLoop.append(aTime);
263 0 : const Primitive2DReference xRef(new BitmapPrimitive2D(aData.stepBitmapEx(a), aTransform));
264 0 : aBitmapPrimitives[a] = xRef;
265 0 : }
266 :
267 : // prepare animation list
268 0 : animation::AnimationEntryList aAnimationList;
269 0 : aAnimationList.append(aAnimationLoop);
270 :
271 : // create and add animated switch primitive
272 0 : xPrimitive = Primitive2DReference(new AnimatedSwitchPrimitive2D(aAnimationList, aBitmapPrimitives, false));
273 0 : }
274 : }
275 7 : else if(aTransformedGraphic.getSvgData().get())
276 : {
277 : // embedded Svg fill, create embed transform
278 0 : const basegfx::B2DRange& rSvgRange(aTransformedGraphic.getSvgData()->getRange());
279 :
280 0 : if(basegfx::fTools::more(rSvgRange.getWidth(), 0.0) && basegfx::fTools::more(rSvgRange.getHeight(), 0.0))
281 : {
282 : // translate back to origin, scale to unit coordinates
283 : basegfx::B2DHomMatrix aEmbedSvg(
284 : basegfx::tools::createTranslateB2DHomMatrix(
285 0 : -rSvgRange.getMinX(),
286 0 : -rSvgRange.getMinY()));
287 :
288 : aEmbedSvg.scale(
289 0 : 1.0 / rSvgRange.getWidth(),
290 0 : 1.0 / rSvgRange.getHeight());
291 :
292 : // apply created object transformation
293 0 : aEmbedSvg = aTransform * aEmbedSvg;
294 :
295 : // add Svg primitives embedded
296 : xPrimitive = new TransformPrimitive2D(
297 : aEmbedSvg,
298 0 : aTransformedGraphic.getSvgData()->getPrimitive2DSequence());
299 : }
300 : }
301 : else
302 : {
303 7 : xPrimitive = Primitive2DReference(new BitmapPrimitive2D(aTransformedGraphic.GetBitmapEx(), aTransform));
304 : }
305 :
306 7 : break;
307 : }
308 :
309 : case GRAPHIC_GDIMETAFILE :
310 : {
311 : // create MetafilePrimitive2D
312 1 : const GDIMetaFile& rMetafile = aTransformedGraphic.GetGDIMetaFile();
313 :
314 : xPrimitive = Primitive2DReference(
315 1 : new MetafilePrimitive2D( aTransform, rMetafile ) );
316 :
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 1 : const Size aMetaFilePrefSize(rMetafile.GetPrefSize());
321 : const Size aMetaFileRealSize(
322 : const_cast< GDIMetaFile& >(rMetafile).GetBoundRect(
323 1 : *Application::GetDefaultDevice()).GetSize());
324 :
325 2 : if(aMetaFileRealSize.getWidth() > aMetaFilePrefSize.getWidth()
326 1 : || aMetaFileRealSize.getHeight() > aMetaFilePrefSize.getHeight())
327 : {
328 : // clipping needed. Embed to MaskPrimitive2D. Create childs and mask polygon
329 0 : const primitive2d::Primitive2DSequence aChildContent(&xPrimitive, 1);
330 0 : basegfx::B2DPolygon aMaskPolygon(basegfx::tools::createUnitPolygon());
331 0 : aMaskPolygon.transform(aTransform);
332 :
333 : xPrimitive = Primitive2DReference(
334 : new MaskPrimitive2D(
335 : basegfx::B2DPolyPolygon(aMaskPolygon),
336 0 : aChildContent));
337 : }
338 : break;
339 : }
340 :
341 : default:
342 : {
343 : // nothing to create
344 0 : break;
345 : }
346 : }
347 :
348 8 : if(xPrimitive.is())
349 : {
350 : // check for cropping
351 8 : if(getGraphicAttr().IsCropped())
352 : {
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 0 : double fFactorX(1.0);
356 0 : double fFactorY(1.0);
357 :
358 : {
359 0 : const MapMode aMapMode100thmm(MAP_100TH_MM);
360 0 : Size aBitmapSize(rGraphicObject.GetPrefSize());
361 :
362 : // #i95968# better support PrefMapMode; special for MAP_PIXEL was missing
363 0 : if(MAP_PIXEL == rGraphicObject.GetPrefMapMode().GetMapUnit())
364 : {
365 0 : aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
366 : }
367 : else
368 : {
369 0 : aBitmapSize = Application::GetDefaultDevice()->LogicToLogic(aBitmapSize, rGraphicObject.GetPrefMapMode(), aMapMode100thmm);
370 : }
371 :
372 0 : const double fDivX(aBitmapSize.Width() - getGraphicAttr().GetLeftCrop() - getGraphicAttr().GetRightCrop());
373 0 : const double fDivY(aBitmapSize.Height() - getGraphicAttr().GetTopCrop() - getGraphicAttr().GetBottomCrop());
374 0 : const basegfx::B2DVector aScale(aTransform * basegfx::B2DVector(1.0, 1.0));
375 :
376 0 : if(!basegfx::fTools::equalZero(fDivX))
377 : {
378 0 : fFactorX = fabs(aScale.getX()) / fDivX;
379 : }
380 :
381 0 : if(!basegfx::fTools::equalZero(fDivY))
382 : {
383 0 : fFactorY = fabs(aScale.getY()) / fDivY;
384 0 : }
385 : }
386 :
387 : // embed content in cropPrimitive
388 : xPrimitive = new CropPrimitive2D(
389 : Primitive2DSequence(&xPrimitive, 1),
390 : aTransform,
391 0 : getGraphicAttr().GetLeftCrop() * fFactorX,
392 0 : getGraphicAttr().GetTopCrop() * fFactorY,
393 0 : getGraphicAttr().GetRightCrop() * fFactorX,
394 0 : getGraphicAttr().GetBottomCrop() * fFactorY);
395 : }
396 :
397 : // add to decomposition
398 8 : appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xPrimitive);
399 8 : }
400 : }
401 :
402 8 : return aRetval;
403 : }
404 :
405 35 : GraphicPrimitive2D::GraphicPrimitive2D(
406 : const basegfx::B2DHomMatrix& rTransform,
407 : const GraphicObject& rGraphicObject,
408 : const GraphicAttr& rGraphicAttr)
409 : : BufferedDecompositionPrimitive2D(),
410 : maTransform(rTransform),
411 : maGraphicObject(rGraphicObject),
412 35 : maGraphicAttr(rGraphicAttr)
413 : {
414 35 : }
415 :
416 0 : bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
417 : {
418 0 : if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
419 : {
420 0 : const GraphicPrimitive2D& rCompare = (GraphicPrimitive2D&)rPrimitive;
421 :
422 0 : return (getTransform() == rCompare.getTransform()
423 0 : && getGraphicObject() == rCompare.getGraphicObject()
424 0 : && getGraphicAttr() == rCompare.getGraphicAttr());
425 : }
426 :
427 0 : return false;
428 : }
429 :
430 59 : basegfx::B2DRange GraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
431 : {
432 59 : basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
433 59 : aRetval.transform(getTransform());
434 59 : return aRetval;
435 : }
436 :
437 : // provide unique ID
438 16 : ImplPrimitrive2DIDBlock(GraphicPrimitive2D, PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D)
439 :
440 : } // end of namespace primitive2d
441 : } // end of namespace drawinglayer
442 :
443 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|