Branch data 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: */
|