1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: gdimtftools.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_slideshow.hxx"
35 #include <canvas/debug.hxx>
36 #include <tools/diagnose_ex.h>
37 #include <gdimtftools.hxx>
39 #include <com/sun/star/document/XExporter.hpp>
40 #include <com/sun/star/document/XFilter.hpp>
41 #include <com/sun/star/graphic/XGraphic.hpp>
42 #include <com/sun/star/graphic/XGraphicRenderer.hpp>
43 #include <com/sun/star/drawing/XShape.hpp>
45 #include <cppuhelper/basemutex.hxx>
46 #include <cppuhelper/compbase1.hxx>
48 #include <comphelper/uno3.hxx>
49 #include <cppuhelper/implbase1.hxx>
51 #include <tools/stream.hxx>
52 #include <vcl/svapp.hxx>
53 #include <vcl/canvastools.hxx>
54 #include <vcl/metaact.hxx>
55 #include <vcl/virdev.hxx>
56 #include <vcl/gdimtf.hxx>
57 #include <vcl/metaact.hxx>
58 #include <vcl/animate.hxx>
59 #include <vcl/graph.hxx>
61 #include <unotools/streamwrap.hxx>
65 using namespace ::com::sun::star
;
68 // free support functions
69 // ======================
75 // TODO(E2): Detect the case when svx/drawing layer is not
76 // in-process, or even not on the same machine, and
77 // fallback to metafile streaming!
79 // For fixing #i48102#, have to be a _lot_ more selective
80 // on which metafiles to convert to bitmaps. The problem
81 // here is that we _always_ get the shape content as a
82 // metafile, even if we have a bitmap graphic shape. Thus,
83 // calling GetBitmapEx on such a Graphic (see below) will
84 // result in one poorly scaled bitmap into another,
85 // somewhat arbitrarily sized bitmap.
86 bool hasUnsupportedActions( const GDIMetaFile
& rMtf
)
88 // search metafile for RasterOp action
91 // TODO(Q3): avoid const-cast
92 for( pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).FirstAction();
94 pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).NextAction() )
96 switch( pCurrAct
->GetType() )
98 case META_RASTEROP_ACTION
:
99 // overpaint is okay - that's the default, anyway
101 static_cast<MetaRasterOpAction
*>(pCurrAct
)->GetRasterOp() )
105 // FALLTHROUGH intended
106 case META_MOVECLIPREGION_ACTION
:
107 // FALLTHROUGH intended
108 case META_REFPOINT_ACTION
:
109 // FALLTHROUGH intended
110 case META_WALLPAPER_ACTION
:
111 return true; // at least one unsupported
112 // action encountered
116 return false; // no unsupported action found
121 typedef ::cppu::WeakComponentImplHelper1
< graphic::XGraphicRenderer
> DummyRenderer_Base
;
123 class DummyRenderer
:
124 public DummyRenderer_Base
,
125 public cppu::BaseMutex
129 DummyRenderer_Base( m_aMutex
),
134 //--- XGraphicRenderer -----------------------------------
135 virtual void SAL_CALL
render( const uno::Reference
< graphic::XGraphic
>& rGraphic
) throw (uno::RuntimeException
)
137 ::osl::MutexGuard
aGuard( m_aMutex
);
138 mxGraphic
= rGraphic
;
141 /** Retrieve GDIMetaFile from renderer
143 @param bForeignSource
144 When true, the source of the metafile might be a
145 foreign application. The metafile is checked
146 against unsupported content, and, if necessary,
147 returned as a pre-rendererd bitmap.
149 GDIMetaFile
getMtf( bool bForeignSource
) const
151 ::osl::MutexGuard
aGuard( m_aMutex
);
153 Graphic
aGraphic( mxGraphic
);
155 if( aGraphic
.GetType() == GRAPHIC_BITMAP
||
157 hasUnsupportedActions(aGraphic
.GetGDIMetaFile()) ) )
159 // wrap bitmap into GDIMetafile
163 ::BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
165 aMtf
.AddAction( new MetaBmpExAction( aEmptyPoint
,
167 aMtf
.SetPrefSize( aBmpEx
.GetPrefSize() );
168 aMtf
.SetPrefMapMode( aBmpEx
.GetPrefMapMode() );
174 return aGraphic
.GetGDIMetaFile();
179 uno::Reference
< graphic::XGraphic
> mxGraphic
;
184 // Quick'n'dirty way: tunnel Graphic (only works for
185 // in-process slideshow, of course)
186 bool getMetaFile( const uno::Reference
< lang::XComponent
>& xSource
,
187 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
190 const uno::Reference
< uno::XComponentContext
>& rxContext
)
192 ENSURE_OR_RETURN( rxContext
.is(),
193 "getMetaFile(): Invalid context" );
195 // create dummy XGraphicRenderer, which receives the
196 // generated XGraphic from the GraphicExporter
198 // TODO(P3): Move creation of DummyRenderer out of the
199 // loop! Either by making it static, or transforming
200 // the whole thing here into a class.
201 DummyRenderer
* pRenderer( new DummyRenderer() );
202 uno::Reference
< graphic::XGraphicRenderer
> xRenderer( pRenderer
);
204 // -> stuff that into UnoGraphicExporter.
205 uno::Reference
<lang::XMultiComponentFactory
> xFactory(
206 rxContext
->getServiceManager() );
208 OSL_ENSURE( xFactory
.is(), "### no UNO?!" );
212 // creating the graphic exporter
213 uno::Reference
< document::XExporter
> xExporter(
214 xFactory
->createInstanceWithContext(
215 OUSTR("com.sun.star.drawing.GraphicExportFilter"),
218 uno::Reference
< document::XFilter
> xFilter( xExporter
, uno::UNO_QUERY
);
220 OSL_ENSURE( xExporter
.is() && xFilter
.is(), "### no graphic exporter?!" );
221 if( !xExporter
.is() || !xFilter
.is() )
224 uno::Sequence
< beans::PropertyValue
> aProps(3);
225 aProps
[0].Name
= OUSTR("FilterName");
226 aProps
[0].Value
<<= OUSTR("SVM");
228 aProps
[1].Name
= OUSTR("GraphicRenderer");
229 aProps
[1].Value
<<= xRenderer
;
231 uno::Sequence
< beans::PropertyValue
> aFilterData(5);
232 aFilterData
[0].Name
= OUSTR("VerboseComments");
233 aFilterData
[0].Value
<<= ((mtfLoadFlags
& MTF_LOAD_VERBOSE_COMMENTS
) != 0);
235 aFilterData
[1].Name
= OUSTR("ScrollText");
236 aFilterData
[1].Value
<<= ((mtfLoadFlags
& MTF_LOAD_SCROLL_TEXT_MTF
) != 0);
238 aFilterData
[2].Name
= OUSTR("ExportOnlyBackground");
239 aFilterData
[2].Value
<<= ((mtfLoadFlags
& MTF_LOAD_BACKGROUND_ONLY
) != 0);
241 aFilterData
[3].Name
= OUSTR("Version");
242 aFilterData
[3].Value
<<= static_cast<sal_Int32
>( SOFFICE_FILEFORMAT_50
);
244 aFilterData
[4].Name
= OUSTR("CurrentPage");
245 aFilterData
[4].Value
<<= uno::Reference
< uno::XInterface
>( xContainingPage
,
246 uno::UNO_QUERY_THROW
);
248 aProps
[2].Name
= OUSTR("FilterData");
249 aProps
[2].Value
<<= aFilterData
;
251 xExporter
->setSourceDocument( xSource
);
252 if( !xFilter
->filter( aProps
) )
255 rMtf
= pRenderer
->getMtf( (mtfLoadFlags
& MTF_LOAD_FOREIGN_SOURCE
) != 0 );
257 // pRenderer is automatically destroyed when xRenderer
260 // TODO(E3): Error handling. Exporter might have
261 // generated nothing, a bitmap, threw an exception,
266 void removeTextActions( GDIMetaFile
& rMtf
)
268 // search metafile for text output
269 MetaAction
* pCurrAct
;
272 pCurrAct
= rMtf
.FirstAction();
275 switch( pCurrAct
->GetType() )
277 case META_TEXTCOLOR_ACTION
:
278 case META_TEXTFILLCOLOR_ACTION
:
279 case META_TEXTLINECOLOR_ACTION
:
280 case META_TEXTALIGN_ACTION
:
281 case META_FONT_ACTION
:
282 case META_LAYOUTMODE_ACTION
:
283 case META_TEXT_ACTION
:
284 case META_TEXTARRAY_ACTION
:
285 case META_TEXTRECT_ACTION
:
286 case META_STRETCHTEXT_ACTION
:
287 case META_TEXTLINE_ACTION
:
289 // remove every text-related actions
290 pCurrAct
= rMtf
.NextAction();
292 rMtf
.RemoveAction( nActionIndex
);
297 pCurrAct
= rMtf
.NextAction();
304 sal_Int32
getNextActionOffset( MetaAction
* pCurrAct
)
306 // Special handling for actions that represent
307 // more than one indexable action
308 // ===========================================
310 switch (pCurrAct
->GetType()) {
311 case META_TEXT_ACTION
: {
312 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
313 return (pAct
->GetLen() == (USHORT
)STRING_LEN
314 ? pAct
->GetText().Len() - pAct
->GetIndex() : pAct
->GetLen());
316 case META_TEXTARRAY_ACTION
: {
317 MetaTextArrayAction
* pAct
=
318 static_cast<MetaTextArrayAction
*>(pCurrAct
);
319 return (pAct
->GetLen() == (USHORT
)STRING_LEN
320 ? pAct
->GetText().Len() - pAct
->GetIndex() : pAct
->GetLen());
322 case META_STRETCHTEXT_ACTION
: {
323 MetaStretchTextAction
* pAct
=
324 static_cast<MetaStretchTextAction
*>(pCurrAct
);
325 return (pAct
->GetLen() == (USHORT
)STRING_LEN
326 ? pAct
->GetText().Len() - pAct
->GetIndex() : pAct
->GetLen());
328 case META_FLOATTRANSPARENT_ACTION
: {
329 MetaFloatTransparentAction
* pAct
=
330 static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
331 // TODO(F2): Recurse into action metafile
332 // (though this is currently not used from the
333 // DrawingLayer - shape transparency gradients
334 // don't affect shape text)
335 return pAct
->GetGDIMetaFile().GetActionCount();
342 bool getAnimationFromGraphic( VectorOfMtfAnimationFrames
& o_rFrames
,
343 ::std::size_t& o_rLoopCount
,
344 CycleMode
& o_eCycleMode
,
345 const Graphic
& rGraphic
)
349 if( !rGraphic
.IsAnimated() )
352 // some loop invariants
353 Animation
aAnimation( rGraphic
.GetAnimation() );
354 const Point aEmptyPoint
;
355 const Size
aAnimSize( aAnimation
.GetDisplaySizePixel() );
357 // setup VDev, into which all bitmaps are painted (want to
358 // normalize animations to n bitmaps of same size. An Animation,
359 // though, can contain bitmaps of varying sizes and different
362 aVDev
.SetOutputSizePixel( aAnimSize
);
363 aVDev
.EnableMapMode( FALSE
);
365 // setup mask VDev (alpha VDev is currently rather slow)
366 VirtualDevice aVDevMask
;
367 aVDevMask
.SetOutputSizePixel( aAnimSize
);
368 aVDevMask
.EnableMapMode( FALSE
);
370 switch( aAnimation
.GetCycleMode() )
374 o_eCycleMode
= CYCLE_LOOP
;
378 // FALLTHROUGH intended
380 o_rLoopCount
= aAnimation
.GetLoopCount();
381 o_eCycleMode
= CYCLE_LOOP
;
385 // FALLTHROUGH intended
386 case CYCLE_REVERS_FALLBACK
:
387 o_rLoopCount
= aAnimation
.GetLoopCount();
388 o_eCycleMode
= CYCLE_PINGPONGLOOP
;
392 ENSURE_OR_RETURN(false,
393 "getAnimationFromGraphic(): Unexpected case" );
397 for( USHORT i
=0, nCount
=aAnimation
.Count(); i
<nCount
; ++i
)
399 const AnimationBitmap
& rAnimBmp( aAnimation
.Get(i
) );
400 switch(rAnimBmp
.eDisposal
)
404 aVDev
.DrawBitmapEx(rAnimBmp
.aPosPix
,
406 Bitmap aMask
= rAnimBmp
.aBmpEx
.GetMask();
408 if( aMask
.IsEmpty() )
411 const Rectangle
aRect(aEmptyPoint
,
412 aVDevMask
.GetOutputSizePixel());
413 const Wallpaper
aWallpaper(COL_BLACK
);
414 aVDevMask
.DrawWallpaper(aRect
,
419 BitmapEx aTmpMask
= BitmapEx(aMask
,
421 aVDevMask
.DrawBitmapEx(rAnimBmp
.aPosPix
,
429 // #i70772# react on no mask
430 const Bitmap
aMask(rAnimBmp
.aBmpEx
.GetMask());
431 const Bitmap
aContent(rAnimBmp
.aBmpEx
.GetBitmap());
434 aVDev
.DrawBitmap(rAnimBmp
.aPosPix
, aContent
);
438 const Rectangle
aRect(rAnimBmp
.aPosPix
, aContent
.GetSizePixel());
439 aVDevMask
.SetFillColor(COL_BLACK
);
440 aVDevMask
.SetLineColor();
441 aVDevMask
.DrawRect(aRect
);
445 aVDevMask
.DrawBitmap(rAnimBmp
.aPosPix
, aMask
);
452 aVDev
.DrawBitmapEx(rAnimBmp
.aPosPix
,
457 case DISPOSE_PREVIOUS
:
459 aVDev
.DrawBitmapEx(rAnimBmp
.aPosPix
,
461 aVDevMask
.DrawBitmap(rAnimBmp
.aPosPix
,
462 rAnimBmp
.aBmpEx
.GetMask());
467 // extract current aVDev content into a new animation
469 GDIMetaFileSharedPtr
pMtf( new GDIMetaFile() );
471 new MetaBmpExAction( aEmptyPoint
,
480 // setup mtf dimensions and pref map mode (for
481 // simplicity, keep it all in pixel. the metafile
482 // renderer scales it down to (1, 1) box anyway)
483 pMtf
->SetPrefMapMode( MapMode() );
484 pMtf
->SetPrefSize( aAnimSize
);
487 // Take care of special value for MultiPage TIFFs. ATM these shall just
488 // show their first page for _quite_ some time.
489 sal_Int32
nWaitTime100thSeconds( rAnimBmp
.nWait
);
490 if( ANIMATION_TIMEOUT_ON_CLICK
== nWaitTime100thSeconds
)
492 // ATM the huge value would block the timer, so use a long
493 // time to show first page (whole day)
494 nWaitTime100thSeconds
= 100 * 60 * 60 * 24;
497 // There are animated GIFs with no WaitTime set. Take 1 sec, then.
498 if( nWaitTime100thSeconds
== 0 )
499 nWaitTime100thSeconds
= 100;
501 o_rFrames
.push_back( MtfAnimationFrame( pMtf
,
502 nWaitTime100thSeconds
/ 100.0 ) );
505 return !o_rFrames
.empty();
508 bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle
& o_rScrollRect
,
509 ::basegfx::B2DRectangle
& o_rPaintRect
,
510 const GDIMetaFileSharedPtr
& rMtf
)
512 // extract bounds: scroll rect, paint rect
513 bool bScrollRectSet(false);
514 bool bPaintRectSet(false);
516 for ( MetaAction
* pCurrAct
= rMtf
->FirstAction();
517 pCurrAct
!= 0; pCurrAct
= rMtf
->NextAction() )
519 if (pCurrAct
->GetType() == META_COMMENT_ACTION
)
521 MetaCommentAction
* pAct
=
522 static_cast<MetaCommentAction
*>(pCurrAct
);
523 // skip comment if not a special XTEXT comment
524 if (pAct
->GetComment().CompareIgnoreCaseToAscii(
525 RTL_CONSTASCII_STRINGPARAM("XTEXT") ) == COMPARE_EQUAL
)
527 if (pAct
->GetComment().CompareIgnoreCaseToAscii(
528 "XTEXT_SCROLLRECT" ) == COMPARE_EQUAL
) {
529 o_rScrollRect
= ::vcl::unotools::b2DRectangleFromRectangle(
530 *reinterpret_cast<Rectangle
const *>(
533 bScrollRectSet
= true;
535 else if (pAct
->GetComment().CompareIgnoreCaseToAscii(
536 "XTEXT_PAINTRECT" ) == COMPARE_EQUAL
) {
537 o_rPaintRect
= ::vcl::unotools::b2DRectangleFromRectangle(
538 *reinterpret_cast<Rectangle
const *>(
541 bPaintRectSet
= true;
547 return bScrollRectSet
&& bPaintRectSet
;