504 lines
21 KiB
C++
504 lines
21 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* 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 .
|
|
*/
|
|
|
|
#include <sal/config.h>
|
|
|
|
#include <tools/gen.hxx>
|
|
#include <vcl/outdev.hxx>
|
|
#include <vcl/alpha.hxx>
|
|
#include <vcl/virdev.hxx>
|
|
#include <vcl/GraphicObject.hxx>
|
|
#include <memory>
|
|
|
|
struct ImplTileInfo
|
|
{
|
|
ImplTileInfo() : 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 nNumTilesX, int nNumTilesY,
|
|
const Size& rTileSizePixel,
|
|
const GraphicAttr* pAttr )
|
|
{
|
|
// how many tiles to generate per recursion step
|
|
const int nExponent = 2;
|
|
|
|
// determine MSB factor
|
|
int nMSBFactor( 1 );
|
|
while( nNumTilesX / nMSBFactor != 0 ||
|
|
nNumTilesY / nMSBFactor != 0 )
|
|
{
|
|
nMSBFactor *= nExponent;
|
|
}
|
|
|
|
// one less
|
|
if(nMSBFactor > 1)
|
|
{
|
|
nMSBFactor /= nExponent;
|
|
}
|
|
ImplTileInfo aTileInfo;
|
|
|
|
// #105229# Switch off mapping (converting to logic and back to
|
|
// pixel might cause roundoff errors)
|
|
bool bOldMap( rVDev.IsMapModeEnabled() );
|
|
rVDev.EnableMapMode( false );
|
|
|
|
bool bRet( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor, nNumTilesX, nNumTilesY,
|
|
nNumTilesX, nNumTilesY, rTileSizePixel, pAttr, 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,
|
|
ImplTileInfo& rTileInfo )
|
|
{
|
|
// gets loaded with our tile bitmap
|
|
std::unique_ptr<GraphicObject> xTmpGraphic;
|
|
GraphicObject* pTileGraphic;
|
|
|
|
// 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;
|
|
|
|
// check for recursion's end condition: LSB place reached?
|
|
if( nMSBFactor == 1 )
|
|
{
|
|
pTileGraphic = 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, aTileInfo ) )
|
|
{
|
|
// extract generated tile -> see comment on the first loop below
|
|
BitmapEx aTileBitmap( rVDev.GetBitmap( aTileInfo.aTileTopLeft, aTileInfo.aTileSizePixel ) );
|
|
|
|
xTmpGraphic.reset(new GraphicObject(std::move(aTileBitmap)));
|
|
pTileGraphic = xTmpGraphic.get();
|
|
|
|
// 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
|
|
// current output position while drawing
|
|
Point aCurrPos(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y());
|
|
for (int nX=0; nX < aTileInfo.nTilesEmptyX; nX += nMSBFactor)
|
|
{
|
|
if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr))
|
|
return false;
|
|
|
|
aCurrPos.AdjustX(aTileInfo.aTileSizePixel.Width() );
|
|
}
|
|
|
|
#ifdef DBG_TEST
|
|
// rVDev.SetFillCOL_WHITE );
|
|
rVDev.SetFillColor();
|
|
rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
|
|
rVDev.DrawEllipse( tools::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.setX( aTileInfo.aTileTopLeft.X() );
|
|
aCurrPos.setY( aTileInfo.aNextTileTopLeft.Y() );
|
|
for (int nY=0; nY < aTileInfo.nTilesEmptyY; nY += nMSBFactor)
|
|
{
|
|
if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr))
|
|
return false;
|
|
|
|
aCurrPos.AdjustY(aTileInfo.aTileSizePixel.Height() );
|
|
}
|
|
|
|
#ifdef DBG_TEST
|
|
rVDev.DrawEllipse( tools::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
|
|
Point 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 (int nY=0; nY < aTileInfo.nTilesEmptyY && nY < nExponent*nMSBFactor; nY += nMSBFactor)
|
|
{
|
|
aCurrPos.setX( aTileInfo.aNextTileTopLeft.X() );
|
|
|
|
for (int nX=0; nX < aTileInfo.nTilesEmptyX && nX < nExponent*nMSBFactor; nX += nMSBFactor)
|
|
{
|
|
if( bNoFirstTileDraw )
|
|
bNoFirstTileDraw = false; // don't draw first tile position
|
|
else if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr))
|
|
return false;
|
|
|
|
aCurrPos.AdjustX(aTileInfo.aTileSizePixel.Width() );
|
|
}
|
|
|
|
aCurrPos.AdjustY(aTileInfo.aTileSizePixel.Height() );
|
|
}
|
|
|
|
#ifdef DBG_TEST
|
|
// rVDev.SetFillCOL_WHITE );
|
|
rVDev.SetFillColor();
|
|
rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) );
|
|
rVDev.DrawRect( tools::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& rOut, const tools::Rectangle& rArea, const Size& rSizePixel,
|
|
const Size& rOffset, const GraphicAttr* pAttr, int nTileCacheSize1D)
|
|
{
|
|
const MapMode aOutMapMode(rOut.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() == GraphicType::Bitmap &&
|
|
static_cast<sal_Int64>(rSizePixel.Width()) * rSizePixel.Height() <
|
|
static_cast<sal_Int64>(nTileCacheSize1D)*nTileCacheSize1D )
|
|
{
|
|
// First combine very small bitmaps into a larger tile
|
|
|
|
|
|
ScopedVclPtrInstance< 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, nNumTilesInCacheX,
|
|
nNumTilesInCacheY, rSizePixel, pAttr ) )
|
|
{
|
|
BitmapEx aTileBitmap( aVDev->GetBitmap( Point(0,0), aVDev->GetOutputSize() ) );
|
|
|
|
// draw alpha content, if any
|
|
if( IsTransparent() )
|
|
{
|
|
GraphicObject aAlphaGraphic;
|
|
|
|
if( GetGraphic().IsAlpha() )
|
|
aAlphaGraphic.SetGraphic(BitmapEx(GetGraphic().GetBitmapEx().GetAlphaMask().GetBitmap()));
|
|
else
|
|
aAlphaGraphic.SetGraphic(BitmapEx(Bitmap()));
|
|
|
|
if( aAlphaGraphic.ImplRenderTempTile( *aVDev, nNumTilesInCacheX,
|
|
nNumTilesInCacheY, rSizePixel, pAttr ) )
|
|
{
|
|
// 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() ).CreateAlphaMask( COL_WHITE ) );
|
|
}
|
|
}
|
|
|
|
// paint generated tile
|
|
GraphicObject aTmpGraphic( aTileBitmap );
|
|
bRet = aTmpGraphic.ImplDrawTiled(rOut, rArea,
|
|
aTileBitmap.GetSizePixel(),
|
|
rOffset, pAttr, nTileCacheSize1D);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const Size aOutOffset( rOut.LogicToPixel( rOffset, aOutMapMode ) );
|
|
const tools::Rectangle aOutArea( rOut.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( rOut.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() );
|
|
|
|
rOut.Push( vcl::PushFlags::CLIPREGION );
|
|
rOut.IntersectClipRegion( rArea );
|
|
|
|
// Paint all tiles
|
|
auto nOutAreaWidth = aOutArea.GetWidth();
|
|
auto nOutAreaHeight = aOutArea.GetHeight();
|
|
assert(nOutAreaWidth >= 0 && nOutAreaHeight >= 0 && "coverity 2023.12.2");
|
|
bRet = ImplDrawTiled(rOut, aOutStart,
|
|
(nOutAreaWidth + aOutArea.Left() - aOutStart.X() + rSizePixel.Width() - 1) / rSizePixel.Width(),
|
|
(nOutAreaHeight + aOutArea.Top() - aOutStart.Y() + rSizePixel.Height() - 1) / rSizePixel.Height(),
|
|
rSizePixel, pAttr);
|
|
|
|
rOut.Pop();
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
bool GraphicObject::ImplDrawTiled( OutputDevice& rOut, const Point& rPosPixel,
|
|
int nNumTilesX, int nNumTilesY,
|
|
const Size& rTileSizePixel, const GraphicAttr* pAttr ) const
|
|
{
|
|
Point aCurrPos( rPosPixel );
|
|
Size aTileSizeLogic( rOut.PixelToLogic( rTileSizePixel ) );
|
|
int nX, nY;
|
|
|
|
// #107607# Use logical coordinates for metafile playing, too
|
|
bool bDrawInPixel( rOut.GetConnectMetaFile() == nullptr && GraphicType::Bitmap == GetType() );
|
|
bool bRet = false;
|
|
|
|
// #105229# Switch off mapping (converting to logic and back to
|
|
// pixel might cause roundoff errors)
|
|
bool bOldMap( rOut.IsMapModeEnabled() );
|
|
|
|
if( bDrawInPixel )
|
|
rOut.EnableMapMode( false );
|
|
|
|
for( nY=0; nY < nNumTilesY; ++nY )
|
|
{
|
|
aCurrPos.setX( 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);
|
|
|
|
aCurrPos.AdjustX(rTileSizePixel.Width() );
|
|
}
|
|
|
|
aCurrPos.AdjustY(rTileSizePixel.Height() );
|
|
}
|
|
|
|
if( bDrawInPixel )
|
|
rOut.EnableMapMode( bOldMap );
|
|
|
|
return bRet;
|
|
}
|
|
|
|
void GraphicObject::ImplTransformBitmap( BitmapEx& rBmpEx,
|
|
const GraphicAttr& rAttr,
|
|
const Size& rCropLeftTop,
|
|
const Size& rCropRightBottom,
|
|
const tools::Rectangle& rCropRect,
|
|
const Size& rDstSize,
|
|
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.IsAlpha() )
|
|
{
|
|
aBmpEx2 = rBmpEx;
|
|
}
|
|
else
|
|
{
|
|
// #104115# Generate mask bitmap and init to zero
|
|
AlphaMask aMask(aBmpSize);
|
|
aMask.Erase(255);
|
|
|
|
// #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.Scale(Size(nPadTotalWidth, nPadTotalHeight));
|
|
aBmpEx2.Erase( Color(ColorAlpha,0,0,0,0) );
|
|
aBmpEx2.CopyPixel( tools::Rectangle( Point(nPadLeft, nPadTop), aBmpSize ), tools::Rectangle( Point(0, 0), aBmpSize ), rBmpEx );
|
|
rBmpEx = aBmpEx2;
|
|
}
|
|
}
|
|
|
|
const Size aSizePixel( rBmpEx.GetSizePixel() );
|
|
|
|
if( rAttr.GetRotation() == 0_deg10 || IsAnimated() )
|
|
return;
|
|
|
|
if( !(aSizePixel.Width() && aSizePixel.Height() && rDstSize.Width() && rDstSize.Height()) )
|
|
return;
|
|
|
|
double fSrcWH = static_cast<double>(aSizePixel.Width()) / aSizePixel.Height();
|
|
double fDstWH = static_cast<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 );
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|