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 META_RASTEROP_ACTION:
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 META_MOVECLIPREGION_ACTION:
94 : // FALLTHROUGH intended
95 : case META_REFPOINT_ACTION:
96 : // FALLTHROUGH intended
97 : case META_WALLPAPER_ACTION:
98 0 : return true; // at least one unsupported
99 : // action encountered
100 : }
101 : }
102 :
103 0 : return false; // no unsupported action found
104 : }
105 :
106 : namespace {
107 :
108 : typedef ::cppu::WeakComponentImplHelper1< graphic::XGraphicRenderer > DummyRenderer_Base;
109 :
110 0 : class DummyRenderer :
111 : public DummyRenderer_Base,
112 : public cppu::BaseMutex
113 : {
114 : public:
115 0 : DummyRenderer() :
116 : DummyRenderer_Base( m_aMutex ),
117 0 : mxGraphic()
118 : {
119 0 : }
120 :
121 : //--- XGraphicRenderer -----------------------------------
122 0 : virtual void SAL_CALL render( const uno::Reference< graphic::XGraphic >& rGraphic ) throw (uno::RuntimeException, std::exception) SAL_OVERRIDE
123 : {
124 0 : ::osl::MutexGuard aGuard( m_aMutex );
125 0 : mxGraphic = rGraphic;
126 0 : }
127 :
128 : /** Retrieve GDIMetaFile from renderer
129 :
130 : @param bForeignSource
131 : When true, the source of the metafile might be a
132 : foreign application. The metafile is checked
133 : against unsupported content, and, if necessary,
134 : returned as a pre-rendererd bitmap.
135 : */
136 0 : GDIMetaFile getMtf( bool bForeignSource ) const
137 : {
138 0 : ::osl::MutexGuard aGuard( m_aMutex );
139 :
140 0 : Graphic aGraphic( mxGraphic );
141 :
142 0 : if( aGraphic.GetType() == GRAPHIC_BITMAP ||
143 0 : (bForeignSource &&
144 0 : hasUnsupportedActions(aGraphic.GetGDIMetaFile()) ) )
145 : {
146 : // wrap bitmap into GDIMetafile
147 0 : GDIMetaFile aMtf;
148 0 : ::Point aEmptyPoint;
149 :
150 0 : ::BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
151 :
152 : aMtf.AddAction( new MetaBmpExAction( aEmptyPoint,
153 0 : aBmpEx ) );
154 0 : aMtf.SetPrefSize( aBmpEx.GetPrefSize() );
155 0 : aMtf.SetPrefMapMode( aBmpEx.GetPrefMapMode() );
156 :
157 0 : return aMtf;
158 : }
159 : else
160 : {
161 0 : return aGraphic.GetGDIMetaFile();
162 0 : }
163 : }
164 :
165 : private:
166 : uno::Reference< graphic::XGraphic > mxGraphic;
167 : };
168 :
169 : } // anon namespace
170 :
171 : // Quick'n'dirty way: tunnel Graphic (only works for
172 : // in-process slideshow, of course)
173 0 : bool getMetaFile( const uno::Reference< lang::XComponent >& xSource,
174 : const uno::Reference< drawing::XDrawPage >& xContainingPage,
175 : GDIMetaFile& rMtf,
176 : int mtfLoadFlags,
177 : const uno::Reference< uno::XComponentContext >& rxContext )
178 : {
179 0 : ENSURE_OR_RETURN_FALSE( rxContext.is(),
180 : "getMetaFile(): Invalid context" );
181 :
182 : // create dummy XGraphicRenderer, which receives the
183 : // generated XGraphic from the GraphicExporter
184 :
185 : // TODO(P3): Move creation of DummyRenderer out of the
186 : // loop! Either by making it static, or transforming
187 : // the whole thing here into a class.
188 0 : DummyRenderer* pRenderer( new DummyRenderer() );
189 0 : uno::Reference< graphic::XGraphicRenderer > xRenderer( pRenderer );
190 :
191 : // creating the graphic exporter
192 : uno::Reference< drawing::XGraphicExportFilter > xExporter =
193 0 : drawing::GraphicExportFilter::create(rxContext);
194 :
195 0 : uno::Sequence< beans::PropertyValue > aProps(3);
196 0 : aProps[0].Name = "FilterName";
197 0 : aProps[0].Value <<= OUString("SVM");
198 :
199 0 : aProps[1].Name = "GraphicRenderer";
200 0 : aProps[1].Value <<= xRenderer;
201 :
202 0 : uno::Sequence< beans::PropertyValue > aFilterData(4);
203 0 : aFilterData[0].Name = "ScrollText";
204 0 : aFilterData[0].Value <<= ((mtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != 0);
205 :
206 0 : aFilterData[1].Name = "ExportOnlyBackground";
207 0 : aFilterData[1].Value <<= ((mtfLoadFlags & MTF_LOAD_BACKGROUND_ONLY) != 0);
208 :
209 0 : aFilterData[2].Name = "Version";
210 0 : aFilterData[2].Value <<= static_cast<sal_Int32>( SOFFICE_FILEFORMAT_50 );
211 :
212 0 : aFilterData[3].Name = "CurrentPage";
213 0 : aFilterData[3].Value <<= uno::Reference< uno::XInterface >( xContainingPage,
214 0 : uno::UNO_QUERY_THROW );
215 :
216 0 : aProps[2].Name = "FilterData";
217 0 : aProps[2].Value <<= aFilterData;
218 :
219 0 : xExporter->setSourceDocument( xSource );
220 0 : if( !xExporter->filter( aProps ) )
221 0 : return false;
222 :
223 0 : rMtf = pRenderer->getMtf( (mtfLoadFlags & MTF_LOAD_FOREIGN_SOURCE) != 0 );
224 :
225 : // pRenderer is automatically destroyed when xRenderer
226 : // goes out of scope
227 :
228 : // TODO(E3): Error handling. Exporter might have
229 : // generated nothing, a bitmap, threw an exception,
230 : // whatever.
231 0 : return true;
232 : }
233 :
234 0 : sal_Int32 getNextActionOffset( MetaAction * pCurrAct )
235 : {
236 : // Special handling for actions that represent
237 : // more than one indexable action
238 : // ===========================================
239 :
240 0 : switch (pCurrAct->GetType()) {
241 : case META_TEXT_ACTION: {
242 0 : MetaTextAction * pAct = static_cast<MetaTextAction *>(pCurrAct);
243 0 : sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
244 0 : return nLen;
245 : }
246 : case META_TEXTARRAY_ACTION: {
247 : MetaTextArrayAction * pAct =
248 0 : static_cast<MetaTextArrayAction *>(pCurrAct);
249 0 : sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
250 0 : return nLen;
251 : }
252 : case META_STRETCHTEXT_ACTION: {
253 : MetaStretchTextAction * pAct =
254 0 : static_cast<MetaStretchTextAction *>(pCurrAct);
255 0 : sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex());
256 0 : return nLen;
257 : }
258 : case META_FLOATTRANSPARENT_ACTION: {
259 : MetaFloatTransparentAction * pAct =
260 0 : static_cast<MetaFloatTransparentAction*>(pCurrAct);
261 : // TODO(F2): Recurse into action metafile
262 : // (though this is currently not used from the
263 : // DrawingLayer - shape transparency gradients
264 : // don't affect shape text)
265 0 : return pAct->GetGDIMetaFile().GetActionSize();
266 : }
267 : default:
268 0 : return 1;
269 : }
270 : }
271 :
272 0 : bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames,
273 : ::std::size_t& o_rLoopCount,
274 : CycleMode& o_eCycleMode,
275 : const Graphic& rGraphic )
276 : {
277 0 : o_rFrames.clear();
278 :
279 0 : if( !rGraphic.IsAnimated() )
280 0 : return false;
281 :
282 : // some loop invariants
283 0 : Animation aAnimation( rGraphic.GetAnimation() );
284 0 : const Point aEmptyPoint;
285 0 : const Size aAnimSize( aAnimation.GetDisplaySizePixel() );
286 :
287 : // setup VDev, into which all bitmaps are painted (want to
288 : // normalize animations to n bitmaps of same size. An Animation,
289 : // though, can contain bitmaps of varying sizes and different
290 : // update modes)
291 0 : VirtualDevice aVDev;
292 0 : aVDev.SetOutputSizePixel( aAnimSize );
293 0 : aVDev.EnableMapMode( false );
294 :
295 : // setup mask VDev (alpha VDev is currently rather slow)
296 0 : VirtualDevice aVDevMask;
297 0 : aVDevMask.SetOutputSizePixel( aAnimSize );
298 0 : aVDevMask.EnableMapMode( false );
299 :
300 0 : switch( aAnimation.GetCycleMode() )
301 : {
302 : case CYCLE_NOT:
303 0 : o_rLoopCount = 1;
304 0 : o_eCycleMode = CYCLE_LOOP;
305 0 : break;
306 :
307 : case CYCLE_FALLBACK:
308 : // FALLTHROUGH intended
309 : case CYCLE_NORMAL:
310 0 : o_rLoopCount = aAnimation.GetLoopCount();
311 0 : o_eCycleMode = CYCLE_LOOP;
312 0 : break;
313 :
314 : case CYCLE_REVERS:
315 : // FALLTHROUGH intended
316 : case CYCLE_REVERS_FALLBACK:
317 0 : o_rLoopCount = aAnimation.GetLoopCount();
318 0 : o_eCycleMode = CYCLE_PINGPONGLOOP;
319 0 : break;
320 :
321 : default:
322 0 : ENSURE_OR_RETURN_FALSE(false,
323 : "getAnimationFromGraphic(): Unexpected case" );
324 : break;
325 : }
326 :
327 0 : for( sal_uInt16 i=0, nCount=aAnimation.Count(); i<nCount; ++i )
328 : {
329 0 : const AnimationBitmap& rAnimBmp( aAnimation.Get(i) );
330 0 : switch(rAnimBmp.eDisposal)
331 : {
332 : case DISPOSE_NOT:
333 : {
334 : aVDev.DrawBitmapEx(rAnimBmp.aPosPix,
335 0 : rAnimBmp.aBmpEx);
336 0 : Bitmap aMask = rAnimBmp.aBmpEx.GetMask();
337 :
338 0 : if( aMask.IsEmpty() )
339 : {
340 : const Rectangle aRect(aEmptyPoint,
341 0 : aVDevMask.GetOutputSizePixel());
342 0 : const Wallpaper aWallpaper(COL_BLACK);
343 : aVDevMask.DrawWallpaper(aRect,
344 0 : aWallpaper);
345 : }
346 : else
347 : {
348 : BitmapEx aTmpMask = BitmapEx(aMask,
349 0 : aMask);
350 : aVDevMask.DrawBitmapEx(rAnimBmp.aPosPix,
351 0 : aTmpMask );
352 : }
353 0 : break;
354 : }
355 :
356 : case DISPOSE_BACK:
357 : {
358 : // #i70772# react on no mask
359 0 : const Bitmap aMask(rAnimBmp.aBmpEx.GetMask());
360 0 : const Bitmap aContent(rAnimBmp.aBmpEx.GetBitmap());
361 :
362 0 : aVDevMask.Erase();
363 0 : aVDev.DrawBitmap(rAnimBmp.aPosPix, aContent);
364 :
365 0 : if(aMask.IsEmpty())
366 : {
367 0 : const Rectangle aRect(rAnimBmp.aPosPix, aContent.GetSizePixel());
368 0 : aVDevMask.SetFillColor(COL_BLACK);
369 0 : aVDevMask.SetLineColor();
370 0 : aVDevMask.DrawRect(aRect);
371 : }
372 : else
373 : {
374 0 : aVDevMask.DrawBitmap(rAnimBmp.aPosPix, aMask);
375 : }
376 0 : break;
377 : }
378 :
379 : case DISPOSE_FULL:
380 : {
381 : aVDev.DrawBitmapEx(rAnimBmp.aPosPix,
382 0 : rAnimBmp.aBmpEx);
383 0 : break;
384 : }
385 :
386 : case DISPOSE_PREVIOUS :
387 : {
388 : aVDev.DrawBitmapEx(rAnimBmp.aPosPix,
389 0 : rAnimBmp.aBmpEx);
390 : aVDevMask.DrawBitmap(rAnimBmp.aPosPix,
391 0 : rAnimBmp.aBmpEx.GetMask());
392 0 : break;
393 : }
394 : }
395 :
396 : // extract current aVDev content into a new animation
397 : // frame
398 0 : GDIMetaFileSharedPtr pMtf( new GDIMetaFile() );
399 : pMtf->AddAction(
400 : new MetaBmpExAction( aEmptyPoint,
401 : BitmapEx(
402 : aVDev.GetBitmap(
403 : aEmptyPoint,
404 : aAnimSize ),
405 : aVDevMask.GetBitmap(
406 : aEmptyPoint,
407 0 : aAnimSize ))));
408 :
409 : // setup mtf dimensions and pref map mode (for
410 : // simplicity, keep it all in pixel. the metafile
411 : // renderer scales it down to (1, 1) box anyway)
412 0 : pMtf->SetPrefMapMode( MapMode() );
413 0 : pMtf->SetPrefSize( aAnimSize );
414 :
415 : // Take care of special value for MultiPage TIFFs. ATM these shall just
416 : // show their first page for _quite_ some time.
417 0 : sal_Int32 nWaitTime100thSeconds( rAnimBmp.nWait );
418 0 : if( ANIMATION_TIMEOUT_ON_CLICK == nWaitTime100thSeconds )
419 : {
420 : // ATM the huge value would block the timer, so use a long
421 : // time to show first page (whole day)
422 0 : nWaitTime100thSeconds = 100 * 60 * 60 * 24;
423 : }
424 :
425 : // There are animated GIFs with no WaitTime set. Take 0.1 sec, the
426 : // same duration that is used by the edit view.
427 0 : if( nWaitTime100thSeconds == 0 )
428 0 : nWaitTime100thSeconds = 10;
429 :
430 : o_rFrames.push_back( MtfAnimationFrame( pMtf,
431 0 : nWaitTime100thSeconds / 100.0 ) );
432 0 : }
433 :
434 0 : return !o_rFrames.empty();
435 : }
436 :
437 0 : bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle& o_rScrollRect,
438 : ::basegfx::B2DRectangle& o_rPaintRect,
439 : const GDIMetaFileSharedPtr& rMtf )
440 : {
441 : // extract bounds: scroll rect, paint rect
442 0 : bool bScrollRectSet(false);
443 0 : bool bPaintRectSet(false);
444 :
445 0 : for ( MetaAction * pCurrAct = rMtf->FirstAction();
446 0 : pCurrAct != 0; pCurrAct = rMtf->NextAction() )
447 : {
448 0 : if (pCurrAct->GetType() == META_COMMENT_ACTION)
449 : {
450 : MetaCommentAction * pAct =
451 0 : static_cast<MetaCommentAction *>(pCurrAct);
452 : // skip comment if not a special XTEXT... comment
453 0 : if( pAct->GetComment().matchIgnoreAsciiCase( OString("XTEXT"), 0 ) )
454 : {
455 0 : if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_SCROLLRECT"))
456 : {
457 : o_rScrollRect = ::vcl::unotools::b2DRectangleFromRectangle(
458 : *reinterpret_cast<Rectangle const *>(
459 0 : pAct->GetData() ) );
460 :
461 0 : bScrollRectSet = true;
462 : }
463 0 : else if (pAct->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTRECT") )
464 : {
465 : o_rPaintRect = ::vcl::unotools::b2DRectangleFromRectangle(
466 : *reinterpret_cast<Rectangle const *>(
467 0 : pAct->GetData() ) );
468 :
469 0 : bPaintRectSet = true;
470 : }
471 : }
472 : }
473 : }
474 :
475 0 : return bScrollRectSet && bPaintRectSet;
476 : }
477 :
478 : }
479 : }
480 :
481 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|