Bump version to 6.0-36
[LibreOffice.git] / slideshow / source / engine / shapes / gdimtftools.cxx
blob4e358df4b9d0f499bc7a33fc2f68285a35566363
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
47 #include <tools.hxx>
49 using namespace ::com::sun::star;
52 // free support functions
53 // ======================
55 namespace slideshow
57 namespace internal
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
73 MetaAction* pCurrAct;
75 // TODO(Q3): avoid const-cast
76 for( pCurrAct = const_cast<GDIMetaFile&>(rMtf).FirstAction();
77 pCurrAct;
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() )
87 break;
89 SAL_FALLTHROUGH;
90 case MetaActionType::MOVECLIPREGION:
91 case MetaActionType::REFPOINT:
92 case MetaActionType::WALLPAPER:
93 return true; // at least one unsupported
94 // action encountered
95 default: break;
99 return false; // no unsupported action found
102 namespace {
104 typedef ::cppu::WeakComponentImplHelper< graphic::XGraphicRenderer > DummyRenderer_Base;
106 class DummyRenderer: public cppu::BaseMutex, public DummyRenderer_Base
108 public:
109 DummyRenderer() :
110 DummyRenderer_Base( m_aMutex ),
111 mxGraphic()
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 ||
137 (bForeignSource &&
138 hasUnsupportedActions(aGraphic.GetGDIMetaFile()) ) )
140 // wrap bitmap into GDIMetafile
141 GDIMetaFileSharedPtr xMtf(new GDIMetaFile);
142 ::Point aEmptyPoint;
144 ::BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
146 xMtf->AddAction( new MetaBmpExAction( aEmptyPoint,
147 aBmpEx ) );
148 xMtf->SetPrefSize( aBmpEx.GetPrefSize() );
149 xMtf->SetPrefMapMode( aBmpEx.GetPrefMapMode() );
151 return xMtf;
153 return GDIMetaFileSharedPtr(new GDIMetaFile(aGraphic.GetGDIMetaFile()));
156 private:
157 uno::Reference< graphic::XGraphic > mxGraphic;
160 } // anon namespace
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,
166 int mtfLoadFlags,
167 const uno::Reference< uno::XComponentContext >& rxContext )
169 if (!rxContext.is())
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
219 // goes out of scope
221 // TODO(E3): Error handling. Exporter might have
222 // generated nothing, a bitmap, threw an exception,
223 // whatever.
224 return xMtf;
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());
237 return nLen;
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());
243 return nLen;
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());
249 return nLen;
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();
260 default:
261 return 1;
265 bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames,
266 sal_uInt32& o_rLoopCount,
267 const Graphic& rGraphic )
269 o_rFrames.clear();
271 if( !rGraphic.IsAnimated() )
272 return false;
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
282 // update modes)
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)
299 case Disposal::Not:
301 pVDev->DrawBitmapEx(rAnimBmp.aPosPix,
302 rAnimBmp.aBmpEx);
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,
311 aWallpaper);
313 else
315 BitmapEx aTmpMask = BitmapEx(aMask,
316 aMask);
317 pVDevMask->DrawBitmapEx(rAnimBmp.aPosPix,
318 aTmpMask );
320 break;
323 case Disposal::Back:
325 // #i70772# react on no mask
326 const Bitmap aMask(rAnimBmp.aBmpEx.GetMask());
327 const Bitmap aContent(rAnimBmp.aBmpEx.GetBitmap());
329 pVDevMask->Erase();
330 pVDev->DrawBitmap(rAnimBmp.aPosPix, aContent);
332 if(aMask.IsEmpty())
334 const tools::Rectangle aRect(rAnimBmp.aPosPix, aContent.GetSizePixel());
335 pVDevMask->SetFillColor(COL_BLACK);
336 pVDevMask->SetLineColor();
337 pVDevMask->DrawRect(aRect);
339 else
341 pVDevMask->DrawBitmap(rAnimBmp.aPosPix, aMask);
343 break;
346 case Disposal::Previous :
348 pVDev->DrawBitmapEx(rAnimBmp.aPosPix,
349 rAnimBmp.aBmpEx);
350 pVDevMask->DrawBitmap(rAnimBmp.aPosPix,
351 rAnimBmp.aBmpEx.GetMask());
352 break;
356 // extract current aVDev content into a new animation
357 // frame
358 GDIMetaFileSharedPtr pMtf( new GDIMetaFile() );
359 pMtf->AddAction(
360 new MetaBmpExAction( aEmptyPoint,
361 BitmapEx(
362 pVDev->GetBitmap(
363 aEmptyPoint,
364 aAnimSize ),
365 pVDevMask->GetBitmap(
366 aEmptyPoint,
367 aAnimSize ))));
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 *>(
418 pAct->GetData() ) );
420 bScrollRectSet = true;
422 else if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
424 o_rPaintRect = vcl::unotools::b2DRectangleFromRectangle(
425 *reinterpret_cast<tools::Rectangle const *>(
426 pAct->GetData() ) );
428 bPaintRectSet = true;
434 return bScrollRectSet && bPaintRectSet;
440 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */