blob: 379760d060ec96c94c6a37a6c6bab152122fd2ec [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"
#include <vos/macros.hxx>
#include <vcl/bmpacc.hxx>
#include <tools/poly.hxx>
#include <vcl/outdev.hxx>
#include <vcl/window.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/metaact.hxx>
#include <vcl/metric.hxx>
#include <vcl/animate.hxx>
#include <vcl/alpha.hxx>
#include <vcl/virdev.hxx>
#include "grfcache.hxx"
#include <svtools/grfmgr.hxx>
// -----------
// - defines -
// -----------
#define MAX_PRINTER_EXT 1024
#define MAP( cVal0, cVal1, nFrac ) ((sal_uInt8)((((long)(cVal0)<<20L)+nFrac*((long)(cVal1)-(cVal0)))>>20L))
#define WATERMARK_LUM_OFFSET 50
#define WATERMARK_CON_OFFSET -70
// -----------
// - helpers -
// -----------
namespace {
void muckWithBitmap( const Point& rDestPoint,
const Size& rDestSize,
const Size& rRefSize,
bool& o_rbNonBitmapActionEncountered )
{
const Point aEmptyPoint;
if( aEmptyPoint != rDestPoint ||
rDestSize != rRefSize )
{
// non-fullscale, or offsetted bmp -> fallback to mtf
// rendering
o_rbNonBitmapActionEncountered = true;
}
}
BitmapEx muckWithBitmap( const BitmapEx& rBmpEx,
const Point& rSrcPoint,
const Size& rSrcSize,
const Point& rDestPoint,
const Size& rDestSize,
const Size& rRefSize,
bool& o_rbNonBitmapActionEncountered )
{
BitmapEx aBmpEx;
muckWithBitmap(rDestPoint,
rDestSize,
rRefSize,
o_rbNonBitmapActionEncountered);
if( o_rbNonBitmapActionEncountered )
return aBmpEx;
aBmpEx = rBmpEx;
if( (rSrcPoint.X() != 0 && rSrcPoint.Y() != 0) ||
rSrcSize != rBmpEx.GetSizePixel() )
{
// crop bitmap to given source rectangle (no
// need to copy and convert the whole bitmap)
const Rectangle aCropRect( rSrcPoint,
rSrcSize );
aBmpEx.Crop( aCropRect );
}
return aBmpEx;
}
} // namespace {
// ------------------
// - GraphicManager -
// ------------------
GraphicManager::GraphicManager( sal_uLong nCacheSize, sal_uLong nMaxObjCacheSize ) :
mpCache( new GraphicCache( *this, nCacheSize, nMaxObjCacheSize ) )
{
}
// -----------------------------------------------------------------------------
GraphicManager::~GraphicManager()
{
for( void* pObj = maObjList.First(); pObj; pObj = maObjList.Next() )
( (GraphicObject*) pObj )->GraphicManagerDestroyed();
delete mpCache;
}
// -----------------------------------------------------------------------------
void GraphicManager::SetMaxCacheSize( sal_uLong nNewCacheSize )
{
mpCache->SetMaxDisplayCacheSize( nNewCacheSize );
}
// -----------------------------------------------------------------------------
sal_uLong GraphicManager::GetMaxCacheSize() const
{
return mpCache->GetMaxDisplayCacheSize();
}
// -----------------------------------------------------------------------------
void GraphicManager::SetMaxObjCacheSize( sal_uLong nNewMaxObjSize, sal_Bool bDestroyGreaterCached )
{
mpCache->SetMaxObjDisplayCacheSize( nNewMaxObjSize, bDestroyGreaterCached );
}
// -----------------------------------------------------------------------------
sal_uLong GraphicManager::GetMaxObjCacheSize() const
{
return mpCache->GetMaxObjDisplayCacheSize();
}
// -----------------------------------------------------------------------------
sal_uLong GraphicManager::GetUsedCacheSize() const
{
return mpCache->GetUsedDisplayCacheSize();
}
// -----------------------------------------------------------------------------
sal_uLong GraphicManager::GetFreeCacheSize() const
{
return mpCache->GetFreeDisplayCacheSize();
}
// -----------------------------------------------------------------------------
void GraphicManager::SetCacheTimeout( sal_uLong nTimeoutSeconds )
{
mpCache->SetCacheTimeout( nTimeoutSeconds );
}
// -----------------------------------------------------------------------------
sal_uLong GraphicManager::GetCacheTimeout() const
{
return mpCache->GetCacheTimeout();
}
// -----------------------------------------------------------------------------
void GraphicManager::ClearCache()
{
mpCache->ClearDisplayCache();
}
// -----------------------------------------------------------------------------
void GraphicManager::ReleaseFromCache( const GraphicObject& /*rObj*/ )
{
// !!!
}
// -----------------------------------------------------------------------------
sal_Bool GraphicManager::IsInCache( OutputDevice* pOut, const Point& rPt,
const Size& rSz, const GraphicObject& rObj,
const GraphicAttr& rAttr ) const
{
return mpCache->IsInDisplayCache( pOut, rPt, rSz, rObj, rAttr );
}
// -----------------------------------------------------------------------------
sal_Bool GraphicManager::DrawObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
GraphicObject& rObj, const GraphicAttr& rAttr,
const sal_uLong nFlags, sal_Bool& rCached )
{
Point aPt( rPt );
Size aSz( rSz );
sal_Bool bRet = sal_False;
rCached = sal_False;
if( ( rObj.GetType() == GRAPHIC_BITMAP ) || ( rObj.GetType() == GRAPHIC_GDIMETAFILE ) )
{
// create output and fill cache
const Size aOutSize( pOut->GetOutputSizePixel() );
if( rObj.IsAnimated() || ( pOut->GetOutDevType() == OUTDEV_PRINTER ) ||
( !( nFlags & GRFMGR_DRAW_NO_SUBSTITUTE ) &&
( ( nFlags & GRFMGR_DRAW_SUBSTITUTE ) ||
!( nFlags & GRFMGR_DRAW_CACHED ) ||
( pOut->GetConnectMetaFile() && !pOut->IsOutputEnabled() ) ) ) )
{
// simple output of transformed graphic
const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
if( aGraphic.IsSupportedGraphic() )
{
const sal_uInt16 nRot10 = rAttr.GetRotation() % 3600;
if( nRot10 )
{
Polygon aPoly( Rectangle( aPt, aSz ) );
aPoly.Rotate( aPt, nRot10 );
const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
aPt = aRotBoundRect.TopLeft();
aSz = aRotBoundRect.GetSize();
}
aGraphic.Draw( pOut, aPt, aSz );
}
bRet = sal_True;
}
if( !bRet )
{
// cached/direct drawing
if( !mpCache->DrawDisplayCacheObj( pOut, aPt, aSz, rObj, rAttr ) )
bRet = ImplDraw( pOut, aPt, aSz, rObj, rAttr, nFlags, rCached );
else
bRet = rCached = sal_True;
}
}
return bRet;
}
// -----------------------------------------------------------------------------
void GraphicManager::ImplRegisterObj( const GraphicObject& rObj, Graphic& rSubstitute,
const ByteString* pID, const GraphicObject* pCopyObj )
{
maObjList.Insert( (void*) &rObj, LIST_APPEND );
mpCache->AddGraphicObject( rObj, rSubstitute, pID, pCopyObj );
}
// -----------------------------------------------------------------------------
void GraphicManager::ImplUnregisterObj( const GraphicObject& rObj )
{
mpCache->ReleaseGraphicObject( rObj );
maObjList.Remove( (void*) &rObj );
}
// -----------------------------------------------------------------------------
void GraphicManager::ImplGraphicObjectWasSwappedOut( const GraphicObject& rObj )
{
mpCache->GraphicObjectWasSwappedOut( rObj );
}
// -----------------------------------------------------------------------------
ByteString GraphicManager::ImplGetUniqueID( const GraphicObject& rObj ) const
{
return mpCache->GetUniqueID( rObj );
}
// -----------------------------------------------------------------------------
namespace
{
struct simpleSortByDataChangeTimeStamp
{
bool operator() (GraphicObject* p1, GraphicObject* p2) const
{
return p1->GetDataChangeTimeStamp() < p2->GetDataChangeTimeStamp();
}
};
} // end of anonymous namespace
void GraphicManager::ImplCheckSizeOfSwappedInGraphics()
{
// only necessary for 32bit systems
if(SAL_TYPES_SIZEOFPOINTER <= 4)
{
// get the currently used memory footprint of all swapped in bitmap graphics
// of this graphic manager. Remember candidates in a vector. The size in bytes is
// already available, thus this loop is not expensive to execute
sal_uLong nUsedSize(0);
GraphicObject* pObj = 0;
std::vector< GraphicObject* > aCandidates;
for(pObj = (GraphicObject*)maObjList.First(); pObj; pObj = (GraphicObject*)maObjList.Next())
{
if(pObj->meType == GRAPHIC_BITMAP && !pObj->IsSwappedOut() && pObj->GetSizeBytes())
{
aCandidates.push_back(pObj);
nUsedSize += pObj->GetSizeBytes();
}
}
// detect maximum allowed memory footprint. Use the user-settings of MaxCacheSize (defaulted
// to 20MB) and add a decent multiplicator (expecrimented to find one). Limit to
// a useful maximum for 32Bit address space
// default is 20MB, so allow 200MB initially
static sal_uLong aMultiplicator(10);
// max at 500MB; I experimented with 800 for debug and 750 for non-debug settings (pics start
// missing when AOO reaches a mem footprint of 1.5GB) but some secure left over space for
// app activity is needed
static sal_uLong aMaxSize32Bit(500 * 1024 * 1024);
// calc max allowed cache size
const sal_uLong nMaxCacheSize(::std::min(GetMaxCacheSize() * aMultiplicator, aMaxSize32Bit));
if(nUsedSize >= nMaxCacheSize && !aCandidates.empty())
{
// if we use more currently, sort by last DataChangeTimeStamp
// sort by DataChangeTimeStamp so that the oldest get removed first
::std::sort(aCandidates.begin(), aCandidates.end(), simpleSortByDataChangeTimeStamp());
for(sal_uInt32 a(0); nUsedSize >= nMaxCacheSize && a < aCandidates.size(); a++)
{
// swap out until we have no more or the goal to use less than nMaxCacheSize
// is reached
pObj = aCandidates[a];
const sal_uLong nSizeBytes(pObj->GetSizeBytes());
// do not swap out when we have less than 16KB data objects
if(nSizeBytes >= (16 * 1024))
{
pObj->FireSwapOutRequest();
nUsedSize = (nSizeBytes < nUsedSize) ? nUsedSize - nSizeBytes : 0;
}
}
}
}
}
// -----------------------------------------------------------------------------
sal_Bool GraphicManager::ImplFillSwappedGraphicObject( const GraphicObject& rObj, Graphic& rSubstitute )
{
return mpCache->FillSwappedGraphicObject(rObj, rSubstitute);
}
// -----------------------------------------------------------------------------
void GraphicManager::ImplGraphicObjectWasSwappedIn( const GraphicObject& rObj )
{
mpCache->GraphicObjectWasSwappedIn( rObj );
}
// -----------------------------------------------------------------------------
sal_Bool GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt,
const Size& rSz, GraphicObject& rObj,
const GraphicAttr& rAttr,
const sal_uLong nFlags, sal_Bool& rCached )
{
const Graphic& rGraphic = rObj.GetGraphic();
sal_Bool bRet = sal_False;
if( rGraphic.IsSupportedGraphic() && !rGraphic.IsSwapOut() )
{
if( GRAPHIC_BITMAP == rGraphic.GetType() )
{
const BitmapEx aSrcBmpEx( rGraphic.GetBitmapEx() );
// #i46805# No point in caching a bitmap that is rendered
// via RectFill on the OutDev
if( !(pOut->GetDrawMode() & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP )) &&
mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) )
{
BitmapEx aDstBmpEx;
if( ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags, &aDstBmpEx ) )
{
rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx );
bRet = sal_True;
}
}
if( !bRet )
bRet = ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags );
}
else
{
const GDIMetaFile& rSrcMtf = rGraphic.GetGDIMetaFile();
if( mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) )
{
GDIMetaFile aDstMtf;
BitmapEx aContainedBmpEx;
if( ImplCreateOutput( pOut, rPt, rSz, rSrcMtf, rAttr, nFlags, aDstMtf, aContainedBmpEx ) )
{
if( !!aContainedBmpEx )
{
// #117889# Use bitmap output method, if
// metafile basically contains only a single
// bitmap
BitmapEx aDstBmpEx;
if( ImplCreateOutput( pOut, rPt, rSz, aContainedBmpEx, rAttr, nFlags, &aDstBmpEx ) )
{
rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx );
bRet = sal_True;
}
}
else
{
rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstMtf );
bRet = sal_True;
}
}
}
if( !bRet )
{
const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
if( aGraphic.IsSupportedGraphic() )
{
aGraphic.Draw( pOut, rPt, rSz );
bRet = sal_True;
}
}
}
}
return bRet;
}
// -----------------------------------------------------------------------------
sal_Bool GraphicManager::ImplCreateOutput( OutputDevice* pOut,
const Point& rPt, const Size& rSz,
const BitmapEx& rBmpEx, const GraphicAttr& rAttr,
const sal_uLong nFlags, BitmapEx* pBmpEx )
{
sal_uInt16 nRot10 = rAttr.GetRotation() % 3600;
Point aOutPtPix;
Size aOutSzPix;
Size aUnrotatedSzPix( pOut->LogicToPixel( rSz ) );
sal_Bool bRet = sal_False;
if( nRot10 )
{
Polygon aPoly( Rectangle( rPt, rSz ) );
aPoly.Rotate( rPt, nRot10 );
const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
aOutPtPix = pOut->LogicToPixel( aRotBoundRect.TopLeft() );
aOutSzPix = pOut->LogicToPixel( aRotBoundRect.GetSize() );
}
else
{
aOutPtPix = pOut->LogicToPixel( rPt );
aOutSzPix = aUnrotatedSzPix;
}
if( aUnrotatedSzPix.Width() && aUnrotatedSzPix.Height() )
{
BitmapEx aBmpEx( rBmpEx );
BitmapEx aOutBmpEx;
Point aOutPt;
Size aOutSz;
const Size& rBmpSzPix = rBmpEx.GetSizePixel();
const long nW = rBmpSzPix.Width();
const long nH = rBmpSzPix.Height();
const long nNewW = aUnrotatedSzPix.Width();
const long nNewH = aUnrotatedSzPix.Height();
double fTmp;
long* pMapIX = new long[ nNewW ];
long* pMapFX = new long[ nNewW ];
long* pMapIY = new long[ nNewH ];
long* pMapFY = new long[ nNewH ];
long nStartX = -1, nStartY = -1, nEndX = -1, nEndY = -1;
long nX, nY, nTmp, nTmpX, nTmpY;
sal_Bool bHMirr = ( rAttr.GetMirrorFlags() & BMP_MIRROR_HORZ ) != 0;
sal_Bool bVMirr = ( rAttr.GetMirrorFlags() & BMP_MIRROR_VERT ) != 0;
if( nFlags & GRFMGR_DRAW_BILINEAR )
{
const double fRevScaleX = ( nNewW > 1L ) ? ( (double) ( nW - 1L ) / ( nNewW - 1L ) ) : 0.0;
const double fRevScaleY = ( nNewH > 1L ) ? ( (double) ( nH - 1L ) / ( nNewH - 1L ) ) : 0.0;
// create horizontal mapping table
for( nX = 0L, nTmpX = nW - 1L, nTmp = nW - 2L; nX < nNewW; nX++ )
{
fTmp = nX * fRevScaleX;
if( bHMirr )
fTmp = nTmpX - fTmp;
pMapFX[ nX ] = (long) ( ( fTmp - ( pMapIX[ nX ] = MinMax( (long) fTmp, 0, nTmp ) ) ) * 1048576. );
}
// create vertical mapping table
for( nY = 0L, nTmpY = nH - 1L, nTmp = nH - 2L; nY < nNewH; nY++ )
{
fTmp = nY * fRevScaleY;
if( bVMirr )
fTmp = nTmpY - fTmp;
pMapFY[ nY ] = (long) ( ( fTmp - ( pMapIY[ nY ] = MinMax( (long) fTmp, 0, nTmp ) ) ) * 1048576. );
}
}
else
{
// #98290# Use a different mapping for non-interpolating mode, to avoid missing rows/columns
const double fRevScaleX = ( nNewW > 1L ) ? ( (double) nW / nNewW ) : 0.0;
const double fRevScaleY = ( nNewH > 1L ) ? ( (double) nH / nNewH ) : 0.0;
// create horizontal mapping table
for( nX = 0L, nTmpX = nW - 1L, nTmp = nW - 2L; nX < nNewW; nX++ )
{
fTmp = nX * fRevScaleX;
if( bHMirr )
fTmp = nTmpX - fTmp;
// #98290# Do not use round to zero, otherwise last column will be missing
pMapIX[ nX ] = MinMax( (long) fTmp, 0, nTmp );
pMapFX[ nX ] = fTmp >= nTmp+1 ? 1048576 : 0;
}
// create vertical mapping table
for( nY = 0L, nTmpY = nH - 1L, nTmp = nH - 2L; nY < nNewH; nY++ )
{
fTmp = nY * fRevScaleY;
if( bVMirr )
fTmp = nTmpY - fTmp;
// #98290# Do not use round to zero, otherwise last row will be missing
pMapIY[ nY ] = MinMax( (long) fTmp, 0, nTmp );
pMapFY[ nY ] = fTmp >= nTmp+1 ? 1048576 : 0;
}
}
// calculate output sizes
if( !pBmpEx )
{
Point aPt;
Rectangle aOutRect( aPt, pOut->GetOutputSizePixel() );
Rectangle aBmpRect( aOutPtPix, aOutSzPix );
if( pOut->GetOutDevType() == OUTDEV_WINDOW )
{
const Region aPaintRgn( ( (Window*) pOut )->GetPaintRegion() );
if( !aPaintRgn.IsNull() )
aOutRect.Intersection( pOut->LogicToPixel( aPaintRgn.GetBoundRect() ) );
}
aOutRect.Intersection( aBmpRect );
if( !aOutRect.IsEmpty() )
{
aOutPt = pOut->PixelToLogic( aOutRect.TopLeft() );
aOutSz = pOut->PixelToLogic( aOutRect.GetSize() );
nStartX = aOutRect.Left() - aBmpRect.Left();
nStartY = aOutRect.Top() - aBmpRect.Top();
nEndX = aOutRect.Right() - aBmpRect.Left();
nEndY = aOutRect.Bottom() - aBmpRect.Top();
}
else
nStartX = -1L; // invalid
}
else
{
aOutPt = pOut->PixelToLogic( aOutPtPix );
aOutSz = pOut->PixelToLogic( aOutSzPix );
nStartX = nStartY = 0;
nEndX = aOutSzPix.Width() - 1L;
nEndY = aOutSzPix.Height() - 1L;
}
// do transformation
if( nStartX >= 0L )
{
const sal_Bool bSimple = ( 1 == nW || 1 == nH );
if( nRot10 )
{
if( bSimple )
{
bRet = ( aOutBmpEx = aBmpEx ).Scale( aUnrotatedSzPix );
if( bRet )
aOutBmpEx.Rotate( nRot10, COL_TRANSPARENT );
}
else
{
bRet = ImplCreateRotatedScaled( aBmpEx,
nRot10, aOutSzPix, aUnrotatedSzPix,
pMapIX, pMapFX, pMapIY, pMapFY, nStartX, nEndX, nStartY, nEndY,
aOutBmpEx );
}
}
else
{
// #105229# Don't scale if output size equals bitmap size
// #107226# Copy through only if we're not mirroring
if( !bHMirr && !bVMirr && aOutSzPix == rBmpSzPix )
{
// #107226# Use original dimensions when just copying through
aOutPt = pOut->PixelToLogic( aOutPtPix );
aOutSz = pOut->PixelToLogic( aOutSzPix );
aOutBmpEx = aBmpEx;
bRet = sal_True;
}
else
{
if( bSimple )
bRet = ( aOutBmpEx = aBmpEx ).Scale( Size( nEndX - nStartX + 1, nEndY - nStartY + 1 ) );
else
{
bRet = ImplCreateScaled( aBmpEx,
pMapIX, pMapFX, pMapIY, pMapFY,
nStartX, nEndX, nStartY, nEndY,
aOutBmpEx );
}
}
}
if( bRet )
{
// attribute adjustment if neccessary
if( rAttr.IsSpecialDrawMode() || rAttr.IsAdjusted() || rAttr.IsTransparent() )
ImplAdjust( aOutBmpEx, rAttr, ADJUSTMENT_DRAWMODE | ADJUSTMENT_COLORS | ADJUSTMENT_TRANSPARENCY );
// OutDev adjustment if neccessary
if( pOut->GetOutDevType() != OUTDEV_PRINTER && pOut->GetBitCount() <= 8 && aOutBmpEx.GetBitCount() >= 8 )
aOutBmpEx.Dither( BMP_DITHER_MATRIX );
}
}
// delete lookup tables
delete[] pMapIX;
delete[] pMapFX;
delete[] pMapIY;
delete[] pMapFY;
// create output
if( bRet )
{
if( !pBmpEx )
pOut->DrawBitmapEx( aOutPt, aOutSz, aOutBmpEx );
else
{
if( !rAttr.IsTransparent() && !aOutBmpEx.IsAlpha() )
aOutBmpEx = BitmapEx( aOutBmpEx.GetBitmap().CreateDisplayBitmap( pOut ), aOutBmpEx.GetMask() );
pOut->DrawBitmapEx( aOutPt, aOutSz, *pBmpEx = aOutBmpEx );
}
}
}
return bRet;
}
// -----------------------------------------------------------------------------
sal_Bool GraphicManager::ImplCreateOutput( OutputDevice* pOut,
const Point& rPt, const Size& rSz,
const GDIMetaFile& rMtf, const GraphicAttr& rAttr,
const sal_uLong /*nFlags*/, GDIMetaFile& rOutMtf, BitmapEx& rOutBmpEx )
{
const Size aNewSize( rMtf.GetPrefSize() );
rOutMtf = rMtf;
// #117889# count bitmap actions, and flag actions that paint, but
// are no bitmaps.
sal_Int32 nNumBitmaps(0);
bool bNonBitmapActionEncountered(false);
if( aNewSize.Width() && aNewSize.Height() && rSz.Width() && rSz.Height() )
{
const double fGrfWH = (double) aNewSize.Width() / aNewSize.Height();
const double fOutWH = (double) rSz.Width() / rSz.Height();
const double fScaleX = fOutWH / fGrfWH;
const double fScaleY = 1.0;
const MapMode& rPrefMapMode( rMtf.GetPrefMapMode() );
const Size& rSizePix( pOut->LogicToPixel( aNewSize,
rPrefMapMode ) );
// taking care of font width default if scaling metafile.
// #117889# use existing metafile scan, to determine whether
// the metafile basically displays a single bitmap. Note that
// the solution, as implemented here, is quite suboptimal (the
// cases where a mtf consisting basically of a single bitmap,
// that fail to pass the test below, are probably frequent). A
// better solution would involve FSAA, but that's currently
// expensive, and might trigger bugs on display drivers, if
// VDevs get bigger than the actual screen.
sal_uInt32 nCurPos;
MetaAction* pAct;
for( nCurPos = 0, pAct = (MetaAction*)rOutMtf.FirstAction(); pAct;
pAct = (MetaAction*)rOutMtf.NextAction(), nCurPos++ )
{
MetaAction* pModAct = NULL;
switch( pAct->GetType() )
{
case META_FONT_ACTION:
{
MetaFontAction* pA = (MetaFontAction*)pAct;
Font aFont( pA->GetFont() );
if ( !aFont.GetWidth() )
{
FontMetric aFontMetric( pOut->GetFontMetric( aFont ) );
aFont.SetWidth( aFontMetric.GetWidth() );
pModAct = new MetaFontAction( aFont );
}
}
// FALLTHROUGH intended
case META_NULL_ACTION:
// FALLTHROUGH intended
// OutDev state changes (which don't affect bitmap
// output)
case META_LINECOLOR_ACTION:
// FALLTHROUGH intended
case META_FILLCOLOR_ACTION:
// FALLTHROUGH intended
case META_TEXTCOLOR_ACTION:
// FALLTHROUGH intended
case META_TEXTFILLCOLOR_ACTION:
// FALLTHROUGH intended
case META_TEXTALIGN_ACTION:
// FALLTHROUGH intended
case META_TEXTLINECOLOR_ACTION:
// FALLTHROUGH intended
case META_TEXTLINE_ACTION:
// FALLTHROUGH intended
case META_PUSH_ACTION:
// FALLTHROUGH intended
case META_POP_ACTION:
// FALLTHROUGH intended
case META_LAYOUTMODE_ACTION:
// FALLTHROUGH intended
case META_TEXTLANGUAGE_ACTION:
// FALLTHROUGH intended
case META_COMMENT_ACTION:
break;
// bitmap output methods
case META_BMP_ACTION:
if( !nNumBitmaps && !bNonBitmapActionEncountered )
{
MetaBmpAction* pAction = (MetaBmpAction*)pAct;
rOutBmpEx = BitmapEx( pAction->GetBitmap() );
muckWithBitmap( pOut->LogicToPixel( pAction->GetPoint(),
rPrefMapMode ),
pAction->GetBitmap().GetSizePixel(),
rSizePix,
bNonBitmapActionEncountered );
++nNumBitmaps;
}
break;
case META_BMPSCALE_ACTION:
if( !nNumBitmaps && !bNonBitmapActionEncountered )
{
MetaBmpScaleAction* pAction = (MetaBmpScaleAction*)pAct;
rOutBmpEx = BitmapEx( pAction->GetBitmap() );
muckWithBitmap( pOut->LogicToPixel( pAction->GetPoint(),
rPrefMapMode ),
pOut->LogicToPixel( pAction->GetSize(),
rPrefMapMode ),
rSizePix,
bNonBitmapActionEncountered );
++nNumBitmaps;
}
break;
case META_BMPSCALEPART_ACTION:
if( !nNumBitmaps && !bNonBitmapActionEncountered )
{
MetaBmpScalePartAction* pAction = (MetaBmpScalePartAction*)pAct;
rOutBmpEx = muckWithBitmap( BitmapEx( pAction->GetBitmap() ),
pAction->GetSrcPoint(),
pAction->GetSrcSize(),
pOut->LogicToPixel( pAction->GetDestPoint(),
rPrefMapMode ),
pOut->LogicToPixel( pAction->GetDestSize(),
rPrefMapMode ),
rSizePix,
bNonBitmapActionEncountered );
++nNumBitmaps;
}
break;
case META_BMPEX_ACTION:
if( !nNumBitmaps && !bNonBitmapActionEncountered )
{
MetaBmpExAction* pAction = (MetaBmpExAction*)pAct;
rOutBmpEx = pAction->GetBitmapEx();
muckWithBitmap( pOut->LogicToPixel( pAction->GetPoint(),
rPrefMapMode ),
pAction->GetBitmapEx().GetSizePixel(),
rSizePix,
bNonBitmapActionEncountered );
++nNumBitmaps;
}
break;
case META_BMPEXSCALE_ACTION:
if( !nNumBitmaps && !bNonBitmapActionEncountered )
{
MetaBmpExScaleAction* pAction = (MetaBmpExScaleAction*)pAct;
rOutBmpEx = pAction->GetBitmapEx();
muckWithBitmap( pOut->LogicToPixel( pAction->GetPoint(),
rPrefMapMode ),
pOut->LogicToPixel( pAction->GetSize(),
rPrefMapMode ),
rSizePix,
bNonBitmapActionEncountered );
++nNumBitmaps;
}
break;
case META_BMPEXSCALEPART_ACTION:
if( !nNumBitmaps && !bNonBitmapActionEncountered )
{
MetaBmpExScalePartAction* pAction = (MetaBmpExScalePartAction*)pAct;
rOutBmpEx = muckWithBitmap( pAction->GetBitmapEx(),
pAction->GetSrcPoint(),
pAction->GetSrcSize(),
pOut->LogicToPixel( pAction->GetDestPoint(),
rPrefMapMode ),
pOut->LogicToPixel( pAction->GetDestSize(),
rPrefMapMode ),
rSizePix,
bNonBitmapActionEncountered );
++nNumBitmaps;
}
break;
// these actions actually output something (that's
// different from a bitmap)
case META_RASTEROP_ACTION:
if( ((MetaRasterOpAction*)pAct)->GetRasterOp() == ROP_OVERPAINT )
break;
// FALLTHROUGH intended
case META_PIXEL_ACTION:
// FALLTHROUGH intended
case META_POINT_ACTION:
// FALLTHROUGH intended
case META_LINE_ACTION:
// FALLTHROUGH intended
case META_RECT_ACTION:
// FALLTHROUGH intended
case META_ROUNDRECT_ACTION:
// FALLTHROUGH intended
case META_ELLIPSE_ACTION:
// FALLTHROUGH intended
case META_ARC_ACTION:
// FALLTHROUGH intended
case META_PIE_ACTION:
// FALLTHROUGH intended
case META_CHORD_ACTION:
// FALLTHROUGH intended
case META_POLYLINE_ACTION:
// FALLTHROUGH intended
case META_POLYGON_ACTION:
// FALLTHROUGH intended
case META_POLYPOLYGON_ACTION:
// FALLTHROUGH intended
case META_TEXT_ACTION:
// FALLTHROUGH intended
case META_TEXTARRAY_ACTION:
// FALLTHROUGH intended
case META_STRETCHTEXT_ACTION:
// FALLTHROUGH intended
case META_TEXTRECT_ACTION:
// FALLTHROUGH intended
case META_MASK_ACTION:
// FALLTHROUGH intended
case META_MASKSCALE_ACTION:
// FALLTHROUGH intended
case META_MASKSCALEPART_ACTION:
// FALLTHROUGH intended
case META_GRADIENT_ACTION:
// FALLTHROUGH intended
case META_HATCH_ACTION:
// FALLTHROUGH intended
case META_WALLPAPER_ACTION:
// FALLTHROUGH intended
case META_TRANSPARENT_ACTION:
// FALLTHROUGH intended
case META_EPS_ACTION:
// FALLTHROUGH intended
case META_FLOATTRANSPARENT_ACTION:
// FALLTHROUGH intended
case META_GRADIENTEX_ACTION:
// FALLTHROUGH intended
// OutDev state changes that _do_ affect bitmap
// output
case META_CLIPREGION_ACTION:
// FALLTHROUGH intended
case META_ISECTRECTCLIPREGION_ACTION:
// FALLTHROUGH intended
case META_ISECTREGIONCLIPREGION_ACTION:
// FALLTHROUGH intended
case META_MOVECLIPREGION_ACTION:
// FALLTHROUGH intended
case META_MAPMODE_ACTION:
// FALLTHROUGH intended
case META_REFPOINT_ACTION:
// FALLTHROUGH intended
default:
bNonBitmapActionEncountered = true;
break;
}
if ( pModAct )
{
rOutMtf.ReplaceAction( pModAct, nCurPos );
pAct->Delete();
}
else
{
if( pAct->GetRefCount() > 1 )
{
rOutMtf.ReplaceAction( pModAct = pAct->Clone(), nCurPos );
pAct->Delete();
}
else
pModAct = pAct;
}
pModAct->Scale( fScaleX, fScaleY );
}
rOutMtf.SetPrefSize( Size( FRound( aNewSize.Width() * fScaleX ),
FRound( aNewSize.Height() * fScaleY ) ) );
}
if( nNumBitmaps != 1 || bNonBitmapActionEncountered )
{
if( rAttr.IsSpecialDrawMode() || rAttr.IsAdjusted() || rAttr.IsMirrored() || rAttr.IsRotated() || rAttr.IsTransparent() )
ImplAdjust( rOutMtf, rAttr, ADJUSTMENT_ALL );
ImplDraw( pOut, rPt, rSz, rOutMtf, rAttr );
rOutBmpEx = BitmapEx();
}
return sal_True;
}
// -----------------------------------------------------------------------------
sal_Bool GraphicManager::ImplCreateScaled( const BitmapEx& rBmpEx,
long* pMapIX, long* pMapFX, long* pMapIY, long* pMapFY,
long nStartX, long nEndX, long nStartY, long nEndY,
BitmapEx& rOutBmpEx )
{
Bitmap aBmp( rBmpEx.GetBitmap() );
Bitmap aOutBmp;
BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
BitmapWriteAccess* pWAcc;
BitmapColor aCol0, aCol1, aColRes;
const long nDstW = nEndX - nStartX + 1L;
const long nDstH = nEndY - nStartY + 1L;
long nX, nY, nTmpX, nTmpY, nTmpFX, nTmpFY;
long nXDst, nYDst;
sal_uInt8 cR0, cG0, cB0, cR1, cG1, cB1;
sal_Bool bRet = sal_False;
DBG_ASSERT( aBmp.GetSizePixel() == rBmpEx.GetSizePixel(),
"GraphicManager::ImplCreateScaled(): bmp size inconsistent" );
if( pAcc )
{
aOutBmp = Bitmap( Size( nDstW, nDstH ), 24 );
pWAcc = aOutBmp.AcquireWriteAccess();
if( pWAcc )
{
if( pAcc->HasPalette() )
{
if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
{
Scanline pLine0, pLine1;
for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
{
nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
pLine0 = pAcc->GetScanline( nTmpY );
pLine1 = pAcc->GetScanline( ++nTmpY );
for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
{
nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
const BitmapColor& rCol0 = pAcc->GetPaletteColor( pLine0[ nTmpX ] );
const BitmapColor& rCol2 = pAcc->GetPaletteColor( pLine1[ nTmpX ] );
const BitmapColor& rCol1 = pAcc->GetPaletteColor( pLine0[ ++nTmpX ] );
const BitmapColor& rCol3 = pAcc->GetPaletteColor( pLine1[ nTmpX ] );
cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTmpFX );
cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTmpFX );
cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTmpFX );
cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTmpFX );
cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTmpFX );
cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTmpFX );
aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
pWAcc->SetPixel( nYDst, nXDst++, aColRes );
}
}
}
else
{
for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
{
nTmpY = pMapIY[ nY ], nTmpFY = pMapFY[ nY ];
for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
{
nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
aCol0 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, nTmpX ) );
aCol1 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, ++nTmpX ) );
cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
aCol1 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( ++nTmpY, nTmpX ) );
aCol0 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY--, --nTmpX ) );
cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
pWAcc->SetPixel( nYDst, nXDst++, aColRes );
}
}
}
}
else
{
if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR )
{
Scanline pLine0, pLine1, pTmp0, pTmp1;
long nOff;
for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
{
nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
pLine0 = pAcc->GetScanline( nTmpY );
pLine1 = pAcc->GetScanline( ++nTmpY );
for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
{
nOff = 3L * ( nTmpX = pMapIX[ nX ] );
nTmpFX = pMapFX[ nX ];
pTmp1 = ( pTmp0 = pLine0 + nOff ) + 3L;
cB0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
cG0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
cR0 = MAP( *pTmp0, *pTmp1, nTmpFX );
pTmp1 = ( pTmp0 = pLine1 + nOff ) + 3L;
cB1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
cG1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
cR1 = MAP( *pTmp0, *pTmp1, nTmpFX );
aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
pWAcc->SetPixel( nYDst, nXDst++, aColRes );
}
}
}
else if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
{
Scanline pLine0, pLine1, pTmp0, pTmp1;
long nOff;
for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
{
nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
pLine0 = pAcc->GetScanline( nTmpY );
pLine1 = pAcc->GetScanline( ++nTmpY );
for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
{
nOff = 3L * ( nTmpX = pMapIX[ nX ] );
nTmpFX = pMapFX[ nX ];
pTmp1 = ( pTmp0 = pLine0 + nOff ) + 3L;
cR0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
cG0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
cB0 = MAP( *pTmp0, *pTmp1, nTmpFX );
pTmp1 = ( pTmp0 = pLine1 + nOff ) + 3L;
cR1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
cG1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++;
cB1 = MAP( *pTmp0, *pTmp1, nTmpFX );
aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
pWAcc->SetPixel( nYDst, nXDst++, aColRes );
}
}
}
else
{
for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
{
nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
{
nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
aCol0 = pAcc->GetPixel( nTmpY, nTmpX );
aCol1 = pAcc->GetPixel( nTmpY, ++nTmpX );
cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
aCol1 = pAcc->GetPixel( ++nTmpY, nTmpX );
aCol0 = pAcc->GetPixel( nTmpY--, --nTmpX );
cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
pWAcc->SetPixel( nYDst, nXDst++, aColRes );
}
}
}
}
aOutBmp.ReleaseAccess( pWAcc );
bRet = sal_True;
}
aBmp.ReleaseAccess( pAcc );
}
if( bRet && rBmpEx.IsTransparent() )
{
bRet = sal_False;
if( rBmpEx.IsAlpha() )
{
DBG_ASSERT( rBmpEx.GetAlpha().GetSizePixel() == rBmpEx.GetSizePixel(),
"GraphicManager::ImplCreateScaled(): alpha mask size inconsistent" );
AlphaMask aAlpha( rBmpEx.GetAlpha() );
AlphaMask aOutAlpha;
pAcc = aAlpha.AcquireReadAccess();
if( pAcc )
{
aOutAlpha = AlphaMask( Size( nDstW, nDstH ) );
pWAcc = aOutAlpha.AcquireWriteAccess();
if( pWAcc )
{
if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL &&
pWAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
{
Scanline pLine0, pLine1, pLineW;
for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
{
nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ];
pLine0 = pAcc->GetScanline( nTmpY );
pLine1 = pAcc->GetScanline( ++nTmpY );
pLineW = pWAcc->GetScanline( nYDst );
for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++, nXDst++ )
{
nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
const long nAlpha0 = pLine0[ nTmpX ];
const long nAlpha2 = pLine1[ nTmpX ];
const long nAlpha1 = pLine0[ ++nTmpX ];
const long nAlpha3 = pLine1[ nTmpX ];
const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX );
*pLineW++ = MAP( n0, n1, nTmpFY );
}
}
}
else
{
BitmapColor aAlphaValue( 0 );
for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ )
{
nTmpY = pMapIY[ nY ], nTmpFY = pMapFY[ nY ];
for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ )
{
nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ];
long nAlpha0 = pAcc->GetPixel( nTmpY, nTmpX ).GetIndex();
long nAlpha1 = pAcc->GetPixel( nTmpY, ++nTmpX ).GetIndex();
const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
nAlpha1 = pAcc->GetPixel( ++nTmpY, nTmpX ).GetIndex();
nAlpha0 = pAcc->GetPixel( nTmpY--, --nTmpX ).GetIndex();
const long n1 = MAP( nAlpha0, nAlpha1, nTmpFX );
aAlphaValue.SetIndex( MAP( n0, n1, nTmpFY ) );
pWAcc->SetPixel( nYDst, nXDst++, aAlphaValue );
}
}
}
aOutAlpha.ReleaseAccess( pWAcc );
bRet = sal_True;
}
aAlpha.ReleaseAccess( pAcc );
if( bRet )
rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha );
}
}
else
{
DBG_ASSERT( rBmpEx.GetMask().GetSizePixel() == rBmpEx.GetSizePixel(),
"GraphicManager::ImplCreateScaled(): mask size inconsistent" );
Bitmap aMsk( rBmpEx.GetMask() );
Bitmap aOutMsk;
pAcc = aMsk.AcquireReadAccess();
if( pAcc )
{
// #i40115# Use the same palette for destination
// bitmap. Otherwise, we'd have to color-map even the
// case below, when both masks are one bit deep.
if( pAcc->HasPalette() )
aOutMsk = Bitmap( Size( nDstW, nDstH ),
1,
&pAcc->GetPalette() );
else
aOutMsk = Bitmap( Size( nDstW, nDstH ), 1 );
pWAcc = aOutMsk.AcquireWriteAccess();
if( pWAcc )
{
long* pMapLX = new long[ nDstW ];
long* pMapLY = new long[ nDstH ];
// create new horizontal mapping table
for( nX = 0UL, nTmpX = nStartX; nX < nDstW; nTmpX++ )
pMapLX[ nX++ ] = FRound( (double) pMapIX[ nTmpX ] + pMapFX[ nTmpX ] / 1048576. );
// create new vertical mapping table
for( nY = 0UL, nTmpY = nStartY; nY < nDstH; nTmpY++ )
pMapLY[ nY++ ] = FRound( (double) pMapIY[ nTmpY ] + pMapFY[ nTmpY ] / 1048576. );
// do normal scaling
if( pAcc->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL &&
pWAcc->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL )
{
// optimized
for( nY = 0; nY < nDstH; nY++ )
{
Scanline pSrc = pAcc->GetScanline( pMapLY[ nY ] );
Scanline pDst = pWAcc->GetScanline( nY );
for( nX = 0L; nX < nDstW; nX++ )
{
const long nSrcX = pMapLX[ nX ];
if( pSrc[ nSrcX >> 3 ] & ( 1 << ( 7 - ( nSrcX & 7 ) ) ) )
pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) );
else
pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) );
}
}
}
else
{
const BitmapColor aB( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
const BitmapColor aWB( pWAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
const BitmapColor aWW( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
if( pAcc->HasPalette() )
{
for( nY = 0L; nY < nDstH; nY++ )
{
for( nX = 0L; nX < nDstW; nX++ )
{
if( pAcc->GetPaletteColor( pAcc->GetPixelIndex( pMapLY[ nY ], pMapLX[ nX ] ) ) == aB )
pWAcc->SetPixel( nY, nX, aWB );
else
pWAcc->SetPixel( nY, nX, aWW );
}
}
}
else
{
for( nY = 0L; nY < nDstH; nY++ )
{
for( nX = 0L; nX < nDstW; nX++ )
{
if( pAcc->GetPixel( pMapLY[ nY ], pMapLX[ nX ] ) == aB )
pWAcc->SetPixel( nY, nX, aWB );
else
pWAcc->SetPixel( nY, nX, aWW );
}
}
}
}
delete[] pMapLX;
delete[] pMapLY;
aOutMsk.ReleaseAccess( pWAcc );
bRet = sal_True;
}
aMsk.ReleaseAccess( pAcc );
if( bRet )
rOutBmpEx = BitmapEx( aOutBmp, aOutMsk );
}
}
if( !bRet )
rOutBmpEx = aOutBmp;
}
else
rOutBmpEx = aOutBmp;
return bRet;
}
// -----------------------------------------------------------------------------
sal_Bool GraphicManager::ImplCreateRotatedScaled( const BitmapEx& rBmpEx,
sal_uInt16 nRot10, const Size& /*rOutSzPix*/, const Size& rUnrotatedSzPix,
long* pMapIX, long* pMapFX, long* pMapIY, long* pMapFY,
long nStartX, long nEndX, long nStartY, long nEndY,
BitmapEx& rOutBmpEx )
{
Point aPt;
Bitmap aBmp( rBmpEx.GetBitmap() );
Bitmap aOutBmp;
BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
BitmapWriteAccess* pWAcc;
Polygon aPoly( Rectangle( aPt, rUnrotatedSzPix ) ); aPoly.Rotate( Point(), nRot10 );
Rectangle aNewBound( aPoly.GetBoundRect() );
const double fCosAngle = cos( nRot10 * F_PI1800 ), fSinAngle = sin( nRot10 * F_PI1800 );
double fTmp;
const long nDstW = nEndX - nStartX + 1L;
const long nDstH = nEndY - nStartY + 1L;
const long nUnRotW = rUnrotatedSzPix.Width();
const long nUnRotH = rUnrotatedSzPix.Height();
long* pCosX = new long[ nDstW ];
long* pSinX = new long[ nDstW ];
long* pCosY = new long[ nDstH ];
long* pSinY = new long[ nDstH ];
long nX, nY, nTmpX, nTmpY, nTmpFX, nTmpFY, nUnRotX, nUnRotY, nSinY, nCosY;
sal_uInt8 cR0, cG0, cB0, cR1, cG1, cB1;
sal_Bool bRet = sal_False;
// create horizontal mapping table
for( nX = 0L, nTmpX = aNewBound.Left() + nStartX; nX < nDstW; nX++ )
{
pCosX[ nX ] = FRound( fCosAngle * ( fTmp = nTmpX++ << 8 ) );
pSinX[ nX ] = FRound( fSinAngle * fTmp );
}
// create vertical mapping table
for( nY = 0L, nTmpY = aNewBound.Top() + nStartY; nY < nDstH; nY++ )
{
pCosY[ nY ] = FRound( fCosAngle * ( fTmp = nTmpY++ << 8 ) );
pSinY[ nY ] = FRound( fSinAngle * fTmp );
}
if( pAcc )
{
aOutBmp = Bitmap( Size( nDstW, nDstH ), 24 );
pWAcc = aOutBmp.AcquireWriteAccess();
if( pWAcc )
{
BitmapColor aColRes;
if( pAcc->HasPalette() )
{
for( nY = 0; nY < nDstH; nY++ )
{
nSinY = pSinY[ nY ];
nCosY = pCosY[ nY ];
for( nX = 0; nX < nDstW; nX++ )
{
nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
{
nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
const BitmapColor& rCol0 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, nTmpX ) );
const BitmapColor& rCol1 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, ++nTmpX ) );
cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTmpFX );
cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTmpFX );
cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTmpFX );
const BitmapColor& rCol3 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( ++nTmpY, nTmpX ) );
const BitmapColor& rCol2 = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nTmpY, --nTmpX ) );
cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTmpFX );
cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTmpFX );
cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTmpFX );
aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
pWAcc->SetPixel( nY, nX, aColRes );
}
}
}
}
else
{
BitmapColor aCol0, aCol1;
for( nY = 0; nY < nDstH; nY++ )
{
nSinY = pSinY[ nY ];
nCosY = pCosY[ nY ];
for( nX = 0; nX < nDstW; nX++ )
{
nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
{
nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
aCol0 = pAcc->GetPixel( nTmpY, nTmpX );
aCol1 = pAcc->GetPixel( nTmpY, ++nTmpX );
cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
aCol1 = pAcc->GetPixel( ++nTmpY, nTmpX );
aCol0 = pAcc->GetPixel( nTmpY, --nTmpX );
cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX );
cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX );
cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX );
aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) );
aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) );
aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) );
pWAcc->SetPixel( nY, nX, aColRes );
}
}
}
}
aOutBmp.ReleaseAccess( pWAcc );
bRet = sal_True;
}
aBmp.ReleaseAccess( pAcc );
}
// mask processing
if( bRet && ( rBmpEx.IsTransparent() || ( nRot10 != 900 && nRot10 != 1800 && nRot10 != 2700 ) ) )
{
bRet = sal_False;
if( rBmpEx.IsAlpha() )
{
AlphaMask aAlpha( rBmpEx.GetAlpha() );
AlphaMask aOutAlpha;
pAcc = aAlpha.AcquireReadAccess();
if( pAcc )
{
aOutAlpha = AlphaMask( Size( nDstW, nDstH ) );
pWAcc = aOutAlpha.AcquireWriteAccess();
if( pWAcc )
{
if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL &&
pWAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
{
Scanline pLine0, pLine1, pLineW;
for( nY = 0; nY < nDstH; nY++ )
{
nSinY = pSinY[ nY ], nCosY = pCosY[ nY ];
pLineW = pWAcc->GetScanline( nY );
for( nX = 0; nX < nDstW; nX++ )
{
nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
{
nTmpX = pMapIX[ nUnRotX ], nTmpFX = pMapFX[ nUnRotX ];
nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
pLine0 = pAcc->GetScanline( nTmpY++ );
pLine1 = pAcc->GetScanline( nTmpY );
const long nAlpha0 = pLine0[ nTmpX ];
const long nAlpha2 = pLine1[ nTmpX++ ];
const long nAlpha1 = pLine0[ nTmpX ];
const long nAlpha3 = pLine1[ nTmpX ];
const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX );
*pLineW++ = MAP( n0, n1, nTmpFY );
}
else
*pLineW++ = 255;
}
}
}
else
{
const BitmapColor aTrans( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
BitmapColor aAlphaVal( 0 );
for( nY = 0; nY < nDstH; nY++ )
{
nSinY = pSinY[ nY ], nCosY = pCosY[ nY ];
for( nX = 0; nX < nDstW; nX++ )
{
nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
{
nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ];
nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ];
const long nAlpha0 = pAcc->GetPixel( nTmpY, nTmpX ).GetIndex();
const long nAlpha1 = pAcc->GetPixel( nTmpY, ++nTmpX ).GetIndex();
const long nAlpha3 = pAcc->GetPixel( ++nTmpY, nTmpX ).GetIndex();
const long nAlpha2 = pAcc->GetPixel( nTmpY, --nTmpX ).GetIndex();
const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX );
const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX );
aAlphaVal.SetIndex( MAP( n0, n1, nTmpFY ) );
pWAcc->SetPixel( nY, nX, aAlphaVal );
}
else
pWAcc->SetPixel( nY, nX, aTrans );
}
}
}
aOutAlpha.ReleaseAccess( pWAcc );
bRet = sal_True;
}
aAlpha.ReleaseAccess( pAcc );
}
if( bRet )
rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha );
}
else
{
Bitmap aOutMsk( Size( nDstW, nDstH ), 1 );
pWAcc = aOutMsk.AcquireWriteAccess();
if( pWAcc )
{
Bitmap aMsk( rBmpEx.GetMask() );
const BitmapColor aB( pWAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
const BitmapColor aW( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
BitmapReadAccess* pMAcc = NULL;
if( !aMsk || ( ( pMAcc = aMsk.AcquireReadAccess() ) != NULL ) )
{
long* pMapLX = new long[ nUnRotW ];
long* pMapLY = new long[ nUnRotH ];
BitmapColor aTestB;
if( pMAcc )
aTestB = pMAcc->GetBestMatchingColor( Color( COL_BLACK ) );
// create new horizontal mapping table
for( nX = 0UL; nX < nUnRotW; nX++ )
pMapLX[ nX ] = FRound( (double) pMapIX[ nX ] + pMapFX[ nX ] / 1048576. );
// create new vertical mapping table
for( nY = 0UL; nY < nUnRotH; nY++ )
pMapLY[ nY ] = FRound( (double) pMapIY[ nY ] + pMapFY[ nY ] / 1048576. );
// do mask rotation
for( nY = 0; nY < nDstH; nY++ )
{
nSinY = pSinY[ nY ];
nCosY = pCosY[ nY ];
for( nX = 0; nX < nDstW; nX++ )
{
nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8;
nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8;
if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) &&
( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) )
{
if( pMAcc )
{
if( pMAcc->GetPixel( pMapLY[ nUnRotY ], pMapLX[ nUnRotX ] ) == aTestB )
pWAcc->SetPixel( nY, nX, aB );
else
pWAcc->SetPixel( nY, nX, aW );
}
else
pWAcc->SetPixel( nY, nX, aB );
}
else
pWAcc->SetPixel( nY, nX, aW );
}
}
delete[] pMapLX;
delete[] pMapLY;
if( pMAcc )
aMsk.ReleaseAccess( pMAcc );
bRet = sal_True;
}
aOutMsk.ReleaseAccess( pWAcc );
}
if( bRet )
rOutBmpEx = BitmapEx( aOutBmp, aOutMsk );
}
if( !bRet )
rOutBmpEx = aOutBmp;
}
else
rOutBmpEx = aOutBmp;
delete[] pSinX;
delete[] pCosX;
delete[] pSinY;
delete[] pCosY;
return bRet;
}
// -----------------------------------------------------------------------------
void GraphicManager::ImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
{
GraphicAttr aAttr( rAttr );
if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
{
switch( aAttr.GetDrawMode() )
{
case( GRAPHICDRAWMODE_MONO ):
rBmpEx.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
break;
case( GRAPHICDRAWMODE_GREYS ):
rBmpEx.Convert( BMP_CONVERSION_8BIT_GREYS );
break;
case( GRAPHICDRAWMODE_WATERMARK ):
{
aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
}
break;
default:
break;
}
}
if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
{
rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
aAttr.GetGamma(), aAttr.IsInvert() );
}
if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
{
rBmpEx.Mirror( aAttr.GetMirrorFlags() );
}
if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
{
rBmpEx.Rotate( aAttr.GetRotation(), Color( COL_TRANSPARENT ) );
}
if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
{
AlphaMask aAlpha;
sal_uInt8 cTrans = aAttr.GetTransparency();
if( !rBmpEx.IsTransparent() )
aAlpha = AlphaMask( rBmpEx.GetSizePixel(), &cTrans );
else if( !rBmpEx.IsAlpha() )
{
aAlpha = rBmpEx.GetMask();
aAlpha.Replace( 0, cTrans );
}
else
{
aAlpha = rBmpEx.GetAlpha();
BitmapWriteAccess* pA = aAlpha.AcquireWriteAccess();
if( pA )
{
sal_uLong nTrans = cTrans, nNewTrans;
const long nWidth = pA->Width(), nHeight = pA->Height();
if( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
{
for( long nY = 0; nY < nHeight; nY++ )
{
Scanline pAScan = pA->GetScanline( nY );
for( long nX = 0; nX < nWidth; nX++ )
{
nNewTrans = nTrans + *pAScan;
*pAScan++ = (sal_uInt8) ( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans );
}
}
}
else
{
BitmapColor aAlphaValue( 0 );
for( long nY = 0; nY < nHeight; nY++ )
{
for( long nX = 0; nX < nWidth; nX++ )
{
nNewTrans = nTrans + pA->GetPixel( nY, nX ).GetIndex();
aAlphaValue.SetIndex( (sal_uInt8) ( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans ) );
pA->SetPixel( nY, nX, aAlphaValue );
}
}
}
aAlpha.ReleaseAccess( pA );
}
}
rBmpEx = BitmapEx( rBmpEx.GetBitmap(), aAlpha );
}
}
// -----------------------------------------------------------------------------
void GraphicManager::ImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
{
GraphicAttr aAttr( rAttr );
if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
{
switch( aAttr.GetDrawMode() )
{
case( GRAPHICDRAWMODE_MONO ):
rMtf.Convert( MTF_CONVERSION_1BIT_THRESHOLD );
break;
case( GRAPHICDRAWMODE_GREYS ):
rMtf.Convert( MTF_CONVERSION_8BIT_GREYS );
break;
case( GRAPHICDRAWMODE_WATERMARK ):
{
aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
}
break;
default:
break;
}
}
if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
{
rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
aAttr.GetGamma(), aAttr.IsInvert() );
}
if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
{
rMtf.Mirror( aAttr.GetMirrorFlags() );
}
if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
{
rMtf.Rotate( aAttr.GetRotation() );
}
if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
{
DBG_WARNING( "Missing implementation: Mtf-Transparency" );
}
}
// -----------------------------------------------------------------------------
void GraphicManager::ImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags )
{
GraphicAttr aAttr( rAttr );
if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() )
{
switch( aAttr.GetDrawMode() )
{
case( GRAPHICDRAWMODE_MONO ):
rAnimation.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
break;
case( GRAPHICDRAWMODE_GREYS ):
rAnimation.Convert( BMP_CONVERSION_8BIT_GREYS );
break;
case( GRAPHICDRAWMODE_WATERMARK ):
{
aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
}
break;
default:
break;
}
}
if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() )
{
rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
aAttr.GetGamma(), aAttr.IsInvert() );
}
if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() )
{
rAnimation.Mirror( aAttr.GetMirrorFlags() );
}
if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() )
{
DBG_ERROR( "Missing implementation: Animation-Rotation" );
}
if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() )
{
DBG_ERROR( "Missing implementation: Animation-Transparency" );
}
}
// -----------------------------------------------------------------------------
void GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt, const Size& rSz,
const GDIMetaFile& rMtf, const GraphicAttr& rAttr )
{
sal_uInt16 nRot10 = rAttr.GetRotation() % 3600;
Point aOutPt( rPt );
Size aOutSz( rSz );
if( nRot10 )
{
Polygon aPoly( Rectangle( aOutPt, aOutSz ) );
aPoly.Rotate( aOutPt, nRot10 );
const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
aOutPt = aRotBoundRect.TopLeft();
aOutSz = aRotBoundRect.GetSize();
}
pOut->Push( PUSH_CLIPREGION );
pOut->IntersectClipRegion( Rectangle( aOutPt, aOutSz ) );
( (GDIMetaFile&) rMtf ).WindStart();
( (GDIMetaFile&) rMtf ).Play( pOut, aOutPt, aOutSz );
( (GDIMetaFile&) rMtf ).WindStart();
pOut->Pop();
}
// -----------------------------------------------------------------------------
struct ImplTileInfo
{
ImplTileInfo() : aTileTopLeft(), aNextTileTopLeft(), aTileSizePixel(), nTilesEmptyX(0), nTilesEmptyY(0) {}
Point aTileTopLeft; // top, left position of the rendered tile
Point aNextTileTopLeft; // top, left position for next recursion
// level's tile
Size aTileSizePixel; // size of the generated tile (might
// differ from
// aNextTileTopLeft-aTileTopLeft, because
// this is nExponent*prevTileSize. The
// generated tile is always nExponent
// times the previous tile, such that it
// can be used in the next stage. The
// required area coverage is often
// less. The extraneous area covered is
// later overwritten by the next stage)
int nTilesEmptyX; // number of original tiles empty right of
// this tile. This counts from
// aNextTileTopLeft, i.e. the additional
// area covered by aTileSizePixel is not
// considered here. This is for
// unification purposes, as the iterative
// calculation of the next level's empty
// tiles has to be based on this value.
int nTilesEmptyY; // as above, for Y
};
bool GraphicObject::ImplRenderTempTile( VirtualDevice& rVDev, int nExponent,
int nNumTilesX, int nNumTilesY,
const Size& rTileSizePixel,
const GraphicAttr* pAttr, sal_uLong nFlags )
{
if( nExponent <= 1 )
return false;
// determine MSB factor
int nMSBFactor( 1 );
while( nNumTilesX / nMSBFactor != 0 ||
nNumTilesY / nMSBFactor != 0 )
{
nMSBFactor *= nExponent;
}
// one less
nMSBFactor /= nExponent;
ImplTileInfo aTileInfo;
// #105229# Switch off mapping (converting to logic and back to
// pixel might cause roundoff errors)
sal_Bool bOldMap( rVDev.IsMapModeEnabled() );
rVDev.EnableMapMode( sal_False );
bool bRet( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor, nNumTilesX, nNumTilesY,
nNumTilesX, nNumTilesY, rTileSizePixel, pAttr, nFlags, aTileInfo ) );
rVDev.EnableMapMode( bOldMap );
return bRet;
}
// -----------------------------------------------------------------------------
// define for debug drawings
//#define DBG_TEST
// see header comment. this works similar to base conversion of a
// number, i.e. if the exponent is 10, then the number for every tile
// size is given by the decimal place of the corresponding decimal
// representation.
bool GraphicObject::ImplRenderTileRecursive( VirtualDevice& rVDev, int nExponent, int nMSBFactor,
int nNumOrigTilesX, int nNumOrigTilesY,
int nRemainderTilesX, int nRemainderTilesY,
const Size& rTileSizePixel, const GraphicAttr* pAttr,
sal_uLong nFlags, ImplTileInfo& rTileInfo )
{
// gets loaded with our tile bitmap
GraphicObject aTmpGraphic;
// stores a flag that renders the zero'th tile position
// (i.e. (0,0)+rCurrPos) only if we're at the bottom of the
// recursion stack. All other position already have that tile
// rendered, because the lower levels painted their generated tile
// there.
bool bNoFirstTileDraw( false );
// what's left when we're done with our tile size
const int nNewRemainderX( nRemainderTilesX % nMSBFactor );
const int nNewRemainderY( nRemainderTilesY % nMSBFactor );
// gets filled out from the recursive call with info of what's
// been generated
ImplTileInfo aTileInfo;
// current output position while drawing
Point aCurrPos;
int nX, nY;
// check for recursion's end condition: LSB place reached?
if( nMSBFactor == 1 )
{
aTmpGraphic = *this;
// set initial tile size -> orig size
aTileInfo.aTileSizePixel = rTileSizePixel;
aTileInfo.nTilesEmptyX = nNumOrigTilesX;
aTileInfo.nTilesEmptyY = nNumOrigTilesY;
}
else if( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor/nExponent,
nNumOrigTilesX, nNumOrigTilesY,
nNewRemainderX, nNewRemainderY,
rTileSizePixel, pAttr, nFlags, aTileInfo ) )
{
// extract generated tile -> see comment on the first loop below
BitmapEx aTileBitmap( rVDev.GetBitmap( aTileInfo.aTileTopLeft, aTileInfo.aTileSizePixel ) );
aTmpGraphic = GraphicObject( aTileBitmap );
// fill stripes left over from upstream levels:
//
// x0000
// 0
// 0
// 0
// 0
//
// where x denotes the place filled by our recursive predecessors
// check whether we have to fill stripes here. Although not
// obvious, there is one case where we can skip this step: if
// the previous recursion level (the one who filled our
// aTileInfo) had zero area to fill, then there are no white
// stripes left, naturally. This happens if the digit
// associated to that level has a zero, and can be checked via
// aTileTopLeft==aNextTileTopLeft.
if( aTileInfo.aTileTopLeft != aTileInfo.aNextTileTopLeft )
{
// now fill one row from aTileInfo.aNextTileTopLeft.X() all
// the way to the right
aCurrPos.X() = aTileInfo.aNextTileTopLeft.X();
aCurrPos.Y() = aTileInfo.aTileTopLeft.Y();
for( nX=0; nX < aTileInfo.nTilesEmptyX; nX += nMSBFactor )
{
if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
return false;
aCurrPos.X() += aTileInfo.aTileSizePixel.Width();
}
#ifdef DBG_TEST
// rVDev.SetFillColor( COL_WHITE );
rVDev.SetFillColor();
rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
rVDev.DrawEllipse( Rectangle(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y(),
aTileInfo.aNextTileTopLeft.X() - 1 + (aTileInfo.nTilesEmptyX/nMSBFactor)*aTileInfo.aTileSizePixel.Width(),
aTileInfo.aTileTopLeft.Y() + aTileInfo.aTileSizePixel.Height() - 1) );
#endif
// now fill one column from aTileInfo.aNextTileTopLeft.Y() all
// the way to the bottom
aCurrPos.X() = aTileInfo.aTileTopLeft.X();
aCurrPos.Y() = aTileInfo.aNextTileTopLeft.Y();
for( nY=0; nY < aTileInfo.nTilesEmptyY; nY += nMSBFactor )
{
if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
return false;
aCurrPos.Y() += aTileInfo.aTileSizePixel.Height();
}
#ifdef DBG_TEST
rVDev.DrawEllipse( Rectangle(aTileInfo.aTileTopLeft.X(), aTileInfo.aNextTileTopLeft.Y(),
aTileInfo.aTileTopLeft.X() + aTileInfo.aTileSizePixel.Width() - 1,
aTileInfo.aNextTileTopLeft.Y() - 1 + (aTileInfo.nTilesEmptyY/nMSBFactor)*aTileInfo.aTileSizePixel.Height()) );
#endif
}
else
{
// Thought that aTileInfo.aNextTileTopLeft tile has always
// been drawn already, but that's wrong: typically,
// _parts_ of that tile have been drawn, since the
// previous level generated the tile there. But when
// aTileInfo.aNextTileTopLeft!=aTileInfo.aTileTopLeft, the
// difference between these two values is missing in the
// lower right corner of this first tile. So, can do that
// only here.
bNoFirstTileDraw = true;
}
}
else
{
return false;
}
// calc number of original tiles in our drawing area without
// remainder
nRemainderTilesX -= nNewRemainderX;
nRemainderTilesY -= nNewRemainderY;
// fill tile info for calling method
rTileInfo.aTileTopLeft = aTileInfo.aNextTileTopLeft;
rTileInfo.aNextTileTopLeft = Point( rTileInfo.aTileTopLeft.X() + rTileSizePixel.Width()*nRemainderTilesX,
rTileInfo.aTileTopLeft.Y() + rTileSizePixel.Height()*nRemainderTilesY );
rTileInfo.aTileSizePixel = Size( rTileSizePixel.Width()*nMSBFactor*nExponent,
rTileSizePixel.Height()*nMSBFactor*nExponent );
rTileInfo.nTilesEmptyX = aTileInfo.nTilesEmptyX - nRemainderTilesX;
rTileInfo.nTilesEmptyY = aTileInfo.nTilesEmptyY - nRemainderTilesY;
// init output position
aCurrPos = aTileInfo.aNextTileTopLeft;
// fill our drawing area. Fill possibly more, to create the next
// bigger tile size -> see bitmap extraction above. This does no
// harm, since everything right or below our actual area is
// overdrawn by our caller. Just in case we're in the last level,
// we don't draw beyond the right or bottom border.
for( nY=0; nY < aTileInfo.nTilesEmptyY && nY < nExponent*nMSBFactor; nY += nMSBFactor )
{
aCurrPos.X() = aTileInfo.aNextTileTopLeft.X();
for( nX=0; nX < aTileInfo.nTilesEmptyX && nX < nExponent*nMSBFactor; nX += nMSBFactor )
{
if( bNoFirstTileDraw )
bNoFirstTileDraw = false; // don't draw first tile position
else if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) )
return false;
aCurrPos.X() += aTileInfo.aTileSizePixel.Width();
}
aCurrPos.Y() += aTileInfo.aTileSizePixel.Height();
}
#ifdef DBG_TEST
// rVDev.SetFillColor( COL_WHITE );
rVDev.SetFillColor();
rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
rVDev.DrawRect( Rectangle((rTileInfo.aTileTopLeft.X())*rTileSizePixel.Width(),
(rTileInfo.aTileTopLeft.Y())*rTileSizePixel.Height(),
(rTileInfo.aNextTileTopLeft.X())*rTileSizePixel.Width()-1,
(rTileInfo.aNextTileTopLeft.Y())*rTileSizePixel.Height()-1) );
#endif
return true;
}
// -----------------------------------------------------------------------------
bool GraphicObject::ImplDrawTiled( OutputDevice* pOut, const Rectangle& rArea, const Size& rSizePixel,
const Size& rOffset, const GraphicAttr* pAttr, sal_uLong nFlags, int nTileCacheSize1D )
{
// how many tiles to generate per recursion step
enum{ SubdivisionExponent=2 };
const MapMode aOutMapMode( pOut->GetMapMode() );
const MapMode aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() );
bool bRet( false );
// #i42643# Casting to Int64, to avoid integer overflow for
// huge-DPI output devices
if( GetGraphic().GetType() == GRAPHIC_BITMAP &&
static_cast<sal_Int64>(rSizePixel.Width()) * rSizePixel.Height() <
static_cast<sal_Int64>(nTileCacheSize1D)*nTileCacheSize1D )
{
// First combine very small bitmaps into a larger tile
// ===================================================
VirtualDevice aVDev;
const int nNumTilesInCacheX( (nTileCacheSize1D + rSizePixel.Width()-1) / rSizePixel.Width() );
const int nNumTilesInCacheY( (nTileCacheSize1D + rSizePixel.Height()-1) / rSizePixel.Height() );
aVDev.SetOutputSizePixel( Size( nNumTilesInCacheX*rSizePixel.Width(),
nNumTilesInCacheY*rSizePixel.Height() ) );
aVDev.SetMapMode( aMapMode );
// draw bitmap content
if( ImplRenderTempTile( aVDev, SubdivisionExponent, nNumTilesInCacheX,
nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) )
{
BitmapEx aTileBitmap( aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ) );
// draw alpha content, if any
if( IsTransparent() )
{
GraphicObject aAlphaGraphic;
if( GetGraphic().IsAlpha() )
aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetAlpha().GetBitmap() );
else
aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetMask() );
if( aAlphaGraphic.ImplRenderTempTile( aVDev, SubdivisionExponent, nNumTilesInCacheX,
nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) )
{
// Combine bitmap and alpha/mask
if( GetGraphic().IsAlpha() )
aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(),
AlphaMask( aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ) ) );
else
aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(),
aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ).CreateMask( Color(COL_WHITE) ) );
}
}
// paint generated tile
GraphicObject aTmpGraphic( aTileBitmap );
bRet = aTmpGraphic.ImplDrawTiled( pOut, rArea,
aTileBitmap.GetSizePixel(),
rOffset, pAttr, nFlags, nTileCacheSize1D );
}
}
else
{
const Size aOutOffset( pOut->LogicToPixel( rOffset, aOutMapMode ) );
const Rectangle aOutArea( pOut->LogicToPixel( rArea, aOutMapMode ) );
// number of invisible (because out-of-area) tiles
int nInvisibleTilesX;
int nInvisibleTilesY;
// round towards -infty for negative offset
if( aOutOffset.Width() < 0 )
nInvisibleTilesX = (aOutOffset.Width() - rSizePixel.Width() + 1) / rSizePixel.Width();
else
nInvisibleTilesX = aOutOffset.Width() / rSizePixel.Width();
// round towards -infty for negative offset
if( aOutOffset.Height() < 0 )
nInvisibleTilesY = (aOutOffset.Height() - rSizePixel.Height() + 1) / rSizePixel.Height();
else
nInvisibleTilesY = aOutOffset.Height() / rSizePixel.Height();
// origin from where to 'virtually' start drawing in pixel
const Point aOutOrigin( pOut->LogicToPixel( Point( rArea.Left() - rOffset.Width(),
rArea.Top() - rOffset.Height() ) ) );
// position in pixel from where to really start output
const Point aOutStart( aOutOrigin.X() + nInvisibleTilesX*rSizePixel.Width(),
aOutOrigin.Y() + nInvisibleTilesY*rSizePixel.Height() );
pOut->Push( PUSH_CLIPREGION );
pOut->IntersectClipRegion( rArea );
// Paint all tiles
// ===============
bRet = ImplDrawTiled( *pOut, aOutStart,
(aOutArea.GetWidth() + aOutArea.Left() - aOutStart.X() + rSizePixel.Width() - 1) / rSizePixel.Width(),
(aOutArea.GetHeight() + aOutArea.Top() - aOutStart.Y() + rSizePixel.Height() - 1) / rSizePixel.Height(),
rSizePixel, pAttr, nFlags );
pOut->Pop();
}
return bRet;
}
// -----------------------------------------------------------------------------
bool GraphicObject::ImplDrawTiled( OutputDevice& rOut, const Point& rPosPixel,
int nNumTilesX, int nNumTilesY,
const Size& rTileSizePixel, const GraphicAttr* pAttr, sal_uLong nFlags )
{
Point aCurrPos( rPosPixel );
Size aTileSizeLogic( rOut.PixelToLogic( rTileSizePixel ) );
int nX, nY;
// #107607# Use logical coordinates for metafile playing, too
bool bDrawInPixel( rOut.GetConnectMetaFile() == NULL && GRAPHIC_BITMAP == GetType() );
sal_Bool bRet( sal_False );
// #105229# Switch off mapping (converting to logic and back to
// pixel might cause roundoff errors)
sal_Bool bOldMap( rOut.IsMapModeEnabled() );
if( bDrawInPixel )
rOut.EnableMapMode( sal_False );
for( nY=0; nY < nNumTilesY; ++nY )
{
aCurrPos.X() = rPosPixel.X();
for( nX=0; nX < nNumTilesX; ++nX )
{
// #105229# work with pixel coordinates here, mapping is disabled!
// #104004# don't disable mapping for metafile recordings
// #108412# don't quit the loop if one draw fails
// update return value. This method should return true, if
// at least one of the looped Draws succeeded.
bRet |= Draw( &rOut,
bDrawInPixel ? aCurrPos : rOut.PixelToLogic( aCurrPos ),
bDrawInPixel ? rTileSizePixel : aTileSizeLogic,
pAttr, nFlags );
aCurrPos.X() += rTileSizePixel.Width();
}
aCurrPos.Y() += rTileSizePixel.Height();
}
if( bDrawInPixel )
rOut.EnableMapMode( bOldMap );
return bRet;
}
// -----------------------------------------------------------------------------
void GraphicObject::ImplTransformBitmap( BitmapEx& rBmpEx,
const GraphicAttr& rAttr,
const Size& rCropLeftTop,
const Size& rCropRightBottom,
const Rectangle& rCropRect,
const Size& rDstSize,
sal_Bool bEnlarge ) const
{
// #107947# Extracted from svdograf.cxx
// #104115# Crop the bitmap
if( rAttr.IsCropped() )
{
rBmpEx.Crop( rCropRect );
// #104115# Negative crop sizes mean: enlarge bitmap and pad
if( bEnlarge && (
rCropLeftTop.Width() < 0 ||
rCropLeftTop.Height() < 0 ||
rCropRightBottom.Width() < 0 ||
rCropRightBottom.Height() < 0 ) )
{
Size aBmpSize( rBmpEx.GetSizePixel() );
sal_Int32 nPadLeft( rCropLeftTop.Width() < 0 ? -rCropLeftTop.Width() : 0 );
sal_Int32 nPadTop( rCropLeftTop.Height() < 0 ? -rCropLeftTop.Height() : 0 );
sal_Int32 nPadTotalWidth( aBmpSize.Width() + nPadLeft + (rCropRightBottom.Width() < 0 ? -rCropRightBottom.Width() : 0) );
sal_Int32 nPadTotalHeight( aBmpSize.Height() + nPadTop + (rCropRightBottom.Height() < 0 ? -rCropRightBottom.Height() : 0) );
BitmapEx aBmpEx2;
if( rBmpEx.IsTransparent() )
{
if( rBmpEx.IsAlpha() )
aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetAlpha() );
else
aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetMask() );
}
else
{
// #104115# Generate mask bitmap and init to zero
Bitmap aMask( aBmpSize, 1 );
aMask.Erase( Color(0,0,0) );
// #104115# Always generate transparent bitmap, we need the border transparent
aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), aMask );
// #104115# Add opaque mask to source bitmap, otherwise the destination remains transparent
rBmpEx = aBmpEx2;
}
aBmpEx2.SetSizePixel( Size(nPadTotalWidth, nPadTotalHeight) );
aBmpEx2.Erase( Color(0xFF,0,0,0) );
aBmpEx2.CopyPixel( Rectangle( Point(nPadLeft, nPadTop), aBmpSize ), Rectangle( Point(0, 0), aBmpSize ), &rBmpEx );
rBmpEx = aBmpEx2;
}
}
const Size aSizePixel( rBmpEx.GetSizePixel() );
if( rAttr.GetRotation() != 0 && !IsAnimated() )
{
if( aSizePixel.Width() && aSizePixel.Height() && rDstSize.Width() && rDstSize.Height() )
{
double fSrcWH = (double) aSizePixel.Width() / aSizePixel.Height();
double fDstWH = (double) rDstSize.Width() / rDstSize.Height();
double fScaleX = 1.0, fScaleY = 1.0;
// always choose scaling to shrink bitmap
if( fSrcWH < fDstWH )
fScaleY = aSizePixel.Width() / ( fDstWH * aSizePixel.Height() );
else
fScaleX = fDstWH * aSizePixel.Height() / aSizePixel.Width();
rBmpEx.Scale( fScaleX, fScaleY );
}
}
}