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/graphicprimitivehelper2d.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 <drawinglayer/primitive2d/maskprimitive2d.hxx>
27 : #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
28 : #include <basegfx/polygon/b2dpolygon.hxx>
29 : #include <basegfx/polygon/b2dpolygontools.hxx>
30 : #include <basegfx/numeric/ftools.hxx>
31 :
32 : // helper class for animated graphics
33 :
34 : #include <vcl/animate.hxx>
35 : #include <vcl/graph.hxx>
36 : #include <vcl/virdev.hxx>
37 : #include <vcl/svapp.hxx>
38 : #include <vcl/metaact.hxx>
39 :
40 : namespace
41 : {
42 0 : struct animationStep
43 : {
44 : BitmapEx maBitmapEx;
45 : sal_uInt32 mnTime;
46 : };
47 :
48 0 : class animatedBitmapExPreparator
49 : {
50 : ::Animation maAnimation;
51 : ::std::vector< animationStep > maSteps;
52 :
53 : sal_uInt32 generateStepTime(sal_uInt32 nIndex) const;
54 :
55 : public:
56 : animatedBitmapExPreparator(const Graphic& rGraphic);
57 :
58 0 : sal_uInt32 count() const { return maSteps.size(); }
59 0 : sal_uInt32 loopCount() const { return (sal_uInt32)maAnimation.GetLoopCount(); }
60 0 : sal_uInt32 stepTime(sal_uInt32 a) const { return maSteps[a].mnTime; }
61 0 : const BitmapEx& stepBitmapEx(sal_uInt32 a) const { return maSteps[a].maBitmapEx; }
62 : };
63 :
64 0 : sal_uInt32 animatedBitmapExPreparator::generateStepTime(sal_uInt32 nIndex) const
65 : {
66 0 : const AnimationBitmap& rAnimBitmap = maAnimation.Get(sal_uInt16(nIndex));
67 0 : sal_uInt32 nWaitTime(rAnimBitmap.nWait * 10);
68 :
69 : // #115934#
70 : // Take care of special value for MultiPage TIFFs. ATM these shall just
71 : // show their first page. Later we will offer some switching when object
72 : // is selected.
73 0 : if(ANIMATION_TIMEOUT_ON_CLICK == rAnimBitmap.nWait)
74 : {
75 : // ATM the huge value would block the timer, so
76 : // use a long time to show first page (whole day)
77 0 : nWaitTime = 100 * 60 * 60 * 24;
78 : }
79 :
80 : // Bad trap: There are animated gifs with no set WaitTime (!).
81 : // In that case use a default value.
82 0 : if(0L == nWaitTime)
83 : {
84 0 : nWaitTime = 100L;
85 : }
86 :
87 0 : return nWaitTime;
88 : }
89 :
90 0 : animatedBitmapExPreparator::animatedBitmapExPreparator(const Graphic& rGraphic)
91 0 : : maAnimation(rGraphic.GetAnimation())
92 : {
93 : OSL_ENSURE(GRAPHIC_BITMAP == rGraphic.GetType() && rGraphic.IsAnimated(), "animatedBitmapExPreparator: graphic is not animated (!)");
94 :
95 : // #128539# secure access to Animation, looks like there exist animated GIFs out there
96 : // with a step count of zero
97 0 : if(maAnimation.Count())
98 : {
99 0 : ScopedVclPtrInstance< VirtualDevice > aVirtualDevice(*Application::GetDefaultDevice());
100 0 : ScopedVclPtrInstance< VirtualDevice > aVirtualDeviceMask(*Application::GetDefaultDevice(), 1L);
101 :
102 : // Prepare VirtualDevices and their states
103 0 : aVirtualDevice->EnableMapMode(false);
104 0 : aVirtualDeviceMask->EnableMapMode(false);
105 0 : aVirtualDevice->SetOutputSizePixel(maAnimation.GetDisplaySizePixel());
106 0 : aVirtualDeviceMask->SetOutputSizePixel(maAnimation.GetDisplaySizePixel());
107 0 : aVirtualDevice->Erase();
108 0 : aVirtualDeviceMask->Erase();
109 :
110 0 : for(size_t a(0); a < maAnimation.Count(); a++)
111 : {
112 0 : animationStep aNextStep;
113 0 : aNextStep.mnTime = generateStepTime(a);
114 :
115 : // prepare step
116 0 : const AnimationBitmap& rAnimBitmap = maAnimation.Get(sal_uInt16(a));
117 :
118 0 : switch(rAnimBitmap.eDisposal)
119 : {
120 : case DISPOSE_NOT:
121 : {
122 0 : aVirtualDevice->DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
123 0 : Bitmap aMask = rAnimBitmap.aBmpEx.GetMask();
124 :
125 0 : if(aMask.IsEmpty())
126 : {
127 0 : const Point aEmpty;
128 0 : const Rectangle aRect(aEmpty, aVirtualDeviceMask->GetOutputSizePixel());
129 0 : const Wallpaper aWallpaper(COL_BLACK);
130 0 : aVirtualDeviceMask->DrawWallpaper(aRect, aWallpaper);
131 : }
132 : else
133 : {
134 0 : BitmapEx aExpandVisibilityMask = BitmapEx(aMask, aMask);
135 0 : aVirtualDeviceMask->DrawBitmapEx(rAnimBitmap.aPosPix, aExpandVisibilityMask);
136 : }
137 :
138 0 : break;
139 : }
140 : case DISPOSE_BACK:
141 : {
142 : // #i70772# react on no mask, for primitives, too.
143 0 : const Bitmap aMask(rAnimBitmap.aBmpEx.GetMask());
144 0 : const Bitmap aContent(rAnimBitmap.aBmpEx.GetBitmap());
145 :
146 0 : aVirtualDeviceMask->Erase();
147 0 : aVirtualDevice->DrawBitmap(rAnimBitmap.aPosPix, aContent);
148 :
149 0 : if(aMask.IsEmpty())
150 : {
151 0 : const Rectangle aRect(rAnimBitmap.aPosPix, aContent.GetSizePixel());
152 0 : aVirtualDeviceMask->SetFillColor(COL_BLACK);
153 0 : aVirtualDeviceMask->SetLineColor();
154 0 : aVirtualDeviceMask->DrawRect(aRect);
155 : }
156 : else
157 : {
158 0 : aVirtualDeviceMask->DrawBitmap(rAnimBitmap.aPosPix, aMask);
159 : }
160 :
161 0 : break;
162 : }
163 : case DISPOSE_FULL:
164 : {
165 0 : aVirtualDevice->DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
166 0 : break;
167 : }
168 : case DISPOSE_PREVIOUS :
169 : {
170 0 : aVirtualDevice->DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
171 0 : aVirtualDeviceMask->DrawBitmap(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx.GetMask());
172 0 : break;
173 : }
174 : }
175 :
176 : // create BitmapEx
177 0 : Bitmap aMainBitmap = aVirtualDevice->GetBitmap(Point(), aVirtualDevice->GetOutputSizePixel());
178 : #if defined(MACOSX) || defined(IOS)
179 : AlphaMask aMaskBitmap( aVirtualDeviceMask->GetBitmap( Point(), aVirtualDeviceMask->GetOutputSizePixel()));
180 : #else
181 0 : Bitmap aMaskBitmap = aVirtualDeviceMask->GetBitmap( Point(), aVirtualDeviceMask->GetOutputSizePixel());
182 : #endif
183 0 : aNextStep.maBitmapEx = BitmapEx(aMainBitmap, aMaskBitmap);
184 :
185 : // add to vector
186 0 : maSteps.push_back(aNextStep);
187 0 : }
188 : }
189 0 : }
190 : } // end of anonymous namespace
191 :
192 : namespace drawinglayer
193 : {
194 : namespace primitive2d
195 : {
196 348 : Primitive2DSequence create2DDecompositionOfGraphic(
197 : const Graphic& rGraphic,
198 : const basegfx::B2DHomMatrix& rTransform)
199 : {
200 348 : Primitive2DSequence aRetval;
201 :
202 348 : switch(rGraphic.GetType())
203 : {
204 : case GRAPHIC_BITMAP :
205 : {
206 189 : if(rGraphic.IsAnimated())
207 : {
208 : // prepare animation data
209 0 : animatedBitmapExPreparator aData(rGraphic);
210 :
211 0 : if(aData.count())
212 : {
213 : // create sub-primitives for animated bitmap and the needed animation loop
214 0 : animation::AnimationEntryLoop aAnimationLoop(aData.loopCount() ? aData.loopCount() : 0xffff);
215 0 : Primitive2DSequence aBitmapPrimitives(aData.count());
216 :
217 0 : for(sal_uInt32 a(0); a < aData.count(); a++)
218 : {
219 0 : animation::AnimationEntryFixed aTime((double)aData.stepTime(a), (double)a / (double)aData.count());
220 0 : aAnimationLoop.append(aTime);
221 0 : aBitmapPrimitives[a] = new BitmapPrimitive2D(
222 : aData.stepBitmapEx(a),
223 0 : rTransform);
224 0 : }
225 :
226 : // prepare animation list
227 0 : animation::AnimationEntryList aAnimationList;
228 0 : aAnimationList.append(aAnimationLoop);
229 :
230 : // create and add animated switch primitive
231 0 : aRetval.realloc(1);
232 0 : aRetval[0] = new AnimatedSwitchPrimitive2D(
233 : aAnimationList,
234 : aBitmapPrimitives,
235 0 : false);
236 0 : }
237 : }
238 189 : else if(rGraphic.getSvgData().get())
239 : {
240 : // embedded Svg fill, create embed transform
241 1 : const basegfx::B2DRange& rSvgRange(rGraphic.getSvgData()->getRange());
242 :
243 1 : if(basegfx::fTools::more(rSvgRange.getWidth(), 0.0) && basegfx::fTools::more(rSvgRange.getHeight(), 0.0))
244 : {
245 : // translate back to origin, scale to unit coordinates
246 : basegfx::B2DHomMatrix aEmbedSvg(
247 : basegfx::tools::createTranslateB2DHomMatrix(
248 1 : -rSvgRange.getMinX(),
249 2 : -rSvgRange.getMinY()));
250 :
251 : aEmbedSvg.scale(
252 1 : 1.0 / rSvgRange.getWidth(),
253 2 : 1.0 / rSvgRange.getHeight());
254 :
255 : // apply created object transformation
256 1 : aEmbedSvg = rTransform * aEmbedSvg;
257 :
258 : // add Svg primitives embedded
259 1 : aRetval.realloc(1);
260 2 : aRetval[0] = new TransformPrimitive2D(
261 : aEmbedSvg,
262 3 : rGraphic.getSvgData()->getPrimitive2DSequence());
263 : }
264 : }
265 : else
266 : {
267 188 : aRetval.realloc(1);
268 564 : aRetval[0] = new BitmapPrimitive2D(
269 : rGraphic.GetBitmapEx(),
270 376 : rTransform);
271 : }
272 :
273 189 : break;
274 : }
275 :
276 : case GRAPHIC_GDIMETAFILE :
277 : {
278 : // create MetafilePrimitive2D
279 129 : const GDIMetaFile& rMetafile = rGraphic.GetGDIMetaFile();
280 :
281 129 : aRetval.realloc(1);
282 258 : aRetval[0] = new MetafilePrimitive2D(
283 : rTransform,
284 258 : rMetafile);
285 :
286 : // #i100357# find out if clipping is needed for this primitive. Unfortunately,
287 : // there exist Metafiles who's content is bigger than the proposed PrefSize set
288 : // at them. This is an error, but we need to work around this
289 129 : const Size aMetaFilePrefSize(rMetafile.GetPrefSize());
290 : const Size aMetaFileRealSize(
291 : rMetafile.GetBoundRect(
292 129 : *Application::GetDefaultDevice()).GetSize());
293 :
294 258 : if(aMetaFileRealSize.getWidth() > aMetaFilePrefSize.getWidth()
295 129 : || aMetaFileRealSize.getHeight() > aMetaFilePrefSize.getHeight())
296 : {
297 : // clipping needed. Embed to MaskPrimitive2D. Create children and mask polygon
298 13 : basegfx::B2DPolygon aMaskPolygon(basegfx::tools::createUnitPolygon());
299 13 : aMaskPolygon.transform(rTransform);
300 :
301 : Primitive2DReference mask = new MaskPrimitive2D(
302 : basegfx::B2DPolyPolygon(aMaskPolygon),
303 26 : aRetval);
304 26 : aRetval[0] = mask;
305 : }
306 129 : break;
307 : }
308 :
309 : default:
310 : {
311 : // nothing to create
312 30 : break;
313 : }
314 : }
315 :
316 348 : return aRetval;
317 : }
318 :
319 0 : Primitive2DSequence create2DColorModifierEmbeddingsAsNeeded(
320 : const Primitive2DSequence& rChildren,
321 : GraphicDrawMode aGraphicDrawMode,
322 : double fLuminance,
323 : double fContrast,
324 : double fRed,
325 : double fGreen,
326 : double fBlue,
327 : double fGamma,
328 : bool bInvert)
329 : {
330 0 : Primitive2DSequence aRetval;
331 :
332 0 : if(!rChildren.getLength())
333 : {
334 : // no child content, done
335 0 : return aRetval;
336 : }
337 :
338 : // set child content as retval; that is what will be used as child content in all
339 : // embeddings from here
340 0 : aRetval = rChildren;
341 :
342 0 : if(GRAPHICDRAWMODE_WATERMARK == aGraphicDrawMode)
343 : {
344 : // this is solved by applying fixed values additionally to luminance
345 : // and contrast, do it here and reset DrawMode to GRAPHICDRAWMODE_STANDARD
346 : // original in svtools uses:
347 : // #define WATERMARK_LUM_OFFSET 50
348 : // #define WATERMARK_CON_OFFSET -70
349 0 : fLuminance = basegfx::clamp(fLuminance + 0.5, -1.0, 1.0);
350 0 : fContrast = basegfx::clamp(fContrast - 0.7, -1.0, 1.0);
351 0 : aGraphicDrawMode = GRAPHICDRAWMODE_STANDARD;
352 : }
353 :
354 : // DrawMode (GRAPHICDRAWMODE_WATERMARK already handled)
355 0 : switch(aGraphicDrawMode)
356 : {
357 : case GRAPHICDRAWMODE_GREYS:
358 : {
359 : // convert to grey
360 : const Primitive2DReference aPrimitiveGrey(
361 : new ModifiedColorPrimitive2D(
362 : aRetval,
363 : basegfx::BColorModifierSharedPtr(
364 0 : new basegfx::BColorModifier_gray())));
365 :
366 0 : aRetval = Primitive2DSequence(&aPrimitiveGrey, 1);
367 0 : break;
368 : }
369 : case GRAPHICDRAWMODE_MONO:
370 : {
371 : // convert to mono (black/white with threshold 0.5)
372 : const Primitive2DReference aPrimitiveBlackAndWhite(
373 : new ModifiedColorPrimitive2D(
374 : aRetval,
375 : basegfx::BColorModifierSharedPtr(
376 0 : new basegfx::BColorModifier_black_and_white(0.5))));
377 :
378 0 : aRetval = Primitive2DSequence(&aPrimitiveBlackAndWhite, 1);
379 0 : break;
380 : }
381 : // coverity[dead_error_begin] - intentional dead case
382 : case GRAPHICDRAWMODE_WATERMARK:
383 : {
384 : assert(false && "OOps, GRAPHICDRAWMODE_WATERMARK should already be handled (see above)");
385 : // fallthrough intended
386 : }
387 : default: // case GRAPHICDRAWMODE_STANDARD:
388 : {
389 : // nothing to do
390 0 : break;
391 : }
392 : }
393 :
394 : // mnContPercent, mnLumPercent, mnRPercent, mnGPercent, mnBPercent
395 : // handled in a single call
396 0 : if(!basegfx::fTools::equalZero(fLuminance)
397 0 : || !basegfx::fTools::equalZero(fContrast)
398 0 : || !basegfx::fTools::equalZero(fRed)
399 0 : || !basegfx::fTools::equalZero(fGreen)
400 0 : || !basegfx::fTools::equalZero(fBlue))
401 : {
402 : const Primitive2DReference aPrimitiveRGBLuminannceContrast(
403 : new ModifiedColorPrimitive2D(
404 : aRetval,
405 : basegfx::BColorModifierSharedPtr(
406 : new basegfx::BColorModifier_RGBLuminanceContrast(
407 : fRed,
408 : fGreen,
409 : fBlue,
410 : fLuminance,
411 0 : fContrast))));
412 :
413 0 : aRetval = Primitive2DSequence(&aPrimitiveRGBLuminannceContrast, 1);
414 : }
415 :
416 : // gamma (boolean)
417 0 : if(!basegfx::fTools::equal(fGamma, 1.0))
418 : {
419 : const Primitive2DReference aPrimitiveGamma(
420 : new ModifiedColorPrimitive2D(
421 : aRetval,
422 : basegfx::BColorModifierSharedPtr(
423 : new basegfx::BColorModifier_gamma(
424 0 : fGamma))));
425 :
426 0 : aRetval = Primitive2DSequence(&aPrimitiveGamma, 1);
427 : }
428 :
429 : // invert (boolean)
430 0 : if(bInvert)
431 : {
432 : const Primitive2DReference aPrimitiveInvert(
433 : new ModifiedColorPrimitive2D(
434 : aRetval,
435 : basegfx::BColorModifierSharedPtr(
436 0 : new basegfx::BColorModifier_invert())));
437 :
438 0 : aRetval = Primitive2DSequence(&aPrimitiveInvert, 1);
439 : }
440 :
441 0 : return aRetval;
442 : }
443 :
444 : } // end of namespace primitive2d
445 : } // end of namespace drawinglayer
446 :
447 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|