Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / gdi / impgraph.cxx
blob824caae1698f7c8db7e4101239e8a88efa998c99
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <comphelper/processfactory.hxx>
25 #include <tools/fract.hxx>
26 #include <tools/vcompat.hxx>
27 #include <tools/urlobj.hxx>
28 #include <tools/stream.hxx>
29 #include <ucbhelper/content.hxx>
30 #include <unotools/ucbstreamhelper.hxx>
31 #include <unotools/tempfile.hxx>
32 #include <vcl/outdev.hxx>
33 #include <vcl/graphicfilter.hxx>
34 #include <vcl/virdev.hxx>
35 #include <vcl/gfxlink.hxx>
36 #include <vcl/cvtgrf.hxx>
37 #include <vcl/graph.hxx>
38 #include <vcl/metaact.hxx>
39 #include <impgraph.hxx>
40 #include <com/sun/star/ucb/CommandAbortedException.hpp>
41 #include <com/sun/star/ucb/ContentCreationException.hpp>
42 #include <com/sun/star/graphic/XPrimitive2D.hpp>
43 #include <vcl/dibtools.hxx>
44 #include <map>
45 #include <memory>
46 #include <vcl/gdimetafiletools.hxx>
47 #include <TypeSerializer.hxx>
48 #include <vcl/pdfread.hxx>
50 #define GRAPHIC_MTFTOBMP_MAXEXT 2048
51 #define GRAPHIC_STREAMBUFSIZE 8192UL
53 #define SYS_WINMETAFILE 0x00000003L
54 #define SYS_WNTMETAFILE 0x00000004L
55 #define SYS_OS2METAFILE 0x00000005L
56 #define SYS_MACMETAFILE 0x00000006L
58 #define GRAPHIC_FORMAT_50 COMPAT_FORMAT( 'G', 'R', 'F', '5' )
59 #define NATIVE_FORMAT_50 COMPAT_FORMAT( 'N', 'A', 'T', '5' )
61 const sal_uInt32 nPdfMagic((sal_uInt32('p') << 24) | (sal_uInt32('d') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
63 using namespace com::sun::star;
65 struct ImpSwapFile
67 INetURLObject aSwapURL;
68 OUString maOriginURL;
69 ~ImpSwapFile();
72 class ReaderData
74 public:
75 Size maPreviewSize;
78 GraphicReader::GraphicReader()
82 GraphicReader::~GraphicReader()
86 void GraphicReader::DisablePreviewMode()
88 if( mpReaderData )
89 mpReaderData->maPreviewSize = Size( 0, 0 );
92 void GraphicReader::SetPreviewSize( const Size& rSize )
94 if( !mpReaderData )
95 mpReaderData.reset( new ReaderData );
96 mpReaderData->maPreviewSize = rSize;
99 Size GraphicReader::GetPreviewSize() const
101 Size aSize( 0, 0 );
102 if( mpReaderData )
103 aSize = mpReaderData->maPreviewSize;
104 return aSize;
107 GraphicID::GraphicID(ImpGraphic const & rGraphic)
109 rGraphic.ensureAvailable();
111 mnID1 = static_cast<sal_uLong>(rGraphic.ImplGetType()) << 28;
112 mnID2 = mnID3 = mnID4 = 0;
114 if (rGraphic.ImplGetType() == GraphicType::Bitmap)
116 if (rGraphic.getVectorGraphicData().get())
118 const VectorGraphicDataPtr& rVectorGraphicDataPtr = rGraphic.getVectorGraphicData();
119 const basegfx::B2DRange& rRange = rVectorGraphicDataPtr->getRange();
121 mnID1 |= rVectorGraphicDataPtr->getVectorGraphicDataArrayLength();
122 mnID2 = basegfx::fround(rRange.getWidth());
123 mnID3 = basegfx::fround(rRange.getHeight());
124 mnID4 = vcl_get_checksum(0, rVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), rVectorGraphicDataPtr->getVectorGraphicDataArrayLength());
126 else if (rGraphic.hasPdfData())
128 std::shared_ptr<std::vector<sal_Int8>> pPdfData = rGraphic.getPdfData();
129 const BitmapEx& rBmpEx = rGraphic.ImplGetBitmapExRef();
131 mnID1 |= (rGraphic.mnPageNumber & 0x0fffffff);
132 mnID2 = rBmpEx.GetSizePixel().Width();
133 mnID3 = rBmpEx.GetSizePixel().Height();
134 mnID4 = vcl_get_checksum(0, pPdfData->data(), pPdfData->size());
136 else if (rGraphic.ImplIsAnimated())
138 const Animation aAnimation(rGraphic.ImplGetAnimation());
140 mnID1 |= ( aAnimation.Count() & 0x0fffffff );
141 mnID2 = aAnimation.GetDisplaySizePixel().Width();
142 mnID3 = aAnimation.GetDisplaySizePixel().Height();
143 mnID4 = rGraphic.ImplGetChecksum();
145 else
147 const BitmapEx aBmpEx(rGraphic.ImplGetBitmapEx(GraphicConversionParameters()));
149 mnID1 |= ( ( ( static_cast<sal_uLong>(aBmpEx.GetTransparentType()) << 8 ) | ( aBmpEx.IsAlpha() ? 1 : 0 ) ) & 0x0fffffff );
150 mnID2 = aBmpEx.GetSizePixel().Width();
151 mnID3 = aBmpEx.GetSizePixel().Height();
152 mnID4 = rGraphic.ImplGetChecksum();
155 else if (rGraphic.ImplGetType() == GraphicType::GdiMetafile)
157 const GDIMetaFile& rMtf = rGraphic.ImplGetGDIMetaFile();
159 mnID1 |= ( rMtf.GetActionSize() & 0x0fffffff );
160 mnID2 = rMtf.GetPrefSize().Width();
161 mnID3 = rMtf.GetPrefSize().Height();
162 mnID4 = rGraphic.ImplGetChecksum();
166 OString GraphicID::getIDString() const
168 static const char aHexData[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
170 sal_Int32 nShift, nIndex = 0;
171 sal_Int32 nLen = 24 + (2 * BITMAP_CHECKSUM_SIZE);
172 OStringBuffer aHexStr(nLen);
173 aHexStr.setLength(nLen);
175 for( nShift = 28; nShift >= 0; nShift -= 4 )
176 aHexStr[nIndex++] = aHexData[ ( mnID1 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
178 for( nShift = 28; nShift >= 0; nShift -= 4 )
179 aHexStr[nIndex++] = aHexData[ ( mnID2 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
181 for( nShift = 28; nShift >= 0; nShift -= 4 )
182 aHexStr[nIndex++] = aHexData[ ( mnID3 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
184 for( nShift = ( 8 * BITMAP_CHECKSUM_SIZE ) - 4; nShift >= 0; nShift -= 4 )
185 aHexStr[nIndex++] = aHexData[ ( mnID4 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
187 return aHexStr.makeStringAndClear();
190 ImpGraphic::ImpGraphic() :
191 meType ( GraphicType::NONE ),
192 mnSizeBytes ( 0 ),
193 mbSwapOut ( false ),
194 mbDummyContext ( false ),
195 maLastUsed (std::chrono::high_resolution_clock::now()),
196 mbPrepared ( false ),
197 mnPageNumber(-1)
201 ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
202 : maMetaFile(rImpGraphic.maMetaFile)
203 , maEx(rImpGraphic.maEx)
204 , maSwapInfo(rImpGraphic.maSwapInfo)
205 , mpContext(rImpGraphic.mpContext)
206 , mpSwapFile(rImpGraphic.mpSwapFile)
207 , mpGfxLink(rImpGraphic.mpGfxLink)
208 , meType(rImpGraphic.meType)
209 , mnSizeBytes(rImpGraphic.mnSizeBytes)
210 , mbSwapOut(rImpGraphic.mbSwapOut)
211 , mbDummyContext(rImpGraphic.mbDummyContext)
212 , maVectorGraphicData(rImpGraphic.maVectorGraphicData)
213 , mpPdfData(rImpGraphic.mpPdfData)
214 , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
215 , maLastUsed (std::chrono::high_resolution_clock::now())
216 , mbPrepared (rImpGraphic.mbPrepared)
217 , mnPageNumber(rImpGraphic.mnPageNumber)
219 if( rImpGraphic.mpAnimation )
221 mpAnimation = std::make_unique<Animation>( *rImpGraphic.mpAnimation );
222 maEx = mpAnimation->GetBitmapEx();
226 ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
227 : maMetaFile(std::move(rImpGraphic.maMetaFile))
228 , maEx(std::move(rImpGraphic.maEx))
229 , maSwapInfo(std::move(rImpGraphic.maSwapInfo))
230 , mpAnimation(std::move(rImpGraphic.mpAnimation))
231 , mpContext(std::move(rImpGraphic.mpContext))
232 , mpSwapFile(std::move(rImpGraphic.mpSwapFile))
233 , mpGfxLink(std::move(rImpGraphic.mpGfxLink))
234 , meType(rImpGraphic.meType)
235 , mnSizeBytes(rImpGraphic.mnSizeBytes)
236 , mbSwapOut(rImpGraphic.mbSwapOut)
237 , mbDummyContext(rImpGraphic.mbDummyContext)
238 , maVectorGraphicData(std::move(rImpGraphic.maVectorGraphicData))
239 , mpPdfData(std::move(rImpGraphic.mpPdfData))
240 , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
241 , maLastUsed (std::chrono::high_resolution_clock::now())
242 , mbPrepared (rImpGraphic.mbPrepared)
243 , mnPageNumber(rImpGraphic.mnPageNumber)
245 rImpGraphic.ImplClear();
246 rImpGraphic.mbDummyContext = false;
249 ImpGraphic::ImpGraphic(GraphicExternalLink const & rGraphicExternalLink) :
250 meType ( GraphicType::Default ),
251 mnSizeBytes ( 0 ),
252 mbSwapOut ( false ),
253 mbDummyContext ( false ),
254 maGraphicExternalLink(rGraphicExternalLink),
255 maLastUsed (std::chrono::high_resolution_clock::now()),
256 mbPrepared (false),
257 mnPageNumber(-1)
261 ImpGraphic::ImpGraphic( const Bitmap& rBitmap ) :
262 maEx ( rBitmap ),
263 meType ( !rBitmap.IsEmpty() ? GraphicType::Bitmap : GraphicType::NONE ),
264 mnSizeBytes ( 0 ),
265 mbSwapOut ( false ),
266 mbDummyContext ( false ),
267 maLastUsed (std::chrono::high_resolution_clock::now()),
268 mbPrepared (false),
269 mnPageNumber(-1)
273 ImpGraphic::ImpGraphic( const BitmapEx& rBitmapEx ) :
274 maEx ( rBitmapEx ),
275 meType ( !rBitmapEx.IsEmpty() ? GraphicType::Bitmap : GraphicType::NONE ),
276 mnSizeBytes ( 0 ),
277 mbSwapOut ( false ),
278 mbDummyContext ( false ),
279 maLastUsed (std::chrono::high_resolution_clock::now()),
280 mbPrepared (false),
281 mnPageNumber(-1)
285 ImpGraphic::ImpGraphic(const VectorGraphicDataPtr& rVectorGraphicDataPtr)
286 : meType( rVectorGraphicDataPtr.get() ? GraphicType::Bitmap : GraphicType::NONE ),
287 mnSizeBytes( 0 ),
288 mbSwapOut( false ),
289 mbDummyContext ( false ),
290 maVectorGraphicData(rVectorGraphicDataPtr),
291 maLastUsed (std::chrono::high_resolution_clock::now()),
292 mbPrepared (false),
293 mnPageNumber(-1)
297 ImpGraphic::ImpGraphic( const Animation& rAnimation ) :
298 maEx ( rAnimation.GetBitmapEx() ),
299 mpAnimation ( std::make_unique<Animation>( rAnimation ) ),
300 meType ( GraphicType::Bitmap ),
301 mnSizeBytes ( 0 ),
302 mbSwapOut ( false ),
303 mbDummyContext ( false ),
304 maLastUsed (std::chrono::high_resolution_clock::now()),
305 mbPrepared (false),
306 mnPageNumber(-1)
310 ImpGraphic::ImpGraphic( const GDIMetaFile& rMtf ) :
311 maMetaFile ( rMtf ),
312 meType ( GraphicType::GdiMetafile ),
313 mnSizeBytes ( 0 ),
314 mbSwapOut ( false ),
315 mbDummyContext ( false ),
316 maLastUsed (std::chrono::high_resolution_clock::now()),
317 mbPrepared (false),
318 mnPageNumber(-1)
322 ImpGraphic::~ImpGraphic()
324 vcl::graphic::Manager::get().unregisterGraphic(this);
327 ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic )
329 if( &rImpGraphic != this )
331 sal_Int64 aOldSizeBytes = mnSizeBytes;
333 maMetaFile = rImpGraphic.maMetaFile;
334 meType = rImpGraphic.meType;
335 mnSizeBytes = rImpGraphic.mnSizeBytes;
337 maSwapInfo = rImpGraphic.maSwapInfo;
338 mpContext = rImpGraphic.mpContext;
339 mbDummyContext = rImpGraphic.mbDummyContext;
340 mnPageNumber = rImpGraphic.mnPageNumber;
341 maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
343 mpAnimation.reset();
345 if ( rImpGraphic.mpAnimation )
347 mpAnimation = std::make_unique<Animation>( *rImpGraphic.mpAnimation );
348 maEx = mpAnimation->GetBitmapEx();
350 else
352 maEx = rImpGraphic.maEx;
355 mbSwapOut = rImpGraphic.mbSwapOut;
356 mpSwapFile = rImpGraphic.mpSwapFile;
357 mbPrepared = rImpGraphic.mbPrepared;
359 mpGfxLink = rImpGraphic.mpGfxLink;
361 maVectorGraphicData = rImpGraphic.maVectorGraphicData;
362 mpPdfData = rImpGraphic.mpPdfData;
363 maLastUsed = std::chrono::high_resolution_clock::now();
365 vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
368 return *this;
371 ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
373 sal_Int64 aOldSizeBytes = mnSizeBytes;
375 maMetaFile = std::move(rImpGraphic.maMetaFile);
376 meType = rImpGraphic.meType;
377 mnSizeBytes = rImpGraphic.mnSizeBytes;
378 maSwapInfo = std::move(rImpGraphic.maSwapInfo);
379 mpContext = std::move(rImpGraphic.mpContext);
380 mbDummyContext = rImpGraphic.mbDummyContext;
381 mnPageNumber = rImpGraphic.mnPageNumber;
382 mpAnimation = std::move(rImpGraphic.mpAnimation);
383 maEx = std::move(rImpGraphic.maEx);
384 mbSwapOut = rImpGraphic.mbSwapOut;
385 mpSwapFile = std::move(rImpGraphic.mpSwapFile);
386 mpGfxLink = std::move(rImpGraphic.mpGfxLink);
387 maVectorGraphicData = std::move(rImpGraphic.maVectorGraphicData);
388 mpPdfData = std::move(rImpGraphic.mpPdfData);
389 maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
390 mbPrepared = rImpGraphic.mbPrepared;
392 rImpGraphic.ImplClear();
393 rImpGraphic.mbDummyContext = false;
394 maLastUsed = std::chrono::high_resolution_clock::now();
396 vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
398 rImpGraphic.mnPageNumber = -1;
400 return *this;
403 bool ImpGraphic::operator==( const ImpGraphic& rImpGraphic ) const
405 bool bRet = false;
407 if( this == &rImpGraphic )
408 bRet = true;
409 else if (mbPrepared && rImpGraphic.mbPrepared)
411 bRet = (*mpGfxLink == *rImpGraphic.mpGfxLink);
413 else if (isAvailable() && rImpGraphic.isAvailable())
415 switch( meType )
417 case GraphicType::NONE:
418 bRet = true;
419 break;
421 case GraphicType::GdiMetafile:
423 if( rImpGraphic.maMetaFile == maMetaFile )
424 bRet = true;
426 break;
428 case GraphicType::Bitmap:
430 if(maVectorGraphicData.get())
432 if(maVectorGraphicData == rImpGraphic.maVectorGraphicData)
434 // equal instances
435 bRet = true;
437 else if(rImpGraphic.maVectorGraphicData)
439 // equal content
440 bRet = (*maVectorGraphicData) == (*rImpGraphic.maVectorGraphicData);
443 else if (mpPdfData && !mpPdfData->empty())
445 bRet = (rImpGraphic.mpPdfData && *mpPdfData == *rImpGraphic.mpPdfData);
447 else if( mpAnimation )
449 if( rImpGraphic.mpAnimation && ( *rImpGraphic.mpAnimation == *mpAnimation ) )
450 bRet = true;
452 else if( !rImpGraphic.mpAnimation && ( rImpGraphic.maEx == maEx ) )
454 bRet = true;
457 break;
459 default:
460 break;
464 return bRet;
467 const VectorGraphicDataPtr& ImpGraphic::getVectorGraphicData() const
469 ensureAvailable();
471 return maVectorGraphicData;
474 void ImpGraphic::setPdfData(const std::shared_ptr<std::vector<sal_Int8>>& rPdfData)
476 ensureAvailable();
478 mpPdfData = rPdfData;
481 const std::shared_ptr<std::vector<sal_Int8>> & ImpGraphic::getPdfData() const
483 ensureAvailable();
485 return mpPdfData;
488 void ImpGraphic::ImplCreateSwapInfo()
490 if (!ImplIsSwapOut())
492 maSwapInfo.maPrefMapMode = ImplGetPrefMapMode();
493 maSwapInfo.maPrefSize = ImplGetPrefSize();
494 maSwapInfo.mbIsAnimated = ImplIsAnimated();
495 maSwapInfo.mbIsEPS = ImplIsEPS();
496 maSwapInfo.mbIsTransparent = ImplIsTransparent();
497 maSwapInfo.mbIsAlpha = ImplIsAlpha();
498 maSwapInfo.mnAnimationLoopCount = ImplGetAnimationLoopCount();
502 void ImpGraphic::ImplClearGraphics()
504 maEx.Clear();
505 maMetaFile.Clear();
506 mpAnimation.reset();
507 mpGfxLink.reset();
508 maVectorGraphicData.reset();
509 mpPdfData.reset();
512 ImpSwapFile::~ImpSwapFile()
516 ::ucbhelper::Content aCnt( aSwapURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
517 css::uno::Reference< css::ucb::XCommandEnvironment >(),
518 comphelper::getProcessComponentContext() );
520 aCnt.executeCommand( "delete", css::uno::makeAny( true ) );
522 catch( const css::ucb::ContentCreationException& )
525 catch( const css::uno::RuntimeException& )
528 catch( const css::ucb::CommandAbortedException& )
531 catch( const css::uno::Exception& )
536 void ImpGraphic::ImplSetPrepared(bool bAnimated, const Size* pSizeHint)
538 mbPrepared = true;
539 mbSwapOut = true;
540 meType = GraphicType::Bitmap;
542 SvMemoryStream aMemoryStream(const_cast<sal_uInt8*>(mpGfxLink->GetData()), mpGfxLink->GetDataSize(), StreamMode::READ | StreamMode::WRITE);
544 if (pSizeHint)
546 maSwapInfo.maPrefSize = *pSizeHint;
547 maSwapInfo.maPrefMapMode = MapMode(MapUnit::Map100thMM);
550 GraphicDescriptor aDescriptor(aMemoryStream, nullptr);
551 if (aDescriptor.Detect(true))
553 if (!pSizeHint)
555 // If we have logic size, work with that, as later pixel -> logic
556 // conversion will work with the output device DPI, not the graphic
557 // DPI.
558 Size aLogSize = aDescriptor.GetSize_100TH_MM();
559 if (aLogSize.getWidth() && aLogSize.getHeight())
561 maSwapInfo.maPrefSize = aLogSize;
562 maSwapInfo.maPrefMapMode = MapMode(MapUnit::Map100thMM);
564 else
566 maSwapInfo.maPrefSize = aDescriptor.GetSizePixel();
567 maSwapInfo.maPrefMapMode = MapMode(MapUnit::MapPixel);
571 maSwapInfo.maSizePixel = aDescriptor.GetSizePixel();
572 maSwapInfo.mbIsTransparent = aDescriptor.IsTransparent();
573 maSwapInfo.mbIsAlpha = aDescriptor.IsAlpha();
574 } else {
575 maSwapInfo.mbIsTransparent = false;
576 maSwapInfo.mbIsAlpha = false;
579 maSwapInfo.mnAnimationLoopCount = 0;
580 maSwapInfo.mbIsEPS = false;
581 maSwapInfo.mbIsAnimated = bAnimated;
584 void ImpGraphic::ImplClear()
586 mpSwapFile.reset();
587 mbSwapOut = false;
588 mbPrepared = false;
590 // cleanup
591 ImplClearGraphics();
592 meType = GraphicType::NONE;
593 sal_Int64 nOldSize = mnSizeBytes;
594 mnSizeBytes = 0;
595 vcl::graphic::Manager::get().changeExisting(this, nOldSize);
596 maGraphicExternalLink.msURL.clear();
599 void ImpGraphic::ImplSetDefaultType()
601 ImplClear();
602 meType = GraphicType::Default;
605 bool ImpGraphic::ImplIsSupportedGraphic() const
607 return( meType != GraphicType::NONE );
610 bool ImpGraphic::ImplIsTransparent() const
612 bool bRet(true);
614 if (mbSwapOut)
616 bRet = maSwapInfo.mbIsTransparent;
618 else if (meType == GraphicType::Bitmap && !maVectorGraphicData.get())
620 bRet = mpAnimation ? mpAnimation->IsTransparent() : maEx.IsTransparent();
623 return bRet;
626 bool ImpGraphic::ImplIsAlpha() const
628 bool bRet(false);
630 if (mbSwapOut)
632 bRet = maSwapInfo.mbIsAlpha;
634 else if (maVectorGraphicData.get())
636 bRet = true;
638 else if (meType == GraphicType::Bitmap)
640 bRet = (nullptr == mpAnimation && maEx.IsAlpha());
643 return bRet;
646 bool ImpGraphic::ImplIsAnimated() const
648 return mbSwapOut ? maSwapInfo.mbIsAnimated : mpAnimation != nullptr;
651 bool ImpGraphic::ImplIsEPS() const
653 if (mbSwapOut)
654 return maSwapInfo.mbIsEPS;
656 return( ( meType == GraphicType::GdiMetafile ) &&
657 ( maMetaFile.GetActionSize() > 0 ) &&
658 ( maMetaFile.GetAction( 0 )->GetType() == MetaActionType::EPS ) );
661 bool ImpGraphic::isAvailable() const
663 return !mbPrepared && !mbSwapOut;
666 bool ImpGraphic::makeAvailable()
668 return ensureAvailable();
671 Bitmap ImpGraphic::ImplGetBitmap(const GraphicConversionParameters& rParameters) const
673 Bitmap aRetBmp;
675 ensureAvailable();
677 if( meType == GraphicType::Bitmap )
679 if(maVectorGraphicData.get() && maEx.IsEmpty())
681 // use maEx as local buffer for rendered svg
682 const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
685 const BitmapEx& rRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maEx );
686 const Color aReplaceColor( COL_WHITE );
688 aRetBmp = rRetBmpEx.GetBitmap( &aReplaceColor );
690 if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
691 aRetBmp.Scale(rParameters.getSizePixel());
693 else if( ( meType != GraphicType::Default ) && ImplIsSupportedGraphic() )
695 if(maEx.IsEmpty())
697 // calculate size
698 ScopedVclPtrInstance< VirtualDevice > aVDev;
699 Size aDrawSize(aVDev->LogicToPixel(maMetaFile.GetPrefSize(), maMetaFile.GetPrefMapMode()));
701 if(rParameters.getSizePixel().Width() && rParameters.getSizePixel().Height())
703 // apply given size if exists
704 aDrawSize = rParameters.getSizePixel();
707 if(aDrawSize.Width() && aDrawSize.Height() && !rParameters.getUnlimitedSize()
708 && (aDrawSize.Width() > GRAPHIC_MTFTOBMP_MAXEXT || aDrawSize.Height() > GRAPHIC_MTFTOBMP_MAXEXT))
710 // limit bitmap size to a maximum of GRAPHIC_MTFTOBMP_MAXEXT x GRAPHIC_MTFTOBMP_MAXEXT
711 double fWH(static_cast<double>(aDrawSize.Width()) / static_cast<double>(aDrawSize.Height()));
713 if(fWH <= 1.0)
715 aDrawSize.setWidth(basegfx::fround(GRAPHIC_MTFTOBMP_MAXEXT * fWH));
716 aDrawSize.setHeight(GRAPHIC_MTFTOBMP_MAXEXT);
718 else
720 aDrawSize.setWidth(GRAPHIC_MTFTOBMP_MAXEXT);
721 aDrawSize.setHeight(basegfx::fround(GRAPHIC_MTFTOBMP_MAXEXT / fWH));
725 // calculate pixel size. Normally, it's the same as aDrawSize, but may
726 // need to be extended when hairlines are on the right or bottom edge
727 Size aPixelSize(aDrawSize);
729 if(GraphicType::GdiMetafile == ImplGetType())
731 // get hairline and full bound rect
732 tools::Rectangle aHairlineRect;
733 const tools::Rectangle aRect(maMetaFile.GetBoundRect(*aVDev, &aHairlineRect));
735 if(!aRect.IsEmpty() && !aHairlineRect.IsEmpty())
737 // expand if needed to allow bottom and right hairlines to be added
738 if(aRect.Right() == aHairlineRect.Right())
740 aPixelSize.setWidth(aPixelSize.getWidth() + 1);
743 if(aRect.Bottom() == aHairlineRect.Bottom())
745 aPixelSize.setHeight(aPixelSize.getHeight() + 1);
750 if(aVDev->SetOutputSizePixel(aPixelSize))
752 if(rParameters.getAntiAliase())
754 aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::EnableB2dDraw);
757 if(rParameters.getSnapHorVerLines())
759 aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::PixelSnapHairline);
762 ImplDraw( aVDev.get(), Point(), aDrawSize );
764 // use maEx as local buffer for rendered metafile
765 const_cast< ImpGraphic* >(this)->maEx = aVDev->GetBitmapEx( Point(), aVDev->GetOutputSizePixel() );
769 aRetBmp = maEx.GetBitmap();
772 if( !!aRetBmp )
774 aRetBmp.SetPrefMapMode( ImplGetPrefMapMode() );
775 aRetBmp.SetPrefSize( ImplGetPrefSize() );
778 return aRetBmp;
781 BitmapEx ImpGraphic::ImplGetBitmapEx(const GraphicConversionParameters& rParameters) const
783 BitmapEx aRetBmpEx;
785 ensureAvailable();
787 if( meType == GraphicType::Bitmap )
789 if(maVectorGraphicData.get() && maEx.IsEmpty())
791 // use maEx as local buffer for rendered svg
792 const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
795 aRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maEx );
797 if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
799 aRetBmpEx.Scale(
800 rParameters.getSizePixel(),
801 BmpScaleFlag::Fast);
804 else if( ( meType != GraphicType::Default ) && ImplIsSupportedGraphic() )
806 if(maEx.IsEmpty())
808 const ImpGraphic aMonoMask( maMetaFile.GetMonochromeMtf( COL_BLACK ) );
810 // use maEx as local buffer for rendered metafile
811 const_cast< ImpGraphic* >(this)->maEx = BitmapEx(ImplGetBitmap(rParameters), aMonoMask.ImplGetBitmap(rParameters));
814 aRetBmpEx = maEx;
817 return aRetBmpEx;
820 Animation ImpGraphic::ImplGetAnimation() const
822 Animation aAnimation;
824 ensureAvailable();
825 if( mpAnimation )
826 aAnimation = *mpAnimation;
828 return aAnimation;
831 const BitmapEx& ImpGraphic::ImplGetBitmapExRef() const
833 ensureAvailable();
834 return maEx;
837 const GDIMetaFile& ImpGraphic::ImplGetGDIMetaFile() const
839 ensureAvailable();
840 if (!maMetaFile.GetActionSize()
841 && maVectorGraphicData.get()
842 && (VectorGraphicDataType::Emf == maVectorGraphicData->getVectorGraphicDataType()
843 || VectorGraphicDataType::Wmf == maVectorGraphicData->getVectorGraphicDataType()))
845 // If we have a Emf/Wmf VectorGraphic object, we
846 // need a way to get the Metafile data out of the primitive
847 // representation. Use a strict virtual hook (MetafileAccessor)
848 // to access the MetafilePrimitive2D directly. Also see comments in
849 // XEmfParser about this.
850 const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > > aSequence(maVectorGraphicData->getPrimitive2DSequence());
852 if (1 == aSequence.size())
854 // try to cast to MetafileAccessor implementation
855 const css::uno::Reference< css::graphic::XPrimitive2D > xReference(aSequence[0]);
856 const MetafileAccessor* pMetafileAccessor = dynamic_cast< const MetafileAccessor* >(xReference.get());
858 if (pMetafileAccessor)
860 // it is a MetafileAccessor implementation, get Metafile
861 pMetafileAccessor->accessMetafile(const_cast< ImpGraphic* >(this)->maMetaFile);
866 if (GraphicType::Bitmap == meType && !maMetaFile.GetActionSize())
868 // #i119735#
869 // Use the local maMetaFile as container for a metafile-representation
870 // of the bitmap graphic. This will be done only once, thus be buffered.
871 // I checked all usages of maMetaFile, it is only used when type is not
872 // GraphicType::Bitmap. In operator= it will get copied, thus buffering will
873 // survive copying (change this if not wanted)
874 ImpGraphic* pThat = const_cast< ImpGraphic* >(this);
876 if(maVectorGraphicData.get() && !maEx)
878 // use maEx as local buffer for rendered svg
879 pThat->maEx = maVectorGraphicData->getReplacement();
882 // #123983# directly create a metafile with the same PrefSize and PrefMapMode
883 // the bitmap has, this will be an always correct metafile
884 if(maEx.IsTransparent())
886 pThat->maMetaFile.AddAction(new MetaBmpExScaleAction(Point(), maEx.GetPrefSize(), maEx));
888 else
890 pThat->maMetaFile.AddAction(new MetaBmpScaleAction(Point(), maEx.GetPrefSize(), maEx.GetBitmap()));
893 pThat->maMetaFile.Stop();
894 pThat->maMetaFile.WindStart();
895 pThat->maMetaFile.SetPrefSize(maEx.GetPrefSize());
896 pThat->maMetaFile.SetPrefMapMode(maEx.GetPrefMapMode());
899 return maMetaFile;
902 Size ImpGraphic::ImplGetSizePixel() const
904 Size aSize;
906 if (ImplIsSwapOut())
907 aSize = maSwapInfo.maSizePixel;
908 else
909 aSize = ImplGetBitmapEx(GraphicConversionParameters()).GetSizePixel();
911 return aSize;
914 Size ImpGraphic::ImplGetPrefSize() const
916 Size aSize;
918 if (ImplIsSwapOut())
920 aSize = maSwapInfo.maPrefSize;
922 else
924 switch( meType )
926 case GraphicType::NONE:
927 case GraphicType::Default:
928 break;
930 case GraphicType::Bitmap:
932 if(maVectorGraphicData.get() && maEx.IsEmpty())
934 // svg not yet buffered in maEx, return size derived from range
935 const basegfx::B2DRange& rRange = maVectorGraphicData->getRange();
937 aSize = Size(basegfx::fround(rRange.getWidth()), basegfx::fround(rRange.getHeight()));
939 else
941 aSize = maEx.GetPrefSize();
943 if( !aSize.Width() || !aSize.Height() )
945 aSize = maEx.GetSizePixel();
949 break;
951 default:
953 if( ImplIsSupportedGraphic() )
954 aSize = maMetaFile.GetPrefSize();
956 break;
960 return aSize;
963 void ImpGraphic::ImplSetPrefSize( const Size& rPrefSize )
965 ensureAvailable();
967 switch( meType )
969 case GraphicType::NONE:
970 case GraphicType::Default:
971 break;
973 case GraphicType::Bitmap:
975 // used when importing a writer FlyFrame with SVG as graphic, added conversion
976 // to allow setting the PrefSize at the BitmapEx to hold it
977 if(maVectorGraphicData.get() && maEx.IsEmpty())
979 // use maEx as local buffer for rendered svg
980 maEx = maVectorGraphicData->getReplacement();
983 // #108077# Push through pref size to animation object,
984 // will be lost on copy otherwise
985 if( ImplIsAnimated() )
987 const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefSize( rPrefSize );
990 maEx.SetPrefSize( rPrefSize );
992 break;
994 default:
996 if( ImplIsSupportedGraphic() )
997 maMetaFile.SetPrefSize( rPrefSize );
999 break;
1003 MapMode ImpGraphic::ImplGetPrefMapMode() const
1005 MapMode aMapMode;
1007 if (ImplIsSwapOut())
1009 aMapMode = maSwapInfo.maPrefMapMode;
1011 else
1013 switch( meType )
1015 case GraphicType::NONE:
1016 case GraphicType::Default:
1017 break;
1019 case GraphicType::Bitmap:
1021 if(maVectorGraphicData.get() && maEx.IsEmpty())
1023 // svg not yet buffered in maEx, return default PrefMapMode
1024 aMapMode = MapMode(MapUnit::Map100thMM);
1026 else
1028 const Size aSize( maEx.GetPrefSize() );
1030 if ( aSize.Width() && aSize.Height() )
1031 aMapMode = maEx.GetPrefMapMode();
1034 break;
1036 default:
1038 if( ImplIsSupportedGraphic() )
1039 return maMetaFile.GetPrefMapMode();
1041 break;
1045 return aMapMode;
1048 void ImpGraphic::ImplSetPrefMapMode( const MapMode& rPrefMapMode )
1050 ensureAvailable();
1052 switch( meType )
1054 case GraphicType::NONE:
1055 case GraphicType::Default:
1056 break;
1058 case GraphicType::Bitmap:
1060 if(maVectorGraphicData.get())
1062 // ignore for Vector Graphic Data. If this is really used (except the grfcache)
1063 // it can be extended by using maEx as buffer for maVectorGraphicData->getReplacement()
1065 else
1067 // #108077# Push through pref mapmode to animation object,
1068 // will be lost on copy otherwise
1069 if( ImplIsAnimated() )
1071 const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefMapMode( rPrefMapMode );
1074 maEx.SetPrefMapMode( rPrefMapMode );
1077 break;
1079 default:
1081 if( ImplIsSupportedGraphic() )
1082 maMetaFile.SetPrefMapMode( rPrefMapMode );
1084 break;
1088 sal_uLong ImpGraphic::ImplGetSizeBytes() const
1090 if( 0 == mnSizeBytes )
1092 if (mbPrepared)
1093 ensureAvailable();
1095 if( meType == GraphicType::Bitmap )
1097 if(maVectorGraphicData.get())
1099 std::pair<VectorGraphicData::State, size_t> tmp(maVectorGraphicData->getSizeBytes());
1100 if (VectorGraphicData::State::UNPARSED == tmp.first)
1102 return tmp.second; // don't cache it until Vector Graphic Data is parsed
1104 mnSizeBytes = tmp.second;
1106 else
1108 mnSizeBytes = mpAnimation ? mpAnimation->GetSizeBytes() : maEx.GetSizeBytes();
1111 else if( meType == GraphicType::GdiMetafile )
1113 mnSizeBytes = maMetaFile.GetSizeBytes();
1117 return mnSizeBytes;
1120 void ImpGraphic::ImplDraw( OutputDevice* pOutDev, const Point& rDestPt ) const
1122 ensureAvailable();
1123 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
1125 switch( meType )
1127 case GraphicType::Default:
1128 break;
1130 case GraphicType::Bitmap:
1132 if(maVectorGraphicData.get() && !maEx)
1134 // use maEx as local buffer for rendered svg
1135 const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
1138 if ( mpAnimation )
1140 mpAnimation->Draw( pOutDev, rDestPt );
1142 else
1144 maEx.Draw( pOutDev, rDestPt );
1147 break;
1149 default:
1150 ImplDraw( pOutDev, rDestPt, maMetaFile.GetPrefSize() );
1151 break;
1156 void ImpGraphic::ImplDraw( OutputDevice* pOutDev,
1157 const Point& rDestPt, const Size& rDestSize ) const
1159 ensureAvailable();
1160 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
1162 switch( meType )
1164 case GraphicType::Default:
1165 break;
1167 case GraphicType::Bitmap:
1169 if(maVectorGraphicData.get() && maEx.IsEmpty())
1171 // use maEx as local buffer for rendered svg
1172 const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
1175 if( mpAnimation )
1177 mpAnimation->Draw( pOutDev, rDestPt, rDestSize );
1179 else
1181 maEx.Draw( pOutDev, rDestPt, rDestSize );
1184 break;
1186 default:
1188 const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
1189 const_cast<ImpGraphic*>(this)->maMetaFile.Play( pOutDev, rDestPt, rDestSize );
1190 const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
1192 break;
1197 void ImpGraphic::ImplStartAnimation( OutputDevice* pOutDev, const Point& rDestPt,
1198 const Size& rDestSize, long nExtraData,
1199 OutputDevice* pFirstFrameOutDev )
1201 ensureAvailable();
1203 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation )
1204 mpAnimation->Start( pOutDev, rDestPt, rDestSize, nExtraData, pFirstFrameOutDev );
1207 void ImpGraphic::ImplStopAnimation( OutputDevice* pOutDev, long nExtraData )
1209 ensureAvailable();
1211 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && mpAnimation )
1212 mpAnimation->Stop( pOutDev, nExtraData );
1215 void ImpGraphic::ImplSetAnimationNotifyHdl( const Link<Animation*,void>& rLink )
1217 ensureAvailable();
1219 if( mpAnimation )
1220 mpAnimation->SetNotifyHdl( rLink );
1223 Link<Animation*,void> ImpGraphic::ImplGetAnimationNotifyHdl() const
1225 Link<Animation*,void> aLink;
1227 ensureAvailable();
1229 if( mpAnimation )
1230 aLink = mpAnimation->GetNotifyHdl();
1232 return aLink;
1235 sal_uInt32 ImpGraphic::ImplGetAnimationLoopCount() const
1237 if (mbSwapOut)
1238 return maSwapInfo.mnAnimationLoopCount;
1240 return mpAnimation ? mpAnimation->GetLoopCount() : 0;
1243 void ImpGraphic::ImplSetContext( const std::shared_ptr<GraphicReader>& pReader )
1245 mpContext = pReader;
1246 mbDummyContext = false;
1249 bool ImpGraphic::ImplReadEmbedded( SvStream& rIStm )
1251 ensureAvailable();
1253 MapMode aMapMode;
1254 Size aSize;
1255 sal_uInt32 nId;
1256 sal_Int32 nType;
1257 const SvStreamEndian nOldFormat = rIStm.GetEndian();
1258 bool bRet = false;
1260 rIStm.SetEndian( SvStreamEndian::LITTLE );
1261 rIStm.ReadUInt32( nId );
1263 // check version
1264 if( GRAPHIC_FORMAT_50 == nId )
1266 // read new style header
1267 std::unique_ptr<VersionCompat> pCompat( new VersionCompat( rIStm, StreamMode::READ ) );
1269 rIStm.ReadInt32( nType );
1270 sal_Int32 nLen;
1271 rIStm.ReadInt32( nLen );
1272 TypeSerializer aSerializer(rIStm);
1273 aSerializer.readSize(aSize);
1274 ReadMapMode( rIStm, aMapMode );
1276 else
1278 // read old style header
1279 sal_Int32 nWidth, nHeight;
1280 sal_Int32 nMapMode, nScaleNumX, nScaleDenomX;
1281 sal_Int32 nScaleNumY, nScaleDenomY, nOffsX, nOffsY;
1283 rIStm.SeekRel( -4 );
1285 sal_Int32 nLen;
1286 rIStm.ReadInt32( nType ).ReadInt32( nLen ).ReadInt32( nWidth ).ReadInt32( nHeight );
1287 rIStm.ReadInt32( nMapMode ).ReadInt32( nScaleNumX ).ReadInt32( nScaleDenomX ).ReadInt32( nScaleNumY );
1288 rIStm.ReadInt32( nScaleDenomY ).ReadInt32( nOffsX ).ReadInt32( nOffsY );
1290 // swapped
1291 if( nType > 100 )
1293 nType = OSL_SWAPDWORD( nType );
1294 nWidth = OSL_SWAPDWORD( nWidth );
1295 nHeight = OSL_SWAPDWORD( nHeight );
1296 nMapMode = OSL_SWAPDWORD( nMapMode );
1297 nScaleNumX = OSL_SWAPDWORD( nScaleNumX );
1298 nScaleDenomX = OSL_SWAPDWORD( nScaleDenomX );
1299 nScaleNumY = OSL_SWAPDWORD( nScaleNumY );
1300 nScaleDenomY = OSL_SWAPDWORD( nScaleDenomY );
1301 nOffsX = OSL_SWAPDWORD( nOffsX );
1302 nOffsY = OSL_SWAPDWORD( nOffsY );
1305 aSize = Size( nWidth, nHeight );
1306 aMapMode = MapMode( static_cast<MapUnit>(nMapMode), Point( nOffsX, nOffsY ),
1307 Fraction( nScaleNumX, nScaleDenomX ),
1308 Fraction( nScaleNumY, nScaleDenomY ) );
1311 meType = static_cast<GraphicType>(nType);
1313 if( meType != GraphicType::NONE )
1315 if( meType == GraphicType::Bitmap )
1317 if(maVectorGraphicData.get() && maEx.IsEmpty())
1319 // use maEx as local buffer for rendered svg
1320 maEx = maVectorGraphicData->getReplacement();
1323 maEx.SetSizePixel(aSize);
1325 if( aMapMode != MapMode() )
1327 maEx.SetPrefMapMode( aMapMode );
1328 maEx.SetPrefSize( aSize );
1331 else
1333 maMetaFile.SetPrefMapMode( aMapMode );
1334 maMetaFile.SetPrefSize( aSize );
1337 if( meType == GraphicType::Bitmap || meType == GraphicType::GdiMetafile )
1339 ReadImpGraphic( rIStm, *this );
1340 bRet = rIStm.GetError() == ERRCODE_NONE;
1342 else if( sal::static_int_cast<sal_uLong>(meType) >= SYS_WINMETAFILE
1343 && sal::static_int_cast<sal_uLong>(meType) <= SYS_MACMETAFILE )
1345 Graphic aSysGraphic;
1346 ConvertDataFormat nCvtType;
1348 switch( sal::static_int_cast<sal_uLong>(meType) )
1350 case SYS_WINMETAFILE:
1351 case SYS_WNTMETAFILE: nCvtType = ConvertDataFormat::WMF; break;
1352 case SYS_OS2METAFILE: nCvtType = ConvertDataFormat::MET; break;
1353 case SYS_MACMETAFILE: nCvtType = ConvertDataFormat::PCT; break;
1355 default:
1356 nCvtType = ConvertDataFormat::Unknown;
1357 break;
1360 if( nType && GraphicConverter::Import( rIStm, aSysGraphic, nCvtType ) == ERRCODE_NONE )
1362 *this = ImpGraphic( aSysGraphic.GetGDIMetaFile() );
1363 bRet = rIStm.GetError() == ERRCODE_NONE;
1365 else
1366 meType = GraphicType::Default;
1369 if( bRet )
1371 ImplSetPrefMapMode( aMapMode );
1372 ImplSetPrefSize( aSize );
1375 else
1376 bRet = true;
1378 rIStm.SetEndian( nOldFormat );
1380 return bRet;
1383 bool ImpGraphic::ImplWriteEmbedded( SvStream& rOStm )
1385 bool bRet = false;
1387 ensureAvailable();
1389 if( ( meType != GraphicType::NONE ) && ( meType != GraphicType::Default ) && !ImplIsSwapOut() )
1391 const MapMode aMapMode( ImplGetPrefMapMode() );
1392 const Size aSize( ImplGetPrefSize() );
1393 const SvStreamEndian nOldFormat = rOStm.GetEndian();
1394 sal_uLong nDataFieldPos;
1396 rOStm.SetEndian( SvStreamEndian::LITTLE );
1398 // write correct version ( old style/new style header )
1399 if( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 )
1401 // write ID for new format (5.0)
1402 rOStm.WriteUInt32( GRAPHIC_FORMAT_50 );
1404 // write new style header
1405 std::unique_ptr<VersionCompat> pCompat( new VersionCompat( rOStm, StreamMode::WRITE, 1 ) );
1407 rOStm.WriteInt32( static_cast<sal_Int32>(meType) );
1409 // data size is updated later
1410 nDataFieldPos = rOStm.Tell();
1411 rOStm.WriteInt32( 0 );
1413 TypeSerializer aSerializer(rOStm);
1414 aSerializer.writeSize(aSize);
1416 WriteMapMode( rOStm, aMapMode );
1418 else
1420 // write old style (<=4.0) header
1421 rOStm.WriteInt32( static_cast<sal_Int32>(meType) );
1423 // data size is updated later
1424 nDataFieldPos = rOStm.Tell();
1425 rOStm.WriteInt32( 0 );
1426 rOStm.WriteInt32( aSize.Width() );
1427 rOStm.WriteInt32( aSize.Height() );
1428 rOStm.WriteInt32( static_cast<sal_uInt16>(aMapMode.GetMapUnit()) );
1429 rOStm.WriteInt32( aMapMode.GetScaleX().GetNumerator() );
1430 rOStm.WriteInt32( aMapMode.GetScaleX().GetDenominator() );
1431 rOStm.WriteInt32( aMapMode.GetScaleY().GetNumerator() );
1432 rOStm.WriteInt32( aMapMode.GetScaleY().GetDenominator() );
1433 rOStm.WriteInt32( aMapMode.GetOrigin().X() );
1434 rOStm.WriteInt32( aMapMode.GetOrigin().Y() );
1437 // write data block
1438 if( !rOStm.GetError() )
1440 const sal_uLong nDataStart = rOStm.Tell();
1442 if( ImplIsSupportedGraphic() )
1443 WriteImpGraphic( rOStm, *this );
1445 if( !rOStm.GetError() )
1447 const sal_uLong nStmPos2 = rOStm.Tell();
1448 rOStm.Seek( nDataFieldPos );
1449 rOStm.WriteInt32( nStmPos2 - nDataStart );
1450 rOStm.Seek( nStmPos2 );
1451 bRet = true;
1455 rOStm.SetEndian( nOldFormat );
1458 return bRet;
1461 bool ImpGraphic::ImplSwapOut()
1463 bool bRet = false;
1465 if( !ImplIsSwapOut() )
1467 ::utl::TempFile aTempFile;
1468 const INetURLObject aTmpURL( aTempFile.GetURL() );
1470 if( !aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty() )
1472 std::unique_ptr<SvStream> xOStm;
1475 xOStm = ::utl::UcbStreamHelper::CreateStream( aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
1477 catch( const css::uno::Exception& )
1480 if( xOStm )
1482 xOStm->SetVersion( SOFFICE_FILEFORMAT_50 );
1483 xOStm->SetCompressMode( SvStreamCompressFlags::NATIVE );
1485 bRet = ImplSwapOut( xOStm.get() );
1486 if( bRet )
1488 mpSwapFile = std::make_unique<ImpSwapFile>();
1489 mpSwapFile->aSwapURL = aTmpURL;
1490 mpSwapFile->maOriginURL = getOriginURL();
1492 else
1494 xOStm.reset();
1498 ::ucbhelper::Content aCnt( aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1499 css::uno::Reference< css::ucb::XCommandEnvironment >(),
1500 comphelper::getProcessComponentContext() );
1502 aCnt.executeCommand( "delete", css::uno::makeAny( true ) );
1504 catch( const css::ucb::ContentCreationException& )
1507 catch( const css::uno::RuntimeException& )
1510 catch( const css::ucb::CommandAbortedException& )
1513 catch( const css::uno::Exception& )
1521 if (bRet)
1522 vcl::graphic::Manager::get().swappedOut(this);
1523 return bRet;
1526 bool ImpGraphic::ImplSwapOut( SvStream* xOStm )
1528 bool bRet = false;
1530 if( xOStm )
1532 xOStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE );
1534 if( !xOStm->GetError() && ImplWriteEmbedded( *xOStm ) )
1536 xOStm->Flush();
1538 if( !xOStm->GetError() )
1540 ImplCreateSwapInfo();
1541 ImplClearGraphics();
1542 bRet = mbSwapOut = true;
1546 else
1548 SAL_WARN("vcl.gdi", "Graphic SwapOut: No stream for swap out!");
1551 return bRet;
1554 bool ImpGraphic::ensureAvailable() const
1556 auto pThis = const_cast<ImpGraphic*>(this);
1558 if (ImplIsSwapOut())
1559 return pThis->ImplSwapIn();
1561 pThis->maLastUsed = std::chrono::high_resolution_clock::now();
1562 return true;
1565 bool ImpGraphic::loadPrepared()
1567 Graphic aGraphic;
1568 if (mpGfxLink->LoadNative(aGraphic))
1570 GraphicExternalLink aLink = maGraphicExternalLink;
1572 Size aPrefSize = maSwapInfo.maPrefSize;
1573 *this = *aGraphic.ImplGetImpGraphic();
1574 if (aPrefSize.getWidth() && aPrefSize.getHeight())
1576 // Use custom preferred size if it was set when the graphic was still unloaded.
1577 ImplSetPrefSize(aPrefSize);
1580 maGraphicExternalLink = aLink;
1582 return true;
1584 return false;
1587 bool ImpGraphic::ImplSwapIn()
1589 bool bRet = false;
1591 if (!ImplIsSwapOut())
1592 return bRet;
1594 if (mbPrepared)
1596 bRet = loadPrepared();
1598 else
1600 OUString aSwapURL;
1602 if( mpSwapFile )
1603 aSwapURL = mpSwapFile->aSwapURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1605 if( !aSwapURL.isEmpty() )
1607 std::unique_ptr<SvStream> xIStm;
1610 xIStm = ::utl::UcbStreamHelper::CreateStream( aSwapURL, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
1612 catch( const css::uno::Exception& )
1616 if( xIStm )
1618 xIStm->SetVersion( SOFFICE_FILEFORMAT_50 );
1619 xIStm->SetCompressMode( SvStreamCompressFlags::NATIVE );
1621 bRet = ImplSwapIn( xIStm.get() );
1622 xIStm.reset();
1623 if (mpSwapFile)
1624 setOriginURL(mpSwapFile->maOriginURL);
1625 mpSwapFile.reset();
1630 if (bRet)
1631 vcl::graphic::Manager::get().swappedIn(this);
1633 return bRet;
1636 bool ImpGraphic::ImplSwapIn( SvStream* xIStm )
1638 bool bRet = false;
1640 if( xIStm )
1642 xIStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE );
1644 if( !xIStm->GetError() )
1646 //keep the swap file alive, because its quite possibly the backing storage
1647 //for xIStm
1648 std::shared_ptr<ImpSwapFile> xSwapFile(std::move(mpSwapFile));
1649 assert(!mpSwapFile);
1651 std::shared_ptr<GraphicReader> xContext(std::move(mpContext));
1652 assert(!mpContext);
1654 bool bDummyContext = mbDummyContext;
1655 mbDummyContext = false;
1657 bRet = ImplReadEmbedded( *xIStm );
1659 //restore ownership of the swap file and context
1660 mpSwapFile = std::move(xSwapFile);
1661 mpContext = std::move(xContext);
1662 mbDummyContext = bDummyContext;
1664 if (!bRet)
1666 //throw away swapfile, etc.
1667 ImplClear();
1670 mbSwapOut = false;
1674 return bRet;
1677 void ImpGraphic::ImplSetLink(const std::shared_ptr<GfxLink>& rGfxLink)
1679 ensureAvailable();
1681 mpGfxLink = rGfxLink;
1684 std::shared_ptr<GfxLink> ImpGraphic::ImplGetSharedGfxLink() const
1686 return mpGfxLink;
1689 GfxLink ImpGraphic::ImplGetLink()
1691 ensureAvailable();
1693 return( mpGfxLink ? *mpGfxLink : GfxLink() );
1696 bool ImpGraphic::ImplIsLink() const
1698 return ( bool(mpGfxLink) );
1701 BitmapChecksum ImpGraphic::ImplGetChecksum() const
1703 if (mnChecksum != 0)
1704 return mnChecksum;
1706 BitmapChecksum nRet = 0;
1708 ensureAvailable();
1710 if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
1712 switch( meType )
1714 case GraphicType::Default:
1715 break;
1717 case GraphicType::Bitmap:
1719 if(maVectorGraphicData)
1720 nRet = maVectorGraphicData->GetChecksum();
1721 else if (mpPdfData && !mpPdfData->empty())
1722 // Include the PDF data in the checksum, so a metafile with
1723 // and without PDF data is considered to be different.
1724 nRet = vcl_get_checksum(nRet, mpPdfData->data(), mpPdfData->size());
1725 else if( mpAnimation )
1726 nRet = mpAnimation->GetChecksum();
1727 else
1728 nRet = maEx.GetChecksum();
1730 break;
1732 default:
1733 nRet = maMetaFile.GetChecksum();
1734 break;
1738 mnChecksum = nRet;
1739 return nRet;
1742 bool ImpGraphic::ImplExportNative( SvStream& rOStm ) const
1744 bool bResult = false;
1746 ensureAvailable();
1748 if( !rOStm.GetError() )
1750 if( !ImplIsSwapOut() )
1752 if( mpGfxLink && mpGfxLink->IsNative() )
1753 bResult = mpGfxLink->ExportNative( rOStm );
1754 else
1756 WriteImpGraphic( rOStm, *this );
1757 bResult = ( rOStm.GetError() == ERRCODE_NONE );
1760 else
1761 rOStm.SetError( SVSTREAM_GENERALERROR );
1764 return bResult;
1767 static std::map<BitmapChecksum, std::shared_ptr<std::vector<sal_Int8>>> sPdfDataCache;
1769 void ReadImpGraphic( SvStream& rIStm, ImpGraphic& rImpGraphic )
1771 if (rIStm.GetError())
1772 return;
1774 const sal_uLong nStmPos1 = rIStm.Tell();
1775 sal_uInt32 nTmp;
1777 rImpGraphic.ImplClear();
1779 // read Id
1780 rIStm.ReadUInt32( nTmp );
1782 // if there is no more data, avoid further expensive
1783 // reading which will create VDevs and other stuff, just to
1784 // read nothing. CAUTION: Eof is only true AFTER reading another
1785 // byte, a speciality of SvMemoryStream (!)
1786 if (!rIStm.good())
1787 return;
1789 if (NATIVE_FORMAT_50 == nTmp)
1791 Graphic aGraphic;
1792 GfxLink aLink;
1794 // read compat info
1795 std::unique_ptr<VersionCompat> pCompat(new VersionCompat( rIStm, StreamMode::READ ));
1796 pCompat.reset(); // destructor writes stuff into the header
1798 ReadGfxLink( rIStm, aLink );
1800 // set dummy link to avoid creation of additional link after filtering;
1801 // we set a default link to avoid unnecessary swapping of native data
1802 aGraphic.SetGfxLink(std::make_shared<GfxLink>());
1804 if( !rIStm.GetError() && aLink.LoadNative( aGraphic ) )
1806 // set link only, if no other link was set
1807 const bool bSetLink = !rImpGraphic.mpGfxLink;
1809 // assign graphic
1810 rImpGraphic = *aGraphic.ImplGetImpGraphic();
1812 if( aLink.IsPrefMapModeValid() )
1813 rImpGraphic.ImplSetPrefMapMode( aLink.GetPrefMapMode() );
1815 if( aLink.IsPrefSizeValid() )
1816 rImpGraphic.ImplSetPrefSize( aLink.GetPrefSize() );
1818 if( bSetLink )
1819 rImpGraphic.ImplSetLink(std::make_shared<GfxLink>(aLink));
1821 else
1823 rIStm.Seek( nStmPos1 );
1824 rIStm.SetError( ERRCODE_IO_WRONGFORMAT );
1826 return;
1829 BitmapEx aBmpEx;
1830 const SvStreamEndian nOldFormat = rIStm.GetEndian();
1832 rIStm.SeekRel( -4 );
1833 rIStm.SetEndian( SvStreamEndian::LITTLE );
1834 ReadDIBBitmapEx(aBmpEx, rIStm);
1836 if( !rIStm.GetError() )
1838 sal_uInt32 nMagic1(0), nMagic2(0);
1839 sal_uLong nActPos = rIStm.Tell();
1841 rIStm.ReadUInt32( nMagic1 ).ReadUInt32( nMagic2 );
1842 rIStm.Seek( nActPos );
1844 rImpGraphic = ImpGraphic( aBmpEx );
1846 if( !rIStm.GetError() && ( 0x5344414e == nMagic1 ) && ( 0x494d4931 == nMagic2 ) )
1848 rImpGraphic.mpAnimation = std::make_unique<Animation>();
1849 ReadAnimation( rIStm, *rImpGraphic.mpAnimation );
1851 // #108077# manually set loaded BmpEx to Animation
1852 // (which skips loading its BmpEx if already done)
1853 rImpGraphic.mpAnimation->SetBitmapEx(aBmpEx);
1855 else
1856 rIStm.ResetError();
1858 else
1860 GDIMetaFile aMtf;
1862 rIStm.Seek( nStmPos1 );
1863 rIStm.ResetError();
1864 ReadGDIMetaFile( rIStm, aMtf );
1866 if( !rIStm.GetError() )
1868 rImpGraphic = aMtf;
1870 else
1872 ErrCode nOrigError = rIStm.GetErrorCode();
1873 // try to stream in Svg defining data (length, byte array and evtl. path)
1874 // See below (operator<<) for more information
1875 const sal_uInt32 nSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
1876 const sal_uInt32 nWmfMagic((sal_uInt32('w') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
1877 const sal_uInt32 nEmfMagic((sal_uInt32('e') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
1878 sal_uInt32 nMagic;
1879 rIStm.Seek(nStmPos1);
1880 rIStm.ResetError();
1881 rIStm.ReadUInt32( nMagic );
1883 if (nSvgMagic == nMagic || nWmfMagic == nMagic || nEmfMagic == nMagic)
1885 sal_uInt32 nVectorGraphicDataArrayLength(0);
1886 rIStm.ReadUInt32(nVectorGraphicDataArrayLength);
1888 if (nVectorGraphicDataArrayLength)
1890 VectorGraphicDataArray aNewData(nVectorGraphicDataArrayLength);
1892 rIStm.ReadBytes(aNewData.getArray(), nVectorGraphicDataArrayLength);
1893 OUString aPath = rIStm.ReadUniOrByteString(rIStm.GetStreamCharSet());
1895 if (!rIStm.GetError())
1897 VectorGraphicDataType aDataType(VectorGraphicDataType::Svg);
1899 if (nWmfMagic == nMagic)
1901 aDataType = VectorGraphicDataType::Wmf;
1903 else if (nEmfMagic == nMagic)
1905 aDataType = VectorGraphicDataType::Emf;
1908 VectorGraphicDataPtr aVectorGraphicDataPtr(new VectorGraphicData(aNewData, aPath, aDataType));
1909 rImpGraphic = aVectorGraphicDataPtr;
1913 else if (nMagic == nPdfMagic)
1915 // Stream in PDF data.
1916 BitmapChecksum nPdfId = 0;
1917 rIStm.ReadUInt64(nPdfId);
1919 rImpGraphic.mnPageNumber = 0;
1920 rIStm.ReadInt32(rImpGraphic.mnPageNumber);
1922 auto it = sPdfDataCache.find(nPdfId);
1923 assert(it != sPdfDataCache.end());
1925 rImpGraphic.mpPdfData = it->second;
1927 Bitmap aBitmap;
1928 rImpGraphic.maEx = aBitmap;
1930 std::vector<Bitmap> aBitmaps;
1931 if (vcl::RenderPDFBitmaps(rImpGraphic.mpPdfData->data(), rImpGraphic.mpPdfData->size(), aBitmaps, rImpGraphic.mnPageNumber, 1) == 1)
1932 rImpGraphic.maEx = aBitmaps[0];
1934 rImpGraphic.meType = GraphicType::Bitmap;
1936 else
1938 rIStm.SetError(nOrigError);
1941 rIStm.Seek(nStmPos1);
1945 rIStm.SetEndian( nOldFormat );
1948 void WriteImpGraphic(SvStream& rOStm, const ImpGraphic& rImpGraphic)
1950 if (rOStm.GetError())
1951 return;
1953 rImpGraphic.ensureAvailable();
1955 if (rImpGraphic.ImplIsSwapOut())
1957 rOStm.SetError( SVSTREAM_GENERALERROR );
1958 return;
1961 if( ( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 ) &&
1962 ( rOStm.GetCompressMode() & SvStreamCompressFlags::NATIVE ) &&
1963 rImpGraphic.mpGfxLink && rImpGraphic.mpGfxLink->IsNative() &&
1964 !rImpGraphic.hasPdfData())
1966 // native format
1967 rOStm.WriteUInt32( NATIVE_FORMAT_50 );
1969 // write compat info
1970 std::unique_ptr<VersionCompat> pCompat(new VersionCompat( rOStm, StreamMode::WRITE, 1 ));
1971 pCompat.reset(); // destructor writes stuff into the header
1973 rImpGraphic.mpGfxLink->SetPrefMapMode( rImpGraphic.ImplGetPrefMapMode() );
1974 rImpGraphic.mpGfxLink->SetPrefSize( rImpGraphic.ImplGetPrefSize() );
1975 WriteGfxLink( rOStm, *rImpGraphic.mpGfxLink );
1977 else
1979 // own format
1980 const SvStreamEndian nOldFormat = rOStm.GetEndian();
1981 rOStm.SetEndian( SvStreamEndian::LITTLE );
1983 switch( rImpGraphic.ImplGetType() )
1985 case GraphicType::NONE:
1986 case GraphicType::Default:
1987 break;
1989 case GraphicType::Bitmap:
1991 if(rImpGraphic.getVectorGraphicData().get())
1993 // stream out Vector Graphic defining data (length, byte array and evtl. path)
1994 // this is used e.g. in swapping out graphic data and in transporting it over UNO API
1995 // as sequence of bytes, but AFAIK not written anywhere to any kind of file, so it should be
1996 // no problem to extend it; only used at runtime
1997 switch (rImpGraphic.getVectorGraphicData()->getVectorGraphicDataType())
1999 case VectorGraphicDataType::Wmf:
2001 const sal_uInt32 nWmfMagic((sal_uInt32('w') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
2002 rOStm.WriteUInt32(nWmfMagic);
2003 break;
2005 case VectorGraphicDataType::Emf:
2007 const sal_uInt32 nEmfMagic((sal_uInt32('e') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
2008 rOStm.WriteUInt32(nEmfMagic);
2009 break;
2011 default: // case VectorGraphicDataType::Svg:
2013 const sal_uInt32 nSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
2014 rOStm.WriteUInt32(nSvgMagic);
2015 break;
2019 rOStm.WriteUInt32( rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength() );
2020 rOStm.WriteBytes(rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArray().getConstArray(),
2021 rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength());
2022 rOStm.WriteUniOrByteString(rImpGraphic.getVectorGraphicData()->getPath(),
2023 rOStm.GetStreamCharSet());
2025 else if (rImpGraphic.hasPdfData())
2027 BitmapChecksum nPdfId = vcl_get_checksum(0, rImpGraphic.mpPdfData->data(), rImpGraphic.mpPdfData->size());
2028 if (sPdfDataCache.find(nPdfId) == sPdfDataCache.end())
2029 sPdfDataCache.emplace(nPdfId, rImpGraphic.mpPdfData);
2031 // Stream out PDF data.
2032 rOStm.WriteUInt32(nPdfMagic);
2033 rOStm.WriteUInt64(nPdfId);
2034 rOStm.WriteInt32(rImpGraphic.mnPageNumber);
2036 else if( rImpGraphic.ImplIsAnimated())
2038 WriteAnimation( rOStm, *rImpGraphic.mpAnimation );
2040 else
2042 WriteDIBBitmapEx(rImpGraphic.maEx, rOStm);
2045 break;
2047 default:
2049 if( rImpGraphic.ImplIsSupportedGraphic() )
2050 WriteGDIMetaFile( rOStm, rImpGraphic.maMetaFile );
2052 break;
2055 rOStm.SetEndian( nOldFormat );
2059 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */