blob: ea2091cc33ee8b887368bb45ac7872f6af9cd1f7 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svtools.hxx"
#define ENABLE_BYTESTRING_STREAM_OPERATORS
#include <algorithm>
#include <tools/vcompat.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <unotools/localfilehelper.hxx>
#include <unotools/tempfile.hxx>
#include <vcl/svapp.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/metaact.hxx>
#include <vcl/virdev.hxx>
#include <vcl/salbtype.hxx>
#include <unotools/cacheoptions.hxx>
#include <svtools/grfmgr.hxx>
// --> OD 2010-01-04 #i105243#
#include <vcl/pdfextoutdevdata.hxx>
// <--
// -----------
// - Defines -
// -----------
#define WATERMARK_LUM_OFFSET 50
#define WATERMARK_CON_OFFSET -70
// -----------
// - statics -
// -----------
GraphicManager* GraphicObject::mpGlobalMgr = NULL;
// ---------------------
// - GrfDirectCacheObj -
// ---------------------
struct GrfSimpleCacheObj
{
Graphic maGraphic;
GraphicAttr maAttr;
GrfSimpleCacheObj( const Graphic& rGraphic, const GraphicAttr& rAttr ) :
maGraphic( rGraphic ), maAttr( rAttr ) {}
};
// -----------------
// - GraphicObject -
// -----------------
TYPEINIT1_AUTOFACTORY( GraphicObject, SvDataCopyStream );
// unique increasing ID for being able to detect the GraphicObject with the
// oldest last data changes
static sal_uLong aIncrementingTimeOfLastDataChange = 1;
void GraphicObject::ImplAfterDataChange()
{
// set unique timestamp ID of last data change
mnDataChangeTimeStamp = aIncrementingTimeOfLastDataChange++;
// check memory footprint of all GraphicObjects managed and evtl. take action
GetGraphicManager().ImplCheckSizeOfSwappedInGraphics();
}
// -----------------------------------------------------------------------------
GraphicObject::GraphicObject( const GraphicManager* pMgr ) :
mpLink ( NULL ),
mpUserData ( NULL )
{
ImplConstruct();
ImplAssignGraphicData();
ImplSetGraphicManager( pMgr );
}
// -----------------------------------------------------------------------------
GraphicObject::GraphicObject( const Graphic& rGraphic, const GraphicManager* pMgr ) :
maGraphic ( rGraphic ),
mpLink ( NULL ),
mpUserData ( NULL )
{
ImplConstruct();
ImplAssignGraphicData();
ImplSetGraphicManager( pMgr );
}
// -----------------------------------------------------------------------------
GraphicObject::GraphicObject( const Graphic& rGraphic, const String& rLink, const GraphicManager* pMgr ) :
maGraphic ( rGraphic ),
mpLink ( rLink.Len() ? ( new String( rLink ) ) : NULL ),
mpUserData ( NULL )
{
ImplConstruct();
ImplAssignGraphicData();
ImplSetGraphicManager( pMgr );
}
// -----------------------------------------------------------------------------
GraphicObject::GraphicObject( const GraphicObject& rGraphicObj, const GraphicManager* pMgr ) :
SvDataCopyStream(),
maGraphic ( rGraphicObj.GetGraphic() ),
maAttr ( rGraphicObj.maAttr ),
mpLink ( rGraphicObj.mpLink ? ( new String( *rGraphicObj.mpLink ) ) : NULL ),
mpUserData ( rGraphicObj.mpUserData ? ( new String( *rGraphicObj.mpUserData ) ) : NULL )
{
ImplConstruct();
ImplAssignGraphicData();
ImplSetGraphicManager( pMgr, NULL, &rGraphicObj );
}
// -----------------------------------------------------------------------------
GraphicObject::GraphicObject( const ByteString& rUniqueID, const GraphicManager* pMgr ) :
mpLink ( NULL ),
mpUserData ( NULL )
{
ImplConstruct();
// assign default properties
ImplAssignGraphicData();
ImplSetGraphicManager( pMgr, &rUniqueID );
// update properties
ImplAssignGraphicData();
}
// -----------------------------------------------------------------------------
GraphicObject::~GraphicObject()
{
if( mpMgr )
{
mpMgr->ImplUnregisterObj( *this );
if( ( mpMgr == mpGlobalMgr ) && !mpGlobalMgr->ImplHasObjects() )
delete mpGlobalMgr, mpGlobalMgr = NULL;
}
delete mpSwapOutTimer;
delete mpSwapStreamHdl;
delete mpLink;
delete mpUserData;
delete mpSimpleCache;
}
// -----------------------------------------------------------------------------
void GraphicObject::ImplConstruct()
{
mpMgr = NULL;
mpSwapStreamHdl = NULL;
mpSwapOutTimer = NULL;
mpSimpleCache = NULL;
mnAnimationLoopCount = 0;
mbAutoSwapped = sal_False;
mbIsInSwapIn = sal_False;
mbIsInSwapOut = sal_False;
// Init with a unique, increasing ID
mnDataChangeTimeStamp = aIncrementingTimeOfLastDataChange++;
}
// -----------------------------------------------------------------------------
void GraphicObject::ImplAssignGraphicData()
{
maPrefSize = maGraphic.GetPrefSize();
maPrefMapMode = maGraphic.GetPrefMapMode();
mnSizeBytes = maGraphic.GetSizeBytes();
meType = maGraphic.GetType();
mbTransparent = maGraphic.IsTransparent();
mbAlpha = maGraphic.IsAlpha();
mbAnimated = maGraphic.IsAnimated();
mbEPS = maGraphic.IsEPS();
mnAnimationLoopCount = ( mbAnimated ? maGraphic.GetAnimationLoopCount() : 0 );
}
// -----------------------------------------------------------------------------
void GraphicObject::ImplSetGraphicManager( const GraphicManager* pMgr, const ByteString* pID, const GraphicObject* pCopyObj )
{
if( !mpMgr || ( pMgr != mpMgr ) )
{
if( !pMgr && mpMgr && ( mpMgr == mpGlobalMgr ) )
return;
else
{
if( mpMgr )
{
mpMgr->ImplUnregisterObj( *this );
if( ( mpMgr == mpGlobalMgr ) && !mpGlobalMgr->ImplHasObjects() )
delete mpGlobalMgr, mpGlobalMgr = NULL;
}
if( !pMgr )
{
if( !mpGlobalMgr )
{
SvtCacheOptions aCacheOptions;
mpGlobalMgr = new GraphicManager( aCacheOptions.GetGraphicManagerTotalCacheSize(),
aCacheOptions.GetGraphicManagerObjectCacheSize() );
mpGlobalMgr->SetCacheTimeout( aCacheOptions.GetGraphicManagerObjectReleaseTime() );
}
mpMgr = mpGlobalMgr;
}
else
mpMgr = (GraphicManager*) pMgr;
mpMgr->ImplRegisterObj( *this, maGraphic, pID, pCopyObj );
}
}
}
// -----------------------------------------------------------------------------
void GraphicObject::ImplAutoSwapIn()
{
if( IsSwappedOut() )
{
if( mpMgr && mpMgr->ImplFillSwappedGraphicObject( *this, maGraphic ) )
mbAutoSwapped = sal_False;
else
{
mbIsInSwapIn = sal_True;
if( maGraphic.SwapIn() )
mbAutoSwapped = sal_False;
else
{
SvStream* pStream = GetSwapStream();
if( GRFMGR_AUTOSWAPSTREAM_NONE != pStream )
{
if( GRFMGR_AUTOSWAPSTREAM_LINK == pStream )
{
if( HasLink() )
{
String aURLStr;
if( ::utl::LocalFileHelper::ConvertPhysicalNameToURL( GetLink(), aURLStr ) )
{
SvStream* pIStm = ::utl::UcbStreamHelper::CreateStream( aURLStr, STREAM_READ );
if( pIStm )
{
(*pIStm) >> maGraphic;
mbAutoSwapped = ( maGraphic.GetType() != GRAPHIC_NONE );
delete pIStm;
}
}
}
}
else if( GRFMGR_AUTOSWAPSTREAM_TEMP == pStream )
mbAutoSwapped = !maGraphic.SwapIn();
else if( GRFMGR_AUTOSWAPSTREAM_LOADED == pStream )
mbAutoSwapped = maGraphic.IsSwapOut();
else
{
mbAutoSwapped = !maGraphic.SwapIn( pStream );
delete pStream;
}
}
else
{
DBG_ASSERT( ( GRAPHIC_NONE == meType ) || ( GRAPHIC_DEFAULT == meType ),
"GraphicObject::ImplAutoSwapIn: could not get stream to swap in graphic! (=>KA)" );
}
}
mbIsInSwapIn = sal_False;
if( !mbAutoSwapped && mpMgr )
mpMgr->ImplGraphicObjectWasSwappedIn( *this );
}
// Handle evtl. needed AfterDataChanges
ImplAfterDataChange();
}
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::ImplGetCropParams( OutputDevice* pOut, Point& rPt, Size& rSz, const GraphicAttr* pAttr,
PolyPolygon& rClipPolyPoly, sal_Bool& bRectClipRegion ) const
{
sal_Bool bRet = sal_False;
if( GetType() != GRAPHIC_NONE )
{
Polygon aClipPoly( Rectangle( rPt, rSz ) );
const sal_uInt16 nRot10 = pAttr->GetRotation() % 3600;
const Point aOldOrigin( rPt );
// --> OD 2005-09-30 #i54875# - It's not needed to get the graphic again.
// const Graphic& rGraphic = GetGraphic();
// <--
const MapMode aMap100( MAP_100TH_MM );
Size aSize100;
long nTotalWidth, nTotalHeight;
long nNewLeft, nNewTop, nNewRight, nNewBottom;
double fScale;
if( nRot10 )
{
aClipPoly.Rotate( rPt, nRot10 );
bRectClipRegion = sal_False;
}
else
bRectClipRegion = sal_True;
rClipPolyPoly = aClipPoly;
// --> OD 2005-09-30 #i54875# - directly access member <maGraphic> to
// get <PrefSize> and <PrefMapMode>.
// if( rGraphic.GetPrefMapMode() == MAP_PIXEL )
// aSize100 = Application::GetDefaultDevice()->PixelToLogic( rGraphic.GetPrefSize(), aMap100 );
// else
// aSize100 = pOut->LogicToLogic( rGraphic.GetPrefSize(), rGraphic.GetPrefMapMode(), aMap100 );
if( maGraphic.GetPrefMapMode() == MAP_PIXEL )
aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 );
else
{
MapMode m(maGraphic.GetPrefMapMode());
aSize100 = pOut->LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 );
}
// <--
nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop();
nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop();
if( aSize100.Width() > 0 && aSize100.Height() > 0 && nTotalWidth > 0 && nTotalHeight > 0 )
{
fScale = (double) aSize100.Width() / nTotalWidth;
nNewLeft = -FRound( ( ( pAttr->GetMirrorFlags() & BMP_MIRROR_HORZ ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * fScale );
nNewRight = nNewLeft + FRound( aSize100.Width() * fScale ) - 1;
fScale = (double) rSz.Width() / aSize100.Width();
rPt.X() += FRound( nNewLeft * fScale );
rSz.Width() = FRound( ( nNewRight - nNewLeft + 1 ) * fScale );
fScale = (double) aSize100.Height() / nTotalHeight;
nNewTop = -FRound( ( ( pAttr->GetMirrorFlags() & BMP_MIRROR_VERT ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * fScale );
nNewBottom = nNewTop + FRound( aSize100.Height() * fScale ) - 1;
fScale = (double) rSz.Height() / aSize100.Height();
rPt.Y() += FRound( nNewTop * fScale );
rSz.Height() = FRound( ( nNewBottom - nNewTop + 1 ) * fScale );
if( nRot10 )
{
Polygon aOriginPoly( 1 );
aOriginPoly[ 0 ] = rPt;
aOriginPoly.Rotate( aOldOrigin, nRot10 );
rPt = aOriginPoly[ 0 ];
}
bRet = sal_True;
}
}
return bRet;
}
// -----------------------------------------------------------------------------
GraphicObject& GraphicObject::operator=( const GraphicObject& rGraphicObj )
{
if( &rGraphicObj != this )
{
mpMgr->ImplUnregisterObj( *this );
delete mpSwapStreamHdl, mpSwapStreamHdl = NULL;
delete mpSimpleCache, mpSimpleCache = NULL;
delete mpLink;
delete mpUserData;
maGraphic = rGraphicObj.GetGraphic();
maAttr = rGraphicObj.maAttr;
mpLink = rGraphicObj.mpLink ? new String( *rGraphicObj.mpLink ) : NULL;
mpUserData = rGraphicObj.mpUserData ? new String( *rGraphicObj.mpUserData ) : NULL;
ImplAssignGraphicData();
mbAutoSwapped = sal_False;
mpMgr = rGraphicObj.mpMgr;
mpMgr->ImplRegisterObj( *this, maGraphic, NULL, &rGraphicObj );
}
return *this;
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const
{
return( ( rGraphicObj.maGraphic == maGraphic ) &&
( rGraphicObj.maAttr == maAttr ) &&
( rGraphicObj.GetLink() == GetLink() ) );
}
// ------------------------------------------------------------------------
void GraphicObject::Load( SvStream& rIStm )
{
rIStm >> *this;
}
// ------------------------------------------------------------------------
void GraphicObject::Save( SvStream& rOStm )
{
rOStm << *this;
}
// ------------------------------------------------------------------------
void GraphicObject::Assign( const SvDataCopyStream& rCopyStream )
{
*this = (const GraphicObject& ) rCopyStream;
}
// -----------------------------------------------------------------------------
ByteString GraphicObject::GetUniqueID() const
{
if ( !IsInSwapIn() && IsEPS() )
const_cast<GraphicObject*>(this)->FireSwapInRequest();
ByteString aRet;
if( mpMgr )
aRet = mpMgr->ImplGetUniqueID( *this );
return aRet;
}
// -----------------------------------------------------------------------------
sal_uLong GraphicObject::GetChecksum() const
{
return( ( maGraphic.IsSupportedGraphic() && !maGraphic.IsSwapOut() ) ? maGraphic.GetChecksum() : 0 );
}
// -----------------------------------------------------------------------------
SvStream* GraphicObject::GetSwapStream() const
{
return( HasSwapStreamHdl() ? (SvStream*) mpSwapStreamHdl->Call( (void*) this ) : GRFMGR_AUTOSWAPSTREAM_NONE );
}
// -----------------------------------------------------------------------------
// !!! to be removed
sal_uLong GraphicObject::GetReleaseFromCache() const
{
return 0;
}
// -----------------------------------------------------------------------------
void GraphicObject::SetAttr( const GraphicAttr& rAttr )
{
maAttr = rAttr;
if( mpSimpleCache && ( mpSimpleCache->maAttr != rAttr ) )
delete mpSimpleCache, mpSimpleCache = NULL;
}
// -----------------------------------------------------------------------------
void GraphicObject::SetLink()
{
if( mpLink )
delete mpLink, mpLink = NULL;
}
// -----------------------------------------------------------------------------
void GraphicObject::SetLink( const String& rLink )
{
delete mpLink, mpLink = new String( rLink );
}
// -----------------------------------------------------------------------------
String GraphicObject::GetLink() const
{
if( mpLink )
return *mpLink;
else
return String();
}
// -----------------------------------------------------------------------------
void GraphicObject::SetUserData()
{
if( mpUserData )
delete mpUserData, mpUserData = NULL;
}
// -----------------------------------------------------------------------------
void GraphicObject::SetUserData( const String& rUserData )
{
delete mpUserData, mpUserData = new String( rUserData );
}
// -----------------------------------------------------------------------------
String GraphicObject::GetUserData() const
{
if( mpUserData )
return *mpUserData;
else
return String();
}
// -----------------------------------------------------------------------------
void GraphicObject::SetSwapStreamHdl()
{
if( mpSwapStreamHdl )
{
delete mpSwapOutTimer, mpSwapOutTimer = NULL;
delete mpSwapStreamHdl, mpSwapStreamHdl = NULL;
}
}
// -----------------------------------------------------------------------------
void GraphicObject::SetSwapStreamHdl( const Link& rHdl, const sal_uLong nSwapOutTimeout )
{
delete mpSwapStreamHdl, mpSwapStreamHdl = new Link( rHdl );
if( nSwapOutTimeout )
{
if( !mpSwapOutTimer )
{
mpSwapOutTimer = new Timer;
mpSwapOutTimer->SetTimeoutHdl( LINK( this, GraphicObject, ImplAutoSwapOutHdl ) );
}
mpSwapOutTimer->SetTimeout( nSwapOutTimeout );
mpSwapOutTimer->Start();
}
else
delete mpSwapOutTimer, mpSwapOutTimer = NULL;
}
// -----------------------------------------------------------------------------
Link GraphicObject::GetSwapStreamHdl() const
{
if( mpSwapStreamHdl )
return *mpSwapStreamHdl;
else
return Link();
}
// -----------------------------------------------------------------------------
void GraphicObject::FireSwapInRequest()
{
ImplAutoSwapIn();
}
// -----------------------------------------------------------------------------
void GraphicObject::FireSwapOutRequest()
{
ImplAutoSwapOutHdl( NULL );
}
// -----------------------------------------------------------------------------
void GraphicObject::GraphicManagerDestroyed()
{
// we're alive, but our manager doesn't live anymore ==> connect to default manager
mpMgr = NULL;
ImplSetGraphicManager( NULL );
}
// -----------------------------------------------------------------------------
void GraphicObject::SetGraphicManager( const GraphicManager& rMgr )
{
ImplSetGraphicManager( &rMgr );
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::IsCached( OutputDevice* pOut, const Point& rPt, const Size& rSz,
const GraphicAttr* pAttr, sal_uLong nFlags ) const
{
sal_Bool bRet;
if( nFlags & GRFMGR_DRAW_CACHED )
{
// --> OD 2005-10-11 #i54875# - Consider cropped graphics.
// Note: The graphic manager caches a cropped graphic with its
// uncropped position and size.
// bRet = mpMgr->IsInCache( pOut, rPt, rSz, *this, ( pAttr ? *pAttr : GetAttr() ) );
Point aPt( rPt );
Size aSz( rSz );
if ( pAttr->IsCropped() )
{
PolyPolygon aClipPolyPoly;
sal_Bool bRectClip;
ImplGetCropParams( pOut, aPt, aSz, pAttr, aClipPolyPoly, bRectClip );
}
bRet = mpMgr->IsInCache( pOut, aPt, aSz, *this, ( pAttr ? *pAttr : GetAttr() ) );
}
else
bRet = sal_False;
return bRet;
}
// -----------------------------------------------------------------------------
void GraphicObject::ReleaseFromCache()
{
mpMgr->ReleaseFromCache( *this );
}
// -----------------------------------------------------------------------------
void GraphicObject::SetAnimationNotifyHdl( const Link& rLink )
{
maGraphic.SetAnimationNotifyHdl( rLink );
}
// -----------------------------------------------------------------------------
List* GraphicObject::GetAnimationInfoList() const
{
return maGraphic.GetAnimationInfoList();
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz,
const GraphicAttr* pAttr, sal_uLong nFlags )
{
GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
Point aPt( rPt );
Size aSz( rSz );
const sal_uInt32 nOldDrawMode = pOut->GetDrawMode();
sal_Bool bCropped = aAttr.IsCropped();
sal_Bool bCached = sal_False;
sal_Bool bRet;
// #i29534# Provide output rects for PDF writer
Rectangle aCropRect;
if( !( GRFMGR_DRAW_USE_DRAWMODE_SETTINGS & nFlags ) )
pOut->SetDrawMode( nOldDrawMode & ( ~( DRAWMODE_SETTINGSLINE | DRAWMODE_SETTINGSFILL | DRAWMODE_SETTINGSTEXT | DRAWMODE_SETTINGSGRADIENT ) ) );
// mirrored horizontically
if( aSz.Width() < 0L )
{
aPt.X() += aSz.Width() + 1;
aSz.Width() = -aSz.Width();
aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BMP_MIRROR_HORZ );
}
// mirrored vertically
if( aSz.Height() < 0L )
{
aPt.Y() += aSz.Height() + 1;
aSz.Height() = -aSz.Height();
aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BMP_MIRROR_VERT );
}
if( bCropped )
{
PolyPolygon aClipPolyPoly;
sal_Bool bRectClip;
const sal_Bool bCrop = ImplGetCropParams( pOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip );
pOut->Push( PUSH_CLIPREGION );
if( bCrop )
{
if( bRectClip )
{
// #i29534# Store crop rect for later forwarding to
// PDF writer
aCropRect = aClipPolyPoly.GetBoundRect();
pOut->IntersectClipRegion( aCropRect );
}
else
{
pOut->IntersectClipRegion( aClipPolyPoly );
}
}
}
bRet = mpMgr->DrawObj( pOut, aPt, aSz, *this, aAttr, nFlags, bCached );
if( bCropped )
pOut->Pop();
pOut->SetDrawMode( nOldDrawMode );
// #i29534# Moved below OutDev restoration, to avoid multiple swap-ins
// (code above needs to call GetGraphic twice)
if( bCached )
{
if( mpSwapOutTimer )
mpSwapOutTimer->Start();
else
FireSwapOutRequest();
}
return bRet;
}
// --> OD 2010-01-04 #i105243#
sal_Bool GraphicObject::DrawWithPDFHandling( OutputDevice& rOutDev,
const Point& rPt, const Size& rSz,
const GraphicAttr* pGrfAttr,
const sal_uLong nFlags )
{
const GraphicAttr aGrfAttr( pGrfAttr ? *pGrfAttr : GetAttr() );
// Notify PDF writer about linked graphic (if any)
sal_Bool bWritingPdfLinkedGraphic( sal_False );
Point aPt( rPt );
Size aSz( rSz );
Rectangle aCropRect;
vcl::PDFExtOutDevData* pPDFExtOutDevData =
dynamic_cast<vcl::PDFExtOutDevData*>(rOutDev.GetExtOutDevData());
if( pPDFExtOutDevData )
{
// only delegate image handling to PDF, if no special treatment is necessary
if( GetGraphic().IsLink() &&
rSz.Width() > 0L &&
rSz.Height() > 0L &&
!aGrfAttr.IsSpecialDrawMode() &&
!aGrfAttr.IsMirrored() &&
!aGrfAttr.IsRotated() &&
!aGrfAttr.IsAdjusted() )
{
bWritingPdfLinkedGraphic = true;
if( aGrfAttr.IsCropped() )
{
PolyPolygon aClipPolyPoly;
sal_Bool bRectClip;
const sal_Bool bCrop = ImplGetCropParams( &rOutDev,
aPt, aSz,
&aGrfAttr,
aClipPolyPoly,
bRectClip );
if ( bCrop && bRectClip )
{
aCropRect = aClipPolyPoly.GetBoundRect();
}
}
pPDFExtOutDevData->BeginGroup();
}
}
sal_Bool bRet = Draw( &rOutDev, rPt, rSz, &aGrfAttr, nFlags );
// Notify PDF writer about linked graphic (if any)
if( bWritingPdfLinkedGraphic )
{
pPDFExtOutDevData->EndGroup( const_cast< Graphic& >(GetGraphic()),
aGrfAttr.GetTransparency(),
Rectangle( aPt, aSz ),
aCropRect );
}
return bRet;
}
// <--
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::DrawTiled( OutputDevice* pOut, const Rectangle& rArea, const Size& rSize,
const Size& rOffset, const GraphicAttr* pAttr, sal_uLong nFlags, int nTileCacheSize1D )
{
if( pOut == NULL || rSize.Width() == 0 || rSize.Height() == 0 )
return sal_False;
const MapMode aOutMapMode( pOut->GetMapMode() );
const MapMode aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() );
// #106258# Clamp size to 1 for zero values. This is okay, since
// logical size of zero is handled above already
const Size aOutTileSize( ::std::max( 1L, pOut->LogicToPixel( rSize, aOutMapMode ).Width() ),
::std::max( 1L, pOut->LogicToPixel( rSize, aOutMapMode ).Height() ) );
//#i69780 clip final tile size to a sane max size
while (((sal_Int64)rSize.Width() * nTileCacheSize1D) > SAL_MAX_UINT16)
nTileCacheSize1D /= 2;
while (((sal_Int64)rSize.Height() * nTileCacheSize1D) > SAL_MAX_UINT16)
nTileCacheSize1D /= 2;
return ImplDrawTiled( pOut, rArea, aOutTileSize, rOffset, pAttr, nFlags, nTileCacheSize1D );
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::StartAnimation( OutputDevice* pOut, const Point& rPt, const Size& rSz,
long nExtraData, const GraphicAttr* pAttr, sal_uLong /*nFlags*/,
OutputDevice* pFirstFrameOutDev )
{
sal_Bool bRet = sal_False;
GetGraphic();
if( !IsSwappedOut() )
{
const GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
if( mbAnimated )
{
Point aPt( rPt );
Size aSz( rSz );
sal_Bool bCropped = aAttr.IsCropped();
if( bCropped )
{
PolyPolygon aClipPolyPoly;
sal_Bool bRectClip;
const sal_Bool bCrop = ImplGetCropParams( pOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip );
pOut->Push( PUSH_CLIPREGION );
if( bCrop )
{
if( bRectClip )
pOut->IntersectClipRegion( aClipPolyPoly.GetBoundRect() );
else
pOut->IntersectClipRegion( aClipPolyPoly );
}
}
if( !mpSimpleCache || ( mpSimpleCache->maAttr != aAttr ) || pFirstFrameOutDev )
{
if( mpSimpleCache )
delete mpSimpleCache;
mpSimpleCache = new GrfSimpleCacheObj( GetTransformedGraphic( &aAttr ), aAttr );
mpSimpleCache->maGraphic.SetAnimationNotifyHdl( GetAnimationNotifyHdl() );
}
mpSimpleCache->maGraphic.StartAnimation( pOut, aPt, aSz, nExtraData, pFirstFrameOutDev );
if( bCropped )
pOut->Pop();
bRet = sal_True;
}
else
bRet = Draw( pOut, rPt, rSz, &aAttr, GRFMGR_DRAW_STANDARD );
}
return bRet;
}
// -----------------------------------------------------------------------------
void GraphicObject::StopAnimation( OutputDevice* pOut, long nExtraData )
{
if( mpSimpleCache )
mpSimpleCache->maGraphic.StopAnimation( pOut, nExtraData );
}
// -----------------------------------------------------------------------------
const Graphic& GraphicObject::GetGraphic() const
{
if( mbAutoSwapped )
( (GraphicObject*) this )->ImplAutoSwapIn();
return maGraphic;
}
// -----------------------------------------------------------------------------
void GraphicObject::SetGraphic( const Graphic& rGraphic, const GraphicObject* pCopyObj )
{
mpMgr->ImplUnregisterObj( *this );
if( mpSwapOutTimer )
mpSwapOutTimer->Stop();
maGraphic = rGraphic;
mbAutoSwapped = sal_False;
ImplAssignGraphicData();
delete mpLink, mpLink = NULL;
delete mpSimpleCache, mpSimpleCache = NULL;
mpMgr->ImplRegisterObj( *this, maGraphic, 0, pCopyObj);
if( mpSwapOutTimer )
mpSwapOutTimer->Start();
// Handle evtl. needed AfterDataChanges
ImplAfterDataChange();
}
// -----------------------------------------------------------------------------
void GraphicObject::SetGraphic( const Graphic& rGraphic, const String& rLink )
{
SetGraphic( rGraphic );
mpLink = new String( rLink );
}
// -----------------------------------------------------------------------------
Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode& rDestMap, const GraphicAttr& rAttr ) const
{
// #104550# Extracted from svx/source/svdraw/svdograf.cxx
Graphic aTransGraphic( maGraphic );
const GraphicType eType = GetType();
const Size aSrcSize( aTransGraphic.GetPrefSize() );
// #104115# Convert the crop margins to graphic object mapmode
const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() );
const MapMode aMap100( MAP_100TH_MM );
Size aCropLeftTop;
Size aCropRightBottom;
if( GRAPHIC_GDIMETAFILE == eType )
{
GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() );
if( aMapGraph == MAP_PIXEL )
{
// crops are in 1/100th mm -> to aMapGraph -> to MAP_PIXEL
aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
aMap100);
aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
aMap100);
}
else
{
// crops are in GraphicObject units -> to aMapGraph
aCropLeftTop = OutputDevice::LogicToLogic(
Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
aMap100,
aMapGraph);
aCropRightBottom = OutputDevice::LogicToLogic(
Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
aMap100,
aMapGraph);
}
// #104115# If the metafile is cropped, give it a special
// treatment: clip against the remaining area, scale up such
// that this area later fills the desired size, and move the
// origin to the upper left edge of that area.
if( rAttr.IsCropped() )
{
const MapMode aMtfMapMode( aMtf.GetPrefMapMode() );
Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(),
aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(),
aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(),
aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() );
// #104115# To correctly crop rotated metafiles, clip by view rectangle
aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 );
// #104115# To crop the metafile, scale larger than the output rectangle
aMtf.Scale( (double)rDestSize.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()),
(double)rDestSize.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) );
// #104115# Adapt the pref size by hand (scale changes it
// proportionally, but we want it to be smaller than the
// former size, to crop the excess out)
aMtf.SetPrefSize( Size( (long)((double)rDestSize.Width() * (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width()) + .5),
(long)((double)rDestSize.Height() * (1.0 + (aCropLeftTop.Height() + aCropRightBottom.Height()) / aSrcSize.Height()) + .5) ) );
// #104115# Adapt the origin of the new mapmode, such that it
// is shifted to the place where the cropped output starts
Point aNewOrigin( (long)((double)aMtfMapMode.GetOrigin().X() + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5),
(long)((double)aMtfMapMode.GetOrigin().Y() + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) );
MapMode aNewMap( rDestMap );
aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) );
aMtf.SetPrefMapMode( aNewMap );
}
else
{
aMtf.Scale( Fraction( rDestSize.Width(), aSrcSize.Width() ), Fraction( rDestSize.Height(), aSrcSize.Height() ) );
aMtf.SetPrefMapMode( rDestMap );
}
aTransGraphic = aMtf;
}
else if( GRAPHIC_BITMAP == eType )
{
BitmapEx aBitmapEx( aTransGraphic.GetBitmapEx() );
Rectangle aCropRect;
// convert crops to pixel
if(rAttr.IsCropped())
{
if( aMapGraph == MAP_PIXEL )
{
// crops are in 1/100th mm -> to MAP_PIXEL
aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
aMap100);
aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
aMap100);
}
else
{
// crops are in GraphicObject units -> to MAP_PIXEL
aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
aMapGraph);
aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
aMapGraph);
}
// convert from prefmapmode to pixel
Size aSrcSizePixel(
Application::GetDefaultDevice()->LogicToPixel(
aSrcSize,
aMapGraph));
if(rAttr.IsCropped()
&& (aSrcSizePixel.Width() != aBitmapEx.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmapEx.GetSizePixel().Height())
&& aSrcSizePixel.Width())
{
// the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode())
// and it's internal size (aTransGraphic.GetPrefSize()) is different from it's real pixel size.
// This can be interpreted as this values to be set wrong, but needs to be corrected since e.g.
// existing cropping is calculated based on this logic values already.
// aBitmapEx.Scale(aSrcSizePixel);
// another possibility is to adapt the values created so far with a factor; this
// will keep the original Bitmap untouched and thus quality will not change
// caution: convert to double first, else pretty big errors may occurr
const double fFactorX((double)aBitmapEx.GetSizePixel().Width() / aSrcSizePixel.Width());
const double fFactorY((double)aBitmapEx.GetSizePixel().Height() / aSrcSizePixel.Height());
aCropLeftTop.Width() = basegfx::fround(aCropLeftTop.Width() * fFactorX);
aCropLeftTop.Height() = basegfx::fround(aCropLeftTop.Height() * fFactorY);
aCropRightBottom.Width() = basegfx::fround(aCropRightBottom.Width() * fFactorX);
aCropRightBottom.Height() = basegfx::fround(aCropRightBottom.Height() * fFactorY);
aSrcSizePixel = aBitmapEx.GetSizePixel();
}
// setup crop rectangle in pixel
aCropRect = Rectangle( aCropLeftTop.Width(), aCropLeftTop.Height(),
aSrcSizePixel.Width() - aCropRightBottom.Width(),
aSrcSizePixel.Height() - aCropRightBottom.Height() );
}
// #105641# Also crop animations
if( aTransGraphic.IsAnimated() )
{
sal_uInt16 nFrame;
Animation aAnim( aTransGraphic.GetAnimation() );
for( nFrame=0; nFrame<aAnim.Count(); ++nFrame )
{
AnimationBitmap aAnimBmp( aAnim.Get( nFrame ) );
if( !aCropRect.IsInside( Rectangle(aAnimBmp.aPosPix, aAnimBmp.aSizePix) ) )
{
// setup actual cropping (relative to frame position)
Rectangle aCropRectRel( aCropRect );
aCropRectRel.Move( -aAnimBmp.aPosPix.X(),
-aAnimBmp.aPosPix.Y() );
// cropping affects this frame, apply it then
// do _not_ apply enlargement, this is done below
ImplTransformBitmap( aAnimBmp.aBmpEx, rAttr, Size(), Size(),
aCropRectRel, rDestSize, sal_False );
aAnim.Replace( aAnimBmp, nFrame );
}
// else: bitmap completely within crop area,
// i.e. nothing is cropped away
}
// now, apply enlargement (if any) through global animation size
if( aCropLeftTop.Width() < 0 ||
aCropLeftTop.Height() < 0 ||
aCropRightBottom.Width() < 0 ||
aCropRightBottom.Height() < 0 )
{
Size aNewSize( aAnim.GetDisplaySizePixel() );
aNewSize.Width() += aCropRightBottom.Width() < 0 ? -aCropRightBottom.Width() : 0;
aNewSize.Width() += aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0;
aNewSize.Height() += aCropRightBottom.Height() < 0 ? -aCropRightBottom.Height() : 0;
aNewSize.Height() += aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0;
aAnim.SetDisplaySizePixel( aNewSize );
}
// if topleft has changed, we must move all frames to the
// right and bottom, resp.
if( aCropLeftTop.Width() < 0 ||
aCropLeftTop.Height() < 0 )
{
Point aPosOffset( aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0,
aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
for( nFrame=0; nFrame<aAnim.Count(); ++nFrame )
{
AnimationBitmap aAnimBmp( aAnim.Get( nFrame ) );
aAnimBmp.aPosPix += aPosOffset;
aAnim.Replace( aAnimBmp, nFrame );
}
}
aTransGraphic = aAnim;
}
else
{
ImplTransformBitmap( aBitmapEx, rAttr, aCropLeftTop, aCropRightBottom,
aCropRect, rDestSize, sal_True );
aTransGraphic = aBitmapEx;
}
aTransGraphic.SetPrefSize( rDestSize );
aTransGraphic.SetPrefMapMode( rDestMap );
}
GraphicObject aGrfObj( aTransGraphic );
aTransGraphic = aGrfObj.GetTransformedGraphic( &rAttr );
return aTransGraphic;
}
// -----------------------------------------------------------------------------
Graphic GraphicObject::GetTransformedGraphic( const GraphicAttr* pAttr ) const // TODO: Change to Impl
{
GetGraphic();
Graphic aGraphic;
GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
if( maGraphic.IsSupportedGraphic() && !maGraphic.IsSwapOut() )
{
if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() )
{
if( GetType() == GRAPHIC_BITMAP )
{
if( IsAnimated() )
{
Animation aAnimation( maGraphic.GetAnimation() );
GraphicManager::ImplAdjust( aAnimation, aAttr, ADJUSTMENT_ALL );
aAnimation.SetLoopCount( mnAnimationLoopCount );
aGraphic = aAnimation;
}
else
{
BitmapEx aBmpEx( maGraphic.GetBitmapEx() );
GraphicManager::ImplAdjust( aBmpEx, aAttr, ADJUSTMENT_ALL );
aGraphic = aBmpEx;
}
}
else
{
GDIMetaFile aMtf( maGraphic.GetGDIMetaFile() );
GraphicManager::ImplAdjust( aMtf, aAttr, ADJUSTMENT_ALL );
aGraphic = aMtf;
}
}
else
{
if( ( GetType() == GRAPHIC_BITMAP ) && IsAnimated() )
{
Animation aAnimation( maGraphic.GetAnimation() );
aAnimation.SetLoopCount( mnAnimationLoopCount );
aGraphic = aAnimation;
}
else
aGraphic = maGraphic;
}
}
return aGraphic;
}
// -----------------------------------------------------------------------------
void GraphicObject::ResetAnimationLoopCount()
{
if( IsAnimated() && !IsSwappedOut() )
{
maGraphic.ResetAnimationLoopCount();
if( mpSimpleCache )
mpSimpleCache->maGraphic.ResetAnimationLoopCount();
}
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::SwapOut()
{
sal_Bool bRet = ( !mbAutoSwapped ? maGraphic.SwapOut() : sal_False );
if( bRet && mpMgr )
mpMgr->ImplGraphicObjectWasSwappedOut( *this );
return bRet;
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::SwapOut( SvStream* pOStm )
{
sal_Bool bRet = ( !mbAutoSwapped ? maGraphic.SwapOut( pOStm ) : sal_False );
if( bRet && mpMgr )
mpMgr->ImplGraphicObjectWasSwappedOut( *this );
return bRet;
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::SwapIn()
{
sal_Bool bRet;
if( mbAutoSwapped )
{
ImplAutoSwapIn();
bRet = sal_True;
}
else if( mpMgr && mpMgr->ImplFillSwappedGraphicObject( *this, maGraphic ) )
{
bRet = sal_True;
}
else
{
bRet = maGraphic.SwapIn();
if( bRet && mpMgr )
mpMgr->ImplGraphicObjectWasSwappedIn( *this );
}
if( bRet )
{
ImplAssignGraphicData();
// Handle evtl. needed AfterDataChanges
ImplAfterDataChange();
}
return bRet;
}
// -----------------------------------------------------------------------------
sal_Bool GraphicObject::SwapIn( SvStream* pIStm )
{
sal_Bool bRet;
if( mbAutoSwapped )
{
ImplAutoSwapIn();
bRet = sal_True;
}
else if( mpMgr && mpMgr->ImplFillSwappedGraphicObject( *this, maGraphic ) )
{
bRet = sal_True;
}
else
{
bRet = maGraphic.SwapIn( pIStm );
if( bRet && mpMgr )
mpMgr->ImplGraphicObjectWasSwappedIn( *this );
}
if( bRet )
{
ImplAssignGraphicData();
//
ImplAfterDataChange();
}
return bRet;
}
// -----------------------------------------------------------------------------
void GraphicObject::SetSwapState()
{
if( !IsSwappedOut() )
{
mbAutoSwapped = sal_True;
if( mpMgr )
mpMgr->ImplGraphicObjectWasSwappedOut( *this );
}
}
// -----------------------------------------------------------------------------
IMPL_LINK( GraphicObject, ImplAutoSwapOutHdl, void*, EMPTYARG )
{
if( !IsSwappedOut() )
{
mbIsInSwapOut = sal_True;
SvStream* pStream = GetSwapStream();
if( GRFMGR_AUTOSWAPSTREAM_NONE != pStream )
{
if( GRFMGR_AUTOSWAPSTREAM_LINK == pStream )
mbAutoSwapped = SwapOut( NULL );
else
{
if( GRFMGR_AUTOSWAPSTREAM_TEMP == pStream )
mbAutoSwapped = SwapOut();
else
{
mbAutoSwapped = SwapOut( pStream );
delete pStream;
}
}
}
mbIsInSwapOut = sal_False;
}
if( mpSwapOutTimer )
mpSwapOutTimer->Start();
return 0L;
}
// ------------------------------------------------------------------------
SvStream& operator>>( SvStream& rIStm, GraphicObject& rGraphicObj )
{
VersionCompat aCompat( rIStm, STREAM_READ );
Graphic aGraphic;
GraphicAttr aAttr;
ByteString aLink;
sal_Bool bLink;
rIStm >> aGraphic >> aAttr >> bLink;
rGraphicObj.SetGraphic( aGraphic );
rGraphicObj.SetAttr( aAttr );
if( bLink )
{
rIStm >> aLink;
rGraphicObj.SetLink( UniString( aLink, RTL_TEXTENCODING_UTF8 ) );
}
else
rGraphicObj.SetLink();
rGraphicObj.SetSwapStreamHdl();
return rIStm;
}
// ------------------------------------------------------------------------
SvStream& operator<<( SvStream& rOStm, const GraphicObject& rGraphicObj )
{
VersionCompat aCompat( rOStm, STREAM_WRITE, 1 );
const sal_Bool bLink = rGraphicObj.HasLink();
rOStm << rGraphicObj.GetGraphic() << rGraphicObj.GetAttr() << bLink;
if( bLink )
rOStm << ByteString( rGraphicObj.GetLink(), RTL_TEXTENCODING_UTF8 );
return rOStm;
}
#define UNO_NAME_GRAPHOBJ_URLPREFIX "vnd.sun.star.GraphicObject:"
GraphicObject GraphicObject::CreateGraphicObjectFromURL( const ::rtl::OUString &rURL )
{
const String aURL( rURL ), aPrefix( RTL_CONSTASCII_STRINGPARAM(UNO_NAME_GRAPHOBJ_URLPREFIX) );
if( aURL.Search( aPrefix ) == 0 )
{
// graphic manager url
ByteString aUniqueID( String(rURL.copy( sizeof( UNO_NAME_GRAPHOBJ_URLPREFIX ) - 1 )), RTL_TEXTENCODING_UTF8 );
return GraphicObject( aUniqueID );
}
else
{
Graphic aGraphic;
if ( aURL.Len() )
{
SvStream* pStream = utl::UcbStreamHelper::CreateStream( aURL, STREAM_READ );
if( pStream )
GraphicConverter::Import( *pStream, aGraphic );
}
return GraphicObject( aGraphic );
}
}
// calculate scalings between real image size and logic object size. This
// is necessary since the crop values are relative to original bitmap size
basegfx::B2DVector GraphicObject::calculateCropScaling(
double fWidth,
double fHeight,
double fLeftCrop,
double fTopCrop,
double fRightCrop,
double fBottomCrop) const
{
const MapMode aMapMode100thmm(MAP_100TH_MM);
Size aBitmapSize(GetPrefSize());
double fFactorX(1.0);
double fFactorY(1.0);
if(MAP_PIXEL == GetPrefMapMode().GetMapUnit())
{
aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
}
else
{
aBitmapSize = Application::GetDefaultDevice()->LogicToLogic(aBitmapSize, GetPrefMapMode(), aMapMode100thmm);
}
const double fDivX(aBitmapSize.Width() - fLeftCrop - fRightCrop);
const double fDivY(aBitmapSize.Height() - fTopCrop - fBottomCrop);
if(!basegfx::fTools::equalZero(fDivX))
{
fFactorX = fabs(fWidth) / fDivX;
}
if(!basegfx::fTools::equalZero(fDivY))
{
fFactorY = fabs(fHeight) / fDivY;
}
return basegfx::B2DVector(fFactorX,fFactorY);
}
// ------------------------------------------------------------------------
// restart SwapOut timer
void GraphicObject::restartSwapOutTimer() const
{
if( mpSwapOutTimer && mpSwapOutTimer->IsActive() )
{
mpSwapOutTimer->Start();
}
}
// eof