android: Update app-specific/MIME type icons
[LibreOffice.git] / slideshow / source / engine / shapes / gdimtftools.cxx
blob0c9e432850c66d587baebc6db5d5dbb6370347e9
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 <sal/log.hxx>
22 #include "gdimtftools.hxx"
24 #include <com/sun/star/graphic/XGraphic.hpp>
25 #include <com/sun/star/graphic/XGraphicRenderer.hpp>
26 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
28 #include <cppuhelper/basemutex.hxx>
29 #include <cppuhelper/compbase.hxx>
31 #include <comphelper/fileformat.h>
32 #include <comphelper/propertyvalue.hxx>
34 #include <vcl/canvastools.hxx>
35 #include <vcl/metaact.hxx>
36 #include <vcl/virdev.hxx>
37 #include <vcl/gdimtf.hxx>
38 #include <vcl/animate/Animation.hxx>
39 #include <vcl/graph.hxx>
41 #include <tools.hxx>
43 using namespace ::com::sun::star;
46 // free support functions
47 // ======================
49 namespace slideshow::internal
51 // TODO(E2): Detect the case when svx/drawing layer is not
52 // in-process, or even not on the same machine, and
53 // fallback to metafile streaming!
55 // For fixing #i48102#, have to be a _lot_ more selective
56 // on which metafiles to convert to bitmaps. The problem
57 // here is that we _always_ get the shape content as a
58 // metafile, even if we have a bitmap graphic shape. Thus,
59 // calling GetBitmapEx on such a Graphic (see below) will
60 // result in one poorly scaled bitmap into another,
61 // somewhat arbitrarily sized bitmap.
62 static bool hasUnsupportedActions( const GDIMetaFile& rMtf )
64 // search metafile for RasterOp action
65 MetaAction* pCurrAct;
67 // TODO(Q3): avoid const-cast
68 for( pCurrAct = const_cast<GDIMetaFile&>(rMtf).FirstAction();
69 pCurrAct;
70 pCurrAct = const_cast<GDIMetaFile&>(rMtf).NextAction() )
72 switch( pCurrAct->GetType() )
74 case MetaActionType::RASTEROP:
75 // overpaint is okay - that's the default, anyway
76 if( RasterOp::OverPaint ==
77 static_cast<MetaRasterOpAction*>(pCurrAct)->GetRasterOp() )
79 break;
81 [[fallthrough]];
82 case MetaActionType::MOVECLIPREGION:
83 case MetaActionType::REFPOINT:
84 case MetaActionType::WALLPAPER:
85 return true; // at least one unsupported
86 // action encountered
87 default: break;
91 return false; // no unsupported action found
94 namespace {
96 typedef ::cppu::WeakComponentImplHelper< graphic::XGraphicRenderer > DummyRenderer_Base;
98 class DummyRenderer: public cppu::BaseMutex, public DummyRenderer_Base
100 public:
101 DummyRenderer() :
102 DummyRenderer_Base( m_aMutex ),
103 mxGraphic()
107 //--- XGraphicRenderer -----------------------------------
108 virtual void SAL_CALL render( const uno::Reference< graphic::XGraphic >& rGraphic ) override
110 ::osl::MutexGuard aGuard( m_aMutex );
111 mxGraphic = rGraphic;
114 /** Retrieve GDIMetaFile from renderer
116 @param bForeignSource
117 When true, the source of the metafile might be a
118 foreign application. The metafile is checked
119 against unsupported content, and, if necessary,
120 returned as a pre-rendered bitmap.
122 GDIMetaFileSharedPtr getMtf( bool bForeignSource ) const
124 ::osl::MutexGuard aGuard( m_aMutex );
126 Graphic aGraphic( mxGraphic );
128 if( aGraphic.GetType() == GraphicType::Bitmap ||
129 (bForeignSource &&
130 hasUnsupportedActions(aGraphic.GetGDIMetaFile()) ) )
132 // wrap bitmap into GDIMetafile
133 GDIMetaFileSharedPtr xMtf = std::make_shared<GDIMetaFile>();
135 ::BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
137 xMtf->AddAction( new MetaBmpExAction( Point(),
138 aBmpEx ) );
139 xMtf->SetPrefSize( aBmpEx.GetPrefSize() );
140 xMtf->SetPrefMapMode( aBmpEx.GetPrefMapMode() );
142 return xMtf;
144 return std::make_shared<GDIMetaFile>(aGraphic.GetGDIMetaFile());
147 private:
148 uno::Reference< graphic::XGraphic > mxGraphic;
151 } // anon namespace
153 // Quick'n'dirty way: tunnel Graphic (only works for
154 // in-process slideshow, of course)
155 GDIMetaFileSharedPtr getMetaFile( const uno::Reference< lang::XComponent >& xSource,
156 const uno::Reference< drawing::XDrawPage >& xContainingPage,
157 int mtfLoadFlags,
158 const uno::Reference< uno::XComponentContext >& rxContext )
160 if (!rxContext.is())
162 SAL_WARN("slideshow.opengl", "getMetaFile(): Invalid context" );
163 return GDIMetaFileSharedPtr();
166 // create dummy XGraphicRenderer, which receives the
167 // generated XGraphic from the GraphicExporter
169 // TODO(P3): Move creation of DummyRenderer out of the
170 // loop! Either by making it static, or transforming
171 // the whole thing here into a class.
172 rtl::Reference<DummyRenderer> xRenderer( new DummyRenderer() );
174 // creating the graphic exporter
175 uno::Reference< drawing::XGraphicExportFilter > xExporter =
176 drawing::GraphicExportFilter::create(rxContext);
178 uno::Sequence< beans::PropertyValue > aFilterData{
179 comphelper::makePropertyValue("ScrollText",
180 ((mtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != 0)),
181 comphelper::makePropertyValue("ExportOnlyBackground",
182 ((mtfLoadFlags & MTF_LOAD_BACKGROUND_ONLY) != 0)),
183 comphelper::makePropertyValue("Version", static_cast<sal_Int32>( SOFFICE_FILEFORMAT_50 )),
184 comphelper::makePropertyValue(
185 "CurrentPage", uno::Reference< uno::XInterface >( xContainingPage,
186 uno::UNO_QUERY_THROW ))
189 uno::Sequence< beans::PropertyValue > aProps{
190 comphelper::makePropertyValue("FilterName", OUString("SVM")),
191 comphelper::makePropertyValue("GraphicRenderer", uno::Reference< graphic::XGraphicRenderer >(xRenderer)),
192 comphelper::makePropertyValue("FilterData", aFilterData)
195 xExporter->setSourceDocument( xSource );
196 if( !xExporter->filter( aProps ) )
197 return GDIMetaFileSharedPtr();
199 GDIMetaFileSharedPtr xMtf = xRenderer->getMtf( (mtfLoadFlags & MTF_LOAD_FOREIGN_SOURCE) != 0 );
201 // pRenderer is automatically destroyed when xRenderer
202 // goes out of scope
204 // TODO(E3): Error handling. Exporter might have
205 // generated nothing, a bitmap, threw an exception,
206 // whatever.
207 return xMtf;
210 sal_Int32 getNextActionOffset( MetaAction * pCurrAct )
212 // Special handling for actions that represent
213 // more than one indexable action
214 // ===========================================
216 switch (pCurrAct->GetType()) {
217 case MetaActionType::TEXT: {
218 MetaTextAction * pAct = static_cast<MetaTextAction *>(pCurrAct);
219 sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
220 return nLen;
222 case MetaActionType::TEXTARRAY: {
223 MetaTextArrayAction * pAct =
224 static_cast<MetaTextArrayAction *>(pCurrAct);
225 sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
226 return nLen;
228 case MetaActionType::STRETCHTEXT: {
229 MetaStretchTextAction * pAct =
230 static_cast<MetaStretchTextAction *>(pCurrAct);
231 sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
232 return nLen;
234 case MetaActionType::FLOATTRANSPARENT: {
235 MetaFloatTransparentAction * pAct =
236 static_cast<MetaFloatTransparentAction*>(pCurrAct);
237 // TODO(F2): Recurse into action metafile
238 // (though this is currently not used from the
239 // DrawingLayer - shape transparency gradients
240 // don't affect shape text)
241 return pAct->GetGDIMetaFile().GetActionSize();
243 default:
244 return 1;
248 bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames,
249 sal_uInt32& o_rLoopCount,
250 const Graphic& rGraphic )
252 o_rFrames.clear();
254 if( !rGraphic.IsAnimated() )
255 return false;
257 // some loop invariants
258 ::Animation aAnimation( rGraphic.GetAnimation() );
259 const Point aEmptyPoint;
260 const Size aAnimSize( aAnimation.GetDisplaySizePixel() );
262 // setup VDev, into which all bitmaps are painted (want to
263 // normalize animations to n bitmaps of same size. An Animation,
264 // though, can contain bitmaps of varying sizes and different
265 // update modes)
266 ScopedVclPtrInstance< VirtualDevice > pVDev;
267 pVDev->SetOutputSizePixel( aAnimSize );
268 pVDev->EnableMapMode( false );
270 // setup mask VDev (alpha VDev is currently rather slow)
271 ScopedVclPtrInstance<VirtualDevice> pVDevMask(DeviceFormat::WITHOUT_ALPHA);
272 pVDevMask->SetOutputSizePixel( aAnimSize );
273 pVDevMask->EnableMapMode( false );
275 o_rLoopCount = aAnimation.GetLoopCount();
277 for( sal_uInt16 i=0, nCount=aAnimation.Count(); i<nCount; ++i )
279 const AnimationFrame& rAnimationFrame( aAnimation.Get(i) );
280 switch(rAnimationFrame.meDisposal)
282 case Disposal::Not:
284 pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel,
285 rAnimationFrame.maBitmapEx);
286 Bitmap aMask = rAnimationFrame.maBitmapEx.GetAlphaMask();
288 if( aMask.IsEmpty() )
290 const tools::Rectangle aRect(aEmptyPoint,
291 pVDevMask->GetOutputSizePixel());
292 const Wallpaper aWallpaper(COL_BLACK);
293 pVDevMask->DrawWallpaper(aRect,
294 aWallpaper);
296 else
298 BitmapEx aTmpMask(aMask, aMask);
299 pVDevMask->DrawBitmapEx(rAnimationFrame.maPositionPixel,
300 aTmpMask );
302 break;
305 case Disposal::Back:
307 // #i70772# react on no mask
308 const Bitmap aMask(rAnimationFrame.maBitmapEx.GetAlphaMask());
309 const Bitmap & rContent(rAnimationFrame.maBitmapEx.GetBitmap());
311 pVDevMask->Erase();
312 pVDev->DrawBitmap(rAnimationFrame.maPositionPixel, rContent);
314 if(aMask.IsEmpty())
316 const tools::Rectangle aRect(rAnimationFrame.maPositionPixel, rContent.GetSizePixel());
317 pVDevMask->SetFillColor( COL_BLACK);
318 pVDevMask->SetLineColor();
319 pVDevMask->DrawRect(aRect);
321 else
323 pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel, aMask);
325 break;
328 case Disposal::Previous :
330 pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel,
331 rAnimationFrame.maBitmapEx);
332 pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel,
333 rAnimationFrame.maBitmapEx.GetAlphaMask());
334 break;
338 // extract current aVDev content into a new animation
339 // frame
340 GDIMetaFileSharedPtr pMtf = std::make_shared<GDIMetaFile>();
341 pMtf->AddAction(
342 new MetaBmpExAction( aEmptyPoint,
343 BitmapEx(
344 pVDev->GetBitmap(
345 aEmptyPoint,
346 aAnimSize ),
347 pVDevMask->GetBitmap(
348 aEmptyPoint,
349 aAnimSize ))));
351 // setup mtf dimensions and pref map mode (for
352 // simplicity, keep it all in pixel. the metafile
353 // renderer scales it down to (1, 1) box anyway)
354 pMtf->SetPrefMapMode( MapMode() );
355 pMtf->SetPrefSize( aAnimSize );
357 // Take care of special value for MultiPage TIFFs. ATM these shall just
358 // show their first page for _quite_ some time.
359 sal_Int32 nWaitTime100thSeconds(rAnimationFrame.mnWait);
360 if( ANIMATION_TIMEOUT_ON_CLICK == nWaitTime100thSeconds )
362 // ATM the huge value would block the timer, so use a long
363 // time to show first page (whole day)
364 nWaitTime100thSeconds = 100 * 60 * 60 * 24;
367 // There are animated GIFs with no WaitTime set. Take 0.1 sec, the
368 // same duration that is used by the edit view.
369 if( nWaitTime100thSeconds == 0 )
370 nWaitTime100thSeconds = 10;
372 o_rFrames.emplace_back( pMtf, nWaitTime100thSeconds / 100.0 );
375 return !o_rFrames.empty();
378 bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle& o_rScrollRect,
379 ::basegfx::B2DRectangle& o_rPaintRect,
380 const GDIMetaFileSharedPtr& rMtf )
382 // extract bounds: scroll rect, paint rect
383 bool bScrollRectSet(false);
384 bool bPaintRectSet(false);
386 for ( MetaAction * pCurrAct = rMtf->FirstAction();
387 pCurrAct != nullptr; pCurrAct = rMtf->NextAction() )
389 if (pCurrAct->GetType() == MetaActionType::COMMENT)
391 MetaCommentAction * pAct =
392 static_cast<MetaCommentAction *>(pCurrAct);
393 // skip comment if not a special XTEXT... comment
394 if( pAct->GetComment().matchIgnoreAsciiCase( "XTEXT" ) )
396 if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_SCROLLRECT"))
398 o_rScrollRect = vcl::unotools::b2DRectangleFromRectangle(
399 *reinterpret_cast<tools::Rectangle const *>(
400 pAct->GetData() ));
402 bScrollRectSet = true;
404 else if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
406 o_rPaintRect = vcl::unotools::b2DRectangleFromRectangle(
407 *reinterpret_cast<tools::Rectangle const *>(
408 pAct->GetData() ));
410 bPaintRectSet = true;
416 return bScrollRectSet && bPaintRectSet;
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */