fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / slideshow / source / engine / shapes / gdimtftools.cxx
blob03a467a7748b5e2d4538831c76a1e45ffc536429
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 // must be first
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>
50 #include "tools.hxx"
52 using namespace ::com::sun::star;
55 // free support functions
56 // ======================
58 namespace slideshow
60 namespace internal
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
76 MetaAction* pCurrAct;
78 // TODO(Q3): avoid const-cast
79 for( pCurrAct = const_cast<GDIMetaFile&>(rMtf).FirstAction();
80 pCurrAct;
81 pCurrAct = const_cast<GDIMetaFile&>(rMtf).NextAction() )
83 switch( pCurrAct->GetType() )
85 case MetaActionType::RASTEROP:
86 // overpaint is okay - that's the default, anyway
87 if( ROP_OVERPAINT ==
88 static_cast<MetaRasterOpAction*>(pCurrAct)->GetRasterOp() )
90 break;
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
99 // action encountered
100 default: break;
104 return false; // no unsupported action found
107 namespace {
109 typedef ::cppu::WeakComponentImplHelper1< graphic::XGraphicRenderer > DummyRenderer_Base;
111 class DummyRenderer: public cppu::BaseMutex, public DummyRenderer_Base
113 public:
114 DummyRenderer() :
115 DummyRenderer_Base( m_aMutex ),
116 mxGraphic()
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 ||
142 (bForeignSource &&
143 hasUnsupportedActions(aGraphic.GetGDIMetaFile()) ) )
145 // wrap bitmap into GDIMetafile
146 GDIMetaFile aMtf;
147 ::Point aEmptyPoint;
149 ::BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
151 aMtf.AddAction( new MetaBmpExAction( aEmptyPoint,
152 aBmpEx ) );
153 aMtf.SetPrefSize( aBmpEx.GetPrefSize() );
154 aMtf.SetPrefMapMode( aBmpEx.GetPrefMapMode() );
156 return aMtf;
158 else
160 return aGraphic.GetGDIMetaFile();
164 private:
165 uno::Reference< graphic::XGraphic > mxGraphic;
168 } // anon namespace
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,
174 GDIMetaFile& rMtf,
175 int mtfLoadFlags,
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 ) )
220 return false;
222 rMtf = pRenderer->getMtf( (mtfLoadFlags & MTF_LOAD_FOREIGN_SOURCE) != 0 );
224 // pRenderer is automatically destroyed when xRenderer
225 // goes out of scope
227 // TODO(E3): Error handling. Exporter might have
228 // generated nothing, a bitmap, threw an exception,
229 // whatever.
230 return true;
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());
243 return nLen;
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());
249 return nLen;
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());
255 return nLen;
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();
266 default:
267 return 1;
271 bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames,
272 ::std::size_t& o_rLoopCount,
273 CycleMode& o_eCycleMode,
274 const Graphic& rGraphic )
276 o_rFrames.clear();
278 if( !rGraphic.IsAnimated() )
279 return false;
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
289 // update modes)
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() )
301 case CYCLE_NOT:
302 o_rLoopCount = 1;
303 o_eCycleMode = CYCLE_LOOP;
304 break;
306 case CYCLE_FALLBACK:
307 // FALLTHROUGH intended
308 case CYCLE_NORMAL:
309 o_rLoopCount = aAnimation.GetLoopCount();
310 o_eCycleMode = CYCLE_LOOP;
311 break;
313 case CYCLE_REVERS:
314 // FALLTHROUGH intended
315 case CYCLE_REVERS_FALLBACK:
316 o_rLoopCount = aAnimation.GetLoopCount();
317 o_eCycleMode = CYCLE_PINGPONGLOOP;
318 break;
320 default:
321 ENSURE_OR_RETURN_FALSE(false,
322 "getAnimationFromGraphic(): Unexpected case" );
323 break;
326 for( sal_uInt16 i=0, nCount=aAnimation.Count(); i<nCount; ++i )
328 const AnimationBitmap& rAnimBmp( aAnimation.Get(i) );
329 switch(rAnimBmp.eDisposal)
331 case DISPOSE_NOT:
333 pVDev->DrawBitmapEx(rAnimBmp.aPosPix,
334 rAnimBmp.aBmpEx);
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,
343 aWallpaper);
345 else
347 BitmapEx aTmpMask = BitmapEx(aMask,
348 aMask);
349 pVDevMask->DrawBitmapEx(rAnimBmp.aPosPix,
350 aTmpMask );
352 break;
355 case DISPOSE_BACK:
357 // #i70772# react on no mask
358 const Bitmap aMask(rAnimBmp.aBmpEx.GetMask());
359 const Bitmap aContent(rAnimBmp.aBmpEx.GetBitmap());
361 pVDevMask->Erase();
362 pVDev->DrawBitmap(rAnimBmp.aPosPix, aContent);
364 if(aMask.IsEmpty())
366 const Rectangle aRect(rAnimBmp.aPosPix, aContent.GetSizePixel());
367 pVDevMask->SetFillColor(COL_BLACK);
368 pVDevMask->SetLineColor();
369 pVDevMask->DrawRect(aRect);
371 else
373 pVDevMask->DrawBitmap(rAnimBmp.aPosPix, aMask);
375 break;
378 case DISPOSE_FULL:
380 pVDev->DrawBitmapEx(rAnimBmp.aPosPix,
381 rAnimBmp.aBmpEx);
382 break;
385 case DISPOSE_PREVIOUS :
387 pVDev->DrawBitmapEx(rAnimBmp.aPosPix,
388 rAnimBmp.aBmpEx);
389 pVDevMask->DrawBitmap(rAnimBmp.aPosPix,
390 rAnimBmp.aBmpEx.GetMask());
391 break;
395 // extract current aVDev content into a new animation
396 // frame
397 GDIMetaFileSharedPtr pMtf( new GDIMetaFile() );
398 pMtf->AddAction(
399 new MetaBmpExAction( aEmptyPoint,
400 BitmapEx(
401 pVDev->GetBitmap(
402 aEmptyPoint,
403 aAnimSize ),
404 pVDevMask->GetBitmap(
405 aEmptyPoint,
406 aAnimSize ))));
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 *>(
458 pAct->GetData() ) );
460 bScrollRectSet = true;
462 else if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
464 o_rPaintRect = vcl::unotools::b2DRectangleFromRectangle(
465 *reinterpret_cast<Rectangle const *>(
466 pAct->GetData() ) );
468 bPaintRectSet = true;
474 return bScrollRectSet && bPaintRectSet;
480 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */