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 .
22 #include <canvas/debug.hxx>
23 #include <tools/diagnose_ex.h>
24 #include <gdimtftools.hxx>
26 #include <com/sun/star/document/XExporter.hpp>
27 #include <com/sun/star/document/XFilter.hpp>
28 #include <com/sun/star/graphic/XGraphic.hpp>
29 #include <com/sun/star/graphic/XGraphicRenderer.hpp>
30 #include <com/sun/star/drawing/XShape.hpp>
31 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
33 #include <cppuhelper/basemutex.hxx>
34 #include <cppuhelper/compbase1.hxx>
36 #include <comphelper/uno3.hxx>
37 #include <cppuhelper/implbase1.hxx>
39 #include <tools/stream.hxx>
40 #include <vcl/svapp.hxx>
41 #include <vcl/canvastools.hxx>
42 #include <vcl/metaact.hxx>
43 #include <vcl/virdev.hxx>
44 #include <vcl/gdimtf.hxx>
45 #include <vcl/animate.hxx>
46 #include <vcl/graph.hxx>
48 #include <unotools/streamwrap.hxx>
52 using namespace ::com::sun::star
;
55 // free support functions
56 // ======================
62 // TODO(E2): Detect the case when svx/drawing layer is not
63 // in-process, or even not on the same machine, and
64 // fallback to metafile streaming!
66 // For fixing #i48102#, have to be a _lot_ more selective
67 // on which metafiles to convert to bitmaps. The problem
68 // here is that we _always_ get the shape content as a
69 // metafile, even if we have a bitmap graphic shape. Thus,
70 // calling GetBitmapEx on such a Graphic (see below) will
71 // result in one poorly scaled bitmap into another,
72 // somewhat arbitrarily sized bitmap.
73 bool hasUnsupportedActions( const GDIMetaFile
& rMtf
)
75 // search metafile for RasterOp action
78 // TODO(Q3): avoid const-cast
79 for( pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).FirstAction();
81 pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).NextAction() )
83 switch( pCurrAct
->GetType() )
85 case MetaActionType::RASTEROP
:
86 // overpaint is okay - that's the default, anyway
88 static_cast<MetaRasterOpAction
*>(pCurrAct
)->GetRasterOp() )
92 // FALLTHROUGH intended
93 case MetaActionType::MOVECLIPREGION
:
94 // FALLTHROUGH intended
95 case MetaActionType::REFPOINT
:
96 // FALLTHROUGH intended
97 case MetaActionType::WALLPAPER
:
98 return true; // at least one unsupported
104 return false; // no unsupported action found
109 typedef ::cppu::WeakComponentImplHelper1
< graphic::XGraphicRenderer
> DummyRenderer_Base
;
111 class DummyRenderer
: public cppu::BaseMutex
, public DummyRenderer_Base
115 DummyRenderer_Base( m_aMutex
),
120 //--- XGraphicRenderer -----------------------------------
121 virtual void SAL_CALL
render( const uno::Reference
< graphic::XGraphic
>& rGraphic
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
123 ::osl::MutexGuard
aGuard( m_aMutex
);
124 mxGraphic
= rGraphic
;
127 /** Retrieve GDIMetaFile from renderer
129 @param bForeignSource
130 When true, the source of the metafile might be a
131 foreign application. The metafile is checked
132 against unsupported content, and, if necessary,
133 returned as a pre-rendererd bitmap.
135 GDIMetaFile
getMtf( bool bForeignSource
) const
137 ::osl::MutexGuard
aGuard( m_aMutex
);
139 Graphic
aGraphic( mxGraphic
);
141 if( aGraphic
.GetType() == GRAPHIC_BITMAP
||
143 hasUnsupportedActions(aGraphic
.GetGDIMetaFile()) ) )
145 // wrap bitmap into GDIMetafile
149 ::BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
151 aMtf
.AddAction( new MetaBmpExAction( aEmptyPoint
,
153 aMtf
.SetPrefSize( aBmpEx
.GetPrefSize() );
154 aMtf
.SetPrefMapMode( aBmpEx
.GetPrefMapMode() );
160 return aGraphic
.GetGDIMetaFile();
165 uno::Reference
< graphic::XGraphic
> mxGraphic
;
170 // Quick'n'dirty way: tunnel Graphic (only works for
171 // in-process slideshow, of course)
172 bool getMetaFile( const uno::Reference
< lang::XComponent
>& xSource
,
173 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
176 const uno::Reference
< uno::XComponentContext
>& rxContext
)
178 ENSURE_OR_RETURN_FALSE( rxContext
.is(),
179 "getMetaFile(): Invalid context" );
181 // create dummy XGraphicRenderer, which receives the
182 // generated XGraphic from the GraphicExporter
184 // TODO(P3): Move creation of DummyRenderer out of the
185 // loop! Either by making it static, or transforming
186 // the whole thing here into a class.
187 DummyRenderer
* pRenderer( new DummyRenderer() );
188 uno::Reference
< graphic::XGraphicRenderer
> xRenderer( pRenderer
);
190 // creating the graphic exporter
191 uno::Reference
< drawing::XGraphicExportFilter
> xExporter
=
192 drawing::GraphicExportFilter::create(rxContext
);
194 uno::Sequence
< beans::PropertyValue
> aProps(3);
195 aProps
[0].Name
= "FilterName";
196 aProps
[0].Value
<<= OUString("SVM");
198 aProps
[1].Name
= "GraphicRenderer";
199 aProps
[1].Value
<<= xRenderer
;
201 uno::Sequence
< beans::PropertyValue
> aFilterData(4);
202 aFilterData
[0].Name
= "ScrollText";
203 aFilterData
[0].Value
<<= ((mtfLoadFlags
& MTF_LOAD_SCROLL_TEXT_MTF
) != 0);
205 aFilterData
[1].Name
= "ExportOnlyBackground";
206 aFilterData
[1].Value
<<= ((mtfLoadFlags
& MTF_LOAD_BACKGROUND_ONLY
) != 0);
208 aFilterData
[2].Name
= "Version";
209 aFilterData
[2].Value
<<= static_cast<sal_Int32
>( SOFFICE_FILEFORMAT_50
);
211 aFilterData
[3].Name
= "CurrentPage";
212 aFilterData
[3].Value
<<= uno::Reference
< uno::XInterface
>( xContainingPage
,
213 uno::UNO_QUERY_THROW
);
215 aProps
[2].Name
= "FilterData";
216 aProps
[2].Value
<<= aFilterData
;
218 xExporter
->setSourceDocument( xSource
);
219 if( !xExporter
->filter( aProps
) )
222 rMtf
= pRenderer
->getMtf( (mtfLoadFlags
& MTF_LOAD_FOREIGN_SOURCE
) != 0 );
224 // pRenderer is automatically destroyed when xRenderer
227 // TODO(E3): Error handling. Exporter might have
228 // generated nothing, a bitmap, threw an exception,
233 sal_Int32
getNextActionOffset( MetaAction
* pCurrAct
)
235 // Special handling for actions that represent
236 // more than one indexable action
237 // ===========================================
239 switch (pCurrAct
->GetType()) {
240 case MetaActionType::TEXT
: {
241 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
242 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
245 case MetaActionType::TEXTARRAY
: {
246 MetaTextArrayAction
* pAct
=
247 static_cast<MetaTextArrayAction
*>(pCurrAct
);
248 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
251 case MetaActionType::STRETCHTEXT
: {
252 MetaStretchTextAction
* pAct
=
253 static_cast<MetaStretchTextAction
*>(pCurrAct
);
254 sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
257 case MetaActionType::FLOATTRANSPARENT
: {
258 MetaFloatTransparentAction
* pAct
=
259 static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
260 // TODO(F2): Recurse into action metafile
261 // (though this is currently not used from the
262 // DrawingLayer - shape transparency gradients
263 // don't affect shape text)
264 return pAct
->GetGDIMetaFile().GetActionSize();
271 bool getAnimationFromGraphic( VectorOfMtfAnimationFrames
& o_rFrames
,
272 ::std::size_t& o_rLoopCount
,
273 CycleMode
& o_eCycleMode
,
274 const Graphic
& rGraphic
)
278 if( !rGraphic
.IsAnimated() )
281 // some loop invariants
282 Animation
aAnimation( rGraphic
.GetAnimation() );
283 const Point aEmptyPoint
;
284 const Size
aAnimSize( aAnimation
.GetDisplaySizePixel() );
286 // setup VDev, into which all bitmaps are painted (want to
287 // normalize animations to n bitmaps of same size. An Animation,
288 // though, can contain bitmaps of varying sizes and different
290 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
291 pVDev
->SetOutputSizePixel( aAnimSize
);
292 pVDev
->EnableMapMode( false );
294 // setup mask VDev (alpha VDev is currently rather slow)
295 ScopedVclPtrInstance
< VirtualDevice
> pVDevMask
;
296 pVDevMask
->SetOutputSizePixel( aAnimSize
);
297 pVDevMask
->EnableMapMode( false );
299 switch( aAnimation
.GetCycleMode() )
303 o_eCycleMode
= CYCLE_LOOP
;
307 // FALLTHROUGH intended
309 o_rLoopCount
= aAnimation
.GetLoopCount();
310 o_eCycleMode
= CYCLE_LOOP
;
314 // FALLTHROUGH intended
315 case CYCLE_REVERS_FALLBACK
:
316 o_rLoopCount
= aAnimation
.GetLoopCount();
317 o_eCycleMode
= CYCLE_PINGPONGLOOP
;
321 ENSURE_OR_RETURN_FALSE(false,
322 "getAnimationFromGraphic(): Unexpected case" );
326 for( sal_uInt16 i
=0, nCount
=aAnimation
.Count(); i
<nCount
; ++i
)
328 const AnimationBitmap
& rAnimBmp( aAnimation
.Get(i
) );
329 switch(rAnimBmp
.eDisposal
)
333 pVDev
->DrawBitmapEx(rAnimBmp
.aPosPix
,
335 Bitmap aMask
= rAnimBmp
.aBmpEx
.GetMask();
337 if( aMask
.IsEmpty() )
339 const Rectangle
aRect(aEmptyPoint
,
340 pVDevMask
->GetOutputSizePixel());
341 const Wallpaper
aWallpaper(COL_BLACK
);
342 pVDevMask
->DrawWallpaper(aRect
,
347 BitmapEx aTmpMask
= BitmapEx(aMask
,
349 pVDevMask
->DrawBitmapEx(rAnimBmp
.aPosPix
,
357 // #i70772# react on no mask
358 const Bitmap
aMask(rAnimBmp
.aBmpEx
.GetMask());
359 const Bitmap
aContent(rAnimBmp
.aBmpEx
.GetBitmap());
362 pVDev
->DrawBitmap(rAnimBmp
.aPosPix
, aContent
);
366 const Rectangle
aRect(rAnimBmp
.aPosPix
, aContent
.GetSizePixel());
367 pVDevMask
->SetFillColor(COL_BLACK
);
368 pVDevMask
->SetLineColor();
369 pVDevMask
->DrawRect(aRect
);
373 pVDevMask
->DrawBitmap(rAnimBmp
.aPosPix
, aMask
);
380 pVDev
->DrawBitmapEx(rAnimBmp
.aPosPix
,
385 case DISPOSE_PREVIOUS
:
387 pVDev
->DrawBitmapEx(rAnimBmp
.aPosPix
,
389 pVDevMask
->DrawBitmap(rAnimBmp
.aPosPix
,
390 rAnimBmp
.aBmpEx
.GetMask());
395 // extract current aVDev content into a new animation
397 GDIMetaFileSharedPtr
pMtf( new GDIMetaFile() );
399 new MetaBmpExAction( aEmptyPoint
,
404 pVDevMask
->GetBitmap(
408 // setup mtf dimensions and pref map mode (for
409 // simplicity, keep it all in pixel. the metafile
410 // renderer scales it down to (1, 1) box anyway)
411 pMtf
->SetPrefMapMode( MapMode() );
412 pMtf
->SetPrefSize( aAnimSize
);
414 // Take care of special value for MultiPage TIFFs. ATM these shall just
415 // show their first page for _quite_ some time.
416 sal_Int32
nWaitTime100thSeconds( rAnimBmp
.nWait
);
417 if( ANIMATION_TIMEOUT_ON_CLICK
== nWaitTime100thSeconds
)
419 // ATM the huge value would block the timer, so use a long
420 // time to show first page (whole day)
421 nWaitTime100thSeconds
= 100 * 60 * 60 * 24;
424 // There are animated GIFs with no WaitTime set. Take 0.1 sec, the
425 // same duration that is used by the edit view.
426 if( nWaitTime100thSeconds
== 0 )
427 nWaitTime100thSeconds
= 10;
429 o_rFrames
.push_back( MtfAnimationFrame( pMtf
,
430 nWaitTime100thSeconds
/ 100.0 ) );
433 return !o_rFrames
.empty();
436 bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle
& o_rScrollRect
,
437 ::basegfx::B2DRectangle
& o_rPaintRect
,
438 const GDIMetaFileSharedPtr
& rMtf
)
440 // extract bounds: scroll rect, paint rect
441 bool bScrollRectSet(false);
442 bool bPaintRectSet(false);
444 for ( MetaAction
* pCurrAct
= rMtf
->FirstAction();
445 pCurrAct
!= 0; pCurrAct
= rMtf
->NextAction() )
447 if (pCurrAct
->GetType() == MetaActionType::COMMENT
)
449 MetaCommentAction
* pAct
=
450 static_cast<MetaCommentAction
*>(pCurrAct
);
451 // skip comment if not a special XTEXT... comment
452 if( pAct
->GetComment().matchIgnoreAsciiCase( OString("XTEXT"), 0 ) )
454 if (pAct
->GetComment().equalsIgnoreAsciiCase("XTEXT_SCROLLRECT"))
456 o_rScrollRect
= vcl::unotools::b2DRectangleFromRectangle(
457 *reinterpret_cast<Rectangle
const *>(
460 bScrollRectSet
= true;
462 else if (pAct
->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
464 o_rPaintRect
= vcl::unotools::b2DRectangleFromRectangle(
465 *reinterpret_cast<Rectangle
const *>(
468 bPaintRectSet
= true;
474 return bScrollRectSet
&& bPaintRectSet
;
480 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */