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 .
21 #include <sal/log.hxx>
22 #include "gdimtftools.hxx"
24 #include <com/sun/star/graphic/XGraphic.hpp>
25 #include <com/sun/star/graphic/XGraphicRenderer.hpp>
26 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
28 #include <cppuhelper/basemutex.hxx>
29 #include <cppuhelper/compbase.hxx>
31 #include <comphelper/fileformat.h>
32 #include <comphelper/propertyvalue.hxx>
34 #include <vcl/canvastools.hxx>
35 #include <vcl/metaact.hxx>
36 #include <vcl/virdev.hxx>
37 #include <vcl/gdimtf.hxx>
38 #include <vcl/animate/Animation.hxx>
39 #include <vcl/graph.hxx>
43 using namespace ::com::sun::star
;
46 // free support functions
47 // ======================
49 namespace slideshow::internal
51 // TODO(E2): Detect the case when svx/drawing layer is not
52 // in-process, or even not on the same machine, and
53 // fallback to metafile streaming!
55 // For fixing #i48102#, have to be a _lot_ more selective
56 // on which metafiles to convert to bitmaps. The problem
57 // here is that we _always_ get the shape content as a
58 // metafile, even if we have a bitmap graphic shape. Thus,
59 // calling GetBitmapEx on such a Graphic (see below) will
60 // result in one poorly scaled bitmap into another,
61 // somewhat arbitrarily sized bitmap.
62 static bool hasUnsupportedActions( const GDIMetaFile
& rMtf
)
64 // search metafile for RasterOp action
67 // TODO(Q3): avoid const-cast
68 for( pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).FirstAction();
70 pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).NextAction() )
72 switch( pCurrAct
->GetType() )
74 case MetaActionType::RASTEROP
:
75 // overpaint is okay - that's the default, anyway
76 if( RasterOp::OverPaint
==
77 static_cast<MetaRasterOpAction
*>(pCurrAct
)->GetRasterOp() )
82 case MetaActionType::MOVECLIPREGION
:
83 case MetaActionType::REFPOINT
:
84 case MetaActionType::WALLPAPER
:
85 return true; // at least one unsupported
91 return false; // no unsupported action found
96 typedef ::cppu::WeakComponentImplHelper
< graphic::XGraphicRenderer
> DummyRenderer_Base
;
98 class DummyRenderer
: public cppu::BaseMutex
, public DummyRenderer_Base
102 DummyRenderer_Base( m_aMutex
),
107 //--- XGraphicRenderer -----------------------------------
108 virtual void SAL_CALL
render( const uno::Reference
< graphic::XGraphic
>& rGraphic
) override
110 ::osl::MutexGuard
aGuard( m_aMutex
);
111 mxGraphic
= rGraphic
;
114 /** Retrieve GDIMetaFile from renderer
116 @param bForeignSource
117 When true, the source of the metafile might be a
118 foreign application. The metafile is checked
119 against unsupported content, and, if necessary,
120 returned as a pre-rendered bitmap.
122 GDIMetaFileSharedPtr
getMtf( bool bForeignSource
) const
124 ::osl::MutexGuard
aGuard( m_aMutex
);
126 Graphic
aGraphic( mxGraphic
);
128 if( aGraphic
.GetType() == GraphicType::Bitmap
||
130 hasUnsupportedActions(aGraphic
.GetGDIMetaFile()) ) )
132 // wrap bitmap into GDIMetafile
133 GDIMetaFileSharedPtr xMtf
= std::make_shared
<GDIMetaFile
>();
135 ::BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
137 xMtf
->AddAction( new MetaBmpExAction( Point(),
139 xMtf
->SetPrefSize( aBmpEx
.GetPrefSize() );
140 xMtf
->SetPrefMapMode( aBmpEx
.GetPrefMapMode() );
144 return std::make_shared
<GDIMetaFile
>(aGraphic
.GetGDIMetaFile());
148 uno::Reference
< graphic::XGraphic
> mxGraphic
;
153 // Quick'n'dirty way: tunnel Graphic (only works for
154 // in-process slideshow, of course)
155 GDIMetaFileSharedPtr
getMetaFile( const uno::Reference
< lang::XComponent
>& xSource
,
156 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
158 const uno::Reference
< uno::XComponentContext
>& rxContext
)
162 SAL_WARN("slideshow.opengl", "getMetaFile(): Invalid context" );
163 return GDIMetaFileSharedPtr();
166 // create dummy XGraphicRenderer, which receives the
167 // generated XGraphic from the GraphicExporter
169 // TODO(P3): Move creation of DummyRenderer out of the
170 // loop! Either by making it static, or transforming
171 // the whole thing here into a class.
172 rtl::Reference
<DummyRenderer
> xRenderer( new DummyRenderer() );
174 // creating the graphic exporter
175 uno::Reference
< drawing::XGraphicExportFilter
> xExporter
=
176 drawing::GraphicExportFilter::create(rxContext
);
178 uno::Sequence
< beans::PropertyValue
> aFilterData
{
179 comphelper::makePropertyValue("ScrollText",
180 ((mtfLoadFlags
& MTF_LOAD_SCROLL_TEXT_MTF
) != 0)),
181 comphelper::makePropertyValue("ExportOnlyBackground",
182 ((mtfLoadFlags
& MTF_LOAD_BACKGROUND_ONLY
) != 0)),
183 comphelper::makePropertyValue("Version", static_cast<sal_Int32
>( SOFFICE_FILEFORMAT_50
)),
184 comphelper::makePropertyValue(
185 "CurrentPage", uno::Reference
< uno::XInterface
>( xContainingPage
,
186 uno::UNO_QUERY_THROW
))
189 uno::Sequence
< beans::PropertyValue
> aProps
{
190 comphelper::makePropertyValue("FilterName", OUString("SVM")),
191 comphelper::makePropertyValue("GraphicRenderer", uno::Reference
< graphic::XGraphicRenderer
>(xRenderer
)),
192 comphelper::makePropertyValue("FilterData", aFilterData
)
195 xExporter
->setSourceDocument( xSource
);
196 if( !xExporter
->filter( aProps
) )
197 return GDIMetaFileSharedPtr();
199 GDIMetaFileSharedPtr xMtf
= xRenderer
->getMtf( (mtfLoadFlags
& MTF_LOAD_FOREIGN_SOURCE
) != 0 );
201 // pRenderer is automatically destroyed when xRenderer
204 // TODO(E3): Error handling. Exporter might have
205 // generated nothing, a bitmap, threw an exception,
210 sal_Int32
getNextActionOffset( MetaAction
* pCurrAct
)
212 // Special handling for actions that represent
213 // more than one indexable action
214 // ===========================================
216 switch (pCurrAct
->GetType()) {
217 case MetaActionType::TEXT
: {
218 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
219 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
222 case MetaActionType::TEXTARRAY
: {
223 MetaTextArrayAction
* pAct
=
224 static_cast<MetaTextArrayAction
*>(pCurrAct
);
225 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
228 case MetaActionType::STRETCHTEXT
: {
229 MetaStretchTextAction
* pAct
=
230 static_cast<MetaStretchTextAction
*>(pCurrAct
);
231 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
234 case MetaActionType::FLOATTRANSPARENT
: {
235 MetaFloatTransparentAction
* pAct
=
236 static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
237 // TODO(F2): Recurse into action metafile
238 // (though this is currently not used from the
239 // DrawingLayer - shape transparency gradients
240 // don't affect shape text)
241 return pAct
->GetGDIMetaFile().GetActionSize();
248 bool getAnimationFromGraphic( VectorOfMtfAnimationFrames
& o_rFrames
,
249 sal_uInt32
& o_rLoopCount
,
250 const Graphic
& rGraphic
)
254 if( !rGraphic
.IsAnimated() )
257 // some loop invariants
258 ::Animation
aAnimation( rGraphic
.GetAnimation() );
259 const Point aEmptyPoint
;
260 const Size
aAnimSize( aAnimation
.GetDisplaySizePixel() );
262 // setup VDev, into which all bitmaps are painted (want to
263 // normalize animations to n bitmaps of same size. An Animation,
264 // though, can contain bitmaps of varying sizes and different
266 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
267 pVDev
->SetOutputSizePixel( aAnimSize
);
268 pVDev
->EnableMapMode( false );
270 // setup mask VDev (alpha VDev is currently rather slow)
271 ScopedVclPtrInstance
<VirtualDevice
> pVDevMask(DeviceFormat::WITHOUT_ALPHA
);
272 pVDevMask
->SetOutputSizePixel( aAnimSize
);
273 pVDevMask
->EnableMapMode( false );
275 o_rLoopCount
= aAnimation
.GetLoopCount();
277 for( sal_uInt16 i
=0, nCount
=aAnimation
.Count(); i
<nCount
; ++i
)
279 const AnimationFrame
& rAnimationFrame( aAnimation
.Get(i
) );
280 switch(rAnimationFrame
.meDisposal
)
284 pVDev
->DrawBitmapEx(rAnimationFrame
.maPositionPixel
,
285 rAnimationFrame
.maBitmapEx
);
286 Bitmap aMask
= rAnimationFrame
.maBitmapEx
.GetAlphaMask();
288 if( aMask
.IsEmpty() )
290 const tools::Rectangle
aRect(aEmptyPoint
,
291 pVDevMask
->GetOutputSizePixel());
292 const Wallpaper
aWallpaper(COL_BLACK
);
293 pVDevMask
->DrawWallpaper(aRect
,
298 BitmapEx
aTmpMask(aMask
, aMask
);
299 pVDevMask
->DrawBitmapEx(rAnimationFrame
.maPositionPixel
,
307 // #i70772# react on no mask
308 const Bitmap
aMask(rAnimationFrame
.maBitmapEx
.GetAlphaMask());
309 const Bitmap
& rContent(rAnimationFrame
.maBitmapEx
.GetBitmap());
312 pVDev
->DrawBitmap(rAnimationFrame
.maPositionPixel
, rContent
);
316 const tools::Rectangle
aRect(rAnimationFrame
.maPositionPixel
, rContent
.GetSizePixel());
317 pVDevMask
->SetFillColor( COL_BLACK
);
318 pVDevMask
->SetLineColor();
319 pVDevMask
->DrawRect(aRect
);
323 pVDevMask
->DrawBitmap(rAnimationFrame
.maPositionPixel
, aMask
);
328 case Disposal::Previous
:
330 pVDev
->DrawBitmapEx(rAnimationFrame
.maPositionPixel
,
331 rAnimationFrame
.maBitmapEx
);
332 pVDevMask
->DrawBitmap(rAnimationFrame
.maPositionPixel
,
333 rAnimationFrame
.maBitmapEx
.GetAlphaMask());
338 // extract current aVDev content into a new animation
340 GDIMetaFileSharedPtr pMtf
= std::make_shared
<GDIMetaFile
>();
342 new MetaBmpExAction( aEmptyPoint
,
347 pVDevMask
->GetBitmap(
351 // setup mtf dimensions and pref map mode (for
352 // simplicity, keep it all in pixel. the metafile
353 // renderer scales it down to (1, 1) box anyway)
354 pMtf
->SetPrefMapMode( MapMode() );
355 pMtf
->SetPrefSize( aAnimSize
);
357 // Take care of special value for MultiPage TIFFs. ATM these shall just
358 // show their first page for _quite_ some time.
359 sal_Int32
nWaitTime100thSeconds(rAnimationFrame
.mnWait
);
360 if( ANIMATION_TIMEOUT_ON_CLICK
== nWaitTime100thSeconds
)
362 // ATM the huge value would block the timer, so use a long
363 // time to show first page (whole day)
364 nWaitTime100thSeconds
= 100 * 60 * 60 * 24;
367 // There are animated GIFs with no WaitTime set. Take 0.1 sec, the
368 // same duration that is used by the edit view.
369 if( nWaitTime100thSeconds
== 0 )
370 nWaitTime100thSeconds
= 10;
372 o_rFrames
.emplace_back( pMtf
, nWaitTime100thSeconds
/ 100.0 );
375 return !o_rFrames
.empty();
378 bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle
& o_rScrollRect
,
379 ::basegfx::B2DRectangle
& o_rPaintRect
,
380 const GDIMetaFileSharedPtr
& rMtf
)
382 // extract bounds: scroll rect, paint rect
383 bool bScrollRectSet(false);
384 bool bPaintRectSet(false);
386 for ( MetaAction
* pCurrAct
= rMtf
->FirstAction();
387 pCurrAct
!= nullptr; pCurrAct
= rMtf
->NextAction() )
389 if (pCurrAct
->GetType() == MetaActionType::COMMENT
)
391 MetaCommentAction
* pAct
=
392 static_cast<MetaCommentAction
*>(pCurrAct
);
393 // skip comment if not a special XTEXT... comment
394 if( pAct
->GetComment().matchIgnoreAsciiCase( "XTEXT" ) )
396 if (pAct
->GetComment().equalsIgnoreAsciiCase("XTEXT_SCROLLRECT"))
398 o_rScrollRect
= vcl::unotools::b2DRectangleFromRectangle(
399 *reinterpret_cast<tools::Rectangle
const *>(
402 bScrollRectSet
= true;
404 else if (pAct
->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
406 o_rPaintRect
= vcl::unotools::b2DRectangleFromRectangle(
407 *reinterpret_cast<tools::Rectangle
const *>(
410 bPaintRectSet
= true;
416 return bScrollRectSet
&& bPaintRectSet
;
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */