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 <tools/diagnose_ex.h>
22 #include "gdimtftools.hxx"
24 #include <com/sun/star/document/XExporter.hpp>
25 #include <com/sun/star/document/XFilter.hpp>
26 #include <com/sun/star/graphic/XGraphic.hpp>
27 #include <com/sun/star/graphic/XGraphicRenderer.hpp>
28 #include <com/sun/star/drawing/XShape.hpp>
29 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
31 #include <cppuhelper/basemutex.hxx>
32 #include <cppuhelper/compbase.hxx>
34 #include <comphelper/uno3.hxx>
36 #include <tools/stream.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vcl/canvastools.hxx>
39 #include <vcl/metaact.hxx>
40 #include <vcl/virdev.hxx>
41 #include <vcl/gdimtf.hxx>
42 #include <vcl/animate.hxx>
43 #include <vcl/graph.hxx>
45 #include <unotools/streamwrap.hxx>
49 using namespace ::com::sun::star
;
52 // free support functions
53 // ======================
59 // TODO(E2): Detect the case when svx/drawing layer is not
60 // in-process, or even not on the same machine, and
61 // fallback to metafile streaming!
63 // For fixing #i48102#, have to be a _lot_ more selective
64 // on which metafiles to convert to bitmaps. The problem
65 // here is that we _always_ get the shape content as a
66 // metafile, even if we have a bitmap graphic shape. Thus,
67 // calling GetBitmapEx on such a Graphic (see below) will
68 // result in one poorly scaled bitmap into another,
69 // somewhat arbitrarily sized bitmap.
70 bool hasUnsupportedActions( const GDIMetaFile
& rMtf
)
72 // search metafile for RasterOp action
75 // TODO(Q3): avoid const-cast
76 for( pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).FirstAction();
78 pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).NextAction() )
80 switch( pCurrAct
->GetType() )
82 case MetaActionType::RASTEROP
:
83 // overpaint is okay - that's the default, anyway
84 if( RasterOp::OverPaint
==
85 static_cast<MetaRasterOpAction
*>(pCurrAct
)->GetRasterOp() )
90 case MetaActionType::MOVECLIPREGION
:
91 case MetaActionType::REFPOINT
:
92 case MetaActionType::WALLPAPER
:
93 return true; // at least one unsupported
99 return false; // no unsupported action found
104 typedef ::cppu::WeakComponentImplHelper
< graphic::XGraphicRenderer
> DummyRenderer_Base
;
106 class DummyRenderer
: public cppu::BaseMutex
, public DummyRenderer_Base
110 DummyRenderer_Base( m_aMutex
),
115 //--- XGraphicRenderer -----------------------------------
116 virtual void SAL_CALL
render( const uno::Reference
< graphic::XGraphic
>& rGraphic
) override
118 ::osl::MutexGuard
aGuard( m_aMutex
);
119 mxGraphic
= rGraphic
;
122 /** Retrieve GDIMetaFile from renderer
124 @param bForeignSource
125 When true, the source of the metafile might be a
126 foreign application. The metafile is checked
127 against unsupported content, and, if necessary,
128 returned as a pre-rendererd bitmap.
130 GDIMetaFileSharedPtr
getMtf( bool bForeignSource
) const
132 ::osl::MutexGuard
aGuard( m_aMutex
);
134 Graphic
aGraphic( mxGraphic
);
136 if( aGraphic
.GetType() == GraphicType::Bitmap
||
138 hasUnsupportedActions(aGraphic
.GetGDIMetaFile()) ) )
140 // wrap bitmap into GDIMetafile
141 GDIMetaFileSharedPtr
xMtf(new GDIMetaFile
);
144 ::BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
146 xMtf
->AddAction( new MetaBmpExAction( aEmptyPoint
,
148 xMtf
->SetPrefSize( aBmpEx
.GetPrefSize() );
149 xMtf
->SetPrefMapMode( aBmpEx
.GetPrefMapMode() );
153 return GDIMetaFileSharedPtr(new GDIMetaFile(aGraphic
.GetGDIMetaFile()));
157 uno::Reference
< graphic::XGraphic
> mxGraphic
;
162 // Quick'n'dirty way: tunnel Graphic (only works for
163 // in-process slideshow, of course)
164 GDIMetaFileSharedPtr
getMetaFile( const uno::Reference
< lang::XComponent
>& xSource
,
165 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
167 const uno::Reference
< uno::XComponentContext
>& rxContext
)
171 SAL_WARN("slideshow.opengl", "getMetaFile(): Invalid context" );
172 return GDIMetaFileSharedPtr();
175 // create dummy XGraphicRenderer, which receives the
176 // generated XGraphic from the GraphicExporter
178 // TODO(P3): Move creation of DummyRenderer out of the
179 // loop! Either by making it static, or transforming
180 // the whole thing here into a class.
181 DummyRenderer
* pRenderer( new DummyRenderer() );
182 uno::Reference
< graphic::XGraphicRenderer
> xRenderer( pRenderer
);
184 // creating the graphic exporter
185 uno::Reference
< drawing::XGraphicExportFilter
> xExporter
=
186 drawing::GraphicExportFilter::create(rxContext
);
188 uno::Sequence
< beans::PropertyValue
> aProps(3);
189 aProps
[0].Name
= "FilterName";
190 aProps
[0].Value
<<= OUString("SVM");
192 aProps
[1].Name
= "GraphicRenderer";
193 aProps
[1].Value
<<= xRenderer
;
195 uno::Sequence
< beans::PropertyValue
> aFilterData(4);
196 aFilterData
[0].Name
= "ScrollText";
197 aFilterData
[0].Value
<<= ((mtfLoadFlags
& MTF_LOAD_SCROLL_TEXT_MTF
) != 0);
199 aFilterData
[1].Name
= "ExportOnlyBackground";
200 aFilterData
[1].Value
<<= ((mtfLoadFlags
& MTF_LOAD_BACKGROUND_ONLY
) != 0);
202 aFilterData
[2].Name
= "Version";
203 aFilterData
[2].Value
<<= static_cast<sal_Int32
>( SOFFICE_FILEFORMAT_50
);
205 aFilterData
[3].Name
= "CurrentPage";
206 aFilterData
[3].Value
<<= uno::Reference
< uno::XInterface
>( xContainingPage
,
207 uno::UNO_QUERY_THROW
);
209 aProps
[2].Name
= "FilterData";
210 aProps
[2].Value
<<= aFilterData
;
212 xExporter
->setSourceDocument( xSource
);
213 if( !xExporter
->filter( aProps
) )
214 return GDIMetaFileSharedPtr();
216 GDIMetaFileSharedPtr xMtf
= pRenderer
->getMtf( (mtfLoadFlags
& MTF_LOAD_FOREIGN_SOURCE
) != 0 );
218 // pRenderer is automatically destroyed when xRenderer
221 // TODO(E3): Error handling. Exporter might have
222 // generated nothing, a bitmap, threw an exception,
227 sal_Int32
getNextActionOffset( MetaAction
* pCurrAct
)
229 // Special handling for actions that represent
230 // more than one indexable action
231 // ===========================================
233 switch (pCurrAct
->GetType()) {
234 case MetaActionType::TEXT
: {
235 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
236 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
239 case MetaActionType::TEXTARRAY
: {
240 MetaTextArrayAction
* pAct
=
241 static_cast<MetaTextArrayAction
*>(pCurrAct
);
242 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
245 case MetaActionType::STRETCHTEXT
: {
246 MetaStretchTextAction
* pAct
=
247 static_cast<MetaStretchTextAction
*>(pCurrAct
);
248 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
251 case MetaActionType::FLOATTRANSPARENT
: {
252 MetaFloatTransparentAction
* pAct
=
253 static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
254 // TODO(F2): Recurse into action metafile
255 // (though this is currently not used from the
256 // DrawingLayer - shape transparency gradients
257 // don't affect shape text)
258 return pAct
->GetGDIMetaFile().GetActionSize();
265 bool getAnimationFromGraphic( VectorOfMtfAnimationFrames
& o_rFrames
,
266 sal_uInt32
& o_rLoopCount
,
267 const Graphic
& rGraphic
)
271 if( !rGraphic
.IsAnimated() )
274 // some loop invariants
275 Animation
aAnimation( rGraphic
.GetAnimation() );
276 const Point aEmptyPoint
;
277 const Size
aAnimSize( aAnimation
.GetDisplaySizePixel() );
279 // setup VDev, into which all bitmaps are painted (want to
280 // normalize animations to n bitmaps of same size. An Animation,
281 // though, can contain bitmaps of varying sizes and different
283 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
284 pVDev
->SetOutputSizePixel( aAnimSize
);
285 pVDev
->EnableMapMode( false );
287 // setup mask VDev (alpha VDev is currently rather slow)
288 ScopedVclPtrInstance
<VirtualDevice
> pVDevMask(DeviceFormat::BITMASK
);
289 pVDevMask
->SetOutputSizePixel( aAnimSize
);
290 pVDevMask
->EnableMapMode( false );
292 o_rLoopCount
= aAnimation
.GetLoopCount();
294 for( sal_uInt16 i
=0, nCount
=aAnimation
.Count(); i
<nCount
; ++i
)
296 const AnimationBitmap
& rAnimBmp( aAnimation
.Get(i
) );
297 switch(rAnimBmp
.eDisposal
)
301 pVDev
->DrawBitmapEx(rAnimBmp
.aPosPix
,
303 Bitmap aMask
= rAnimBmp
.aBmpEx
.GetMask();
305 if( aMask
.IsEmpty() )
307 const tools::Rectangle
aRect(aEmptyPoint
,
308 pVDevMask
->GetOutputSizePixel());
309 const Wallpaper
aWallpaper(COL_BLACK
);
310 pVDevMask
->DrawWallpaper(aRect
,
315 BitmapEx aTmpMask
= BitmapEx(aMask
,
317 pVDevMask
->DrawBitmapEx(rAnimBmp
.aPosPix
,
325 // #i70772# react on no mask
326 const Bitmap
aMask(rAnimBmp
.aBmpEx
.GetMask());
327 const Bitmap
aContent(rAnimBmp
.aBmpEx
.GetBitmap());
330 pVDev
->DrawBitmap(rAnimBmp
.aPosPix
, aContent
);
334 const tools::Rectangle
aRect(rAnimBmp
.aPosPix
, aContent
.GetSizePixel());
335 pVDevMask
->SetFillColor(COL_BLACK
);
336 pVDevMask
->SetLineColor();
337 pVDevMask
->DrawRect(aRect
);
341 pVDevMask
->DrawBitmap(rAnimBmp
.aPosPix
, aMask
);
346 case Disposal::Previous
:
348 pVDev
->DrawBitmapEx(rAnimBmp
.aPosPix
,
350 pVDevMask
->DrawBitmap(rAnimBmp
.aPosPix
,
351 rAnimBmp
.aBmpEx
.GetMask());
356 // extract current aVDev content into a new animation
358 GDIMetaFileSharedPtr
pMtf( new GDIMetaFile() );
360 new MetaBmpExAction( aEmptyPoint
,
365 pVDevMask
->GetBitmap(
369 // setup mtf dimensions and pref map mode (for
370 // simplicity, keep it all in pixel. the metafile
371 // renderer scales it down to (1, 1) box anyway)
372 pMtf
->SetPrefMapMode( MapMode() );
373 pMtf
->SetPrefSize( aAnimSize
);
375 // Take care of special value for MultiPage TIFFs. ATM these shall just
376 // show their first page for _quite_ some time.
377 sal_Int32
nWaitTime100thSeconds( rAnimBmp
.nWait
);
378 if( ANIMATION_TIMEOUT_ON_CLICK
== nWaitTime100thSeconds
)
380 // ATM the huge value would block the timer, so use a long
381 // time to show first page (whole day)
382 nWaitTime100thSeconds
= 100 * 60 * 60 * 24;
385 // There are animated GIFs with no WaitTime set. Take 0.1 sec, the
386 // same duration that is used by the edit view.
387 if( nWaitTime100thSeconds
== 0 )
388 nWaitTime100thSeconds
= 10;
390 o_rFrames
.emplace_back( pMtf
, nWaitTime100thSeconds
/ 100.0 );
393 return !o_rFrames
.empty();
396 bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle
& o_rScrollRect
,
397 ::basegfx::B2DRectangle
& o_rPaintRect
,
398 const GDIMetaFileSharedPtr
& rMtf
)
400 // extract bounds: scroll rect, paint rect
401 bool bScrollRectSet(false);
402 bool bPaintRectSet(false);
404 for ( MetaAction
* pCurrAct
= rMtf
->FirstAction();
405 pCurrAct
!= nullptr; pCurrAct
= rMtf
->NextAction() )
407 if (pCurrAct
->GetType() == MetaActionType::COMMENT
)
409 MetaCommentAction
* pAct
=
410 static_cast<MetaCommentAction
*>(pCurrAct
);
411 // skip comment if not a special XTEXT... comment
412 if( pAct
->GetComment().matchIgnoreAsciiCase( OString("XTEXT") ) )
414 if (pAct
->GetComment().equalsIgnoreAsciiCase("XTEXT_SCROLLRECT"))
416 o_rScrollRect
= vcl::unotools::b2DRectangleFromRectangle(
417 *reinterpret_cast<tools::Rectangle
const *>(
420 bScrollRectSet
= true;
422 else if (pAct
->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
424 o_rPaintRect
= vcl::unotools::b2DRectangleFromRectangle(
425 *reinterpret_cast<tools::Rectangle
const *>(
428 bPaintRectSet
= true;
434 return bScrollRectSet
&& bPaintRectSet
;
440 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */