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 :
32 : #include <cppuhelper/basemutex.hxx>
33 : #include <cppuhelper/compbase1.hxx>
34 :
35 : #include <comphelper/uno3.hxx>
36 : #include <cppuhelper/implbase1.hxx>
37 :
38 : #include <tools/stream.hxx>
39 : #include <vcl/svapp.hxx>
40 : #include <vcl/canvastools.hxx>
41 : #include <vcl/metaact.hxx>
42 : #include <vcl/virdev.hxx>
43 : #include <vcl/gdimtf.hxx>
44 : #include <vcl/metaact.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)
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 : (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 : // -> stuff that into UnoGraphicExporter.
192 : uno::Reference<lang::XMultiComponentFactory> xFactory(
193 0 : rxContext->getServiceManager() );
194 :
195 : OSL_ENSURE( xFactory.is(), "### no UNO?!" );
196 0 : if( !xFactory.is() )
197 0 : return false;
198 :
199 : // creating the graphic exporter
200 : uno::Reference< document::XExporter > xExporter(
201 0 : xFactory->createInstanceWithContext(
202 : OUSTR("com.sun.star.drawing.GraphicExportFilter"),
203 0 : rxContext),
204 0 : uno::UNO_QUERY );
205 0 : uno::Reference< document::XFilter > xFilter( xExporter, uno::UNO_QUERY );
206 :
207 : OSL_ENSURE( xExporter.is() && xFilter.is(), "### no graphic exporter?!" );
208 0 : if( !xExporter.is() || !xFilter.is() )
209 0 : return false;
210 :
211 0 : uno::Sequence< beans::PropertyValue > aProps(3);
212 0 : aProps[0].Name = OUSTR("FilterName");
213 0 : aProps[0].Value <<= OUSTR("SVM");
214 :
215 0 : aProps[1].Name = OUSTR("GraphicRenderer");
216 0 : aProps[1].Value <<= xRenderer;
217 :
218 0 : uno::Sequence< beans::PropertyValue > aFilterData(4);
219 0 : aFilterData[0].Name = OUSTR("ScrollText");
220 0 : aFilterData[0].Value <<= ((mtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != 0);
221 :
222 0 : aFilterData[1].Name = OUSTR("ExportOnlyBackground");
223 0 : aFilterData[1].Value <<= ((mtfLoadFlags & MTF_LOAD_BACKGROUND_ONLY) != 0);
224 :
225 0 : aFilterData[2].Name = OUSTR("Version");
226 0 : aFilterData[2].Value <<= static_cast<sal_Int32>( SOFFICE_FILEFORMAT_50 );
227 :
228 0 : aFilterData[3].Name = OUSTR("CurrentPage");
229 0 : aFilterData[3].Value <<= uno::Reference< uno::XInterface >( xContainingPage,
230 0 : uno::UNO_QUERY_THROW );
231 :
232 0 : aProps[2].Name = OUSTR("FilterData");
233 0 : aProps[2].Value <<= aFilterData;
234 :
235 0 : xExporter->setSourceDocument( xSource );
236 0 : if( !xFilter->filter( aProps ) )
237 0 : return false;
238 :
239 0 : rMtf = pRenderer->getMtf( (mtfLoadFlags & MTF_LOAD_FOREIGN_SOURCE) != 0 );
240 :
241 : // pRenderer is automatically destroyed when xRenderer
242 : // goes out of scope
243 :
244 : // TODO(E3): Error handling. Exporter might have
245 : // generated nothing, a bitmap, threw an exception,
246 : // whatever.
247 0 : return true;
248 : }
249 :
250 0 : sal_Int32 getNextActionOffset( MetaAction * pCurrAct )
251 : {
252 : // Special handling for actions that represent
253 : // more than one indexable action
254 : // ===========================================
255 :
256 0 : switch (pCurrAct->GetType()) {
257 : case META_TEXT_ACTION: {
258 0 : MetaTextAction * pAct = static_cast<MetaTextAction *>(pCurrAct);
259 0 : return (pAct->GetLen() == (sal_uInt16)STRING_LEN
260 0 : ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen());
261 : }
262 : case META_TEXTARRAY_ACTION: {
263 : MetaTextArrayAction * pAct =
264 0 : static_cast<MetaTextArrayAction *>(pCurrAct);
265 0 : return (pAct->GetLen() == (sal_uInt16)STRING_LEN
266 0 : ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen());
267 : }
268 : case META_STRETCHTEXT_ACTION: {
269 : MetaStretchTextAction * pAct =
270 0 : static_cast<MetaStretchTextAction *>(pCurrAct);
271 0 : return (pAct->GetLen() == (sal_uInt16)STRING_LEN
272 0 : ? pAct->GetText().getLength() - pAct->GetIndex() : pAct->GetLen());
273 : }
274 : case META_FLOATTRANSPARENT_ACTION: {
275 : MetaFloatTransparentAction * pAct =
276 0 : static_cast<MetaFloatTransparentAction*>(pCurrAct);
277 : // TODO(F2): Recurse into action metafile
278 : // (though this is currently not used from the
279 : // DrawingLayer - shape transparency gradients
280 : // don't affect shape text)
281 0 : return pAct->GetGDIMetaFile().GetActionSize();
282 : }
283 : default:
284 0 : return 1;
285 : }
286 : }
287 :
288 0 : bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames,
289 : ::std::size_t& o_rLoopCount,
290 : CycleMode& o_eCycleMode,
291 : const Graphic& rGraphic )
292 : {
293 0 : o_rFrames.clear();
294 :
295 0 : if( !rGraphic.IsAnimated() )
296 0 : return false;
297 :
298 : // some loop invariants
299 0 : Animation aAnimation( rGraphic.GetAnimation() );
300 0 : const Point aEmptyPoint;
301 0 : const Size aAnimSize( aAnimation.GetDisplaySizePixel() );
302 :
303 : // setup VDev, into which all bitmaps are painted (want to
304 : // normalize animations to n bitmaps of same size. An Animation,
305 : // though, can contain bitmaps of varying sizes and different
306 : // update modes)
307 0 : VirtualDevice aVDev;
308 0 : aVDev.SetOutputSizePixel( aAnimSize );
309 0 : aVDev.EnableMapMode( sal_False );
310 :
311 : // setup mask VDev (alpha VDev is currently rather slow)
312 0 : VirtualDevice aVDevMask;
313 0 : aVDevMask.SetOutputSizePixel( aAnimSize );
314 0 : aVDevMask.EnableMapMode( sal_False );
315 :
316 0 : switch( aAnimation.GetCycleMode() )
317 : {
318 : case CYCLE_NOT:
319 0 : o_rLoopCount = 1;
320 0 : o_eCycleMode = CYCLE_LOOP;
321 0 : break;
322 :
323 : case CYCLE_FALLBACK:
324 : // FALLTHROUGH intended
325 : case CYCLE_NORMAL:
326 0 : o_rLoopCount = aAnimation.GetLoopCount();
327 0 : o_eCycleMode = CYCLE_LOOP;
328 0 : break;
329 :
330 : case CYCLE_REVERS:
331 : // FALLTHROUGH intended
332 : case CYCLE_REVERS_FALLBACK:
333 0 : o_rLoopCount = aAnimation.GetLoopCount();
334 0 : o_eCycleMode = CYCLE_PINGPONGLOOP;
335 0 : break;
336 :
337 : default:
338 0 : ENSURE_OR_RETURN_FALSE(false,
339 : "getAnimationFromGraphic(): Unexpected case" );
340 : break;
341 : }
342 :
343 0 : for( sal_uInt16 i=0, nCount=aAnimation.Count(); i<nCount; ++i )
344 : {
345 0 : const AnimationBitmap& rAnimBmp( aAnimation.Get(i) );
346 0 : switch(rAnimBmp.eDisposal)
347 : {
348 : case DISPOSE_NOT:
349 : {
350 : aVDev.DrawBitmapEx(rAnimBmp.aPosPix,
351 0 : rAnimBmp.aBmpEx);
352 0 : Bitmap aMask = rAnimBmp.aBmpEx.GetMask();
353 :
354 0 : if( aMask.IsEmpty() )
355 : {
356 0 : const Point aEmpty;
357 : const Rectangle aRect(aEmptyPoint,
358 0 : aVDevMask.GetOutputSizePixel());
359 0 : const Wallpaper aWallpaper(COL_BLACK);
360 : aVDevMask.DrawWallpaper(aRect,
361 0 : aWallpaper);
362 : }
363 : else
364 : {
365 : BitmapEx aTmpMask = BitmapEx(aMask,
366 0 : aMask);
367 : aVDevMask.DrawBitmapEx(rAnimBmp.aPosPix,
368 0 : aTmpMask );
369 : }
370 0 : break;
371 : }
372 :
373 : case DISPOSE_BACK:
374 : {
375 : // #i70772# react on no mask
376 0 : const Bitmap aMask(rAnimBmp.aBmpEx.GetMask());
377 0 : const Bitmap aContent(rAnimBmp.aBmpEx.GetBitmap());
378 :
379 0 : aVDevMask.Erase();
380 0 : aVDev.DrawBitmap(rAnimBmp.aPosPix, aContent);
381 :
382 0 : if(aMask.IsEmpty())
383 : {
384 0 : const Rectangle aRect(rAnimBmp.aPosPix, aContent.GetSizePixel());
385 0 : aVDevMask.SetFillColor(COL_BLACK);
386 0 : aVDevMask.SetLineColor();
387 0 : aVDevMask.DrawRect(aRect);
388 : }
389 : else
390 : {
391 0 : aVDevMask.DrawBitmap(rAnimBmp.aPosPix, aMask);
392 : }
393 0 : break;
394 : }
395 :
396 : case DISPOSE_FULL:
397 : {
398 : aVDev.DrawBitmapEx(rAnimBmp.aPosPix,
399 0 : rAnimBmp.aBmpEx);
400 0 : break;
401 : }
402 :
403 : case DISPOSE_PREVIOUS :
404 : {
405 : aVDev.DrawBitmapEx(rAnimBmp.aPosPix,
406 0 : rAnimBmp.aBmpEx);
407 : aVDevMask.DrawBitmap(rAnimBmp.aPosPix,
408 0 : rAnimBmp.aBmpEx.GetMask());
409 0 : break;
410 : }
411 : }
412 :
413 : // extract current aVDev content into a new animation
414 : // frame
415 0 : GDIMetaFileSharedPtr pMtf( new GDIMetaFile() );
416 : pMtf->AddAction(
417 : new MetaBmpExAction( aEmptyPoint,
418 : BitmapEx(
419 : aVDev.GetBitmap(
420 : aEmptyPoint,
421 : aAnimSize ),
422 : aVDevMask.GetBitmap(
423 : aEmptyPoint,
424 0 : aAnimSize ))));
425 :
426 : // setup mtf dimensions and pref map mode (for
427 : // simplicity, keep it all in pixel. the metafile
428 : // renderer scales it down to (1, 1) box anyway)
429 0 : pMtf->SetPrefMapMode( MapMode() );
430 0 : pMtf->SetPrefSize( aAnimSize );
431 :
432 : // Take care of special value for MultiPage TIFFs. ATM these shall just
433 : // show their first page for _quite_ some time.
434 0 : sal_Int32 nWaitTime100thSeconds( rAnimBmp.nWait );
435 0 : if( ANIMATION_TIMEOUT_ON_CLICK == nWaitTime100thSeconds )
436 : {
437 : // ATM the huge value would block the timer, so use a long
438 : // time to show first page (whole day)
439 0 : nWaitTime100thSeconds = 100 * 60 * 60 * 24;
440 : }
441 :
442 : // There are animated GIFs with no WaitTime set. Take 0.1 sec, the
443 : // same duration that is used by the edit view.
444 0 : if( nWaitTime100thSeconds == 0 )
445 0 : nWaitTime100thSeconds = 10;
446 :
447 : o_rFrames.push_back( MtfAnimationFrame( pMtf,
448 0 : nWaitTime100thSeconds / 100.0 ) );
449 0 : }
450 :
451 0 : return !o_rFrames.empty();
452 : }
453 :
454 0 : bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle& o_rScrollRect,
455 : ::basegfx::B2DRectangle& o_rPaintRect,
456 : const GDIMetaFileSharedPtr& rMtf )
457 : {
458 : // extract bounds: scroll rect, paint rect
459 0 : bool bScrollRectSet(false);
460 0 : bool bPaintRectSet(false);
461 :
462 0 : for ( MetaAction * pCurrAct = rMtf->FirstAction();
463 0 : pCurrAct != 0; pCurrAct = rMtf->NextAction() )
464 : {
465 0 : if (pCurrAct->GetType() == META_COMMENT_ACTION)
466 : {
467 : MetaCommentAction * pAct =
468 0 : static_cast<MetaCommentAction *>(pCurrAct);
469 : // skip comment if not a special XTEXT... comment
470 0 : if( pAct->GetComment().matchIgnoreAsciiCase(
471 : rtl::OString(RTL_CONSTASCII_STRINGPARAM("XTEXT")),
472 0 : 0) )
473 : {
474 0 : if (pAct->GetComment().equalsIgnoreAsciiCaseL(
475 0 : RTL_CONSTASCII_STRINGPARAM("XTEXT_SCROLLRECT") ))
476 : {
477 : o_rScrollRect = ::vcl::unotools::b2DRectangleFromRectangle(
478 : *reinterpret_cast<Rectangle const *>(
479 0 : pAct->GetData() ) );
480 :
481 0 : bScrollRectSet = true;
482 : }
483 0 : else if (pAct->GetComment().equalsIgnoreAsciiCaseL(
484 0 : RTL_CONSTASCII_STRINGPARAM("XTEXT_PAINTRECT")) )
485 : {
486 : o_rPaintRect = ::vcl::unotools::b2DRectangleFromRectangle(
487 : *reinterpret_cast<Rectangle const *>(
488 0 : pAct->GetData() ) );
489 :
490 0 : bPaintRectSet = true;
491 : }
492 : }
493 : }
494 : }
495 :
496 0 : return bScrollRectSet && bPaintRectSet;
497 : }
498 :
499 : }
500 : }
501 :
502 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|