Line data Source code
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 : // must be first
22 : #include <canvas/debug.hxx>
23 : #include <tools/diagnose_ex.h>
24 : #include <gdimtftools.hxx>
25 :
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>
32 :
33 : #include <cppuhelper/basemutex.hxx>
34 : #include <cppuhelper/compbase1.hxx>
35 :
36 : #include <comphelper/uno3.hxx>
37 : #include <cppuhelper/implbase1.hxx>
38 :
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>
47 :
48 : #include <unotools/streamwrap.hxx>
49 :
50 : #include "tools.hxx"
51 :
52 : using namespace ::com::sun::star;
53 :
54 :
55 : // free support functions
56 : // ======================
57 :
58 : namespace slideshow
59 : {
60 : namespace internal
61 : {
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!
65 :
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 0 : bool hasUnsupportedActions( const GDIMetaFile& rMtf )
74 : {
75 : // search metafile for RasterOp action
76 : MetaAction* pCurrAct;
77 :
78 : // TODO(Q3): avoid const-cast
79 0 : for( pCurrAct = const_cast<GDIMetaFile&>(rMtf).FirstAction();
80 : pCurrAct;
81 : pCurrAct = const_cast<GDIMetaFile&>(rMtf).NextAction() )
82 : {
83 0 : switch( pCurrAct->GetType() )
84 : {
85 : case MetaActionType::RASTEROP:
86 : // overpaint is okay - that's the default, anyway
87 0 : if( ROP_OVERPAINT ==
88 0 : static_cast<MetaRasterOpAction*>(pCurrAct)->GetRasterOp() )
89 : {
90 0 : break;
91 : }
92 : // FALLTHROUGH intended
93 : case MetaActionType::MOVECLIPREGION:
94 : // FALLTHROUGH intended
95 : case MetaActionType::REFPOINT:
96 : // FALLTHROUGH intended
97 : case MetaActionType::WALLPAPER:
98 0 : return true; // at least one unsupported
99 : // action encountered
100 0 : default: break;
101 : }
102 : }
103 :
104 0 : return false; // no unsupported action found
105 : }
106 :
107 : namespace {
108 :
109 : typedef ::cppu::WeakComponentImplHelper1< graphic::XGraphicRenderer > DummyRenderer_Base;
110 :
111 0 : class DummyRenderer: public cppu::BaseMutex, public DummyRenderer_Base
112 : {
113 : public:
114 0 : DummyRenderer() :
115 : DummyRenderer_Base( m_aMutex ),
116 0 : mxGraphic()
117 : {
118 0 : }
119 :
120 : //--- XGraphicRenderer -----------------------------------
121 0 : virtual void SAL_CALL render( const uno::Reference< graphic::XGraphic >& rGraphic ) throw (uno::RuntimeException, std::exception) SAL_OVERRIDE
122 : {
123 0 : ::osl::MutexGuard aGuard( m_aMutex );
124 0 : mxGraphic = rGraphic;
125 0 : }
126 :
127 : /** Retrieve GDIMetaFile from renderer
128 :
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.
134 : */
135 0 : GDIMetaFile getMtf( bool bForeignSource ) const
136 : {
137 0 : ::osl::MutexGuard aGuard( m_aMutex );
138 :
139 0 : Graphic aGraphic( mxGraphic );
140 :
141 0 : if( aGraphic.GetType() == GRAPHIC_BITMAP ||
142 0 : (bForeignSource &&
143 0 : hasUnsupportedActions(aGraphic.GetGDIMetaFile()) ) )
144 : {
145 : // wrap bitmap into GDIMetafile
146 0 : GDIMetaFile aMtf;
147 0 : ::Point aEmptyPoint;
148 :
149 0 : ::BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
150 :
151 : aMtf.AddAction( new MetaBmpExAction( aEmptyPoint,
152 0 : aBmpEx ) );
153 0 : aMtf.SetPrefSize( aBmpEx.GetPrefSize() );
154 0 : aMtf.SetPrefMapMode( aBmpEx.GetPrefMapMode() );
155 :
156 0 : return aMtf;
157 : }
158 : else
159 : {
160 0 : return aGraphic.GetGDIMetaFile();
161 0 : }
162 : }
163 :
164 : private:
165 : uno::Reference< graphic::XGraphic > mxGraphic;
166 : };
167 :
168 : } // anon namespace
169 :
170 : // Quick'n'dirty way: tunnel Graphic (only works for
171 : // in-process slideshow, of course)
172 0 : 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 )
177 : {
178 0 : ENSURE_OR_RETURN_FALSE( rxContext.is(),
179 : "getMetaFile(): Invalid context" );
180 :
181 : // create dummy XGraphicRenderer, which receives the
182 : // generated XGraphic from the GraphicExporter
183 :
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 0 : DummyRenderer* pRenderer( new DummyRenderer() );
188 0 : uno::Reference< graphic::XGraphicRenderer > xRenderer( pRenderer );
189 :
190 : // creating the graphic exporter
191 : uno::Reference< drawing::XGraphicExportFilter > xExporter =
192 0 : drawing::GraphicExportFilter::create(rxContext);
193 :
194 0 : uno::Sequence< beans::PropertyValue > aProps(3);
195 0 : aProps[0].Name = "FilterName";
196 0 : aProps[0].Value <<= OUString("SVM");
197 :
198 0 : aProps[1].Name = "GraphicRenderer";
199 0 : aProps[1].Value <<= xRenderer;
200 :
201 0 : uno::Sequence< beans::PropertyValue > aFilterData(4);
202 0 : aFilterData[0].Name = "ScrollText";
203 0 : aFilterData[0].Value <<= ((mtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != 0);
204 :
205 0 : aFilterData[1].Name = "ExportOnlyBackground";
206 0 : aFilterData[1].Value <<= ((mtfLoadFlags & MTF_LOAD_BACKGROUND_ONLY) != 0);
207 :
208 0 : aFilterData[2].Name = "Version";
209 0 : aFilterData[2].Value <<= static_cast<sal_Int32>( SOFFICE_FILEFORMAT_50 );
210 :
211 0 : aFilterData[3].Name = "CurrentPage";
212 0 : aFilterData[3].Value <<= uno::Reference< uno::XInterface >( xContainingPage,
213 0 : uno::UNO_QUERY_THROW );
214 :
215 0 : aProps[2].Name = "FilterData";
216 0 : aProps[2].Value <<= aFilterData;
217 :
218 0 : xExporter->setSourceDocument( xSource );
219 0 : if( !xExporter->filter( aProps ) )
220 0 : return false;
221 :
222 0 : rMtf = pRenderer->getMtf( (mtfLoadFlags & MTF_LOAD_FOREIGN_SOURCE) != 0 );
223 :
224 : // pRenderer is automatically destroyed when xRenderer
225 : // goes out of scope
226 :
227 : // TODO(E3): Error handling. Exporter might have
228 : // generated nothing, a bitmap, threw an exception,
229 : // whatever.
230 0 : return true;
231 : }
232 :
233 0 : sal_Int32 getNextActionOffset( MetaAction * pCurrAct )
234 : {
235 : // Special handling for actions that represent
236 : // more than one indexable action
237 : // ===========================================
238 :
239 0 : switch (pCurrAct->GetType()) {
240 : case MetaActionType::TEXT: {
241 0 : MetaTextAction * pAct = static_cast<MetaTextAction *>(pCurrAct);
242 0 : sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
243 0 : return nLen;
244 : }
245 : case MetaActionType::TEXTARRAY: {
246 : MetaTextArrayAction * pAct =
247 0 : static_cast<MetaTextArrayAction *>(pCurrAct);
248 0 : sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
249 0 : return nLen;
250 : }
251 : case MetaActionType::STRETCHTEXT: {
252 : MetaStretchTextAction * pAct =
253 0 : static_cast<MetaStretchTextAction *>(pCurrAct);
254 0 : sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
255 0 : return nLen;
256 : }
257 : case MetaActionType::FLOATTRANSPARENT: {
258 : MetaFloatTransparentAction * pAct =
259 0 : 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 0 : return pAct->GetGDIMetaFile().GetActionSize();
265 : }
266 : default:
267 0 : return 1;
268 : }
269 : }
270 :
271 0 : bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames,
272 : ::std::size_t& o_rLoopCount,
273 : CycleMode& o_eCycleMode,
274 : const Graphic& rGraphic )
275 : {
276 0 : o_rFrames.clear();
277 :
278 0 : if( !rGraphic.IsAnimated() )
279 0 : return false;
280 :
281 : // some loop invariants
282 0 : Animation aAnimation( rGraphic.GetAnimation() );
283 0 : const Point aEmptyPoint;
284 0 : const Size aAnimSize( aAnimation.GetDisplaySizePixel() );
285 :
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 0 : ScopedVclPtrInstance< VirtualDevice > pVDev;
291 0 : pVDev->SetOutputSizePixel( aAnimSize );
292 0 : pVDev->EnableMapMode( false );
293 :
294 : // setup mask VDev (alpha VDev is currently rather slow)
295 0 : ScopedVclPtrInstance< VirtualDevice > pVDevMask;
296 0 : pVDevMask->SetOutputSizePixel( aAnimSize );
297 0 : pVDevMask->EnableMapMode( false );
298 :
299 0 : switch( aAnimation.GetCycleMode() )
300 : {
301 : case CYCLE_NOT:
302 0 : o_rLoopCount = 1;
303 0 : o_eCycleMode = CYCLE_LOOP;
304 0 : break;
305 :
306 : case CYCLE_FALLBACK:
307 : // FALLTHROUGH intended
308 : case CYCLE_NORMAL:
309 0 : o_rLoopCount = aAnimation.GetLoopCount();
310 0 : o_eCycleMode = CYCLE_LOOP;
311 0 : break;
312 :
313 : case CYCLE_REVERS:
314 : // FALLTHROUGH intended
315 : case CYCLE_REVERS_FALLBACK:
316 0 : o_rLoopCount = aAnimation.GetLoopCount();
317 0 : o_eCycleMode = CYCLE_PINGPONGLOOP;
318 0 : break;
319 :
320 : default:
321 0 : ENSURE_OR_RETURN_FALSE(false,
322 : "getAnimationFromGraphic(): Unexpected case" );
323 : break;
324 : }
325 :
326 0 : for( sal_uInt16 i=0, nCount=aAnimation.Count(); i<nCount; ++i )
327 : {
328 0 : const AnimationBitmap& rAnimBmp( aAnimation.Get(i) );
329 0 : switch(rAnimBmp.eDisposal)
330 : {
331 : case DISPOSE_NOT:
332 : {
333 0 : pVDev->DrawBitmapEx(rAnimBmp.aPosPix,
334 0 : rAnimBmp.aBmpEx);
335 0 : Bitmap aMask = rAnimBmp.aBmpEx.GetMask();
336 :
337 0 : if( aMask.IsEmpty() )
338 : {
339 : const Rectangle aRect(aEmptyPoint,
340 0 : pVDevMask->GetOutputSizePixel());
341 0 : const Wallpaper aWallpaper(COL_BLACK);
342 0 : pVDevMask->DrawWallpaper(aRect,
343 0 : aWallpaper);
344 : }
345 : else
346 : {
347 : BitmapEx aTmpMask = BitmapEx(aMask,
348 0 : aMask);
349 0 : pVDevMask->DrawBitmapEx(rAnimBmp.aPosPix,
350 0 : aTmpMask );
351 : }
352 0 : break;
353 : }
354 :
355 : case DISPOSE_BACK:
356 : {
357 : // #i70772# react on no mask
358 0 : const Bitmap aMask(rAnimBmp.aBmpEx.GetMask());
359 0 : const Bitmap aContent(rAnimBmp.aBmpEx.GetBitmap());
360 :
361 0 : pVDevMask->Erase();
362 0 : pVDev->DrawBitmap(rAnimBmp.aPosPix, aContent);
363 :
364 0 : if(aMask.IsEmpty())
365 : {
366 0 : const Rectangle aRect(rAnimBmp.aPosPix, aContent.GetSizePixel());
367 0 : pVDevMask->SetFillColor(COL_BLACK);
368 0 : pVDevMask->SetLineColor();
369 0 : pVDevMask->DrawRect(aRect);
370 : }
371 : else
372 : {
373 0 : pVDevMask->DrawBitmap(rAnimBmp.aPosPix, aMask);
374 : }
375 0 : break;
376 : }
377 :
378 : case DISPOSE_FULL:
379 : {
380 0 : pVDev->DrawBitmapEx(rAnimBmp.aPosPix,
381 0 : rAnimBmp.aBmpEx);
382 0 : break;
383 : }
384 :
385 : case DISPOSE_PREVIOUS :
386 : {
387 0 : pVDev->DrawBitmapEx(rAnimBmp.aPosPix,
388 0 : rAnimBmp.aBmpEx);
389 0 : pVDevMask->DrawBitmap(rAnimBmp.aPosPix,
390 0 : rAnimBmp.aBmpEx.GetMask());
391 0 : break;
392 : }
393 : }
394 :
395 : // extract current aVDev content into a new animation
396 : // frame
397 0 : GDIMetaFileSharedPtr pMtf( new GDIMetaFile() );
398 : pMtf->AddAction(
399 : new MetaBmpExAction( aEmptyPoint,
400 : BitmapEx(
401 0 : pVDev->GetBitmap(
402 : aEmptyPoint,
403 0 : aAnimSize ),
404 0 : pVDevMask->GetBitmap(
405 : aEmptyPoint,
406 0 : aAnimSize ))));
407 :
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 0 : pMtf->SetPrefMapMode( MapMode() );
412 0 : pMtf->SetPrefSize( aAnimSize );
413 :
414 : // Take care of special value for MultiPage TIFFs. ATM these shall just
415 : // show their first page for _quite_ some time.
416 0 : sal_Int32 nWaitTime100thSeconds( rAnimBmp.nWait );
417 0 : if( ANIMATION_TIMEOUT_ON_CLICK == nWaitTime100thSeconds )
418 : {
419 : // ATM the huge value would block the timer, so use a long
420 : // time to show first page (whole day)
421 0 : nWaitTime100thSeconds = 100 * 60 * 60 * 24;
422 : }
423 :
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 0 : if( nWaitTime100thSeconds == 0 )
427 0 : nWaitTime100thSeconds = 10;
428 :
429 : o_rFrames.push_back( MtfAnimationFrame( pMtf,
430 0 : nWaitTime100thSeconds / 100.0 ) );
431 0 : }
432 :
433 0 : return !o_rFrames.empty();
434 : }
435 :
436 0 : bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle& o_rScrollRect,
437 : ::basegfx::B2DRectangle& o_rPaintRect,
438 : const GDIMetaFileSharedPtr& rMtf )
439 : {
440 : // extract bounds: scroll rect, paint rect
441 0 : bool bScrollRectSet(false);
442 0 : bool bPaintRectSet(false);
443 :
444 0 : for ( MetaAction * pCurrAct = rMtf->FirstAction();
445 0 : pCurrAct != 0; pCurrAct = rMtf->NextAction() )
446 : {
447 0 : if (pCurrAct->GetType() == MetaActionType::COMMENT)
448 : {
449 : MetaCommentAction * pAct =
450 0 : static_cast<MetaCommentAction *>(pCurrAct);
451 : // skip comment if not a special XTEXT... comment
452 0 : if( pAct->GetComment().matchIgnoreAsciiCase( OString("XTEXT"), 0 ) )
453 : {
454 0 : if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_SCROLLRECT"))
455 : {
456 : o_rScrollRect = vcl::unotools::b2DRectangleFromRectangle(
457 : *reinterpret_cast<Rectangle const *>(
458 0 : pAct->GetData() ) );
459 :
460 0 : bScrollRectSet = true;
461 : }
462 0 : else if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
463 : {
464 : o_rPaintRect = vcl::unotools::b2DRectangleFromRectangle(
465 : *reinterpret_cast<Rectangle const *>(
466 0 : pAct->GetData() ) );
467 :
468 0 : bPaintRectSet = true;
469 : }
470 : }
471 : }
472 : }
473 :
474 0 : return bScrollRectSet && bPaintRectSet;
475 : }
476 :
477 : }
478 : }
479 :
480 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|