1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <comphelper/fileformat.h>
24 #include <o3tl/make_shared.hxx>
25 #include <tools/fract.hxx>
26 #include <tools/vcompat.hxx>
27 #include <tools/urlobj.hxx>
28 #include <tools/stream.hxx>
29 #include <unotools/ucbhelper.hxx>
30 #include <unotools/ucbstreamhelper.hxx>
31 #include <unotools/tempfile.hxx>
33 #include <vcl/filter/SvmReader.hxx>
34 #include <vcl/filter/SvmWriter.hxx>
35 #include <vcl/outdev.hxx>
36 #include <vcl/graphicfilter.hxx>
37 #include <vcl/virdev.hxx>
38 #include <vcl/gfxlink.hxx>
39 #include <vcl/cvtgrf.hxx>
40 #include <vcl/graph.hxx>
41 #include <vcl/metaact.hxx>
42 #include <impgraph.hxx>
43 #include <com/sun/star/graphic/XPrimitive2D.hpp>
44 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
45 #include <vcl/dibtools.hxx>
48 #include <vcl/gdimetafiletools.hxx>
49 #include <vcl/TypeSerializer.hxx>
50 #include <vcl/pdfread.hxx>
51 #include <graphic/VectorGraphicLoader.hxx>
53 #define GRAPHIC_MTFTOBMP_MAXEXT 2048
54 #define GRAPHIC_STREAMBUFSIZE 8192UL
56 #define SWAP_FORMAT_ID COMPAT_FORMAT( 'S', 'W', 'A', 'P' )
58 using namespace com::sun::star
;
64 utl::TempFileFast maTempFile
;
68 ImpSwapFile(OUString aOriginURL
)
69 : maOriginURL(std::move(aOriginURL
))
73 SvStream
* getStream() { return maTempFile
.GetStream(StreamMode::READWRITE
); }
74 OUString
const & getOriginURL() const { return maOriginURL
; }
77 SvStream
* ImpGraphic::getSwapFileStream() const
80 return mpSwapFile
->getStream();
84 ImpGraphic::ImpGraphic(bool bDefault
)
85 : MemoryManaged(false)
86 , meType(bDefault
? GraphicType::Default
: GraphicType::NONE
)
90 ImpGraphic::ImpGraphic(const ImpGraphic
& rImpGraphic
)
91 : MemoryManaged(rImpGraphic
)
92 , maMetaFile(rImpGraphic
.maMetaFile
)
93 , maBitmapEx(rImpGraphic
.maBitmapEx
)
94 , maSwapInfo(rImpGraphic
.maSwapInfo
)
95 , mpSwapFile(rImpGraphic
.mpSwapFile
)
96 , mpGfxLink(rImpGraphic
.mpGfxLink
)
97 , maVectorGraphicData(rImpGraphic
.maVectorGraphicData
)
98 , meType(rImpGraphic
.meType
)
99 , mnSizeBytes(rImpGraphic
.mnSizeBytes
)
100 , mbSwapOut(rImpGraphic
.mbSwapOut
)
101 , mbDummyContext(rImpGraphic
.mbDummyContext
)
102 , maGraphicExternalLink(rImpGraphic
.maGraphicExternalLink
)
103 , mbPrepared(rImpGraphic
.mbPrepared
)
105 updateCurrentSizeInBytes(mnSizeBytes
);
107 // Special case for animations
108 if (rImpGraphic
.mpAnimation
)
110 mpAnimation
= std::make_unique
<Animation
>(*rImpGraphic
.mpAnimation
);
111 maBitmapEx
= mpAnimation
->GetBitmapEx();
115 ImpGraphic::ImpGraphic(ImpGraphic
&& rImpGraphic
) noexcept
116 : MemoryManaged(rImpGraphic
)
117 , maMetaFile(std::move(rImpGraphic
.maMetaFile
))
118 , maBitmapEx(std::move(rImpGraphic
.maBitmapEx
))
119 , maSwapInfo(std::move(rImpGraphic
.maSwapInfo
))
120 , mpAnimation(std::move(rImpGraphic
.mpAnimation
))
121 , mpSwapFile(std::move(rImpGraphic
.mpSwapFile
))
122 , mpGfxLink(std::move(rImpGraphic
.mpGfxLink
))
123 , maVectorGraphicData(std::move(rImpGraphic
.maVectorGraphicData
))
124 , meType(rImpGraphic
.meType
)
125 , mnSizeBytes(rImpGraphic
.mnSizeBytes
)
126 , mbSwapOut(rImpGraphic
.mbSwapOut
)
127 , mbDummyContext(rImpGraphic
.mbDummyContext
)
128 , maGraphicExternalLink(rImpGraphic
.maGraphicExternalLink
)
129 , mbPrepared (rImpGraphic
.mbPrepared
)
131 updateCurrentSizeInBytes(mnSizeBytes
);
134 rImpGraphic
.mbDummyContext
= false;
137 ImpGraphic::ImpGraphic(std::shared_ptr
<GfxLink
> xGfxLink
, sal_Int32 nPageIndex
)
138 : MemoryManaged(true)
139 , mpGfxLink(std::move(xGfxLink
))
140 , meType(GraphicType::Bitmap
)
143 maSwapInfo
.mbIsTransparent
= true;
144 maSwapInfo
.mbIsAlpha
= true;
145 maSwapInfo
.mbIsEPS
= false;
146 maSwapInfo
.mbIsAnimated
= false;
147 maSwapInfo
.mnAnimationLoopCount
= 0;
148 maSwapInfo
.mnPageIndex
= nPageIndex
;
150 ensureCurrentSizeInBytes();
153 ImpGraphic::ImpGraphic(GraphicExternalLink aGraphicExternalLink
)
154 : MemoryManaged(true)
155 , meType(GraphicType::Default
)
156 , maGraphicExternalLink(std::move(aGraphicExternalLink
))
158 ensureCurrentSizeInBytes();
161 ImpGraphic::ImpGraphic(const BitmapEx
& rBitmapEx
)
162 : MemoryManaged(!rBitmapEx
.IsEmpty())
163 , maBitmapEx(rBitmapEx
)
164 , meType(rBitmapEx
.IsEmpty() ? GraphicType::NONE
: GraphicType::Bitmap
)
166 ensureCurrentSizeInBytes();
169 ImpGraphic::ImpGraphic(const std::shared_ptr
<VectorGraphicData
>& rVectorGraphicDataPtr
)
170 : MemoryManaged(bool(rVectorGraphicDataPtr
))
171 , maVectorGraphicData(rVectorGraphicDataPtr
)
172 , meType(rVectorGraphicDataPtr
? GraphicType::Bitmap
: GraphicType::NONE
)
174 ensureCurrentSizeInBytes();
177 ImpGraphic::ImpGraphic(const Animation
& rAnimation
)
178 : MemoryManaged(true)
179 , maBitmapEx(rAnimation
.GetBitmapEx())
180 , mpAnimation(std::make_unique
<Animation
>(rAnimation
))
181 , meType(GraphicType::Bitmap
)
183 ensureCurrentSizeInBytes();
186 ImpGraphic::ImpGraphic(const GDIMetaFile
& rMetafile
)
187 : MemoryManaged(true)
188 , maMetaFile(rMetafile
)
189 , meType(GraphicType::GdiMetafile
)
191 ensureCurrentSizeInBytes();
194 ImpGraphic::~ImpGraphic()
198 ImpGraphic
& ImpGraphic::operator=( const ImpGraphic
& rImpGraphic
)
200 if( &rImpGraphic
!= this )
202 maMetaFile
= rImpGraphic
.maMetaFile
;
203 meType
= rImpGraphic
.meType
;
204 mnSizeBytes
= rImpGraphic
.mnSizeBytes
;
205 updateCurrentSizeInBytes(mnSizeBytes
);
207 maSwapInfo
= rImpGraphic
.maSwapInfo
;
208 mbDummyContext
= rImpGraphic
.mbDummyContext
;
209 maGraphicExternalLink
= rImpGraphic
.maGraphicExternalLink
;
213 if ( rImpGraphic
.mpAnimation
)
215 mpAnimation
= std::make_unique
<Animation
>( *rImpGraphic
.mpAnimation
);
216 maBitmapEx
= mpAnimation
->GetBitmapEx();
220 maBitmapEx
= rImpGraphic
.maBitmapEx
;
223 mbSwapOut
= rImpGraphic
.mbSwapOut
;
224 mpSwapFile
= rImpGraphic
.mpSwapFile
;
225 mbPrepared
= rImpGraphic
.mbPrepared
;
227 mpGfxLink
= rImpGraphic
.mpGfxLink
;
229 maVectorGraphicData
= rImpGraphic
.maVectorGraphicData
;
232 changeExisting(mnSizeBytes
);
238 ImpGraphic
& ImpGraphic::operator=(ImpGraphic
&& rImpGraphic
)
240 maMetaFile
= std::move(rImpGraphic
.maMetaFile
);
241 meType
= rImpGraphic
.meType
;
242 mnSizeBytes
= rImpGraphic
.mnSizeBytes
;
243 maSwapInfo
= std::move(rImpGraphic
.maSwapInfo
);
244 mbDummyContext
= rImpGraphic
.mbDummyContext
;
245 mpAnimation
= std::move(rImpGraphic
.mpAnimation
);
246 maBitmapEx
= std::move(rImpGraphic
.maBitmapEx
);
247 mbSwapOut
= rImpGraphic
.mbSwapOut
;
248 mpSwapFile
= std::move(rImpGraphic
.mpSwapFile
);
249 mpGfxLink
= std::move(rImpGraphic
.mpGfxLink
);
250 maVectorGraphicData
= std::move(rImpGraphic
.maVectorGraphicData
);
251 maGraphicExternalLink
= rImpGraphic
.maGraphicExternalLink
;
252 mbPrepared
= rImpGraphic
.mbPrepared
;
255 rImpGraphic
.mbDummyContext
= false;
258 changeExisting(mnSizeBytes
);
263 bool ImpGraphic::operator==( const ImpGraphic
& rOther
) const
265 if( this == &rOther
)
268 if (mbPrepared
&& rOther
.mbPrepared
)
269 return (*mpGfxLink
== *rOther
.mpGfxLink
);
271 if (!isAvailable() || !rOther
.isAvailable())
274 if ( meType
!= rOther
.meType
)
280 case GraphicType::NONE
:
281 case GraphicType::Default
:
284 case GraphicType::GdiMetafile
:
285 return ( rOther
.maMetaFile
== maMetaFile
);
287 case GraphicType::Bitmap
:
289 if(maVectorGraphicData
)
291 if(maVectorGraphicData
== rOther
.maVectorGraphicData
)
296 else if(rOther
.maVectorGraphicData
)
299 bRet
= (*maVectorGraphicData
) == (*rOther
.maVectorGraphicData
);
302 else if( mpAnimation
)
304 if( rOther
.mpAnimation
&& ( *rOther
.mpAnimation
== *mpAnimation
) )
307 else if( !rOther
.mpAnimation
&& ( rOther
.maBitmapEx
== maBitmapEx
) )
318 const std::shared_ptr
<VectorGraphicData
>& ImpGraphic::getVectorGraphicData() const
322 return maVectorGraphicData
;
325 void ImpGraphic::createSwapInfo()
330 if (!maBitmapEx
.IsEmpty())
331 maSwapInfo
.maSizePixel
= maBitmapEx
.GetSizePixel();
333 maSwapInfo
.maSizePixel
= Size();
335 maSwapInfo
.maPrefMapMode
= getPrefMapMode();
336 maSwapInfo
.maPrefSize
= getPrefSize();
337 maSwapInfo
.mbIsAnimated
= isAnimated();
338 maSwapInfo
.mbIsEPS
= isEPS();
339 maSwapInfo
.mbIsTransparent
= isTransparent();
340 maSwapInfo
.mbIsAlpha
= isAlpha();
341 maSwapInfo
.mnAnimationLoopCount
= getAnimationLoopCount();
342 maSwapInfo
.mnPageIndex
= getPageNumber();
345 void ImpGraphic::clearGraphics()
350 maVectorGraphicData
.reset();
353 void ImpGraphic::setPrepared(bool bAnimated
, const Size
* pSizeHint
)
357 meType
= GraphicType::Bitmap
;
359 SvMemoryStream
aMemoryStream(const_cast<sal_uInt8
*>(mpGfxLink
->GetData()), mpGfxLink
->GetDataSize(), StreamMode::READ
| StreamMode::WRITE
);
363 maSwapInfo
.maPrefSize
= *pSizeHint
;
364 maSwapInfo
.maPrefMapMode
= MapMode(MapUnit::Map100thMM
);
367 GraphicDescriptor
aDescriptor(aMemoryStream
, nullptr);
368 if (aDescriptor
.Detect(true))
372 // If we have logic size, work with that, as later pixel -> logic
373 // conversion will work with the output device DPI, not the graphic
375 Size aLogSize
= aDescriptor
.GetSize_100TH_MM();
376 if (aDescriptor
.GetPreferredLogSize() && aDescriptor
.GetPreferredMapMode())
378 maSwapInfo
.maPrefSize
= *aDescriptor
.GetPreferredLogSize();
379 maSwapInfo
.maPrefMapMode
= *aDescriptor
.GetPreferredMapMode();
381 else if (aLogSize
.getWidth() && aLogSize
.getHeight())
383 maSwapInfo
.maPrefSize
= aLogSize
;
384 maSwapInfo
.maPrefMapMode
= MapMode(MapUnit::Map100thMM
);
388 maSwapInfo
.maPrefSize
= aDescriptor
.GetSizePixel();
389 maSwapInfo
.maPrefMapMode
= MapMode(MapUnit::MapPixel
);
393 maSwapInfo
.maSizePixel
= aDescriptor
.GetSizePixel();
394 maSwapInfo
.mbIsTransparent
= aDescriptor
.IsTransparent();
395 maSwapInfo
.mbIsAlpha
= aDescriptor
.IsAlpha();
397 maSwapInfo
.mbIsTransparent
= false;
398 maSwapInfo
.mbIsAlpha
= false;
401 maSwapInfo
.mnAnimationLoopCount
= 0;
402 maSwapInfo
.mbIsEPS
= false;
403 maSwapInfo
.mbIsAnimated
= bAnimated
;
405 if (maVectorGraphicData
)
406 maSwapInfo
.mnPageIndex
= maVectorGraphicData
->getPageIndex();
409 void ImpGraphic::clear()
417 meType
= GraphicType::NONE
;
420 changeExisting(mnSizeBytes
);
421 maGraphicExternalLink
.msURL
.clear();
424 bool ImpGraphic::isSupportedGraphic() const
426 return meType
!= GraphicType::NONE
;
429 bool ImpGraphic::isTransparent() const
435 bRet
= maSwapInfo
.mbIsTransparent
;
437 else if (meType
== GraphicType::Bitmap
&& !maVectorGraphicData
)
439 bRet
= mpAnimation
? mpAnimation
->IsTransparent() : maBitmapEx
.IsAlpha();
445 bool ImpGraphic::isAlpha() const
451 bRet
= maSwapInfo
.mbIsAlpha
;
453 else if (maVectorGraphicData
)
457 else if (meType
== GraphicType::Bitmap
)
459 bRet
= (nullptr == mpAnimation
&& maBitmapEx
.IsAlpha());
465 bool ImpGraphic::isAnimated() const
467 return mbSwapOut
? maSwapInfo
.mbIsAnimated
: mpAnimation
!= nullptr;
470 bool ImpGraphic::isEPS() const
473 return maSwapInfo
.mbIsEPS
;
475 return( ( meType
== GraphicType::GdiMetafile
) &&
476 ( maMetaFile
.GetActionSize() > 0 ) &&
477 ( maMetaFile
.GetAction( 0 )->GetType() == MetaActionType::EPS
) );
480 bool ImpGraphic::isAvailable() const
482 return !mbPrepared
&& !mbSwapOut
;
485 bool ImpGraphic::makeAvailable()
487 return ensureAvailable();
490 void ImpGraphic::updateBitmapFromVectorGraphic(const Size
& pixelSize
) const
492 assert (maVectorGraphicData
);
494 // use maBitmapEx as local buffer for rendered vector image
495 if (pixelSize
.Width() && pixelSize
.Height())
497 if (maBitmapEx
.IsEmpty() || maBitmapEx
.GetSizePixel() != pixelSize
)
498 const_cast<ImpGraphic
*>(this)->maBitmapEx
= maVectorGraphicData
->getBitmap(pixelSize
);
500 else // maVectorGraphicData caches the replacement, so updating unconditionally is cheap
502 const_cast<ImpGraphic
*>(this)->maBitmapEx
= maVectorGraphicData
->getReplacement();
505 if (maExPrefSize
.getWidth() && maExPrefSize
.getHeight())
506 const_cast<ImpGraphic
*>(this)->maBitmapEx
.SetPrefSize(maExPrefSize
);
509 Bitmap
ImpGraphic::getBitmap(const GraphicConversionParameters
& rParameters
) const
515 if( meType
== GraphicType::Bitmap
)
517 if (!mpAnimation
&& maVectorGraphicData
)
518 updateBitmapFromVectorGraphic(rParameters
.getSizePixel());
520 const BitmapEx
& rRetBmpEx
= ( mpAnimation
? mpAnimation
->GetBitmapEx() : maBitmapEx
);
522 aRetBmp
= rRetBmpEx
.GetBitmap( COL_WHITE
);
524 if(rParameters
.getSizePixel().Width() || rParameters
.getSizePixel().Height())
525 aRetBmp
.Scale(rParameters
.getSizePixel());
527 else if( ( meType
!= GraphicType::Default
) && isSupportedGraphic() )
529 if(maBitmapEx
.IsEmpty())
532 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
533 Size
aDrawSize(aVDev
->LogicToPixel(maMetaFile
.GetPrefSize(), maMetaFile
.GetPrefMapMode()));
535 if(rParameters
.getSizePixel().Width() && rParameters
.getSizePixel().Height())
537 // apply given size if exists
538 aDrawSize
= rParameters
.getSizePixel();
541 if(aDrawSize
.Width() && aDrawSize
.Height() && !rParameters
.getUnlimitedSize()
542 && (aDrawSize
.Width() > GRAPHIC_MTFTOBMP_MAXEXT
|| aDrawSize
.Height() > GRAPHIC_MTFTOBMP_MAXEXT
))
544 // limit bitmap size to a maximum of GRAPHIC_MTFTOBMP_MAXEXT x GRAPHIC_MTFTOBMP_MAXEXT
545 double fWH(static_cast<double>(aDrawSize
.Width()) / static_cast<double>(aDrawSize
.Height()));
549 aDrawSize
.setWidth(basegfx::fround
<tools::Long
>(GRAPHIC_MTFTOBMP_MAXEXT
* fWH
));
550 aDrawSize
.setHeight(GRAPHIC_MTFTOBMP_MAXEXT
);
554 aDrawSize
.setWidth(GRAPHIC_MTFTOBMP_MAXEXT
);
555 aDrawSize
.setHeight(basegfx::fround
<tools::Long
>(GRAPHIC_MTFTOBMP_MAXEXT
/ fWH
));
559 // calculate pixel size. Normally, it's the same as aDrawSize, but may
560 // need to be extended when hairlines are on the right or bottom edge
561 Size
aPixelSize(aDrawSize
);
563 if(GraphicType::GdiMetafile
== getType())
565 // tdf#126319 Removed correction based on hairline-at-the-extremes of
566 // the metafile. The task shows that this is no longer sufficient since
567 // less hairlines get used in general - what is good, but breaks that
568 // old fix. Anyways, hairlines are a left-over from non-AA times
569 // when it was not possible to paint lines taller than one pixel.
570 // This might need to be corrected further using primitives and
571 // the possibility to get better-quality ranges for correction. For
572 // now, always add that one pixel.
573 aPixelSize
.setWidth(aPixelSize
.getWidth() + 1);
574 aPixelSize
.setHeight(aPixelSize
.getHeight() + 1);
577 if(aVDev
->SetOutputSizePixel(aPixelSize
))
579 if(rParameters
.getAntiAliase())
581 aVDev
->SetAntialiasing(aVDev
->GetAntialiasing() | AntialiasingFlags::Enable
);
584 if(rParameters
.getSnapHorVerLines())
586 aVDev
->SetAntialiasing(aVDev
->GetAntialiasing() | AntialiasingFlags::PixelSnapHairline
);
589 draw(*aVDev
, Point(), aDrawSize
);
591 // use maBitmapEx as local buffer for rendered metafile
592 const_cast< ImpGraphic
* >(this)->maBitmapEx
= aVDev
->GetBitmapEx( Point(), aVDev
->GetOutputSizePixel() );
596 aRetBmp
= maBitmapEx
.GetBitmap();
599 if( !aRetBmp
.IsEmpty() )
601 aRetBmp
.SetPrefMapMode(getPrefMapMode());
602 aRetBmp
.SetPrefSize(getPrefSize());
608 BitmapEx
ImpGraphic::getBitmapEx(const GraphicConversionParameters
& rParameters
) const
614 if( meType
== GraphicType::Bitmap
)
616 if (!mpAnimation
&& maVectorGraphicData
)
617 updateBitmapFromVectorGraphic(rParameters
.getSizePixel());
619 aRetBmpEx
= ( mpAnimation
? mpAnimation
->GetBitmapEx() : maBitmapEx
);
621 if(rParameters
.getSizePixel().Width() || rParameters
.getSizePixel().Height())
624 rParameters
.getSizePixel(),
628 else if( ( meType
!= GraphicType::Default
) && isSupportedGraphic() )
630 if(maBitmapEx
.IsEmpty())
632 const ImpGraphic
aMonoMask( maMetaFile
.GetMonochromeMtf( COL_BLACK
) );
634 // use maBitmapEx as local buffer for rendered metafile
635 const_cast< ImpGraphic
* >(this)->maBitmapEx
= BitmapEx(getBitmap(rParameters
), aMonoMask
.getBitmap(rParameters
));
638 aRetBmpEx
= maBitmapEx
;
644 Animation
ImpGraphic::getAnimation() const
646 Animation aAnimation
;
650 aAnimation
= *mpAnimation
;
655 const BitmapEx
& ImpGraphic::getBitmapExRef() const
661 const GDIMetaFile
& ImpGraphic::getGDIMetaFile() const
664 if (!maMetaFile
.GetActionSize()
665 && maVectorGraphicData
666 && (VectorGraphicDataType::Emf
== maVectorGraphicData
->getType()
667 || VectorGraphicDataType::Wmf
== maVectorGraphicData
->getType()))
669 // If we have a Emf/Wmf VectorGraphic object, we
670 // need a way to get the Metafile data out of the primitive
671 // representation. Use a strict virtual hook (MetafileAccessor)
672 // to access the MetafilePrimitive2D directly. Also see comments in
673 // XEmfParser about this.
674 const std::deque
< css::uno::Reference
< css::graphic::XPrimitive2D
> > aSequence(maVectorGraphicData
->getPrimitive2DSequence());
676 if (1 == aSequence
.size())
678 // try to cast to MetafileAccessor implementation
679 const css::uno::Reference
< css::graphic::XPrimitive2D
>& xReference(aSequence
[0]);
680 auto pUnoPrimitive
= static_cast< const drawinglayer::primitive2d::UnoPrimitive2D
* >(xReference
.get());
683 const MetafileAccessor
* pMetafileAccessor
= dynamic_cast< const MetafileAccessor
* >(pUnoPrimitive
->getBasePrimitive2D().get());
685 if (pMetafileAccessor
)
687 // it is a MetafileAccessor implementation, get Metafile
688 pMetafileAccessor
->accessMetafile(const_cast< ImpGraphic
* >(this)->maMetaFile
);
694 if (GraphicType::Bitmap
== meType
&& !maMetaFile
.GetActionSize())
696 if (maVectorGraphicData
)
697 updateBitmapFromVectorGraphic();
700 // Use the local maMetaFile as container for a metafile-representation
701 // of the bitmap graphic. This will be done only once, thus be buffered.
702 // I checked all usages of maMetaFile, it is only used when type is not
703 // GraphicType::Bitmap. In operator= it will get copied, thus buffering will
704 // survive copying (change this if not wanted)
705 ImpGraphic
* pThat
= const_cast< ImpGraphic
* >(this);
707 // #123983# directly create a metafile with the same PrefSize and PrefMapMode
708 // the bitmap has, this will be an always correct metafile
709 if(maBitmapEx
.IsAlpha())
711 pThat
->maMetaFile
.AddAction(new MetaBmpExScaleAction(Point(), maBitmapEx
.GetPrefSize(), maBitmapEx
));
715 pThat
->maMetaFile
.AddAction(new MetaBmpScaleAction(Point(), maBitmapEx
.GetPrefSize(), maBitmapEx
.GetBitmap()));
718 pThat
->maMetaFile
.Stop();
719 pThat
->maMetaFile
.WindStart();
720 pThat
->maMetaFile
.SetPrefSize(maBitmapEx
.GetPrefSize());
721 pThat
->maMetaFile
.SetPrefMapMode(maBitmapEx
.GetPrefMapMode());
727 Size
ImpGraphic::getSizePixel() const
732 aSize
= maSwapInfo
.maSizePixel
;
734 aSize
= getBitmapEx(GraphicConversionParameters()).GetSizePixel();
739 Size
ImpGraphic::getPrefSize() const
745 aSize
= maSwapInfo
.maPrefSize
;
751 case GraphicType::Bitmap
:
753 if (maVectorGraphicData
&& maBitmapEx
.IsEmpty())
755 if (!maExPrefSize
.getWidth() || !maExPrefSize
.getHeight())
757 // svg not yet buffered in maBitmapEx, return size derived from range
758 const basegfx::B2DRange
& rRange
= maVectorGraphicData
->getRange();
761 // tdf#157680 scale down estimated size of embedded PDF
762 // For some unknown reason, the embedded PDF sizes
763 // are 20x larger than expected. This only occurs on
764 // macOS so possibly there is some special conversion
765 // from MapUnit::MapPoint to MapUnit::MapTwip elsewhere
767 if (maVectorGraphicData
->getType() == VectorGraphicDataType::Pdf
)
768 aSize
= Size(basegfx::fround(rRange
.getWidth() / 20.0f
), basegfx::fround(rRange
.getHeight() / 20.0f
));
771 aSize
= Size(basegfx::fround
<tools::Long
>(rRange
.getWidth()), basegfx::fround
<tools::Long
>(rRange
.getHeight()));
775 aSize
= maExPrefSize
;
780 aSize
= maBitmapEx
.GetPrefSize();
782 if( !aSize
.Width() || !aSize
.Height() )
784 aSize
= maBitmapEx
.GetSizePixel();
790 case GraphicType::GdiMetafile
:
792 aSize
= maMetaFile
.GetPrefSize();
796 case GraphicType::NONE
:
797 case GraphicType::Default
:
805 void ImpGraphic::setValuesForPrefSize(const Size
& rPrefSize
)
809 case GraphicType::Bitmap
:
811 // used when importing a writer FlyFrame with SVG as graphic, added conversion
812 // to allow setting the PrefSize at the BitmapEx to hold it
813 if (maVectorGraphicData
)
815 maExPrefSize
= rPrefSize
;
818 // #108077# Push through pref size to animation object,
819 // will be lost on copy otherwise
822 const_cast< BitmapEx
& >(mpAnimation
->GetBitmapEx()).SetPrefSize(rPrefSize
);
825 maBitmapEx
.SetPrefSize(rPrefSize
);
829 case GraphicType::GdiMetafile
:
831 if (isSupportedGraphic())
832 maMetaFile
.SetPrefSize(rPrefSize
);
836 case GraphicType::NONE
:
837 case GraphicType::Default
:
842 void ImpGraphic::setPrefSize(const Size
& rPrefSize
)
845 setValuesForPrefSize(rPrefSize
);
848 MapMode
ImpGraphic::getPrefMapMode() const
854 aMapMode
= maSwapInfo
.maPrefMapMode
;
860 case GraphicType::Bitmap
:
862 if (maVectorGraphicData
&& maBitmapEx
.IsEmpty())
864 // svg not yet buffered in maBitmapEx, return default PrefMapMode
865 aMapMode
= MapMode(MapUnit::Map100thMM
);
869 const Size
aSize(maBitmapEx
.GetPrefSize());
871 if (aSize
.Width() && aSize
.Height())
872 aMapMode
= maBitmapEx
.GetPrefMapMode();
877 case GraphicType::GdiMetafile
:
879 return maMetaFile
.GetPrefMapMode();
883 case GraphicType::NONE
:
884 case GraphicType::Default
:
892 void ImpGraphic::setValuesForPrefMapMod(const MapMode
& rPrefMapMode
)
896 case GraphicType::Bitmap
:
898 if (maVectorGraphicData
)
900 // ignore for Vector Graphic Data. If this is really used (except the grfcache)
901 // it can be extended by using maBitmapEx as buffer for updateBitmapFromVectorGraphic()
905 // #108077# Push through pref mapmode to animation object,
906 // will be lost on copy otherwise
909 const_cast<BitmapEx
&>(mpAnimation
->GetBitmapEx()).SetPrefMapMode(rPrefMapMode
);
912 maBitmapEx
.SetPrefMapMode(rPrefMapMode
);
917 case GraphicType::GdiMetafile
:
919 maMetaFile
.SetPrefMapMode(rPrefMapMode
);
923 case GraphicType::NONE
:
924 case GraphicType::Default
:
929 void ImpGraphic::setPrefMapMode(const MapMode
& rPrefMapMode
)
932 setValuesForPrefMapMod(rPrefMapMode
);
935 void ImpGraphic::ensureCurrentSizeInBytes()
938 changeExisting(getSizeBytes());
943 sal_uLong
ImpGraphic::getSizeBytes() const
953 case GraphicType::Bitmap
:
955 if (maVectorGraphicData
)
957 std::pair
<VectorGraphicData::State
, size_t> aPair(maVectorGraphicData
->getSizeBytes());
958 if (VectorGraphicData::State::UNPARSED
== aPair
.first
)
960 return aPair
.second
; // don't cache it until Vector Graphic Data is parsed
962 mnSizeBytes
= aPair
.second
;
966 mnSizeBytes
= mpAnimation
? mpAnimation
->GetSizeBytes() : maBitmapEx
.GetSizeBytes();
971 case GraphicType::GdiMetafile
:
973 mnSizeBytes
= maMetaFile
.GetSizeBytes();
977 case GraphicType::NONE
:
978 case GraphicType::Default
:
985 void ImpGraphic::draw(OutputDevice
& rOutDev
, const Point
& rDestPt
) const
994 case GraphicType::Bitmap
:
998 mpAnimation
->Draw(rOutDev
, rDestPt
);
1002 if (maVectorGraphicData
)
1003 updateBitmapFromVectorGraphic();
1004 maBitmapEx
.Draw(&rOutDev
, rDestPt
);
1009 case GraphicType::GdiMetafile
:
1011 draw(rOutDev
, rDestPt
, maMetaFile
.GetPrefSize());
1015 case GraphicType::Default
:
1016 case GraphicType::NONE
:
1021 void ImpGraphic::draw(OutputDevice
& rOutDev
,
1022 const Point
& rDestPt
, const Size
& rDestSize
) const
1031 case GraphicType::Bitmap
:
1035 mpAnimation
->Draw(rOutDev
, rDestPt
, rDestSize
);
1039 if (maVectorGraphicData
)
1040 updateBitmapFromVectorGraphic(rOutDev
.LogicToPixel(rDestSize
));
1041 maBitmapEx
.Draw(&rOutDev
, rDestPt
, rDestSize
);
1046 case GraphicType::GdiMetafile
:
1048 const_cast<ImpGraphic
*>(this)->maMetaFile
.WindStart();
1049 const_cast<ImpGraphic
*>(this)->maMetaFile
.Play(rOutDev
, rDestPt
, rDestSize
);
1050 const_cast<ImpGraphic
*>(this)->maMetaFile
.WindStart();
1054 case GraphicType::Default
:
1055 case GraphicType::NONE
:
1060 void ImpGraphic::startAnimation(OutputDevice
& rOutDev
, const Point
& rDestPt
,
1061 const Size
& rDestSize
, tools::Long nRendererId
,
1062 OutputDevice
* pFirstFrameOutDev
)
1066 if( isSupportedGraphic() && !isSwappedOut() && mpAnimation
)
1067 mpAnimation
->Start(rOutDev
, rDestPt
, rDestSize
, nRendererId
, pFirstFrameOutDev
);
1070 void ImpGraphic::stopAnimation( const OutputDevice
* pOutDev
, tools::Long nRendererId
)
1074 if( isSupportedGraphic() && !isSwappedOut() && mpAnimation
)
1075 mpAnimation
->Stop( pOutDev
, nRendererId
);
1078 void ImpGraphic::setAnimationNotifyHdl( const Link
<Animation
*,void>& rLink
)
1083 mpAnimation
->SetNotifyHdl( rLink
);
1086 Link
<Animation
*,void> ImpGraphic::getAnimationNotifyHdl() const
1088 Link
<Animation
*,void> aLink
;
1093 aLink
= mpAnimation
->GetNotifyHdl();
1098 sal_uInt32
ImpGraphic::getAnimationLoopCount() const
1101 return maSwapInfo
.mnAnimationLoopCount
;
1103 return mpAnimation
? mpAnimation
->GetLoopCount() : 0;
1106 bool ImpGraphic::swapInContent(SvStream
& rStream
)
1114 rStream
.ReadUInt32(nId
);
1117 if (SWAP_FORMAT_ID
!= nId
)
1119 SAL_WARN("vcl", "Incompatible swap file!");
1123 rStream
.ReadInt32(nType
);
1124 rStream
.ReadInt32(nLength
);
1126 meType
= static_cast<GraphicType
>(nType
);
1128 if (meType
== GraphicType::NONE
|| meType
== GraphicType::Default
)
1134 bRet
= swapInGraphic(rStream
);
1140 bool ImpGraphic::swapOutGraphic(SvStream
& rStream
)
1142 if (rStream
.GetError())
1149 rStream
.SetError(SVSTREAM_GENERALERROR
);
1155 case GraphicType::GdiMetafile
:
1157 if(!rStream
.GetError())
1159 SvmWriter
aWriter(rStream
);
1160 aWriter
.Write(maMetaFile
);
1165 case GraphicType::Bitmap
:
1167 if (maVectorGraphicData
)
1169 rStream
.WriteInt32(sal_Int32(GraphicContentType::Vector
));
1170 // stream out Vector Graphic defining data (length, byte array and evtl. path)
1171 // this is used e.g. in swapping out graphic data and in transporting it over UNO API
1172 // as sequence of bytes, but AFAIK not written anywhere to any kind of file, so it should be
1173 // no problem to extend it; only used at runtime
1174 switch (maVectorGraphicData
->getType())
1176 case VectorGraphicDataType::Wmf
:
1178 rStream
.WriteUInt32(constWmfMagic
);
1181 case VectorGraphicDataType::Emf
:
1183 rStream
.WriteUInt32(constEmfMagic
);
1186 case VectorGraphicDataType::Svg
:
1188 rStream
.WriteUInt32(constSvgMagic
);
1191 case VectorGraphicDataType::Pdf
:
1193 rStream
.WriteUInt32(constPdfMagic
);
1198 rStream
.WriteUInt32(maVectorGraphicData
->getBinaryDataContainer().getSize());
1199 maVectorGraphicData
->getBinaryDataContainer().writeToStream(rStream
);
1201 else if (mpAnimation
)
1203 rStream
.WriteInt32(sal_Int32(GraphicContentType::Animation
));
1204 WriteAnimation(rStream
, *mpAnimation
);
1208 rStream
.WriteInt32(sal_Int32(GraphicContentType::Bitmap
));
1209 WriteDIBBitmapEx(maBitmapEx
, rStream
);
1214 case GraphicType::NONE
:
1215 case GraphicType::Default
:
1220 mpGfxLink
->getDataContainer().swapOut();
1225 bool ImpGraphic::swapOutContent(SvStream
& rStream
)
1231 if (meType
== GraphicType::NONE
|| meType
== GraphicType::Default
|| isSwappedOut())
1234 sal_uLong nDataFieldPos
;
1236 // Write the SWAP ID
1237 rStream
.WriteUInt32(SWAP_FORMAT_ID
);
1239 rStream
.WriteInt32(static_cast<sal_Int32
>(meType
));
1241 // data size is updated later
1242 nDataFieldPos
= rStream
.Tell();
1243 rStream
.WriteInt32(0);
1246 const sal_uInt64 nDataStart
= rStream
.Tell();
1248 swapOutGraphic(rStream
);
1250 if (!rStream
.GetError())
1252 // Write the written length th the header
1253 const sal_uInt64 nCurrentPosition
= rStream
.Tell();
1254 rStream
.Seek(nDataFieldPos
);
1255 rStream
.WriteInt32(nCurrentPosition
- nDataStart
);
1256 rStream
.Seek(nCurrentPosition
);
1263 bool ImpGraphic::swapOut()
1268 bool bResult
= false;
1270 // We have GfxLink so we have the source available
1271 if (mpGfxLink
&& mpGfxLink
->IsNative())
1277 // reset the swap file
1280 mpGfxLink
->getDataContainer().swapOut();
1282 // mark as swapped out
1289 // Create a swap file
1290 auto pSwapFile
= o3tl::make_shared
<ImpSwapFile
>(getOriginURL());
1292 // Open a stream to write the swap file to
1294 SvStream
* pOutputStream
= pSwapFile
->getStream();
1300 pOutputStream
->SetVersion(SOFFICE_FILEFORMAT_50
);
1301 pOutputStream
->SetCompressMode(SvStreamCompressFlags::NATIVE
);
1302 pOutputStream
->SetBufferSize(GRAPHIC_STREAMBUFSIZE
);
1304 if (!pOutputStream
->GetError() && swapOutContent(*pOutputStream
))
1306 pOutputStream
->FlushBuffer();
1307 bResult
= !pOutputStream
->GetError();
1311 // Check if writing was successful
1314 // We have swapped out, so can clean memory and prepare swap info
1318 mpSwapFile
= std::move(pSwapFile
);
1325 // Signal to manager that we have swapped out
1332 bool ImpGraphic::ensureAvailable() const
1334 bool bResult
= true;
1338 auto pThis
= const_cast<ImpGraphic
*>(this);
1339 pThis
->registerIntoManager();
1341 bResult
= pThis
->swapIn();
1348 void ImpGraphic::updateFromLoadedGraphic(const ImpGraphic
* pGraphic
)
1352 GraphicExternalLink aLink
= maGraphicExternalLink
;
1353 Size aPrefSize
= maSwapInfo
.maPrefSize
;
1354 MapMode aPrefMapMode
= maSwapInfo
.maPrefMapMode
;
1356 if (aPrefSize
.getWidth() && aPrefSize
.getHeight() && aPrefMapMode
== getPrefMapMode())
1358 // Use custom preferred size if it was set when the graphic was still unloaded.
1359 // Only set the size in case the unloaded and loaded unit matches.
1360 setPrefSize(aPrefSize
);
1362 maGraphicExternalLink
= std::move(aLink
);
1366 // Move over only graphic content
1367 mpAnimation
.reset();
1368 if (pGraphic
->mpAnimation
)
1370 mpAnimation
= std::make_unique
<Animation
>(*pGraphic
->mpAnimation
);
1371 maBitmapEx
= mpAnimation
->GetBitmapEx();
1375 maBitmapEx
= pGraphic
->maBitmapEx
;
1378 maMetaFile
= pGraphic
->maMetaFile
;
1379 maVectorGraphicData
= pGraphic
->maVectorGraphicData
;
1381 // Set to 0, to force recalculation
1385 restoreFromSwapInfo();
1391 void ImpGraphic::dumpState(rtl::OStringBuffer
&rState
)
1393 if (meType
== GraphicType::NONE
&& mnSizeBytes
== 0)
1394 return; // uninteresting.
1396 rState
.append("\n\t");
1399 rState
.append("swapped\t");
1401 rState
.append("loaded\t");
1403 rState
.append(static_cast<sal_Int32
>(meType
));
1404 rState
.append("\tsize:\t");
1405 rState
.append(static_cast<sal_Int64
>(mnSizeBytes
));
1406 rState
.append("\tgfxl:\t");
1407 rState
.append(static_cast<sal_Int64
>(mpGfxLink
? mpGfxLink
->getSizeBytes() : -1));
1408 rState
.append("\t");
1409 rState
.append(static_cast<sal_Int32
>(maSwapInfo
.maSizePixel
.Width()));
1411 rState
.append(static_cast<sal_Int32
>(maSwapInfo
.maSizePixel
.Height()));
1412 rState
.append("\t");
1413 rState
.append(static_cast<sal_Int32
>(maExPrefSize
.Width()));
1415 rState
.append(static_cast<sal_Int32
>(maExPrefSize
.Height()));
1418 void ImpGraphic::restoreFromSwapInfo()
1420 setValuesForPrefMapMod(maSwapInfo
.maPrefMapMode
);
1421 setValuesForPrefSize(maSwapInfo
.maPrefSize
);
1423 if (maVectorGraphicData
)
1425 maVectorGraphicData
->setPageIndex(maSwapInfo
.mnPageIndex
);
1432 std::optional
<VectorGraphicDataType
> lclConvertToVectorGraphicType(GfxLink
const & rLink
)
1434 switch(rLink
.GetType())
1436 case GfxLinkType::NativePdf
:
1437 return VectorGraphicDataType::Pdf
;
1439 case GfxLinkType::NativeWmf
:
1441 return VectorGraphicDataType::Emf
;
1443 return VectorGraphicDataType::Wmf
;
1445 case GfxLinkType::NativeSvg
:
1446 return VectorGraphicDataType::Svg
;
1451 return std::optional
<VectorGraphicDataType
>();
1456 bool ImpGraphic::swapIn()
1458 if (!isSwappedOut())
1461 bool bReturn
= false;
1466 if (!mpGfxLink
->LoadNative(aGraphic
))
1469 updateFromLoadedGraphic(aGraphic
.ImplGetImpGraphic());
1474 else if (mpGfxLink
&& mpGfxLink
->IsNative())
1476 std::optional
<VectorGraphicDataType
> oType
= lclConvertToVectorGraphicType(*mpGfxLink
);
1479 maVectorGraphicData
= vcl::loadVectorGraphic(mpGfxLink
->getDataContainer(), *oType
);
1481 // Set to 0, to force recalculation
1485 restoreFromSwapInfo();
1492 if (!mpGfxLink
->LoadNative(aGraphic
))
1495 ImpGraphic
* pImpGraphic
= aGraphic
.ImplGetImpGraphic();
1496 if (meType
!= pImpGraphic
->meType
)
1499 updateFromLoadedGraphic(pImpGraphic
);
1507 SvStream
* pStream
= nullptr;
1510 pStream
= mpSwapFile
->getStream();
1514 pStream
->SetVersion(SOFFICE_FILEFORMAT_50
);
1515 pStream
->SetCompressMode(SvStreamCompressFlags::NATIVE
);
1516 pStream
->SetBufferSize(GRAPHIC_STREAMBUFSIZE
);
1517 pStream
->Seek(STREAM_SEEK_TO_BEGIN
);
1519 bReturn
= swapInFromStream(*pStream
);
1521 restoreFromSwapInfo();
1523 setOriginURL(mpSwapFile
->getOriginURL());
1531 swappedIn(getSizeBytes());
1537 bool ImpGraphic::swapInFromStream(SvStream
& rStream
)
1541 if (rStream
.GetError())
1548 bRet
= swapInContent(rStream
);
1552 //throw away swapfile, etc.
1561 bool ImpGraphic::swapInGraphic(SvStream
& rStream
)
1563 bool bReturn
= false;
1565 if (rStream
.GetError())
1568 if (meType
== GraphicType::Bitmap
)
1570 sal_Int32 nContentType
= -1;
1571 rStream
.ReadInt32(nContentType
);
1572 if (nContentType
< 0)
1575 auto eContentType
= static_cast<GraphicContentType
>(nContentType
);
1577 switch (eContentType
)
1579 case GraphicContentType::Bitmap
:
1582 ReadDIBBitmapEx(aBitmapEx
, rStream
);
1583 if (!rStream
.GetError())
1585 maBitmapEx
= aBitmapEx
;
1591 case GraphicContentType::Animation
:
1593 auto pAnimation
= std::make_unique
<Animation
>();
1594 ReadAnimation(rStream
, *pAnimation
);
1595 if (!rStream
.GetError())
1597 mpAnimation
= std::move(pAnimation
);
1598 maBitmapEx
= mpAnimation
->GetBitmapEx();
1604 case GraphicContentType::Vector
:
1606 // try to stream in Svg defining data (length, byte array and evtl. path)
1607 // See below (operator<<) for more information
1609 rStream
.ReadUInt32(nMagic
);
1611 if (constSvgMagic
== nMagic
|| constWmfMagic
== nMagic
|| constEmfMagic
== nMagic
|| constPdfMagic
== nMagic
)
1613 sal_uInt32
nVectorGraphicDataSize(0);
1614 rStream
.ReadUInt32(nVectorGraphicDataSize
);
1616 if (nVectorGraphicDataSize
)
1618 BinaryDataContainer
aDataContainer(rStream
, nVectorGraphicDataSize
);
1620 if (rStream
.GetError())
1623 VectorGraphicDataType aDataType
;
1628 aDataType
= VectorGraphicDataType::Svg
;
1631 aDataType
= VectorGraphicDataType::Wmf
;
1634 aDataType
= VectorGraphicDataType::Emf
;
1637 aDataType
= VectorGraphicDataType::Pdf
;
1643 auto aVectorGraphicDataPtr
= std::make_shared
<VectorGraphicData
>(aDataContainer
, aDataType
);
1645 if (!rStream
.GetError())
1647 maVectorGraphicData
= std::move(aVectorGraphicDataPtr
);
1656 else if (meType
== GraphicType::GdiMetafile
)
1658 GDIMetaFile aMetaFile
;
1659 SvmReader
aReader(rStream
);
1660 aReader
.Read(aMetaFile
);
1661 if (!rStream
.GetError())
1663 maMetaFile
= aMetaFile
;
1670 void ImpGraphic::setGfxLink(const std::shared_ptr
<GfxLink
>& rGfxLink
)
1674 mpGfxLink
= rGfxLink
;
1677 const std::shared_ptr
<GfxLink
> & ImpGraphic::getSharedGfxLink() const
1682 GfxLink
ImpGraphic::getGfxLink() const
1686 return( mpGfxLink
? *mpGfxLink
: GfxLink() );
1689 bool ImpGraphic::isGfxLink() const
1691 return ( bool(mpGfxLink
) );
1694 BitmapChecksum
ImpGraphic::getChecksum() const
1696 if (mnChecksum
!= 0)
1703 case GraphicType::NONE
:
1704 case GraphicType::Default
:
1707 case GraphicType::Bitmap
:
1709 if (maVectorGraphicData
)
1710 mnChecksum
= maVectorGraphicData
->GetChecksum();
1711 else if (mpAnimation
)
1712 mnChecksum
= mpAnimation
->GetChecksum();
1714 mnChecksum
= maBitmapEx
.GetChecksum();
1718 case GraphicType::GdiMetafile
:
1720 mnChecksum
= SvmWriter::GetChecksum(maMetaFile
);
1727 sal_Int32
ImpGraphic::getPageNumber() const
1730 return maSwapInfo
.mnPageIndex
;
1732 if (maVectorGraphicData
)
1733 return maVectorGraphicData
->getPageIndex();
1737 bool ImpGraphic::canReduceMemory() const
1739 return !isSwappedOut();
1742 bool ImpGraphic::reduceMemory()
1747 std::chrono::high_resolution_clock::time_point
ImpGraphic::getLastUsed() const
1752 void ImpGraphic::resetLastUsed() const
1754 maLastUsed
= std::chrono::high_resolution_clock::now();
1757 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */