LibreOffice Module slideshow (master) 1
gdimtftools.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
21#include <sal/log.hxx>
22#include "gdimtftools.hxx"
23
24#include <com/sun/star/graphic/XGraphic.hpp>
25#include <com/sun/star/graphic/XGraphicRenderer.hpp>
26#include <com/sun/star/drawing/GraphicExportFilter.hpp>
27
30
33
34#include <vcl/canvastools.hxx>
35#include <vcl/metaact.hxx>
36#include <vcl/virdev.hxx>
37#include <vcl/gdimtf.hxx>
39#include <vcl/graph.hxx>
40
41#include <tools.hxx>
42
43using namespace ::com::sun::star;
44
45
46// free support functions
47// ======================
48
49namespace slideshow::internal
50{
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!
54
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.
62static bool hasUnsupportedActions( const GDIMetaFile& rMtf )
63{
64 // search metafile for RasterOp action
65 MetaAction* pCurrAct;
66
67 // TODO(Q3): avoid const-cast
68 for( pCurrAct = const_cast<GDIMetaFile&>(rMtf).FirstAction();
69 pCurrAct;
70 pCurrAct = const_cast<GDIMetaFile&>(rMtf).NextAction() )
71 {
72 switch( pCurrAct->GetType() )
73 {
74 case MetaActionType::RASTEROP:
75 // overpaint is okay - that's the default, anyway
76 if( RasterOp::OverPaint ==
77 static_cast<MetaRasterOpAction*>(pCurrAct)->GetRasterOp() )
78 {
79 break;
80 }
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;
88 }
89 }
90
91 return false; // no unsupported action found
92}
93
94namespace {
95
96typedef ::cppu::WeakComponentImplHelper< graphic::XGraphicRenderer > DummyRenderer_Base;
97
98class DummyRenderer: public cppu::BaseMutex, public DummyRenderer_Base
99{
100public:
101 DummyRenderer() :
102 DummyRenderer_Base( m_aMutex ),
103 mxGraphic()
104 {
105 }
106
107 //--- XGraphicRenderer -----------------------------------
108 virtual void SAL_CALL render( const uno::Reference< graphic::XGraphic >& rGraphic ) override
109 {
110 ::osl::MutexGuard aGuard( m_aMutex );
111 mxGraphic = rGraphic;
112 }
113
122 GDIMetaFileSharedPtr getMtf( bool bForeignSource ) const
123 {
124 ::osl::MutexGuard aGuard( m_aMutex );
125
126 Graphic aGraphic( mxGraphic );
127
128 if( aGraphic.GetType() == GraphicType::Bitmap ||
129 (bForeignSource &&
130 hasUnsupportedActions(aGraphic.GetGDIMetaFile()) ) )
131 {
132 // wrap bitmap into GDIMetafile
133 GDIMetaFileSharedPtr xMtf = std::make_shared<GDIMetaFile>();
134
135 ::BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
136
137 xMtf->AddAction( new MetaBmpExAction( Point(),
138 aBmpEx ) );
139 xMtf->SetPrefSize( aBmpEx.GetPrefSize() );
140 xMtf->SetPrefMapMode( aBmpEx.GetPrefMapMode() );
141
142 return xMtf;
143 }
144 return std::make_shared<GDIMetaFile>(aGraphic.GetGDIMetaFile());
145 }
146
147private:
148 uno::Reference< graphic::XGraphic > mxGraphic;
149};
150
151} // anon namespace
152
153// Quick'n'dirty way: tunnel Graphic (only works for
154// in-process slideshow, of course)
156 const uno::Reference< drawing::XDrawPage >& xContainingPage,
157 int mtfLoadFlags,
159{
160 if (!rxContext.is())
161 {
162 SAL_WARN("slideshow.opengl", "getMetaFile(): Invalid context" );
163 return GDIMetaFileSharedPtr();
164 }
165
166 // create dummy XGraphicRenderer, which receives the
167 // generated XGraphic from the GraphicExporter
168
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() );
173
174 // creating the graphic exporter
176 drawing::GraphicExportFilter::create(rxContext);
177
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 )),
185 "CurrentPage", uno::Reference< uno::XInterface >( xContainingPage,
186 uno::UNO_QUERY_THROW ))
187 };
188
190 comphelper::makePropertyValue("FilterName", OUString("SVM")),
192 comphelper::makePropertyValue("FilterData", aFilterData)
193 };
194
195 xExporter->setSourceDocument( xSource );
196 if( !xExporter->filter( aProps ) )
197 return GDIMetaFileSharedPtr();
198
199 GDIMetaFileSharedPtr xMtf = xRenderer->getMtf( (mtfLoadFlags & MTF_LOAD_FOREIGN_SOURCE) != 0 );
200
201 // pRenderer is automatically destroyed when xRenderer
202 // goes out of scope
203
204 // TODO(E3): Error handling. Exporter might have
205 // generated nothing, a bitmap, threw an exception,
206 // whatever.
207 return xMtf;
208}
209
210sal_Int32 getNextActionOffset( MetaAction * pCurrAct )
211{
212 // Special handling for actions that represent
213 // more than one indexable action
214 // ===========================================
215
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;
221 }
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;
227 }
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;
233 }
234 case MetaActionType::FLOATTRANSPARENT: {
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();
242 }
243 default:
244 return 1;
245 }
246}
247
249 sal_uInt32& o_rLoopCount,
250 const Graphic& rGraphic )
251{
252 o_rFrames.clear();
253
254 if( !rGraphic.IsAnimated() )
255 return false;
256
257 // some loop invariants
258 ::Animation aAnimation( rGraphic.GetAnimation() );
259 const Point aEmptyPoint;
260 const Size aAnimSize( aAnimation.GetDisplaySizePixel() );
261
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)
267 pVDev->SetOutputSizePixel( aAnimSize );
268 pVDev->EnableMapMode( false );
269
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 );
274
275 o_rLoopCount = aAnimation.GetLoopCount();
276
277 for( sal_uInt16 i=0, nCount=aAnimation.Count(); i<nCount; ++i )
278 {
279 const AnimationFrame& rAnimationFrame( aAnimation.Get(i) );
280 switch(rAnimationFrame.meDisposal)
281 {
282 case Disposal::Not:
283 {
284 pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel,
285 rAnimationFrame.maBitmapEx);
286 AlphaMask aMask = rAnimationFrame.maBitmapEx.GetAlphaMask();
287
288 if( aMask.IsEmpty() )
289 {
290 const tools::Rectangle aRect(aEmptyPoint,
291 pVDevMask->GetOutputSizePixel());
292 const Wallpaper aWallpaper(COL_BLACK);
293 pVDevMask->DrawWallpaper(aRect,
294 aWallpaper);
295 }
296 else
297 {
298 BitmapEx aTmpMask(aMask, aMask);
299 pVDevMask->DrawBitmapEx(rAnimationFrame.maPositionPixel,
300 aTmpMask );
301 }
302 break;
303 }
304
305 case Disposal::Back:
306 {
307 // #i70772# react on no mask
308 const Bitmap aMask(rAnimationFrame.maBitmapEx.GetAlphaMask());
309 const Bitmap & rContent(rAnimationFrame.maBitmapEx.GetBitmap());
310
311 pVDevMask->Erase();
312 pVDev->DrawBitmap(rAnimationFrame.maPositionPixel, rContent);
313
314 if(aMask.IsEmpty())
315 {
316 const tools::Rectangle aRect(rAnimationFrame.maPositionPixel, rContent.GetSizePixel());
317 pVDevMask->SetFillColor( COL_BLACK);
318 pVDevMask->SetLineColor();
319 pVDevMask->DrawRect(aRect);
320 }
321 else
322 {
323 pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel, aMask);
324 }
325 break;
326 }
327
328 case Disposal::Previous :
329 {
330 pVDev->DrawBitmapEx(rAnimationFrame.maPositionPixel,
331 rAnimationFrame.maBitmapEx);
332 pVDevMask->DrawBitmap(rAnimationFrame.maPositionPixel,
333 rAnimationFrame.maBitmapEx.GetAlphaMask());
334 break;
335 }
336 }
337
338 // extract current aVDev content into a new animation
339 // frame
340 GDIMetaFileSharedPtr pMtf = std::make_shared<GDIMetaFile>();
341 Bitmap aAlphaMask = pVDevMask->GetBitmap(aEmptyPoint, aAnimSize);
342 aAlphaMask.Invert(); // convert from transparency to alpha
343 pMtf->AddAction(
344 new MetaBmpExAction( aEmptyPoint,
345 BitmapEx(
346 pVDev->GetBitmap(
347 aEmptyPoint,
348 aAnimSize ),
349 aAlphaMask)));
350
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 );
356
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 )
361 {
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;
365 }
366
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;
371
372 o_rFrames.emplace_back( pMtf, nWaitTime100thSeconds / 100.0 );
373 }
374
375 return !o_rFrames.empty();
376}
377
379 ::basegfx::B2DRectangle& o_rPaintRect,
380 const GDIMetaFileSharedPtr& rMtf )
381{
382 // extract bounds: scroll rect, paint rect
383 bool bScrollRectSet(false);
384 bool bPaintRectSet(false);
385
386 for ( MetaAction * pCurrAct = rMtf->FirstAction();
387 pCurrAct != nullptr; pCurrAct = rMtf->NextAction() )
388 {
389 if (pCurrAct->GetType() == MetaActionType::COMMENT)
390 {
391 MetaCommentAction * pAct =
392 static_cast<MetaCommentAction *>(pCurrAct);
393 // skip comment if not a special XTEXT... comment
394 if( pAct->GetComment().matchIgnoreAsciiCase( "XTEXT" ) )
395 {
396 if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_SCROLLRECT"))
397 {
399 *reinterpret_cast<tools::Rectangle const *>(
400 pAct->GetData() ));
401
402 bScrollRectSet = true;
403 }
404 else if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
405 {
407 *reinterpret_cast<tools::Rectangle const *>(
408 pAct->GetData() ));
409
410 bPaintRectSet = true;
411 }
412 }
413 }
414 }
415
416 return bScrollRectSet && bPaintRectSet;
417}
418
419}
420
421/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define ANIMATION_TIMEOUT_ON_CLICK
bool IsEmpty() const
size_t Count() const
const Size & GetDisplaySizePixel() const
const AnimationFrame & Get(sal_uInt16 nAnimation) const
sal_uInt32 GetLoopCount() const
const AlphaMask & GetAlphaMask() const
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Size GetSizePixel() const
bool IsEmpty() const
bool Invert()
size_t GetActionSize() const
Animation GetAnimation() const
bool IsAnimated() const
MetaActionType GetType() const
const sal_uInt8 * GetData() const
const OString & GetComment() const
const GDIMetaFile & GetGDIMetaFile() const
RasterOp GetRasterOp() const
sal_Int32 GetLen() const
sal_Int32 GetIndex() const
const OUString & GetText() const
sal_Int32 GetLen() const
const OUString & GetText() const
sal_Int32 GetIndex() const
sal_Int32 GetIndex() const
sal_Int32 GetLen() const
const OUString & GetText() const
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
int nCount
::std::shared_ptr< GDIMetaFile > GDIMetaFileSharedPtr
#define SOFFICE_FILEFORMAT_50
uno::Reference< graphic::XGraphic > mxGraphic
std::mutex m_aMutex
#define SAL_WARN(area, stream)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
int i
@ MTF_LOAD_FOREIGN_SOURCE
the source of the metafile might be a foreign application.
Definition: gdimtftools.hxx:50
@ MTF_LOAD_BACKGROUND_ONLY
retrieve a meta file for the page background only
Definition: gdimtftools.hxx:52
@ MTF_LOAD_SCROLL_TEXT_MTF
retrieve the drawing layer scroll text metafile
Definition: gdimtftools.hxx:54
static bool hasUnsupportedActions(const GDIMetaFile &rMtf)
Definition: gdimtftools.cxx:62
std::shared_ptr< GDIMetaFile > GDIMetaFileSharedPtr
Definition: tools.hxx:63
bool getRectanglesFromScrollMtf(::basegfx::B2DRectangle &o_rScrollRect, ::basegfx::B2DRectangle &o_rPaintRect, const GDIMetaFileSharedPtr &rMtf)
Retrieve scroll text animation rectangles from given metafile.
bool getAnimationFromGraphic(VectorOfMtfAnimationFrames &o_rFrames, sal_uInt32 &o_rLoopCount, const Graphic &rGraphic)
Extract a vector of animation frames from given Graphic.
::std::vector< MtfAnimationFrame > VectorOfMtfAnimationFrames
Definition: gdimtftools.hxx:79
GDIMetaFileSharedPtr getMetaFile(const uno::Reference< lang::XComponent > &xSource, const uno::Reference< drawing::XDrawPage > &xContainingPage, int mtfLoadFlags, const uno::Reference< uno::XComponentContext > &rxContext)
sal_Int32 getNextActionOffset(MetaAction *pCurrAct)
Gets the next action offset for iterating meta actions which is most often returns 1.
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
tools::Long mnWait
Point maPositionPixel
BitmapEx maBitmapEx
Disposal meDisposal